# Table of Contents - [Farcaster Docs / Farcaster Docs](#farcaster-docs-farcaster-docs) - [Farcaster Client API Reference / Farcaster Docs](#farcaster-client-api-reference-farcaster-docs) - [Overview / Farcaster Docs](#overview-farcaster-docs) - [Getting Started / Farcaster Docs](#getting-started-farcaster-docs) - [AuthKit / Farcaster Docs](#authkit-farcaster-docs) - [Farcaster Docs / Farcaster Docs](#farcaster-docs-farcaster-docs) - [ID Gateway / Farcaster Docs](#id-gateway-farcaster-docs) - [Accounts / Farcaster Docs](#accounts-farcaster-docs) - [Usernames / Farcaster Docs](#usernames-farcaster-docs) - [Id Registry / Farcaster Docs](#id-registry-farcaster-docs) - [Resources / Farcaster Docs](#resources-farcaster-docs) - [Messages / Farcaster Docs](#messages-farcaster-docs) - [SignInButton / Farcaster Docs](#signinbutton-farcaster-docs) - [Channels / Farcaster Docs](#channels-farcaster-docs) - [Key Registry / Farcaster Docs](#key-registry-farcaster-docs) - [AuthKitProvider / Farcaster Docs](#authkitprovider-farcaster-docs) - [Create an account / Farcaster Docs](#create-an-account-farcaster-docs) - [Key Gateway / Farcaster Docs](#key-gateway-farcaster-docs) - [Create an account key / Farcaster Docs](#create-an-account-key-farcaster-docs) - [Storage Registry / Farcaster Docs](#storage-registry-farcaster-docs) - [Apps / Farcaster Docs](#apps-farcaster-docs) - [useSignIn / Farcaster Docs](#usesignin-farcaster-docs) - [Architecture / Farcaster Docs](#architecture-farcaster-docs) - [useSignInMessage / Farcaster Docs](#usesigninmessage-farcaster-docs) - [Find account by username / Farcaster Docs](#find-account-by-username-farcaster-docs) - [Frames v2 have been rebranded to Mini Apps / Farcaster Docs](#frames-v2-have-been-rebranded-to-mini-apps-farcaster-docs) - [Direct Casts / Farcaster Docs](#direct-casts-farcaster-docs) - [Farcaster Client Embeds Reference / Farcaster Docs](#farcaster-client-embeds-reference-farcaster-docs) - [Bundler / Farcaster Docs](#bundler-farcaster-docs) - [How to have your video displayed on the Farcaster client / Farcaster Docs](#how-to-have-your-video-displayed-on-the-farcaster-client-farcaster-docs) - [Signer Requests / Farcaster Docs](#signer-requests-farcaster-docs) - [Sign In with Farcaster / Farcaster Docs](#sign-in-with-farcaster-farcaster-docs) - [Signed Key Request Validator / Farcaster Docs](#signed-key-request-validator-farcaster-docs) - [Change Farcaster name / Farcaster Docs](#change-farcaster-name-farcaster-docs) - [Installation / Farcaster Docs](#installation-farcaster-docs) - [Overview / Farcaster Docs](#overview-farcaster-docs) - [Schema / Farcaster Docs](#schema-farcaster-docs) - [Contracts / Farcaster Docs](#contracts-farcaster-docs) - [ENS Names / Farcaster Docs](#ens-names-farcaster-docs) - [useProfile / Farcaster Docs](#useprofile-farcaster-docs) - [Auth client / Farcaster Docs](#auth-client-farcaster-docs) - [Register ENS Name / Farcaster Docs](#register-ens-name-farcaster-docs) - [Change custody address / Farcaster Docs](#change-custody-address-farcaster-docs) - [Deployments / Farcaster Docs](#deployments-farcaster-docs) - [Tier Registry / Farcaster Docs](#tier-registry-farcaster-docs) - [Overview / Farcaster Docs](#overview-farcaster-docs) - [FAQ / Farcaster Docs](#faq-farcaster-docs) - [Contributing / Farcaster Docs](#contributing-farcaster-docs) - [FName Registry Server API Reference / Farcaster Docs](#fname-registry-server-api-reference-farcaster-docs) - [Examples / Farcaster Docs](#examples-farcaster-docs) - [Governance / Farcaster Docs](#governance-farcaster-docs) - [FIPs / Farcaster Docs](#fips-farcaster-docs) - [App Client / Farcaster Docs](#app-client-farcaster-docs) - [createChannel / Farcaster Docs](#createchannel-farcaster-docs) - [status / Farcaster Docs](#status-farcaster-docs) - [watchStatus / Farcaster Docs](#watchstatus-farcaster-docs) - [verifySignInMessage / Farcaster Docs](#verifysigninmessage-farcaster-docs) - [Wallet Client / Farcaster Docs](#wallet-client-farcaster-docs) - [parseSignInURI / Farcaster Docs](#parsesigninuri-farcaster-docs) - [buildSignInMessage / Farcaster Docs](#buildsigninmessage-farcaster-docs) - [authenticate / Farcaster Docs](#authenticate-farcaster-docs) - [Change recovery address / Farcaster Docs](#change-recovery-address-farcaster-docs) - [Get account messages / Farcaster Docs](#get-account-messages-farcaster-docs) - [Get account profile / Farcaster Docs](#get-account-profile-farcaster-docs) - [Fetch casts from a channel / Farcaster Docs](#fetch-casts-from-a-channel-farcaster-docs) - [Create casts / Farcaster Docs](#create-casts-farcaster-docs) - [Create messages / Farcaster Docs](#create-messages-farcaster-docs) - [Create an address verification / Farcaster Docs](#create-an-address-verification-farcaster-docs) - [Submit data to the hub / Farcaster Docs](#submit-data-to-the-hub-farcaster-docs) - [Neynar / Farcaster Docs](#neynar-farcaster-docs) - [Counting signups by day / Farcaster Docs](#counting-signups-by-day-farcaster-docs) - [Decode key metadata / Farcaster Docs](#decode-key-metadata-farcaster-docs) - [Hello World / Farcaster Docs](#hello-world-farcaster-docs) - [Farcaster Docs / Farcaster Docs](#farcaster-docs-farcaster-docs) --- # Farcaster Docs / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/#VPContent) Farcaster Docs ============== Permissionlessly build and distribute social apps [Build a mini app](https://miniapps.farcaster.xyz/docs/getting-started) [Explore Sign In with Farcaster](https://docs.farcaster.xyz/developers/siwf/) [Learn about the protocol](https://docs.farcaster.xyz/learn/) ### Build a Mini App [​](https://docs.farcaster.xyz/#build-a-mini-app) Learn how to build Mini Apps (previously known as Frames v2) that run inside a Farcaster feed. * [Introduction to Mini Apps](https://miniapps.farcaster.xyz/) \- Understand what a mini app is and how it works. * [Build your first Mini App](https://miniapps.farcaster.xyz/docs/getting-started) \- Make mini apps that run inside Farcaster. ### Explore Sign In with Farcaster [​](https://docs.farcaster.xyz/#explore-sign-in-with-farcaster) Allow users to Sign In with Farcaster and leverage social data in your app. * [Introduction](https://docs.farcaster.xyz/developers/siwf/) - Learn about Sign In with Farcaster. * [Add SIWF using AuthKit](https://docs.farcaster.xyz/auth-kit/installation) - a React toolkit to add SIWF to your app. * [Examples](https://docs.farcaster.xyz/auth-kit/examples) - see Sign In with Farcaster in action. ### Analyze Farcaster data [​](https://docs.farcaster.xyz/#analyze-farcaster-data) Sync the Farcaster network to a local machine so you can run queries on the data. * [Write your first snapchain query](https://snapchain.farcaster.xyz/getting-started#query-a-node) - get an account's casts from a snapchain node. * [Set up the replicator](https://snapchain.farcaster.xyz/guides/syncing-to-db) - sync a snapchain node to a postgres database to run advanced queries. * [Run a snapchain node](https://snapchain.farcaster.xyz/guides/running-a-node) - get realtime access to Farcaster data. --- # Farcaster Client API Reference / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/warpcast/api#VPContent) Return to top Farcaster Client API Reference [​](https://docs.farcaster.xyz/reference/warpcast/api#farcaster-client-api-reference) ===================================================================================================================== This page documents public APIs provided by the Farcaster client with information that is not available on the protocol. The hostname is always `https://api.farcaster.xyz`. Pagination [​](https://docs.farcaster.xyz/reference/warpcast/api#pagination) ----------------------------------------------------------------------------- Paginated endpoints return a `next.cursor` property next to the `result` object. To fetch the next page, send the value as a `cursor` query parameter. An optional `limit` query parameter can be used to specify the page size. json { "result": { ... }, "next": { "cursor": "eyJwYWdlIjoxLCJsaW1pdCI6MTAwfQ" } } Authentication [​](https://docs.farcaster.xyz/reference/warpcast/api#authentication) ------------------------------------------------------------------------------------- Authenticated endpoints use a self-signed token, signed as an App Key for FID: tsx import { NobleEd25519Signer } from "@farcaster/hub-nodejs"; // private / public keys of an App Key you are holding for an FID const fid = 6841; //replace const privateKey = 'secret'; // replace const publicKey = 'pubkey'; // replace const signer = new NobleEd25519Signer(new Uint8Array(Buffer.from(privateKey))); const header = { fid, type: 'app_key', key: publicKey }; const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url'); const payload = { exp: Math.floor(Date.now() / 1000) + 300 }; // 5 minutes const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url'); const signatureResult = await signer.signMessageHash(Buffer.from(`${encodedHeader}.${encodedPayload}`, 'utf-8')); if (signatureResult.isErr()) { throw new Error("Failed to sign message"); } const encodedSignature = Buffer.from(signatureResult.value).toString("base64url"); const authToken = encodedHeader + "." + encodedPayload + "." + encodedSignature; await got.post( "https://api.farcaster.xyz/fc/channel-follows", { body: { channelKey: 'evm' } headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + authToken; } } ) Concepts [​](https://docs.farcaster.xyz/reference/warpcast/api#concepts) ------------------------------------------------------------------------- * Channels: The Farcaster client has the concept of channels which build upon FIP-2 (setting `parentUrl` on casts). You can read more about channels in the [documentation](https://www.notion.so/farcaster/Channels-4f249d22575348a5a0488b5d86f0dd1c?pvs=4) . Get All Channels [​](https://docs.farcaster.xyz/reference/warpcast/api#get-all-channels) ----------------------------------------------------------------------------------------- `GET /v2/all-channels` List all channels. No parameters. Not paginated. Not authenticated. Returns: a `channels` array with properties: * `id` - unique channel id that cannot be changed (called 'Name' when creating a channel) * `url` - FIP-2 `parentUrl` used for main casts in the channel * `name` - friendly name displayed to users (called 'Display name' when editing a channel) * `description` - description of the channel, if present * `descriptionMentions` - an array of the user fids mentioned in the description. Multiple mentions result in multiple entries. * `descriptionMentionsPositions` - the indexes within the description where the mentioned users (from `descriptionMentions`) appear. This array is always the same size as `descriptionMentions`. The mention is placed to the left of any existing character at that index. * `leadFid` - fid of the user who created the channel, if present * `moderatorFids` - fids of the moderators (under new channel membership scheme) * `createdAt` - UNIX time when channel was created, in seconds * `followerCount` - number of users following the channel * `memberCount` - number of members of the channel, including the owner and moderators * `pinnedCastHash` - hash of the cast pinned in the channel, if present * `publicCasting` - `true`/`false` indicating whether channel allows anybody to cast into it, or only members * `externalLink` - external link that appears in the header in the Farcaster client, if present, with 2 properties: * `title` - title shown in the channel header * `url` - url of the link json { "result": { "channels": [\ {\ "id": "illustrations",\ "url": "https://farcaster.xyz/~/channel/illustrations",\ "name": "illustrations",\ "description": "Share your wips, sketches, arts, drops, GMs, artworks you adore or collected — all content related to illustration, tag to join ⊹ ࣪ ˖ cover by ",\ "descriptionMentions": [\ 367850,\ 335503\ ],\ "descriptionMentionsPositions": [\ 122,\ 151\ ],\ "imageUrl": "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/7721c951-b0ed-44ee-aa9c-c31507b69c00/original",\ "headerImageUrl": "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/64efe955-c3ab-4aad-969d-1aed978a3e00/original",\ "leadFid": 367850,\ "moderatorFids": [\ 367850\ ],\ "createdAt": 1709753166,\ "followerCount": 2361,\ "memberCount": 300,\ "pinnedCastHash": "0x3ef52987ccacd89af096a753c07efcd55a93e143",\ "publicCasting": false,\ "externalLink": {\ "title": "/creatorssupport",\ "url": "https://farcaster.xyz/~/channel/creators-support"\ }\ },\ ...\ ] } } Example: bash curl 'https://api.farcaster.xyz/v2/all-channels' Get a Channel [​](https://docs.farcaster.xyz/reference/warpcast/api#get-a-channel) ----------------------------------------------------------------------------------- `GET /v1/channel` Get a single channel. Not authenticated. Query parameters: * `channelId` - the id of the channel Returns: a single channel object, as documented in the "Get All Channels" endpoint above. json { "result": { "channel": { "id": "illustrations", "url": "https://farcaster.xyz/~/channel/illustrations", "name": "illustrations", "description": "Share your wips, sketches, arts, drops, GMs, artworks you adore or collected — all content related to illustration, tag to join ⊹ ࣪ ˖ cover by ", "descriptionMentions": [367850, 335503], "descriptionMentionsPositions": [122, 151], "imageUrl": "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/7721c951-b0ed-44ee-aa9c-c31507b69c00/original", "headerImageUrl": "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/64efe955-c3ab-4aad-969d-1aed978a3e00/original", "leadFid": 367850, "moderatorFids": [367850], "createdAt": 1709753166, "followerCount": 2361, "memberCount": 300, "pinnedCastHash": "0x3ef52987ccacd89af096a753c07efcd55a93e143", "publicCasting": false, "externalLink": { "title": "/creatorssupport", "url": "https://farcaster.xyz/~/channel/creators-support" } } } } bash curl 'https://api.farcaster.xyz/v1/channel?channelId=illustrations' Get Channel Followers [​](https://docs.farcaster.xyz/reference/warpcast/api#get-channel-followers) --------------------------------------------------------------------------------------------------- `GET /v1/channel-followers` List the followers of a channel. Ordered by the time when the channel was followed, descending. Paginated. Not authenticated. Query Parameters: * `channelId` - the id of the channel Returns: a `users` array with properties: * `fid` - the fid of the user * `followedAt` - UNIX time when channel was followed, in seconds json { "result": { "users": [\ {\ "fid": 466624,\ "followedAt": 1712685183\ },\ {\ "fid": 469283,\ "followedAt": 1712685067\ },\ ...\ ], }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/v1/channel-followers?channelId=books' Get Channels a User is Following [​](https://docs.farcaster.xyz/reference/warpcast/api#get-channels-a-user-is-following) ------------------------------------------------------------------------------------------------------------------------- `GET /v1/user-following-channels` List all channels a user is following. Ordered by the time when the channel was followed, descending. Paginated. Not authenticated. Parameters: * `fid` - the fid of the user Returns: a `channels` array with properties: * All properties documented in the "Get All Channels" endpoint above * `followedAt` - UNIX time when channel was followed, in seconds json { "result": { "channels": [\ {\ "id": "fc-updates",\ "url": "https://farcaster.xyz/~/channel/fc-updates",\ "name": "fc-updates",\ "description": "Important updates about things happening in Farcaster",\ "imageUrl": "https://i.imgur.com/YnnrPaH.png",\ "leadFid": 2,\ "moderatorFid": 5448,\ "moderatorFids": [\ 5448,\ 3\ ],\ "createdAt": 1712162074,\ "followerCount": 17034,\ "followedAt": 1712162620\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/v1/user-following-channels?fid=3' Get User Following Channel Status [​](https://docs.farcaster.xyz/reference/warpcast/api#get-user-following-channel-status) --------------------------------------------------------------------------------------------------------------------------- `GET /v1/user-channel` Check whether a user is following a channel. Query parameters: * `fid` - the fid of the user * `channelId` - the id of the channel Returns: 2 properties: * `following` - indicates whether the channel is followed * `followedAt` - UNIX time when channel was followed, in seconds (optional, only present when channel is followed) json { "result": { "following": true, "followedAt": 1687943747 } } Example: bash curl 'https://api.farcaster.xyz/v1/user-channel?fid=3&channelId=books' Get Channel Members [​](https://docs.farcaster.xyz/reference/warpcast/api#get-channel-members) ----------------------------------------------------------------------------------------------- `GET /fc/channel-members` List the members of a channel. Ordered by the time when the user became a member, descending. Paginated. Not authenticated. Query parameters: * `channelId` - the id of the channel * `fid` - (optional) and fid of a user to filter by Returns: a `members` array: * `fid` - the fid of the member * `memberAt` - UNIX time when user became a member, in seconds json { "result": { "members": [\ {\ "fid": 466624,\ "memberAt": 1712685183\ },\ {\ "fid": 469283,\ "memberAt": 1712685067\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/fc/channel-members?channelId=memes' Get Channel Invites [​](https://docs.farcaster.xyz/reference/warpcast/api#get-channel-invites) ----------------------------------------------------------------------------------------------- `GET /fc/channel-invites` List outstanding invites to channels. Ordered by the time when the user was invited, descending. Paginated. Not authenticated. There is max one outstanding invite per user (`invitedFid`) and channel (`channelId`). Query parameters: * `channelId` - (optional) an id of a channel to filter by * `fid` - (optional) an fid of a user to filter by Returns: an `invites` array: * `channelId` - the id of the channel to which the user was inviter * `invitedFid` - the fid of the user being invited * `invitedAt` - UNIX time when user was invited, in seconds * `inviterFid` - the fid of the user who created the invite * `role` - the role the user was invited to, `member` or `moderator` json { "result": { "invites": [\ {\ "channelId": "coke-zero",\ "invitedFid": 194,\ "invitedAt": 1726879628,\ "inviterFid": 18949,\ "role": "member"\ },\ {\ "channelId": "brain-teasers",\ "invitedFid": 627785,\ "invitedAt": 1726874566,\ "inviterFid": 235128,\ "role": "member"\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/fc/channel-invites?channelId=memes' Get Cast Moderation Actions [​](https://docs.farcaster.xyz/reference/warpcast/api#get-cast-moderation-actions) --------------------------------------------------------------------------------------------------------------- `GET /fc/moderated-casts` List moderation actions. Ordered by the time when the action was taken, descending. Paginated. Not authenticated. Query parameters: * `channelId` - (optional) an id of a channel to filter by Returns: a `moderationActions` array: * `castHash` - hash of the cast that was moderated (including `0x` prefix) * `channelId` - id of the channel in which the cast resides * `action` - `hide` or `unhide` * `moderatedAt` - UNIX time when moderation took place, in seconds json { "result": { "moderationActions": [\ {\ "castHash": "0x6b2253105ef8c1d1b984a5df87182b105a1f0b3a",\ "channelId": "welcome",\ "action": "hide",\ "moderatedAt": 1727767637\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/fc/moderated-casts?channelId=welcome' Get Channel Restricted Users [​](https://docs.farcaster.xyz/reference/warpcast/api#get-channel-restricted-users) ----------------------------------------------------------------------------------------------------------------- `GET /fc/channel-restricted-users` Get users restricted from joining channels via an invite link (they can still be manually invited). Users get into this state automatically after being removed as a member, and after being unbanned. Users get out of this state after being invited (even if they haven't accepted/declined the invite), and being banned. It is not possible to restrict or unrestrict users directly. Ordered by the time when the user was restricted, descending. Paginated. Not authenticated. **Note:** * Replies by restricted users are still visible below the fold * This endpoint returns only actively restricted users. If a user was restricted and subsequently invited back or banned, the entry from this endpoint will disappear. Query parameters: * `channelId` - (optional) channel id to filter by * `fid` - (optional) user fid to filter by Returns: a `restrictedUsers` array: * `fid` - the fid of the restricted user * `channelId` - the id of the channel the user is restricted from * `restrictedAt` - UNIX time when the restriction started, in seconds json { "result": { "restrictedUsers": [\ {\ "fid": 1234,\ "channelId": "welcome",\ "restrictedAt": 1727767637\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/fc/channel-restricted-users?channelId=memes' Get Channel Banned Users [​](https://docs.farcaster.xyz/reference/warpcast/api#get-channel-banned-users) --------------------------------------------------------------------------------------------------------- `GET /fc/channel-bans` Get users banned from channels. Ordered by the time when the user was banned, descending. Paginated. Not authenticated. **Note:** * This endpoint returns only active bans. If a user is unbanned, the entry from this endpoint will disappear. Query parameters: * `channelId` - (optional) channel id to filter by * `fid` - (optional) user fid to filter by Returns: a `bannedUsers` array: * `fid` - the fid of the banned user * `channelId` - the id of the channel the user is banned from * `bannedAt` - UNIX time when the ban started, in seconds json { "result": { "bannedUsers": [\ {\ "fid": 1234,\ "channelId": "welcome",\ "bannedAt": 1727767637\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/fc/channel-bans?channelId=memes' Ban User From Channel [​](https://docs.farcaster.xyz/reference/warpcast/api#ban-user-from-channel) --------------------------------------------------------------------------------------------------- `POST /fc/channel-bans` Ban a user from a channel. A banned user can no longer reply to channel casts, and all their existing replies are hidden. Authenticated. The caller must own or moderate the channel. Body parameters: * `channelId` - the id of the channel to ban the user from * `banFid` - the fid of the user to ban Returns: * `success: true` Example: bash curl -X POST \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "channelId": "memes", "banFid": 1234 }' \ https://api.farcaster.xyz/fc/channel-bans Unban User From Channel [​](https://docs.farcaster.xyz/reference/warpcast/api#unban-user-from-channel) ------------------------------------------------------------------------------------------------------- `DELETE /fc/channel-bans` Unban a user from a channel. All the user's existing replies will become visible and they will be able to reply again (appearing below the fold). The user goes into restricted state. Authenticated. The caller must own or moderate the channel. Body parameters: * `channelId` - the id of the channel to unban the user from * `unbanFid` - the fid of the user to unban Returns: * `success: true` Example: bash curl -X DELETE \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "channelId": "memes", "banFid": 1234 }' \ https://api.farcaster.xyz/fc/channel-bans Follow/Unfollow Channel [​](https://docs.farcaster.xyz/reference/warpcast/api#follow-unfollow-channel) ------------------------------------------------------------------------------------------------------- Allows following and unfollowing a channel. Authenticated. * `POST /fc/channel-follows` * `DELETE /fc/channel-follows` Body parameters: * `channelId` - id of channel to follow or unfollow **Examples** bash curl -X POST -H 'Content-Type: application/json' -H 'Authorization: Bearer ' -d '{ "channelId": "evm" }' https://api.farcaster.xyz/fc/channel-follows curl -X DELETE -H 'Content-Type: application/json' -H 'Authorization: Bearer ' -d '{ "channelId": "evm" }' https://api.farcaster.xyz/fc/channel-follows Invite to Channel [​](https://docs.farcaster.xyz/reference/warpcast/api#invite-to-channel) ------------------------------------------------------------------------------------------- `POST /fc/channel-invites` Invites a user to be either a member or moderator of a channel. It is idempotent, i.e. if there is already an outstanding invite or the user already has the desired role, the API returns success. Authenticated. The caller must own or moderate the channel. **Note:** we can allowlist bots that are ok to be directly added as moderators to channels. Any channel owner can then add the bot as an active mod without the requirement that the bot is first a member or that it has to accept an invite. Let us know if you’d like your bot to be allowlisted. Rate limit: 100 calls per caller per channel per hour Body parameters: * `channelId` - id of channel to invite user to * `inviteFid` - fid of the user to invite * `role` - either of: * `member`: invites the user to be a member. The user must already follow either the channel or the user calling the endpoint. The caller must be a channel moderator or the channel owner. * `moderator`: invites a user to be a moderator. The user must already be a channel member (i.e. has accepted a prior `member` invitation). The caller must be the channel owner. The number of active moderators + outstanding moderator invites cannot go above 10 (you'll get an error). Returns: * `success: true` Example: bash curl -X POST \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "channelId": "evm", "inviteFid": 341234, "role": "member" }' \ https://api.farcaster.xyz/fc/channel-invites Remove from Channel [​](https://docs.farcaster.xyz/reference/warpcast/api#remove-from-channel) ----------------------------------------------------------------------------------------------- `DELETE /fc/channel-invites` Removes a user from a channel role, including revoking any outstanding unaccepted invite. It is idempotent, i.e. if the user does not have an invite for the role or does not have the role, the API returns success. Authenticated. The caller must own or moderate the channel. Rate limit: 100 calls per caller per channel per hour Body parameters: * `channelId` - id of channel to remove user from * `removeFid` - fid of the user to remove * `role` - either of: * `member`: removes user from member role or revokes an existing member invitation. If the user was removed, the user is blocked from becoming a member again via a link (i.e. to become a member again, they have to be invited by a moderator). If only an invitation was revoked, the user is not blocked from becoming a member via a link. The caller must be a channel moderator or the channel owner. * `moderator`: removes user from moderator role or revokes an existing moderator invitation. The caller must be the channel owner. Returns: * `success: true` Example: bash curl -X DELETE \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "channelId": "evm", "removeFid": 341234, "role": "member" }' \ https://api.farcaster.xyz/fc/channel-invites Respond to Channel Invite [​](https://docs.farcaster.xyz/reference/warpcast/api#respond-to-channel-invite) ----------------------------------------------------------------------------------------------------------- `PATCH /fc/channel-invites` Accept or decline an outstanding member/moderator channel invite. Once an invite has been accepted or declined, the response cannot be changed. Authenticated. The caller must have an active invite. Body parameters: * `channelId` - id of channel to which user was invited * `role` - the role of the invite, either `member` or `moderator` * `accept` - boolean, `true` or `false`, indicating accept/decline Returns: * `success: true` Example: bash curl -X PATCH \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "channelId": "evm", "role": "member", "accept": true }' \ https://api.farcaster.xyz/fc/channel-invites Moderate Channel Cast [​](https://docs.farcaster.xyz/reference/warpcast/api#moderate-channel-cast) --------------------------------------------------------------------------------------------------- `POST /fc/moderated-casts` Moderate (hide/unhide) a specific channel cast. Authenticated. The cast must be in a channel the caller moderates or owns. Body parameters: * `castHash` - the hash of the cast (including `0x` prefix) * `action` - either `hide` or `unhide` Returns: * `success: true` Example: bash curl -X POST \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "castHash": "0x2694aa649f3608bd11fe6621946782d1eb0ae3c4", "action": "hide" }' \ https://api.farcaster.xyz/fc/moderate-cast Pin Channel Cast [​](https://docs.farcaster.xyz/reference/warpcast/api#pin-channel-cast) ----------------------------------------------------------------------------------------- `PUT /fc/pinned-casts` Pin (and optionally announce) a cast to a channel, replacing any currently pinned cast. If the provided cast is already pinned, nothing changes (and success is returned). Authenticated. Body parameters: * `castHash` - the hash of the cast to pin (including `0x` prefix) * `notifyChannelFollowers` - (optional) `true` / `false` whether to send an announcement notification to all channel followers about the pinned cast. Defaults to `false` Returns: * `success: true` Example: bash curl -X PUT \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "castHash": "0x2694aa649f3608bd11fe6621946782d1eb0ae3c4", "notifyChannelFollowers": true }' \ https://api.farcaster.xyz/fc/pinned-casts Unpin Channel Cast [​](https://docs.farcaster.xyz/reference/warpcast/api#unpin-channel-cast) --------------------------------------------------------------------------------------------- `DELETE /fc/pinned-casts` Unpin a cast from a channel. Does not remove existing announcement notifications. Authenticated. Body parameters (**provide only one of the two**): * `castHash` - the hash of the cast to unpin (including `0x` prefix). Will unpin only if this exact cast is pinned. * `channelId` - the id of the channel to unpin from. Will unpin any pinned cast from the channel. Returns: * `success: true` Example: bash curl -X DELETE \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "channelId": "welcome" }' \ https://api.farcaster.xyz/fc/pinned-casts Get Farcaster actions [​](https://docs.farcaster.xyz/reference/warpcast/api#get-farcaster-actions) --------------------------------------------------------------------------------------------------- `GET /v2/discover-actions` Retrieve a list of Farcaster actions. Paginated. Not authenticated. Query parameters: * `list` - the list to retrieve. Must be `'top'`, a list ordered by total users. Returns: an array of `action` objects with properties: * `name` - action name * `icon` - an [Octicon](https://primer.style/foundations/icons) identifying the action * `description` - a short text description of the action * `aboutUrl` - external link to a page with additional instructions or source code * `actionUrl` - action metadata URL. Clients retrieve metadata with a GET to this URL. * `action.actionType` - action type, only `'post'` * `action.postUrl` - action POST URL. Clients POSt signed messages to this URL. json { "result": { "actions": [\ {\ "name": "Upthumb",\ "icon": "thumbsup",\ "description": "Give casts 'upthumbs' and see them on a leaderboard.",\ "aboutUrl": "https://github.com/horsefacts/upthumbs",\ "actionUrl": "https://upthumbs.app/api/upthumb",\ "action": {\ "actionType": "post",\ "postUrl": "https://upthumbs.app/api/upthumb"\ }\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/v2/discover-actions?list=top&limit=10' Get Blocked Users [​](https://docs.farcaster.xyz/reference/warpcast/api#get-blocked-users) ------------------------------------------------------------------------------------------- `GET /fc/blocked-users` The Farcaster client allows users to block others from replying, quoting and mentioning them. This endpoint provides access to all blocked users. Paginated, in reverse chronological order by block creation time. Not authenticated. Query parameters: * `blockerFid` (**optional**) - limit the response to only blocks by a specific user Returns: a `blockedUsers` array with properties: * `blockerFid` - the user who created the block * `blockedFid` - the user who is blocked (cannot reply to, quote or mention the `blockerFid`) * `createdAt` - UNIX time when channel was created, in seconds json { "result": { "blockedUsers": [\ {\ "blockerFid": 5,\ "blockedFid": 10,\ "createdAt": 1724854521\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/fc/blocked-users' Block User [​](https://docs.farcaster.xyz/reference/warpcast/api#block-user) ----------------------------------------------------------------------------- `POST /fc/blocked-users` Block a user. Authenticated. Body parameters: * `blockFid` - the fid of the user to block Returns: * `success: true` Example: bash curl -X POST \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "blockFid": 1234 }' \ https://api.farcaster.xyz/fc/blocked-users Unblock User [​](https://docs.farcaster.xyz/reference/warpcast/api#unblock-user) --------------------------------------------------------------------------------- `DELETE /fc/blocked-users` Unblock a user. Authenticated. Body parameters: * `unblockFid` - the fid of the user to unblock Returns: * `success: true` Example: bash curl -X DELETE \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "unblockFid": 1234 }' \ https://api.farcaster.xyz/fc/blocked-users Get Account Verifications [​](https://docs.farcaster.xyz/reference/warpcast/api#get-account-verifications) ----------------------------------------------------------------------------------------------------------- `GET /fc/account-verifications` ### This endpoint is not stable (beta) and likely to have breaking changes in the future or get depracated. [​](https://docs.farcaster.xyz/reference/warpcast/api#this-endpoint-is-not-stable-beta-and-likely-to-have-breaking-changes-in-the-future-or-get-depracated) List of account verifications attested by the Farcaster client. Ordered by the time when the verification occurred, descending. Paginated. Not authenticated. Query parameters: * `fid` (**optional**) - limit the response to specific user * `platform` (**optional**) - limit the response to specific platform `'x' | 'github' | 'discord'`. Defaults to `'x'` for backwards compatibility. Returns: a `verifications` array: * `fid` - account Farcaster id * `platform` - platform of the verification `'x' | 'github' | 'discord'` * `platformId` - string value representing platform identifier (generated by the platform, not the Farcaster client) * `platformUsername` - string value representing platform username * `verifiedAt` - UNIX time when verification took place, in seconds json { "result": { "verifications": [\ {\ "fid": 3,\ "platform": "x",\ "platformId": "9615352",\ "platformUsername": "dwr",\ "verifiedAt": 1728505748\ },\ {\ "fid": 3,\ "platform": "github",\ "platformId": "86492",\ "platformUsername": "danromero",\ "verifiedAt": 1728505748\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/fc/account-verifications' Get creator reward winners [​](https://docs.farcaster.xyz/reference/warpcast/api#get-creator-reward-winners) ------------------------------------------------------------------------------------------------------------- `GET /v1/creator-rewards-winner-history` The Farcaster client gives out weekly rewards to top creators on the network. This endpoint provides access to all winners for a given period (week). Paginated, with the list of winners in rank order. Not authenticated. Data is refreshed every Tuesday at 17:00 UTC. Query parameters: * `periodsAgo` (**optional**) - how many periods ago to fetch the results for. 0 or undefined returns results for the most recent period. Returns: * `history`: Object containing: * `periodStartTimestamp`: Unix time in milliseconds when rewards period began * `periodEndTimestamp`: Unix time in milliseconds when rewards period ended * `winners`: Paginated list of fid, username, score, rank, wallet address (optional) and reward amount in rank order. A missing wallet address indicates that the user does not have a verified wallet on the Farcaster Client. * `next`: Pagination cursor json { "result": { "history": { "periodStartTimestamp": 1738080000000, "periodEndTimestamp": 1738684800000, "winners": [\ {\ "fid": 12345,\ "score": 50000,\ "rank": 1,\ "rewardCents": 60000,\ "username": "alice.eth",\ "walletAddress": "0x0000000000000000000000000000000000000001"\ },\ {\ "fid": 67890,\ "score": 45000,\ "rank": 2,\ "rewardCents": 60000,\ "username": "bob",\ "walletAddress": "0x0000000000000000000000000000000000000002"\ },\ ...\ ] } }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/v1/creator-rewards-winner-history' Get developer reward winners [​](https://docs.farcaster.xyz/reference/warpcast/api#get-developer-reward-winners) ----------------------------------------------------------------------------------------------------------------- `GET /v1/developer-rewards-winner-history` The Farcaster client gives out weekly rewards to top developers on the network. This endpoint provides access to all winners for a given period (week). Paginated, with the list of winners in rank order. Not authenticated. Data is refreshed every Wednesday at 17:00 UTC. Query parameters: * `periodsAgo` (**optional**) - how many periods ago to fetch the results for. 0 or undefined returns results for the most recent period. Returns: * `periodStartTimestamp`: Unix time in milliseconds when rewards period began * `periodEndTimestamp`: Unix time in milliseconds when rewards period ended * `winners`: Paginated list of fid, domain, frame (mini app) name, score, rank, wallet address (optional) and reward amount in rank order. A missing wallet address indicates that the user does not have a verified wallet on the Farcaster client. json { "result": { "periodStartTimestamp": 1738080000000, "periodEndTimestamp": 1738684800000, "winners": [\ {\ "fid": 1,\ "domain": "example.com",\ "frameName": "App Name",\ "score": 10,\ "rank": 1,\ "rewardCents": 1000,\ "walletAddress": "0x0000000000000000000000000000000000000000",\ },\ {\ "fid": 420,\ "domain": "example.com",\ "frameName": "App Name",\ "score": 1,\ "rank": 2,\ "rewardCents": 500,\ "walletAddress": "0x0000000000000000000000000000000000000001",\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/v1/developer-rewards-winner-history' Get User Primary Address [​](https://docs.farcaster.xyz/reference/warpcast/api#get-user-primary-address) --------------------------------------------------------------------------------------------------------- `GET /fc/primary-address?fid=12152&protocol=ethereum` Fetch the primary address from the user. This is picked by the user whenever they expressed a preference, or picked by the Farcaster client. Query parameters: * `fid` - the fid of the user to fetch the primary address for * `protocol` - the protocol of the address to fetch. Both `ethereum` and `solana` are supported. Returns: * `address` - the primary address of the user json { "result": { "address": { "fid": 12152, "protocol": "ethereum", "address": "0x0BD6b1DFE1eA61C2b487806ECd06b5A95383a4e3" } } } Example: bash curl 'https://api.farcaster.xyz/fc/primary-address?fid=12152&protocol=ethereum' Get Multiple User Primary Addresses [​](https://docs.farcaster.xyz/reference/warpcast/api#get-multiple-user-primary-addresses) ------------------------------------------------------------------------------------------------------------------------------- `GET /fc/primary-addresses` Fetch primary addresses for multiple users at once. This is a batch version of the single primary address endpoint. For each FID, this returns the primary address picked by the user whenever they expressed a preference, or picked by the Farcaster client. Query parameters: * `fids` - comma-separated list of FIDs to fetch primary addresses for * `protocol` - the protocol of the addresses to fetch. Both `ethereum` and `solana` are supported. For now, only 100 FIDs can be fetched at once. We can change this quickly if needed (just reach out to us). Returns: * `addresses` - an array of address results, one for each requested FID: * `fid` - the FID that was requested * `success` - boolean indicating whether the address was found * `address` - (only present when `success` is true) object containing: * `fid` - the FID of the user * `protocol` - the protocol of the address (`ethereum` or `solana`) * `address` - the primary address string json { "result": { "addresses": [\ {\ "fid": 12152,\ "success": true,\ "address": {\ "fid": 12152,\ "protocol": "ethereum",\ "address": "0x0BD6b1DFE1eA61C2b487806ECd06b5A95383a4e3"\ }\ },\ {\ "fid": 2,\ "success": true,\ "address": {\ "fid": 2,\ "protocol": "ethereum",\ "address": "0x661E2209B9C6B06C1F32A0639f60D3294185ab35"\ }\ },\ {\ "fid": 1315,\ "success": true,\ "address": {\ "fid": 1315,\ "protocol": "ethereum",\ "address": "0x0450a8545028547Df4129Aa5b4EC5794D5aF2409"\ }\ },\ {\ "fid": 39939393939,\ "success": false\ }\ ] } } Example: bash curl 'https://api.farcaster.xyz/fc/primary-addresses?fids=12152,2,1315,39939393939&protocol=ethereum' Get Starter Pack Members [​](https://docs.farcaster.xyz/reference/warpcast/api#get-starter-pack-members) --------------------------------------------------------------------------------------------------------- `GET /fc/starter-pack-members` Starter pack members. Ordered by the time when they were added to the pack, descending. Paginated. Not authenticated. Query parameters: * `id` - starter pack id found as a part of the public pack URL or in the non-authed public API of starter pack metadata. Returns: a `members` array: * `fid` - account Farcaster id * `memberAt` - time when member was added to the starter pack, in milliseconds json { "result": { "members": [\ {\ "fid": 3,\ "memberAt": 1740172669691\ },\ {\ "fid": 296646,\ "memberAt": 1740172669691\ },\ ...\ ] }, "next": { "cursor": "..." } } Example: bash curl 'https://api.farcaster.xyz/fc/starter-pack-members?id=Underrated-CT-1y7n9b' --- # Overview / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/#VPContent) Return to top Overview [​](https://docs.farcaster.xyz/reference/#overview) ============================================================= The reference sections documents API's, standards and protocols used commonly used by Farcaster developers. * [Mini Apps](https://miniapps.farcaster.xyz/) \- A specification for writing and rendering mini apps. * [Farcaster Client APIs](https://docs.farcaster.xyz/reference/warpcast/api) - An overview of Farcaster Client APIs that are publicly available. * [Snapchain](https://snapchain.farcaster.xyz/) - A design overview and API reference for Farcaster Hubs. * [Replicator](https://docs.farcaster.xyz/reference/replicator/schema) - An overview and schema for the replicator. * [Contracts](https://docs.farcaster.xyz/reference/contracts/index) - A design overview and ABI reference for Farcaster contracts. * [FName Registry](https://docs.farcaster.xyz/reference/fname/api) - An overview and API reference for the Farcaster Name Server. * [Neynar](https://docs.farcaster.xyz/reference/third-party/neynar/index) - An overview of Neynar APIs that can help developers get started with Farcaster --- # Getting Started / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/#VPContent) Return to top Getting Started [​](https://docs.farcaster.xyz/learn/#getting-started) ======================================================================= Farcaster is a [sufficiently decentralized](https://www.varunsrinivasan.com/2022/01/11/sufficient-decentralization-for-social-networks) social network built on Ethereum. It is a public social network similar to X and Reddit. Users can create profiles, post "casts" and follow others. They own their accounts and relationships with other users and are free to move between different apps. Join Farcaster If you're not on Farcaster, get started by [creating your account](https://www.farcaster.xyz/) with the official Farcaster client. Learn [​](https://docs.farcaster.xyz/learn/#learn) --------------------------------------------------- If you want to learn more, get started by diving into these concepts: * [Farcaster 101](https://www.youtube.com/playlist?list=PL0eq1PLf6eUdm35v_840EGLXkVJDhxhcF) - a walkthrough of the Farcaster protocol in short, 5 minute videos. * [Core Concepts](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts) - learn about the building blocks of Farcaster, starting with accounts. * [Architecture](https://docs.farcaster.xyz/learn/architecture/overview) - a breakdown of Farcaster's onchain and offchain systems. Tutorials [​](https://docs.farcaster.xyz/learn/#tutorials) ----------------------------------------------------------- * [Build your first mini app](https://miniapps.farcaster.xyz/docs/getting-started) - Make mini-apps that run inside Farcaster. * [Sign in with Farcaster](https://docs.farcaster.xyz/auth-kit/installation) - Let users login to your app with their Farcaster account. * [Write your first app](https://docs.farcaster.xyz/developers/index) - Publish a "Hello World" message to Farcaster. Find more how-tos, guide and tutorials like this in the [developers](https://docs.farcaster.xyz/developers/) section. Documentation [​](https://docs.farcaster.xyz/learn/#documentation) ------------------------------------------------------------------- * [Farcaster Spec](https://github.com/farcasterxyz/protocol) - Specifications for Farcaster, including its contracts and nodes. * [Mini Apps Spec](https://miniapps.farcaster.xyz/docs/specification) - Specifications for writing and rendering mini apps in Farcaster apps. * [APIs](https://docs.farcaster.xyz/reference/) - Docs for API's and ABI's for onchain and offchain systems. Contributing [​](https://docs.farcaster.xyz/learn/#contributing) ----------------------------------------------------------------- To learn about how to contribute to the protocol, including this documentation site, check out the [Contributing](https://docs.farcaster.xyz/learn/contributing/overview) section. --- # AuthKit / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/#VPContent) Return to top AuthKit [​](https://docs.farcaster.xyz/auth-kit/#authkit) ========================================================== [![NPM Version](https://img.shields.io/npm/v/@farcaster/auth-kit)](https://www.npmjs.com/package/@farcaster/auth-kit) AuthKit is a React library that lets users log in to your app with a Farcaster account. Click "Sign in With Farcaster" above to try it out on web or click [here](https://sign-in-with-farcaster-demo.replit.app/) for mobile. ### How does it work? [​](https://docs.farcaster.xyz/auth-kit/#how-does-it-work) It uses the [Sign In With Farcaster](https://docs.farcaster.xyz/auth-kit/#sign-in-with-farcaster-siwf) standard under the hood, which is conceptually like "Sign in with Google". When integrated, AuthKit will: 1. Show a "Sign in with Farcaster" button to the user. 2. Wait for the user to click, scan a QR code and approve the request in Warpcast. 3. Receive and verify a signature from Warpcast. 4. Show the logged in user's profile picture and username. --- # Farcaster Docs / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/#VPContent) Return to top Join the conversation Ask questions and hang out with other Farcaster developers in the [/fc-devs](https://warpcast.com/~/channel/fc-devs) channel on Farcaster. Create mini apps [​](https://docs.farcaster.xyz/developers/#create-mini-apps) ------------------------------------------------------------------------------ Learn how to build mini apps (previously called Frames) that run inside a Farcaster feed. * [Introduction](https://miniapps.farcaster.xyz/) \- understand what a mini app is and how it works * [Getting Started](https://miniapps.farcaster.xyz/docs/getting-started) \- Build your first mini app Sign in with Farcaster [​](https://docs.farcaster.xyz/developers/#sign-in-with-farcaster) ------------------------------------------------------------------------------------------ Make it easy for users to sign in to your app with their Farcaster account. * [Examples](https://docs.farcaster.xyz/auth-kit/examples) - see Sign in with Farcaster (SIWF) in action * [AuthKit](https://docs.farcaster.xyz/auth-kit/installation) - a React toolkit to integrate SIWF * [FIP-11](https://github.com/farcasterxyz/protocol/discussions/110) - the formal standard for SIWF Analyze Farcaster data [​](https://docs.farcaster.xyz/developers/#analyze-farcaster-data) ------------------------------------------------------------------------------------------ Sync the Farcaster network to a local machine so you can run queries on the data. * [Run a Snapchain node](https://snapchain.farcaster.xyz/getting-started#getting-started) - get realtime access to Farcaster data * [Write your first Snapchain query](https://snapchain.farcaster.xyz/getting-started#query-a-node) - get an account's casts from a Snapchain node * [Set up the replicator](https://snapchain.farcaster.xyz/guides/syncing-to-db) - sync a Snapchain node to a postgres database to run advanced queries Write to Farcaster [​](https://docs.farcaster.xyz/developers/#write-to-farcaster) ---------------------------------------------------------------------------------- * [Hello World](https://docs.farcaster.xyz/developers/guides/basics/hello-world) - programmatically create an account and publish a cast --- # ID Gateway / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#VPContent) Return to top ID Gateway [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#id-gateway) =============================================================================================== The ID Gateway registers new Farcaster IDs and adds them to the [Id Registry](https://docs.farcaster.xyz/reference/contracts/reference/id-registry) . If you want to create a new Farcaster ID, use the ID Gateway. Read [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#read) ----------------------------------------------------------------------------------- ### price [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#price) Get the price in wei to register an fid. This includes the price of 1 storage unit. Use the `extraStorage` parameter to include extra storage units in the total price. | Parameter | type | Description | | --- | --- | --- | | extraStorage | `uint256` (optional) | The number of additional storage units | ### nonces [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#nonces) Get the next unused nonce for an address. Used for generating an EIP-712 [`Register`](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#register-signature) signature for [registerFor](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#registerfor) . | Parameter | type | Description | | --- | --- | --- | | owner | `address` | Address to get the nonce for | Write [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#write) ------------------------------------------------------------------------------------- ### register [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#register) Register a new fid to the caller and pay for storage. The caller must not already own an fid. | Parameter | type | Description | | --- | --- | --- | | `msg.value` | `wei` | Amount to pay for registration | | recovery | `address` | Recovery address for the new fid | | extraStorage | `uint256` (optional) | Number of additional storage units to rent | ### registerFor [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#registerfor) Register a new fid to a specific address and pay for storage. The receiving address must sign an EIP-712 [`Register`](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#register-signature) message approving the registration. The receiver must not already own an fid. | Parameter | type | Description | | --- | --- | --- | | `msg.value` | `wei` | Amount to pay for registration | | to | `address` | The address to register the fid to | | recovery | `address` | Recovery address for the new fid | | deadline | `uint256` | Signature expiration timestamp | | sig | `bytes` | EIP-712 `Register` signature from the `to` address | | extraStorage | `uint256` (optional) | Additional storage units | #### Register signature [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#register-signature) To register an fid on behalf of another account, you must provide an EIP-712 typed signature from the receiving address in the following format: `Register(address to,address recovery,uint256 nonce,uint256 deadline)` | Parameter | type | Description | | --- | --- | --- | | to | `address` | Address to register the fid to. The typed message must be signed by this address. | | recovery | `address` | Recovery address for the new fid | | nonce | `uint256` | Current nonce of the `to` address | | deadline | `uint256` | Signature expiration timestamp | @farcaster/hub-webViemhelpers.tsclients.ts ts import { ViemWalletEip712Signer } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const eip712Signer = new ViemWalletEip712Signer(walletClient); const signature = await eip712signer.signRegister({ to: account, recovery: '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7', nonce, deadline, }); ts import { ID_GATEWAY_EIP_712_TYPES } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const signature = await walletClient.signTypedData({ account, ...ID_GATEWAY_EIP_712_TYPES, primaryType: 'Register', message: { to: account, recovery: '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7', nonce, deadline, }, }); ts import { ID_GATEWAY_ADDRESS, idGatewayABI } from '@farcaster/hub-web'; import { publicClient, account } from './clients.ts'; export const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return now + oneHour; }; export const readNonce = async () => { return await publicClient.readContract({ address: ID_GATEWAY_ADDRESS, abi: idGatewayABI, functionName: 'nonces', args: [account], }); }; ts import { createWalletClient, createPublicClient, custom, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; export const publicClient = createPublicClient({ chain: optimism, transport: http(), }); export const walletClient = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }); // JSON-RPC Account export const [account] = await walletClient.getAddresses(); // Local Account export const account = privateKeyToAccount('0x...'); Errors [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#errors) --------------------------------------------------------------------------------------- | Error | Selector | Description | | --- | --- | --- | | InvalidSignature | `8baa579f` | The provided signature is invalid. It may be incorrectly formatted, or signed by the wrong address. | | SignatureExpired | `0819bdcd` | The provided signature has expired. Collect a new signature from the signer with a later deadline timestamp. | Source [​](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#source) --------------------------------------------------------------------------------------- [`IdGateway.sol`](https://github.com/farcasterxyz/contracts/blob/1aceebe916de446f69b98ba1745a42f071785730/src/IdGateway.sol) --- # Accounts / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#VPContent) Return to top Accounts [​](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#accounts) =================================================================================== A Farcaster account lets you set up a username, profile picture and publish short text messages known as casts. Any Ethereum address can register a Farcaster account by making an onchain transaction. Creating an account [​](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#creating-an-account) --------------------------------------------------------------------------------------------------------- A Farcaster account is created by calling the IdGateway contract. It will assign a new Farcaster ID or fid to your Ethereum address. You'll need to get a username, rent storage and add a key before you can use your account. These steps require many signatures and onchain transactions and can be tedious with a regular Ethereum wallet. We recommend starting with the [official client](https://www.farcaster.xyz/) , developed by the Farcaster team which will handle the entire flow for you. It also uses a separate Ethereum account to sign transactions, so you can keep your main Ethereum account secure. Adding account keys [​](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#adding-account-keys) --------------------------------------------------------------------------------------------------------- Accounts can issue keys which let apps write messages on their behalf. Users will typically issue a key to each Farcaster app they use. Keys are managed by the KeyRegistry contract. To add a key, you'll need to submit the public key of an EdDSA key pair along with a requestor signature. The requestor can be the account itself or an app that wants to operate on its behalf. Recovering an account [​](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#recovering-an-account) ------------------------------------------------------------------------------------------------------------- Users often set up separate wallets for their social apps and it's easy to lose access. Farcaster lets any account specify a recovery address which can be used to recover the account. It can be configured when creating the account or anytime after. Users can set the recovery address to trusted services like the official Farcaster client or they can manage it themselves using a regular Ethereum wallet. Resources [​](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#resources) ------------------------------------------------------------------------------------- ### Specifications [​](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#specifications) * [Contract Specifications](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#1-smart-contracts) - The onchain contracts that manage Farcaster accounts, account keys and recovery addresses. ### APIs [​](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#apis) * [IdRegistry](https://docs.farcaster.xyz/reference/contracts/reference/id-registry) - Lookup account data onchain. * [IdGateway](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway) - Create accounts onchain. * [KeyRegistry](https://docs.farcaster.xyz/reference/contracts/reference/key-registry) - Lookup account key data onchain. * [KeyGateway](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway) - Create account keys onchain. * [Get Farcaster Ids](https://snapchain.farcaster.xyz/reference/httpapi/fids) - Fetch a list of all registered account fids from a Snapchain node. * [Get account keys](https://snapchain.farcaster.xyz/reference/httpapi/onchain#onchainsignersbyfid) - Fetch the account keys (signers) for an account from a Snapchain node. ### Tutorials [​](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#tutorials) * [Create an account](https://docs.farcaster.xyz/developers/guides/accounts/create-account) - Create a new account on Farcaster. * [Create an account key](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key) - Create a new account key for your account. * [Find account by username](https://docs.farcaster.xyz/developers/guides/accounts/find-by-name) - Find an account by its username. * [Change custody address](https://docs.farcaster.xyz/developers/guides/accounts/change-custody) - Change the address that owns your account. * [Change recovery address](https://docs.farcaster.xyz/developers/guides/accounts/change-recovery) - Change the address that recovers your account. * [Find account key requestor](https://docs.farcaster.xyz/developers/guides/advanced/decode-key-metadata) - Find the app that the user granted an account key to. * [Query signups from replicator](https://docs.farcaster.xyz/developers/guides/advanced/query-signups) - Query the number of signups from the replicator. --- # Usernames / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#VPContent) Return to top Usernames [​](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#usernames) ====================================================================================== A Farcaster account needs a username so it can be found and mentioned by other users. Farcaster uses the [Ethereum Name Service](https://ens.domains/) to manage usernames. ENS usernames are owned by Ethereum addresses, just like Farcaster accounts. The difference is that an address can own multiple ENS names, so the Farcaster account must specify the name it wishes to use. ENS names can only be used on Farcaster if they are <= 16 characters and contain only lowercase letters, numbers and hyphens. Changing usernames [​](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#changing-usernames) -------------------------------------------------------------------------------------------------------- A Farcaster account can change between different usernames at any time. Changing names does not affect your history or your followers. It's safe to change your name a few times a year. But changing your name more often may cause users or apps to lose trust in your account. If you want to change a public indicator, consider changing your display name instead. Offchain vs Onchain Names [​](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#offchain-vs-onchain-names) ---------------------------------------------------------------------------------------------------------------------- An account can choose between two kinds of usernames: * **Offchain ENS Names**: free and controlled by farcaster. (e.g. @alice) * **Onchain ENS Names**: costs money and controlled by your wallet. (e.g. @alice.eth) Choose an offchain ENS name if you want to get started quickly and don't have an onchain ENS name. An account can always upgrade to an onchain name later. It's recommended to use an app like the official Farcaster client to set this up for you. ![Usernames](https://docs.farcaster.xyz/assets/usernames.D0JzJrCz.png) ### Offchain ENS Names [​](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#offchain-ens-names) * Offchain ENS names, also called fnames, are free and issued by Farcaster. * Any Ethereum account can get one unique fname by calling the [Fname Registry](https://docs.farcaster.xyz/learn/architecture/ens-names) . * Fnames are free but they can be revoked by Farcaster at any time. ### Onchain ENS fnames [​](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#onchain-ens-fnames) * Onchain ENS names, also called .eth names, are onchain and issued by ENS. * Any Ethereum account can get an ENS by calling the [ENS Registry](https://docs.ens.domains/dapp-developer-guide/the-ens-registry) . * Names are not free but they cannot be revoked by Farcaster. Resources [​](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#resources) -------------------------------------------------------------------------------------- ### Specifications [​](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#specifications) * [Farcaster Name](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#5-fname-specifications) - An ENSIP-10 offchain ENS name usable within Farcaster. * [UserData: Username](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#23-user-data) - Sets a valid Username Proof as the current username. * [Username Proof](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#17-username-proof) - Proves ownership of an onchain or offchain username. * [Verifications](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#25-verifications) - Proves ownership of an address, required for onchain Username Proofs. ### APIs [​](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#apis) * [UserData API](https://snapchain.farcaster.xyz/reference/httpapi/userdata) - Fetch the UserData for a user's current username. * [Username Proofs API](https://snapchain.farcaster.xyz/reference/httpapi/usernameproof) - Fetch a user's Username Proofs from a Snapchain node. * [Verification Proofs API](https://snapchain.farcaster.xyz/reference/httpapi/verification) - Fetch a user's Verifications from a Snapchain node. * [Fname Registry API](https://docs.farcaster.xyz/reference/fname/api) - Register and track fname ownership programmatically. ### Tutorials [​](https://docs.farcaster.xyz/learn/what-is-farcaster/usernames#tutorials) * [Get UserData](https://docs.farcaster.xyz/developers/guides/querying/fetch-profile) - Get UserData messages from an account. * [Create UserData](https://docs.farcaster.xyz/developers/guides/writing/messages#user-data) - Create a UserData message to select a valid username. * [Verify an Address](https://docs.farcaster.xyz/developers/guides/writing/verify-address) - Verify ownership of an Ethereum account. * [Find account by username](https://docs.farcaster.xyz/developers/guides/accounts/find-by-name) - Find an account by its username. * [Change farcaster name](https://docs.farcaster.xyz/developers/guides/accounts/change-fname) - Change a farcaster username. --- # Id Registry / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#VPContent) Return to top Id Registry [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#id-registry) ================================================================================================== The Id Registry records which fid is associated with which Ethereum address, and manages fid transfers and recoveries. If you want to read information about an fid, transfer or recover an fid, or manage an fid's recovery address, use the Id Registry. If you want to register a new fid, use the [ID Gateway](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway) instead. Read [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#read) ------------------------------------------------------------------------------------ ### idOf [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#idof) Returns the fid (`uint256`) owned by an address, or returns zero if the address does not own an fid. | Parameter | type | Description | | --- | --- | --- | | owner | `address` | The address to check for an fid | ### custodyOf [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#custodyof) Returns the custody address (`address`) that owns a specific fid. Returns the zero address if the fid does not exist. | Parameter | type | Description | | --- | --- | --- | | fid | `uint256` | The fid to find the owner for | ### recoveryOf [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#recoveryof) Returns the recovery address (`address`) of an fid. Returns the zero address if the fid does not exist. | Parameter | type | Description | | --- | --- | --- | | fid | `uint256` | The fid to find the recovery address for | ### idCounter [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#idcounter) Returns the highest registered fid (`uint256`) so far. ### verifyFidSignature [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#verifyfidsignature) Checks that a message was signed by the current custody address of an fid. Returns a `bool`. | Parameter | type | Description | | --- | --- | --- | | custodyAddress | `address` | The address to check the signature of | | fid | `uint256` | The fid associated with the signature | | digest | `bytes32` | Hashed signed data | | sig | `bytes` | Signature to check | ### nonces [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#nonces) Returns the next unused nonce (`uint256`) for an address. Used for generating EIP-712 signatures. | Parameter | type | Description | | --- | --- | --- | | owner | `address` | The address to get the nonce for | Write [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#write) -------------------------------------------------------------------------------------- ### register [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#register) Will revert if called directly. Must be called via the [ID Gateway](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway) . ### changeRecoveryAddress [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#changerecoveryaddress) Change the recovery address for the caller's fid. | Parameter | type | Description | | --- | --- | --- | | recovery | `address` | The new recovery address | ### transfer [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer) Transfer the caller's fid to a new address. The `to` address must sign an EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature) message accepting the transfer. The `to` address must not already own an fid. | Parameter | type | Description | | --- | --- | --- | | to | `address` | Address to transfer the fid to | | deadline | `uint256` | Signature deadline | | sig | `bytes` | EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature)
signature from the `to` address | WARNING Transferring an fid does not reset its recovery address. To transfer an fid and update its recovery address, call [`transferAndChangeRecovery`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery) . If you are receiving an fid from an untrusted sender, ensure its recovery address is cleared or changed on transfer. #### Transfer signature [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature) To transfer an fid to another account, the caller must provide an EIP-712 typed signature from the receiving address in the following format: `Transfer(uint256 fid,address to,uint256 nonce,uint256 deadline)` | Parameter | type | Description | | --- | --- | --- | | fid | `uint256` | The fid being transferred | | to | `address` | The address receiving the fid. | | nonce | `uint256` | Current nonce of the signer address | | deadline | `uint256` | Signature expiration timestamp | @farcaster/hub-webViemhelpers.tsclients.ts ts import { ViemWalletEip712Signer } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const eip712Signer = new ViemWalletEip712Signer(walletClient); const signature = await eip712signer.signTransfer({ fid: 1n, to: account, nonce, deadline, }); ts import { ID_REGISTRY_EIP_712_TYPES } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const signature = await walletClient.signTypedData({ account, ...ID_REGISTRY_EIP_712_TYPES, primaryType: 'Transfer', message: { fid: 1n, to: account, nonce, deadline, }, }); ts import { ID_REGISTRY_ADDRESS, idRegistryABI } from '@farcaster/hub-web'; import { publicClient, account } from './clients.ts'; export const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return now + oneHour; }; export const readNonce = async () => { return await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'nonce', args: [account], }); }; ts import { createWalletClient, createPublicClient, custom, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; export const publicClient = createPublicClient({ chain: optimism, transport: http(), }); export const walletClient = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }); // JSON-RPC Account export const [account] = await walletClient.getAddresses(); // Local Account export const account = privateKeyToAccount('0x...'); ### transferAndChangeRecovery [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery) Transfer the fid of the caller to a new address _and_ change the fid's recovery address. This can be used to safely receive an fid transfer from an untrusted address. The receiving address must sign an EIP-712 [`TransferAndChangeRecovery`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery-signature) message accepting the transfer. The `to` address must not already own an fid. | Parameter | type | Description | | --- | --- | --- | | to | `address` | The address to transfer the fid to | | recovery | `address` | The new recovery address | | deadline | `uint256` | Signature deadline | | sig | `bytes` | EIP-712 [`TransferAndChangeRecovery`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery)
signature from the `to` address | #### TransferAndChangeRecovery signature [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery-signature) To transfer an fid to another account and change recovery, you must provide an EIP-712 typed signature from the `to` address in the following format: `TransferAndChangeRecovery(uint256 fid,address to,address recovery,uint256 nonce,uint256 deadline)` | Parameter | type | Description | | --- | --- | --- | | fid | `uint256` | The fid being transferred | | to | `address` | The address receiving the fid | | recovery | `address` | The new recovery address | | nonce | `uint256` | Current nonce of the signer address | | deadline | `uint256` | Signature expiration timestamp | @farcaster/hub-webViemhelpers.tsclients.ts ts import { ViemWalletEip712Signer } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const eip712Signer = new ViemWalletEip712Signer(walletClient); const signature = await eip712signer.signTransferAndChangeRecovery({ fid: 1n, to: account, recovery: '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7', nonce, deadline, }); ts import { ID_REGISTRY_EIP_712_TYPES } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const signature = await walletClient.signTypedData({ account, ...ID_REGISTRY_EIP_712_TYPES, primaryType: 'TransferAndChangeRecovery', message: { fid: 1n, to: account, recovery: '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7', nonce, deadline, }, }); ts import { ID_REGISTRY_ADDRESS, idGatewayABI } from '@farcaster/hub-web'; import { publicClient, account } from './clients.ts'; export const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return now + oneHour; }; export const readNonce = async () => { return await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'nonce', args: [account], }); }; ts import { createWalletClient, createPublicClient, custom, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; export const publicClient = createPublicClient({ chain: optimism, transport: http(), }); export const walletClient = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }); // JSON-RPC Account export const [account] = await walletClient.getAddresses(); // Local Account export const account = privateKeyToAccount('0x...'); ### recover [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#recover) Transfer an fid to a new address if caller is the recovery address. The `to` address must sign an EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature) message accepting the transfer. The `to` address must not already own an fid. | Parameter | type | Description | | --- | --- | --- | | from | `address` | The address to transfer the fid from | | to | `address` | The address to transfer the fid to | | deadline | `uint256` | Signature deadline | | sig | `bytes` | EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature)
signature from the `to` address | ### changeRecoveryAddressFor [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#changerecoveryaddressfor) Change the recovery address of an fid on behalf of the owner by providing a signature. The owner must sign an EIP-712 `ChangeRecoveryAddress` signature approving the change. | Parameter | type | Description | | --- | --- | --- | | owner | `address` | Address of the fid owner | | recovery | `address` | The new recovery address | | deadline | `uint256` | Signature deadline | | sig | `bytes` | EIP-712 [`ChangeRecoveryAddress`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature)
signature from the `to` address | ### ChangeRecoveryAddress signature [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#changerecoveryaddress-signature) To change a recovery address on behalf of an fid owner, the caller must provide an EIP-712 typed signature from the `owner` address in the following format: `ChangeRecoveryAddress(uint256 fid,address from,address to,uint256 nonce,uint256 deadline)` | Parameter | type | Description | | --- | --- | --- | | fid | `uint256` | The owner's fid | | from | `address` | The previous recovery address | | to | `address` | The new recovery address | | nonce | `uint256` | Current nonce of the signer address | | deadline | `uint256` | Signature expiration timestamp | @farcaster/hub-webViemhelpers.tsclients.ts ts import { ViemWalletEip712Signer } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const eip712Signer = new ViemWalletEip712Signer(walletClient); const signature = await eip712signer.signChangeRecoveryAddress({ fid: 1n, from: '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7', to: '0xD7029BDEa1c17493893AAfE29AAD69EF892B8ff2', nonce, deadline, }); ts import { ID_REGISTRY_EIP_712_TYPES } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const signature = await walletClient.signTypedData({ account, ...ID_REGISTRY_EIP_712_TYPES, primaryType: 'ChangeRecoveryAddress', message: { fid: 1n, from: '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7', to: '0xD7029BDEa1c17493893AAfE29AAD69EF892B8ff2', nonce, deadline, }, }); ts import { ID_REGISTRY_ADDRESS, idGatewayABI } from '@farcaster/hub-web'; import { publicClient, account } from './clients.ts'; export const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return now + oneHour; }; export const readNonce = async () => { return await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'nonces', args: [account], }); }; ts import { createWalletClient, createPublicClient, custom, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; export const publicClient = createPublicClient({ chain: optimism, transport: http(), }); export const walletClient = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }); // JSON-RPC Account export const [account] = await walletClient.getAddresses(); // Local Account export const account = privateKeyToAccount('0x...'); ### transferFor [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferfor) Transfer the fid owned by the `from` address to the `to` address. The caller must provide two EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature) signatures: one from the `from` address authorizing the transfer out and one from the `to` address accepting the transfer in. These messages have the [same format](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature) . The `to` address must not already own an fid. | Parameter | type | Description | | --- | --- | --- | | from | `address` | The address to transfer the fid from | | to | `address` | The address to transfer the fid to | | fromDeadline | `uint256` | Signature deadline | | fromSig | `bytes` | EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature)
signature from the `from` address | | toDeadline | `uint256` | Signature deadline | | toSig | `bytes` | EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature)
signature from the `to` address | ### transferAndChangeRecoveryFor [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecoveryfor) Transfer the fid owned by the `from` address to the `to` address, and change the fid's recovery address. This can be used to safely receive an fid transfer from an untrusted address. The caller must provide two EIP-712 [`TransferAndChangeRecovery`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery-signature) signatures: one from the `from` address authorizing the transfer out and one from the `to` address accepting the transfer in. These messages have the [same format](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery-signature) . The `to` address must not already own an fid. | Parameter | type | Description | | --- | --- | --- | | from | `address` | The address to transfer the fid from | | to | `address` | The address to transfer the fid to | | recovery | `address` | The new recovery address | | fromDeadline | `uint256` | Signature deadline | | fromSig | `bytes` | EIP-712 [`TransferAndChangeRecovery`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery-signature)
signature from the `from` address | | toDeadline | `uint256` | Signature deadline | | toSig | `bytes` | EIP-712 [`TransferAndChangeRecovery`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery-signature)
signature from the `to` address | ### recoverFor [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#recoverfor) Transfer an fid to a new address with a signature from the fid's recovery address. The caller must provide two EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature) signatures: one from the recovery address authorizing the transfer out and one from the `to` address accepting the transfer in. These messages have the [same format](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature) . The `to` address must not already own an fid. | Parameter | type | Description | | --- | --- | --- | | from | `address` | The address to transfer the fid from | | to | `address` | The address to transfer the fid to | | recoveryDeadline | `uint256` | The deadline for the recovery signature | | recoverySig | `bytes` | EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature)
signature from the recovery address | | toDeadline | `uint256` | The deadline for the receiver signature | | toSig | `bytes` | EIP-712 [`Transfer`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer-signature)
signature from the `to` address | Errors [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#errors) ---------------------------------------------------------------------------------------- | Error | Selector | Description | | --- | --- | --- | | HasId | `f90230a9` | The `to` address already owns an fid. | | HasNoId | `210b4b26` | The `from` address does not own an fid. | | InvalidSignature | `8baa579f` | The provided signature is invalid. It may be incorrectly formatted, or signed by the wrong address. | | SignatureExpired | `0819bdcd` | The provided signature has expired. Collect a new signature from the signer with a later deadline timestamp. | Source [​](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#source) ---------------------------------------------------------------------------------------- [`IdRegistry.sol`](https://github.com/farcasterxyz/contracts/blob/1aceebe916de446f69b98ba1745a42f071785730/src/IdRegistry.sol) --- # Resources / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/resources#VPContent) Return to top Resources [​](https://docs.farcaster.xyz/developers/resources#resources) ========================================================================= An opinionated list of the best libraries and tools for Farcaster developers. A more comprehensive list of resources can be found in a16z's [awesome-farcaster](https://github.com/a16z/awesome-farcaster) repo. Resources may be added via pull request. They must be purpose built for Farcaster only, actively maintained and adhere to the ethos and specification of the protocol. WARNING Resources are often from third parties and are not reviewed. Use them at your own risk. Libraries [​](https://docs.farcaster.xyz/developers/resources#libraries) ------------------------------------------------------------------------- ### Mini Apps [​](https://docs.farcaster.xyz/developers/resources#mini-apps) * [@farcaster/mini-app](https://miniapps.farcaster.xyz/docs/getting-started) \- CLI tool for building mini apps. * [@neynar/create-farcaster-mini-app](https://www.npmjs.com/package/@neynar/create-farcaster-mini-app) - A Mini App quickstart npx script from Neynar. ### Legacy Frames [​](https://docs.farcaster.xyz/developers/resources#legacy-frames) * [@frame-js/frames](https://framesjs.org/) - next.js template for building and debugging frames. * [@coinbase/onchainkit](https://github.com/coinbase/onchainkit) - react toolkit to create frames. * [@frog](https://frog.fm/) - framework for frames. ### Apps [​](https://docs.farcaster.xyz/developers/resources#apps) * [farcaster kit](https://www.farcasterkit.com/) - react hooks for building Farcaster apps. ### Snapchain [​](https://docs.farcaster.xyz/developers/resources#snapchain) * [@farcaster/hub-nodejs](https://www.npmjs.com/package/@farcaster/hub-nodejs) - lightweight, fast Typescript interface for Farcaster clients. ### Onchain [​](https://docs.farcaster.xyz/developers/resources#onchain) * [farcaster-solidity](https://github.com/pavlovdog/farcaster-solidity/) - libraries for verifying and parsing Farcaster messages onchain Dashboards [​](https://docs.farcaster.xyz/developers/resources#dashboards) --------------------------------------------------------------------------- * [Farcaster Hub Map](https://farcaster.spindl.xyz/) - geographical map of Farcaster hubs * [Dune: Farcaster](https://dune.com/pixelhack/farcaster) - @pixelhack's dashboard for network stats * [Dune: Farcaster User Onchain Activities](https://dune.com/yesyes/farcaster-users-onchain-activities) - dashboard of farcaster user's onchain activity Learning [​](https://docs.farcaster.xyz/developers/resources#learning) ----------------------------------------------------------------------- * [dTech Farcaster Tutorials](https://dtech.vision/farcaster) Open Source Examples [​](https://docs.farcaster.xyz/developers/resources#open-source-examples) ----------------------------------------------------------------------------------------------- * [quikcast](https://github.com/farcasterxyz/quikcast) - an end-to-end connected app implementation for Farcaster * [fc-polls](https://github.com/farcasterxyz/fc-polls) - a simple polling app built using frames Services [​](https://docs.farcaster.xyz/developers/resources#services) ----------------------------------------------------------------------- * [Neynar](https://neynar.com/) - infrastructure and services for building farcaster apps. * [dTech](https://dtech.vision/) - Farcaster Development and Consulting Agency --- # Messages / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#VPContent) Return to top Messages [​](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#messages) =================================================================================== Farcaster accounts interact by signing and publishing messages. Alice can create a message that says "_Hello @bob_" and sign it with her key. Messages are stored on a peer-to-peer network of nodes. A node in the Farcaster network is called a Hub, and each Hub stores a copy of the entire network. A user can publish a message to one Hub and it will propagate to the entire network in a few seconds. Farcaster's compact message format and eventually consistent model lets this architecture scale to millions of users. An account can generate a [key](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts#adding-account-keys) and give it to an app which can use it to sign messages. Users can use multiple apps with the same account, and each application can have its own key. Separating the signing keys from the ownership keys helps keep the account secure. Types [​](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#types) ----------------------------------------------------------------------------- Accounts can publish five different kinds of messages to the network: | Type | Description | Example | | --- | --- | --- | | Casts | Public messages that can be seen by anyone. | "Hello world!" | | Reactions | A relationship between an account and a cast. | Alice liked Bob's cast. | | Links | A relationship between two accounts. | Alice follows Bob. | | Profile Data | Metadata about the account. | Profile picture, display name. | | Verifications | A proof of ownership of something. | An Ethereum address. | Storage [​](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#storage) --------------------------------------------------------------------------------- An account must pay rent to keep their messages on the Farcaster network. Charging rent prevents users from spamming the network. An account can rent a unit of storage by making an onchain transaction to the Storage Registry. A unit of storage costs $7 today, lasts for one year and lets each account store a certain number of messages of each type. The limits for each type today are: * 5000 Casts * 2500 Reactions * 2500 Links * 50 Profile Data * 50 Verifications If an account exceeds its limit for a message type, the oldest message is pruned to make space for the new one. The user can keep using the network without paying for more storage and Hubs can keep the storage load under control. An account can always purchase more storage to increase its limits. An account that lets its storage expire may lose all its messages. There is a 30-day grace period after a storage unit expires during which an account must renew or lose its messages. The price and size of each storage unit is re-calculated periodically to balance growth and quality of the network. See [FIP-6](https://github.com/farcasterxyz/protocol/discussions/98) for more details. Deletion [​](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#deletion) ----------------------------------------------------------------------------------- An account can delete messages at any time by publishing a corresponding delete message. The delete message will remove the contents of the original message, leaving a tombstone in its place. A deleted message will still count towards the account's storage limit until it expires by being pushed out by a newer message. Timestamps [​](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#timestamps) --------------------------------------------------------------------------------------- Messages have timestamps which count seconds from the Farcaster Epoch, which began on `Jan 1, 2021 00:00:00 UTC`. Using a recent epoch makes timestamps and messages much smaller, which is important for the network. Timestamps are unverified and can be backdated by users, similar to a blog post. They cannot be more than 15 minutes into the future, as the network will reject such messages. Resources [​](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#resources) ------------------------------------------------------------------------------------- ### Specifications [​](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#specifications) * [Messages](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#2-message-specifications) - the atomic unit of change on Farcaster * [CRDTs](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#31-crdts) - rules for keeping messages in sync on the network * [Storage Registry](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#13-storage-registry) - contract to acquire storage units ### APIs [​](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#apis) * [Get Casts](https://snapchain.farcaster.xyz/reference/httpapi/casts) - fetch an account's casts from a Snapchain node * [Get Reactions](https://snapchain.farcaster.xyz/reference/httpapi/reactions) - fetch an account's reactions from a Snapchain node * [Get Links](https://snapchain.farcaster.xyz/reference/httpapi/links) - fetch an account's links or follows from a Snapchain node * [Get UserData](https://snapchain.farcaster.xyz/reference/httpapi/userdata) - fetch an account's profile data from a Snapchain node * [Submit Message](https://snapchain.farcaster.xyz/reference/httpapi/message#submitmessage) - broadcast a message to the Snapchain network * [Validate Message](https://snapchain.farcaster.xyz/reference/httpapi/message#validatemessage) - verify a message's authenticity with a Snapchain node * [Storage Registry](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry) - Acquire or check storage units for an account ### Tutorials [​](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#tutorials) * [Get casts](https://docs.farcaster.xyz/developers/guides/querying/fetch-casts) - Get an account's casts from a Snapchain node. * [Get profile](https://docs.farcaster.xyz/developers/guides/querying/fetch-profile) - Get an account's profile from a Snapchain node. * [Create common message types](https://docs.farcaster.xyz/developers/guides/writing/messages) - Create casts, links, reactions and userdata. * [Create casts with advanced features](https://docs.farcaster.xyz/developers/guides/writing/casts) - Create casts with embeds, emojis and mentions. --- # SignInButton / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/sign-in-button#VPContent) Return to top `SignInButton` [​](https://docs.farcaster.xyz/auth-kit/sign-in-button#signinbutton) ==================================================================================== The main component. Renders a "Sign in With Farcaster" button that prompts the user to scan a QR code with their phone in a web browser or redirects to a mobile device. You can use the `onSuccess` callback prop or the `useProfile` hook to access the user's authentication status and profile information. **Note:** Make sure you've wrapped your application in an [`AuthKitProvider`](https://docs.farcaster.xyz/auth-kit/auth-kit-provider) to use the `SignInButton` component. tsx import { SignInButton } from '@farcaster/auth-kit'; export const Login = () => { return ( console.log(`Hello, ${username}! Your fid is ${fid}.`) } /> ); }; Props [​](https://docs.farcaster.xyz/auth-kit/sign-in-button#props) -------------------------------------------------------------------- | Prop | Type | Description | Default | | --- | --- | --- | --- | | `timeout` | `number` | Return an error after polling for this long. | `300_000` (5 minutes) | | `interval` | `number` | Poll the relay server for updates at this interval. | `1500` (1.5 seconds) | | `nonce` | `string` | A random nonce to include in the Sign In With Farcaster message. | None | | `notBefore` | `string` | Time when the message becomes valid. ISO 8601 datetime string. | None | | `expirationTime` | `string` | Time when the message expires. ISO 8601 datetime string. | None | | `requestId` | `string` | An optional system-specific ID to include in the message. | None | | `onSuccess` | `function` | Callback invoked when sign in is complete and the user is authenticated. | None | | `onStatusResponse` | `function` | Callback invoked when the component receives a status update from the relay server. | None | | `onError` | `function` | Error callback function. | None | | `onSignOut` | `function` | Callback invoked when the user signs out. | None | | `hideSignOut` | `function` | Hide the Sign out button. | `false` | | `debug` | `boolean` | Render a debug panel displaying internal auth-kit state. | `false` | Examples [​](https://docs.farcaster.xyz/auth-kit/sign-in-button#examples) -------------------------------------------------------------------------- ### Custom nonce [​](https://docs.farcaster.xyz/auth-kit/sign-in-button#custom-nonce) tsx import { SignInButton } from '@farcaster/auth-kit'; export const Login = ({ nonce }: { nonce: string }) => { return ( console.log(`Hello, ${username}! Your fid is ${fid}.`) } /> ); }; --- # Channels / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#VPContent) Return to top Channels [​](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#channels) =================================================================================== A channel is a public space for your community to have conversations around a topic. Creating a channel starts a new feed for your community. People can join, cast and find other interesting people. It sparks conversations that wouldn’t otherwise happen on the home feed. Experimental Feature Channels are being prototyped in the Farcaster client and not fully supported by the Farcaster protocol. They may be ported to the protocol in the future if the feature is deemed successful or they may be removed entirely. Hosting Channels [​](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#hosting-channels) --------------------------------------------------------------------------------------------------- Anyone can create a channel host by paying a fee in the Farcaster client and choosing a channel name. The name must be under 16 characters and can only contain lowercase alphabets and numbers. A channel's creator is called a host and may invite other co-hosts to operate the channel. Hosts have special privileges like: 1. Defining “channel norms" which everyone must agree to when joining. 2. Pinning or hiding casts in a channel. 3. Blocking other users from casting in their channel. 4. Setting a channel picture, description and other metadata. Channel metadata is not part of the protocol and stored in the Farcaster client while channels are in the experimental stage. Casting in Channels [​](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#casting-in-channels) --------------------------------------------------------------------------------------------------------- Anyone can post into a channel by using the Farcaster client and selecting the channel when creating the cast. The client automatically sets the cast's `parentUrl` to `https://farcaster.xyz/~/channel/`. A cast is considered "in a channel" if it's parentUrl is the channel URI or another cast which is "in a channel". Channel casts are part of the protocol and stored on hubs. Using a replicator, you can fetch all casts in a channel by filtering the `parentUrl` field for the channel's FIP-2 URL. Following Channels [​](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#following-channels) ------------------------------------------------------------------------------------------------------- Anyone can follow a channel just like a user. A user will see casts from a followed channel in their home feed when using the Farcaster client. Channel follows are not part of the protocol and are stored in the Farcaster client while channels are in the experimental stage. Cast Visibility [​](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#cast-visibility) ------------------------------------------------------------------------------------------------- If a user casts in a channel, Farcaster will: 1. Always send the casts to the home feeds of any user who follows the channel. 2. Usually send the casts to the home feeds of any user who follows the author. The determination for (2) is made based on the user's preferences, channel contents and other social graph data. This algorithm is still being fine tuned and will be documented once it is stable. Usage Policy [​](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#usage-policy) ------------------------------------------------------------------------------------------- The Farcaster client may remove your channel and will NOT refund your warps if: 1. Your profile or channel impersonates someone. 2. You squat a channel without using it. 3. You violate the Farcaster client's terms and conditions or app store rules. FAQ [​](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#faq) ------------------------------------------------------------------------- **Why are channel hosts allowed to hide and ban? Isn’t this censorship?** Channels are not free-for-all public spaces, they are owned and moderated by their creators. You are always free to start your own channel at any time with its own rules. **Why is there a fee for creating channels?** The fee discourages people from squatting short names and not using the channels. **What's the benefit of creating a channel?** Starting a channel also helps grow your audience: 1. The Farcaster client will send your followers a notification about your channel. 2. Your channel will be promoted to users who follow similar channels. 3. Users who follow your channel will see channel casts in their home feed. Resources [​](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#resources) ------------------------------------------------------------------------------------- ### APIs [​](https://docs.farcaster.xyz/learn/what-is-farcaster/channels#apis) * [Farcaster Client Channel APIs](https://docs.farcaster.xyz/reference/warpcast/api) - fetch a list of all known channels --- # Key Registry / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#VPContent) Return to top Key Registry [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#key-registry) ===================================================================================================== The Key Registry stores the public keys associated with each Farcaster account. If you want to read information about a Farcaster account's keys or remove an existing key, use the Key Registry. If you want to add a new key, use the [Key Gateway](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway) instead. Read [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#read) ------------------------------------------------------------------------------------- ### totalKeys [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#totalkeys) Get the number of active keys (`uint256`) for an fid. | Parameter | type | Description | | --- | --- | --- | | fid | `uint256` | fid to look up | | state | `uint8` (1 for Added, 2 for Removed) | State of the key (added or removed) | ### keysOf [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#keysof) List all public keys (`bytes[]`) for an fid. | Parameter | type | Description | | --- | --- | --- | | fid | `uint256` | fid to look up | | state | `uint8` (1 for Added, 2 for Removed) | State of the key (added or removed) | | startIdx | `uint256` (optional) | Start index for pagination | | batchSize | `uint256` (optional) | Batch size for pagination | WARNING Don't call this onchain! This function is very gas intensive. It's meant to be called by offchain tools, not by other contracts. ### keyDataOf [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#keydataof) Returns the state (`uint8`) and keyType (`uint32`) of particular key for an fid. | Parameter | type | Description | | --- | --- | --- | | fid | `uint256` | fid to look up | | key | `bytes` | public key to check | Write [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#write) --------------------------------------------------------------------------------------- ### add [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#add) Will revert if called directly. Must be called via the [Key Gateway](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway) ### remove [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#remove) Removes a public key from the caller's fid and marks it as `Removed`. | Parameter | type | Description | | --- | --- | --- | | key | `bytes` | public key to remove | WARNING Removing a key will delete all offchain messages associated with the key from Hubs. ### removeFor [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#removefor) Remove a key on behalf of another fid by providing a signature. The fid owner must sign an EIP-712 `Remove` message approving the removal. Reverts if the key does not exist or is already removed. | Parameter | type | Description | | --- | --- | --- | | fidOwner | `address` | fid owner address | | key | `bytes` | public key to remove | | deadline | `uint256` | signature deadline | | sig | `bytes` | EIP-712 signature from the `fidOwner` | WARNING Removing a key will delete all offchain messages associated with the key from Hubs. #### Remove signature [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#remove-signature) To remove a key on behalf of another account, you must provide an EIP-712 typed signature from the account in the following format: `Remove(address owner,bytes key,uint256 nonce,uint256 deadline)` | Parameter | type | Description | | --- | --- | --- | | owner | `address` | Address that owns the fid. The typed message must be signed by this address. | | key | `bytes` | The public key to remove | | nonce | `uint256` | Current nonce of the `owner` address | | deadline | `uint256` | Signature expiration timestamp | @farcaster/hub-webViemhelpers.tssigner.tsclients.ts ts import { ViemWalletEip712Signer } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { getPublicKey } from './signer.ts'; import { readNonce, getDeadline } from './helpers.ts'; const publicKey = await getPublicKey(); const nonce = await readNonce(); const deadline = getDeadline(); const eip712Signer = new ViemWalletEip712Signer(walletClient); const signature = await eip712signer.signRemove({ owner: account, key: publicKey, nonce, deadline, }); ts import { KEY_REGISTRY_EIP_712_TYPES } from '@farcaster/hub-web'; import { bytesToHex } from 'viem'; import { walletClient, account } from './clients.ts'; import { getPublicKey } from './signer.ts'; import { readNonce, getDeadline } from './helpers.ts'; const publicKey = await getPublicKey(); const nonce = await readNonce(); const deadline = getDeadline(); const signature = await walletClient.signTypedData({ account, ...KEY_REGISTRY_EIP_712_TYPES, primaryType: 'Remove', message: { owner: account, key: bytesToHex(publicKey), nonce, deadline, }, }); ts import { KEY_REGISTRY_ADDRESS, keyRegistryABI } from '@farcaster/hub-web'; import { publicClient, account } from './clients.ts'; export const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return now + oneHour; }; export const readNonce = async () => { return await publicClient.readContract({ address: KEY_REGISTRY_ADDRESS, abi: keyRegistryABI, functionName: 'nonces', args: [account], }); }; ts import * as ed from '@noble/ed25519'; import { NobleEd25519Signer } from '@farcaster/hub-web'; const privateKeyBytes = ed.utils.randomPrivateKey(); export const accountKey = new NobleEd25519Signer(privateKeyBytes); export const getPublicKey = async () => { const accountKeyResult = await accountKey.getSignerKey(); if (accountKeyResult.isOk()) { return accountKeyResult.value; } }; ts import { createWalletClient, createPublicClient, custom, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; export const publicClient = createPublicClient({ chain: optimism, transport: http(), }); export const walletClient = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }); // JSON-RPC Account export const [account] = await walletClient.getAddresses(); // Local Account export const account = privateKeyToAccount('0x...'); Errors [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#errors) ----------------------------------------------------------------------------------------- | Error | Selector | Description | | --- | --- | --- | | ExceedsMaximum | `29264042` | Adding the key exceeds the maximum number of keys allowed per fid (currently 1000) | | InvalidSignature | `8baa579f` | The provided signature is invalid. It may be incorrectly formatted, or signed by the wrong address. | | InvalidState | `baf3f0f7` | The action violates state transition rules. (Adding a key that already exists, removing a key that does not exist, adding a key that has been removed) | | SignatureExpired | `0819bdcd` | The provided signature has expired. Collect a new signature from the signer with a later deadline timestamp. | Source [​](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#source) ----------------------------------------------------------------------------------------- [`KeyRegistry.sol`](https://github.com/farcasterxyz/contracts/blob/1aceebe916de446f69b98ba1745a42f071785730/src/KeyRegistry.sol) --- # AuthKitProvider / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/auth-kit-provider#VPContent) Return to top `AuthKitProvider` [​](https://docs.farcaster.xyz/auth-kit/auth-kit-provider#authkitprovider) ============================================================================================= Wrap your application in an `AuthKitProvider` to use Farcaster Auth. This provider component stores configuration information about your app and makes it available to auth-kit components and hooks. **Note:** You must create an `AuthKitProvider` to use Farcaster Connect. Don't forget to create one at the top level of your application. tsx const config = { domain: 'example.com', siweUri: 'https://example.com/login', rpcUrl: process.env.OP_MAINNET_RPC_URL, relay: 'https://relay.farcaster.xyz', }; const App = () => { return ( {/* Your App */} ); }; Props [​](https://docs.farcaster.xyz/auth-kit/auth-kit-provider#props) ======================================================================= | Prop | Type | Required | Description | | --- | --- | --- | --- | | `config` | `AuthKitConfig` | No | Configuration object. See the options in the table below. | `config` object options: | Parameter | Type | Required | Description | Default | | --- | --- | --- | --- | --- | | `domain` | `string` | No | The domain of your application. | `window.location.host` | | `siweUri` | `string` | No | The login URL of your application. | `window.location.href` | | `relay` | `string` | No | Farcaster Auth relay server URL | `https://relay.farcaster.xyz` | | `rpcUrl` | `string` | No | Optimism RPC server URL | `https://mainnet.optimism.io` | | `version` | `string` | No | Farcaster Auth version | `v1` | --- # Create an account / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/accounts/create-account#VPContent) Return to top Create an account [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account#create-an-account) ============================================================================================================== Pre-requisites * An Ethereum wallet on OP Mainnet, with sufficient ETH for gas costs. * An Ethereum provider URL for OP Mainnet (e.g. via [Alchemy](https://www.alchemy.com/) , [Infura](https://www.infura.io/) or [QuickNode](https://www.quicknode.com/) ). You can register a new user using the Bundler contract. To do so, you'll need to: 1. Set up [Viem](https://viem.sh/) clients and [`@farcaster/hub-web`](https://www.npmjs.com/package/@farcaster/hub-web) account keys. 2. Register an [app FID](https://docs.farcaster.xyz/reference/contracts/faq#what-is-an-app-fid-how-do-i-get-one) if your app does not already have one. 3. Collect a [`Register`](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#register-signature) signature from the user. 4. Create a new account key pair for the user. 5. Use your app account to create a [Signed Key Request](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator) . 6. Collect an [`Add`](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#add-signature) signature from the user. 7. Call the [Bundler](https://docs.farcaster.xyz/reference/contracts/reference/bundler#register) contract to register onchain. ### 1\. Set up clients and account keys [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account#_1-set-up-clients-and-account-keys) First, set up Viem clients and `@farcaster/hub-web` account keys. In this example, we'll use Viem local accounts and account keys, but you can also use `ViemWalletEip712Signer` to connect to a user's wallet rather than a local account. ts import * as ed from '@noble/ed25519'; import { ID_GATEWAY_ADDRESS, ID_REGISTRY_ADDRESS, ViemLocalEip712Signer, idGatewayABI, idRegistryABI, NobleEd25519Signer, BUNDLER_ADDRESS, bundlerABI, KEY_GATEWAY_ADDRESS, keyGatewayABI, } from '@farcaster/hub-nodejs'; import { bytesToHex, createPublicClient, createWalletClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; const APP_PRIVATE_KEY = '0x00'; const ALICE_PRIVATE_KEY = '0x00'; const publicClient = createPublicClient({ chain: optimism, transport: http(), }); const walletClient = createWalletClient({ chain: optimism, transport: http(), }); const app = privateKeyToAccount(APP_PRIVATE_KEY); const appAccountKey = new ViemLocalEip712Signer(app as any); const alice = privateKeyToAccount(ALICE_PRIVATE_KEY); const aliceAccountKey = new ViemLocalEip712Signer(alice as any); const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600); // Set the signatures' deadline to 1 hour from now const FARCASTER_RECOVERY_PROXY = '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7'; ### 2\. Register an app FID [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account#_2-register-an-app-fid) Register an app FID if you don't already have one. To register an FID, you'll need to read the price from the ID Gateway, then call the ID Gateway and pay the registration price. You can read back your new FID from the Id Registry contract, or parse it from a `Register` event. Here, we'll read it from the registry contract. ts const price = await publicClient.readContract({ address: ID_GATEWAY_ADDRESS, abi: idGatewayABI, functionName: 'price', args: [0n], }); const { request } = await publicClient.simulateContract({ account: app, address: ID_GATEWAY_ADDRESS, abi: idGatewayABI, functionName: 'register', args: [FARCASTER_RECOVERY_PROXY, 0n], value: price, }); await walletClient.writeContract(request); const APP_FID = await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'idOf', args: [app.address], }); ### 3\. Collect a `Register` signature from the user. [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account#_3-collect-a-register-signature-from-the-user) Collect an EIP-712 `Register` signature from the user. In a real world app, you'll likely collect this signature on your frontend, from the user's wallet. In a frontend context, you can us a `ViemEip712WalletSigner` to connect to a browser wallet rather than a local signer. ts let nonce = await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'nonces', args: [alice.address], }); const registerSignatureResult = await aliceAccountKey.signRegister({ to: alice.address as `0x${string}`, recovery: FARCASTER_RECOVERY_PROXY, nonce, deadline, }); let registerSignature; if (registerSignatureResult.isOk()) { registerSignature = registerSignatureResult.value; } else { throw new Error('Failed to generate register signature'); } ### 4\. Create a new account keypair [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account#_4-create-a-new-account-keypair) Create a new Ed25519 account keypair for the user. In a real app, ensure you keep the user's private key secret. ts const privateKeyBytes = ed.utils.randomPrivateKey(); const accountKey = new NobleEd25519Signer(privateKeyBytes); let accountPubKey = new Uint8Array(); const accountKeyResult = await accountKey.getSignerKey(); ### 5\. Use your app account to create a Signed Key Request [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account#_5-use-your-app-account-to-create-a-signed-key-request) Create a Signed Key Request, signed by your app account. To do so, you can use the `getSignedKeyRequestMetadata` helper, which generates and signs the Signed Key Request. ts if (accountKeyResult.isOk()) { accountPubKey = accountKeyResult.value; const signedKeyRequestMetadata = await appAccountKey.getSignedKeyRequestMetadata({ requestFid: APP_FID, key: accountPubKey, deadline, }); } ### 6\. Collect an `Add` signature from the user. [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account#_6-collect-an-add-signature-from-the-user) Collect an EIP-712 `Add` signature from the user to authorize adding an account key to their FID. ts if (signedKeyRequestMetadata.isOk()) { const metadata = bytesToHex(signedKeyRequestMetadata.value); nonce = await publicClient.readContract({ address: KEY_GATEWAY_ADDRESS, abi: keyGatewayABI, functionName: 'nonces', args: [alice.address], }); const addSignatureResult = await aliceAccountKey.signAdd({ owner: alice.address as `0x${string}`, keyType: 1, key: accountPubKey, metadataType: 1, metadata, nonce, deadline, }); } ### 7\. Call the Bundler contract to register onchain. [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account#_7-call-the-bundler-contract-to-register-onchain) Call the Key Gateway contract and provide the user's signature to add the key onchain. ts if (addSignatureResult.isOk()) { const addSignature = addSignatureResult.value; const price = await publicClient.readContract({ address: BUNDLER_ADDRESS, abi: bundlerABI, functionName: 'price', args: [0n], }); const { request } = await publicClient.simulateContract({ account: app, address: BUNDLER_ADDRESS, abi: bundlerABI, functionName: 'register', args: [\ {\ to: alice.address,\ recovery: FARCASTER_RECOVERY_PROXY,\ sig: bytesToHex(registerSignature),\ deadline,\ },\ [\ {\ keyType: 1,\ key: bytesToHex(accountPubKey),\ metadataType: 1,\ metadata: metadata,\ sig: bytesToHex(addSignature.value),\ deadline,\ },\ ],\ 0n,\ ], value: price, }); await walletClient.writeContract(request); } ### Full code example [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account#full-code-example) See the full code example below for all the steps above. @farcaster/hub-web ts import * as ed from '@noble/ed25519'; import { ID_GATEWAY_ADDRESS, ID_REGISTRY_ADDRESS, ViemLocalEip712Signer, idGatewayABI, idRegistryABI, NobleEd25519Signer, BUNDLER_ADDRESS, bundlerABI, KEY_GATEWAY_ADDRESS, keyGatewayABI, } from '@farcaster/hub-nodejs'; import { bytesToHex, createPublicClient, createWalletClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; const APP_PRIVATE_KEY = '0x00'; const ALICE_PRIVATE_KEY = '0x00'; const publicClient = createPublicClient({ chain: optimism, transport: http('http://localhost:8545'), }); const walletClient = createWalletClient({ chain: optimism, transport: http('http://localhost:8545'), }); const app = privateKeyToAccount(APP_PRIVATE_KEY); const appAccountKey = new ViemLocalEip712Signer(app as any); const alice = privateKeyToAccount(ALICE_PRIVATE_KEY); const aliceAccountKey = new ViemLocalEip712Signer(alice as any); const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600); // Set the signatures' deadline to 1 hour from now const FARCASTER_RECOVERY_PROXY = '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7'; /******************************************************************************* * IdGateway - register - Register an app FID. *******************************************************************************/ /** * Get the current price to register. We're not going to register any * extra storage, so we pass 0n as the only argument. */ const price = await publicClient.readContract({ address: ID_GATEWAY_ADDRESS, abi: idGatewayABI, functionName: 'price', args: [0n], }); /** * Call `register` to register an FID to the app account. */ const { request } = await publicClient.simulateContract({ account: app, address: ID_GATEWAY_ADDRESS, abi: idGatewayABI, functionName: 'register', args: [FARCASTER_RECOVERY_PROXY, 0n], value: price, }); await walletClient.writeContract(request); /** * Read the app fid from the Id Registry contract. */ const APP_FID = await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'idOf', args: [app.address], }); /******************************************************************************* * Collect Register signature from Alice *******************************************************************************/ let nonce = await publicClient.readContract({ address: KEY_GATEWAY_ADDRESS, abi: keyGatewayABI, functionName: 'nonces', args: [alice.address], }); const registerSignatureResult = await aliceAccountKey.signRegister({ to: alice.address as `0x${string}`, recovery: FARCASTER_RECOVERY_PROXY, nonce, deadline, }); let registerSignature; if (registerSignatureResult.isOk()) { registerSignature = registerSignatureResult.value; } else { throw new Error('Failed to generate register signature'); } /******************************************************************************* * Collect Add signature from Alice. *******************************************************************************/ /** * 1. Create an Ed25519 account keypair for Alice and get the public key. */ const privateKeyBytes = ed.utils.randomPrivateKey(); const accountKey = new NobleEd25519Signer(privateKeyBytes); let accountPubKey = new Uint8Array(); const accountKeyResult = await accountKey.getSignerKey(); if (accountKeyResult.isOk()) { accountPubKey = accountKeyResult.value; /** * 2. Generate a Signed Key Request from the app account. */ const signedKeyRequestMetadata = await appAccountKey.getSignedKeyRequestMetadata({ requestFid: APP_FID, key: accountPubKey, deadline, }); if (signedKeyRequestMetadata.isOk()) { const metadata = bytesToHex(signedKeyRequestMetadata.value); /** * 3. Read Alice's nonce from the Key Gateway. */ nonce = await publicClient.readContract({ address: KEY_GATEWAY_ADDRESS, abi: keyGatewayABI, functionName: 'nonces', args: [alice.address], }); /** * Then, collect her `Add` signature. */ const addSignatureResult = await aliceAccountKey.signAdd({ owner: alice.address as `0x${string}`, keyType: 1, key: accountPubKey, metadataType: 1, metadata, nonce, deadline, }); if (addSignatureResult.isOk()) { const addSignature = addSignatureResult.value; /** * Read the current registration price. */ const price = await publicClient.readContract({ address: BUNDLER_ADDRESS, abi: bundlerABI, functionName: 'price', args: [0n], }); /** * Call `register` with Alice's signatures, registration, and key parameters. */ const { request } = await publicClient.simulateContract({ account: app, address: BUNDLER_ADDRESS, abi: bundlerABI, functionName: 'register', args: [\ {\ to: alice.address,\ recovery: FARCASTER_RECOVERY_PROXY,\ sig: bytesToHex(registerSignature),\ deadline,\ },\ [\ {\ keyType: 1,\ key: bytesToHex(accountPubKey),\ metadataType: 1,\ metadata: metadata,\ sig: bytesToHex(addSignature),\ deadline,\ },\ ],\ 0n,\ ], value: price, }); await walletClient.writeContract(request); } } } See the [Bundler](https://docs.farcaster.xyz/reference/contracts/reference/bundler#register) reference for more details. --- # Key Gateway / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#VPContent) Return to top Key Gateway [​](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#key-gateway) ================================================================================================== The Key Gateway adds new keys to the [Key Registry](https://docs.farcaster.xyz/reference/contracts/reference/key-registry) . If you want to add a new public key to a Farcaster account, use the Key Gateway. Read [​](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#read) ------------------------------------------------------------------------------------ ### nonces [​](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#nonces) Returns the next available nonce for an address. Used for generating EIP-712 signatures in [addFor](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#addFor) . | Parameter | type | Description | | --- | --- | --- | | owner | `address` | The address to query the nonce for | Write [​](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#write) -------------------------------------------------------------------------------------- ### add [​](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#add) Add a new key for the caller's fid and set its state to `Added`. Revert if the key is already registered. | Parameter | type | Description | | --- | --- | --- | | keyType | `uint32` | Must be set to `1`. This is currently the only supported `keyType`. | | key | `bytes` | The public key to add | | metadataType | `uint8` | Must be set to `1`. This is currently the only supported `metadataType`. | | metadata | `bytes` | Encoded [`SignedKeyRequestMetadata`](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequestmetadata-struct) | ### addFor [​](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#addfor) Add a key on behalf of another fid by providing a signature. The owner must sign an EIP-712 `Add` message approving the key. Reverts if the key is already registered. | Parameter | type | Description | | --- | --- | --- | | fidOwner | `address` | Address of the fid owner | | keyType | `uint32` | Must be set to `1`. This is currently the only supported `keyType`. | | key | `bytes` | The public key to add | | metadataType | `uint8` | Must be set to `1`. This is currently the only supported `metadataType`. | | metadata | `bytes` | Encoded [`SignedKeyRequestMetadata`](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequestmetadata-struct) | | deadline | `uint256` | Signature expiration timestamp | | sig | `bytes` | EIP-712 [`Add`](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#add-signature)
signature from `fidOwner` | #### Add signature [​](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#add-signature) To add a key on behalf of another account, you must provide an EIP-712 typed signature from the account in the following format: `Add(address owner,uint32 keyType,bytes key,uint8 metadataType,bytes metadata,uint256 nonce,uint256 deadline)` | Parameter | type | Description | | --- | --- | --- | | owner | `address` | Address that owns the fid. The typed message must be signed by this address. | | keyType | `uint32` | Must be set to `1`. This is currently the only supported `keyType`. | | key | `bytes` | The public key to add | | metadataType | `uint8` | Must be set to `1`. This is currently the only supported `metadataType`. | | metadata | `bytes` | Encoded [`SignedKeyRequestMetadata`](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequestmetadata-struct) | | nonce | `uint256` | Current nonce of the `owner` address | | deadline | `uint256` | Signature expiration timestamp | @farcaster/hub-webViemhelpers.tsmetadata.tssigner.tsclients.ts ts import { ViemWalletEip712Signer } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { getPublicKey } from './signer.ts'; import { readNonce, getDeadline } from './helpers.ts'; const publicKey = await getPublicKey(); const metadata = await getMetadata(); const nonce = await readNonce(); const deadline = getDeadline(); const eip712Signer = new ViemWalletEip712Signer(walletClient); const signature = await eip712signer.signAdd({ owner: account, keyType: 1, key: publicKey, metadataType: 1, metadata, nonce, deadline, }); ts import { KEY_GATEWAY_EIP_712_TYPES } from '@farcaster/hub-web'; import { bytesToHex } from 'viem'; import { walletClient, account } from './clients.ts'; import { getPublicKey } from './signer.ts'; import { getMetadata } from './metadata.ts'; import { readNonce, getDeadline } from './helpers.ts'; const publicKey = await getPublicKey(); const metadata = await getMetadata(); const nonce = await readNonce(); const deadline = getDeadline(); const signature = await walletClient.signTypedData({ account, ...KEY_GATEWAY_EIP_712_TYPES, primaryType: 'Add', message: { owner: account, keyType: 1, key: bytesToHex(publicKey), metadataType: 1, metadata, nonce, deadline, }, }); ts import { KEY_GATEWAY_ADDRESS, keyGatewayABI } from '@farcaster/hub-web'; import { publicClient, account } from './clients.ts'; export const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return now + oneHour; }; export const readNonce = async () => { return await publicClient.readContract({ address: KEY_GATEWAY_ADDRESS, abi: keyGatewayABI, functionName: 'nonces', args: [account], }); }; ts import { ViemLocalEip712Signer } from '@farcaster/hub-web'; import { privateKeyToAccount } from 'viem/accounts'; import { getDeadline } from './helpers.ts'; import { getPublicKey } from './signer.ts'; // App account export const appAccount = privateKeyToAccount('0x...'); const deadline = getDeadline(); const publicKey = await getPublicKey(); export const getMetadata = async () => { const eip712signer = new ViemLocalEip712Signer(appAccount); const metadata = await eip712signer.getSignedKeyRequestMetadata({ requestFid: 9152n, // App fid key: publicKey, deadline, }); if (metadata.isOk()) { return metadata.value; } }; ts import * as ed from '@noble/ed25519'; import { NobleEd25519Signer } from '@farcaster/hub-web'; const privateKeyBytes = ed.utils.randomPrivateKey(); export const accountKey = new NobleEd25519Signer(privateKeyBytes); export const getPublicKey = async () => { const accountKeyResult = await accountKey.getSignerKey(); if (accountKeyResult.isOk()) { return accountKeyResult.value; } }; ts import { createWalletClient, createPublicClient, custom, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; export const publicClient = createPublicClient({ chain: optimism, transport: http(), }); export const walletClient = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }); // JSON-RPC Account export const [account] = await walletClient.getAddresses(); // Local Account export const account = privateKeyToAccount('0x...'); Errors [​](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#errors) ---------------------------------------------------------------------------------------- | Error | Selector | Description | | --- | --- | --- | | InvalidMetadata | `bcecb64a` | The signed metadata provided with the key is invalid. | | InvalidSignature | `8baa579f` | The provided signature is invalid. It may be incorrectly formatted, or signed by the wrong address. | | SignatureExpired | `0819bdcd` | The provided signature has expired. Collect a new signature from the signer with a later deadline timestamp. | Source [​](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#source) ---------------------------------------------------------------------------------------- [`KeyGateway.sol`](https://github.com/farcasterxyz/contracts/blob/1aceebe916de446f69b98ba1745a42f071785730/src/KeyGateway.sol) --- # Create an account key / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#VPContent) Return to top Create an account key [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#create-an-account-key) ========================================================================================================================== An account can authorize account keys, which can create messages on its behalf. The owner of the account can revoke an account key at any time. To add an account key, you'll need to follow six steps: 1. Set up [Viem](https://viem.sh/) clients and [`@farcaster/hub-web`](https://www.npmjs.com/package/@farcaster/hub-web) account key. 2. Register an [app FID](https://docs.farcaster.xyz/reference/contracts/faq#what-is-an-app-fid-how-do-i-get-one) if your app does not already have one. 3. Create a new account key for the user. 4. Use your app account to create a [Signed Key Request](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator) . 5. Collect an [`Add`](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#add-signature) signature from the user. 6. Call the [Key Gateway](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#addFor) contract to add the key onchain. ### Requirements [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#requirements) * An ETH wallet on OP Mainnet, with some ETH. * An ETH RPC URL for OP Mainnet (e.g. via [Alchemy](https://www.alchemy.com/) , [Infura](https://www.infura.io/) or [QuickNode](https://www.quicknode.com/) ). ### 1\. Set up clients and account key [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#_1-set-up-clients-and-account-key) First, set up Viem clients and `@farcaster/hub-web` account key. In this example, we'll use Viem local accounts and account key, but you can also use `ViemWalletEip712Signer` to connect to a user's wallet rather than a local account. ts import * as ed from '@noble/ed25519'; import { ID_GATEWAY_ADDRESS, ID_REGISTRY_ADDRESS, ViemLocalEip712Signer, idGatewayABI, idRegistryABI, NobleEd25519Signer, KEY_GATEWAY_ADDRESS, keyGatewayABI, } from '@farcaster/hub-nodejs'; import { bytesToHex, createPublicClient, createWalletClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; const APP_PRIVATE_KEY = '0x00'; const ALICE_PRIVATE_KEY = '0x00'; const publicClient = createPublicClient({ chain: optimism, transport: http(), }); const walletClient = createWalletClient({ chain: optimism, transport: http(), }); const app = privateKeyToAccount(APP_PRIVATE_KEY); const appAccountKey = new ViemLocalEip712Signer(app as any); const alice = privateKeyToAccount(ALICE_PRIVATE_KEY); const aliceAccountKey = new ViemLocalEip712Signer(alice as any); const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600); // Set the signatures' deadline to 1 hour from now const FARCASTER_RECOVERY_PROXY = '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7'; ### 2\. Register an app FID [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#_2-register-an-app-fid) Register an app FID if you don't already have one. To register an FID, you'll need to read the price from the ID Gateway, then call the ID Gateway and pay the registration price. You can read back your new FID from the Id Registry contract, or parse it from a `Register` event. Here, we'll read it from the registry contract. ts const price = await publicClient.readContract({ address: ID_GATEWAY_ADDRESS, abi: idGatewayABI, functionName: 'price', args: [0n], }); const { request } = await publicClient.simulateContract({ account: app, address: ID_GATEWAY_ADDRESS, abi: idGatewayABI, functionName: 'register', args: [FARCASTER_RECOVERY_PROXY, 0n], value: price, }); await walletClient.writeContract(request); const APP_FID = await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'idOf', args: [app.address], }); ### 3\. Create a new account key [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#_3-create-a-new-account-key) Create a new Ed25519 key pair for the user. In a real app, ensure you keep the private key secret. ts const privateKeyBytes = ed.utils.randomPrivateKey(); const accountKey = new NobleEd25519Signer(privateKeyBytes); let accountPubKey = new Uint8Array(); const accountKeyResult = await accountKey.getSignerKey(); ### 4\. Use your app account to create a Signed Key Request [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#_4-use-your-app-account-to-create-a-signed-key-request) Create a Signed Key Request, signed by your app account. To do so, you can use the `getSignedKeyRequestMetadata` helper, which generates and signs the Signed Key Request. ts if (accountKeyResult.isOk()) { accountPubKey = accountKeyResult.value; const signedKeyRequestMetadata = await appAccountKey.getSignedKeyRequestMetadata({ requestFid: APP_FID, key: accountPubKey, deadline, }); } ### 5\. Collect an `Add` signature from the user. [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#_5-collect-an-add-signature-from-the-user) Collect an EIP-712 `Add` signature from the user to authorize adding an account key to their FID. In a real world app, you'll likely collect this signature on your frontend, from the user's wallet. In a frontend context, you can us a `ViemEip712WalletSigner` to connect to a browser wallet rather than a local signer. ts if (signedKeyRequestMetadata.isOk()) { const metadata = bytesToHex(signedKeyRequestMetadata.value); const aliceNonce = await publicClient.readContract({ address: KEY_GATEWAY_ADDRESS, abi: keyGatewayABI, functionName: 'nonces', args: [alice.address], }); const aliceSignature = await aliceAccountKey.signAdd({ owner: alice.address as `0x${string}`, keyType: 1, key: accountPubKey, metadataType: 1, metadata, nonce, deadline, }); } ### 6\. Call the Key Gateway contract to add the key onchain. [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#_6-call-the-key-gateway-contract-to-add-the-key-onchain) Call the Key Gateway contract and provide the user's signature to add the key onchain. ts if (aliceSignature.isOk()) { const { request } = await publicClient.simulateContract({ account: app, address: KEY_GATEWAY_ADDRESS, abi: keyGatewayABI, functionName: 'addFor', args: [\ alice.address,\ 1,\ bytesToHex(accountPubKey),\ 1,\ metadata,\ deadline,\ bytesToHex(aliceSignature.value),\ ], }); await walletClient.writeContract(request); } ### Full code example [​](https://docs.farcaster.xyz/developers/guides/accounts/create-account-key#full-code-example) See the full code example below for all the steps above. @farcaster/hub-web ts import * as ed from '@noble/ed25519'; import { ID_GATEWAY_ADDRESS, ID_REGISTRY_ADDRESS, ViemLocalEip712Signer, idGatewayABI, idRegistryABI, NobleEd25519Signer, KEY_GATEWAY_ADDRESS, keyGatewayABI, } from '@farcaster/hub-nodejs'; import { bytesToHex, createPublicClient, createWalletClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; const APP_PRIVATE_KEY = '0x00'; const ALICE_PRIVATE_KEY = '0x00'; /******************************************************************************* * Setup - Create local accounts, Viem clients, helpers, and constants. *******************************************************************************/ /** * Create Viem public (read) and wallet (write) clients. */ const publicClient = createPublicClient({ chain: optimism, transport: http(), }); const walletClient = createWalletClient({ chain: optimism, transport: http(), }); /** * A local account representing your app. You'll * use this to sign key metadata and send * transactions on behalf of users. */ const app = privateKeyToAccount(APP_PRIVATE_KEY); const appAccountKey = new ViemLocalEip712Signer(app as any); console.log('App:', app.address); /** * A local account representing Alice, a user. */ const alice = privateKeyToAccount(ALICE_PRIVATE_KEY); const aliceAccountKey = new ViemLocalEip712Signer(alice as any); console.log('Alice:', alice.address); /** * Generate a deadline timestamp one hour from now. * All Farcaster EIP-712 signatures include a deadline, a block timestamp * after which the signature is no longer valid. */ const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600); // Set the signatures' deadline to 1 hour from now const FARCASTER_RECOVERY_PROXY = '0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7'; /******************************************************************************* * IdGateway - register - Register an app FID. *******************************************************************************/ /** * Get the current price to register. We're not going to register any * extra storage, so we pass 0n as the only argument. */ const price = await publicClient.readContract({ address: ID_GATEWAY_ADDRESS, abi: idGatewayABI, functionName: 'price', args: [0n], }); /** * Call `register` to register an FID to the app account. */ const { request } = await publicClient.simulateContract({ account: app, address: ID_GATEWAY_ADDRESS, abi: idGatewayABI, functionName: 'register', args: [FARCASTER_RECOVERY_PROXY, 0n], value: price, }); await walletClient.writeContract(request); /** * Read the app FID from the Id Registry contract. */ const APP_FID = await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'idOf', args: [app.address], }); /******************************************************************************* * KeyGateway - addFor - Add an account key to Alice's FID. *******************************************************************************/ /** * To add an account key to Alice's FID, we need to follow four steps: * * 1. Create a new account key pair for Alice. * 2. Use our app account to create a Signed Key Request. * 3. Collect Alice's `Add` signature. * 4. Call the contract to add the key onchain. */ /** * 1. Create an Ed25519 account key pair for Alice and get the public key. */ const privateKeyBytes = ed.utils.randomPrivateKey(); const accountKey = new NobleEd25519Signer(privateKeyBytes); let accountPubKey = new Uint8Array(); const accountKeyResult = await accountKey.getSignerKey(); if (accountKeyResult.isOk()) { accountPubKey = accountKeyResult.value; /** * 2. Generate a Signed Key Request from the app account. */ const signedKeyRequestMetadata = await appAccountKey.getSignedKeyRequestMetadata({ requestFid: APP_FID, key: accountPubKey, deadline, }); if (signedKeyRequestMetadata.isOk()) { const metadata = bytesToHex(signedKeyRequestMetadata.value); /** * 3. Read Alice's nonce from the Key Gateway. */ const aliceNonce = await publicClient.readContract({ address: KEY_GATEWAY_ADDRESS, abi: keyGatewayABI, functionName: 'nonces', args: [alice.address], }); /** * Then, collect her `Add` signature. */ const aliceSignature = await aliceAccountKey.signAdd({ owner: alice.address as `0x${string}`, keyType: 1, key: accountPubKey, metadataType: 1, metadata, nonce: aliceNonce, deadline, }); if (aliceSignature.isOk()) { /** * Call `addFor` with Alice's signature and the signed key request. */ const { request } = await publicClient.simulateContract({ account: app, address: KEY_GATEWAY_ADDRESS, abi: keyGatewayABI, functionName: 'addFor', args: [\ alice.address,\ 1,\ bytesToHex(accountPubKey),\ 1,\ metadata,\ deadline,\ bytesToHex(aliceSignature.value),\ ], }); await walletClient.writeContract(request); } } } See the [Key Registry](https://docs.farcaster.xyz/reference/contracts/reference/key-registry#add) reference for more details. --- # Storage Registry / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#VPContent) Return to top Storage Registry [​](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#storage-registry) ================================================================================================================= The Storage Registry allows Farcaster accounts to rent one or more "units" of storage on the network. If you want to rent storage for a Farcaster account, use the Storage Registry. Read [​](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#read) ----------------------------------------------------------------------------------------- ### unitPrice [​](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#unitprice) Get the price in wei (`uint256`) to register 1 unit of storage. ### price [​](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#price) Get the price in wei (`uint256`) to register a specific number of storage units. | Param Name | type | Description | | --- | --- | --- | | units | `uint256` | The number of storage units to rent | Write [​](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#write) ------------------------------------------------------------------------------------------- ### rent [​](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#rent) Rent a specific number of storage units for a given fid. Excess ether will be returned to the caller. Rented units are valid for 1 year from the time of registration. | Param Name | type | Description | | --- | --- | --- | | `msg.value` | `wei` | Payment amount | | fid | `uint256` | The fid to credit the storage units to | | units | `uint256` | The number of units of storage to rent | ### batchRent [​](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#batchrent) Rent storage for multiple fids in one transaction. The caller must send enough ether to cover the total cost of all units. Like single-unit rental, extra ether is returned and units are valid for 1 year. | Param Name | type | Description | | --- | --- | --- | | `msg.value` | `wei` | Total payment amount | | fids | `uint256[]` | Array of fids | | units | `uint256[]` | Array of unit quantities, corresponding to each fid in the `fids` array | Errors [​](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#errors) --------------------------------------------------------------------------------------------- | Error | Selector | Description | | --- | --- | --- | | InvalidPayment | `3c6b4b28` | The caller didn't provide enough ether to pay for the number of storage units requested. | | InvalidBatchInput | `0a514b99` | The caller provided mismatched arrays of `fids` and `units`. | Source [​](https://docs.farcaster.xyz/reference/contracts/reference/storage-registry#source) --------------------------------------------------------------------------------------------- [`StorageRegistry.sol`](https://github.com/farcasterxyz/contracts/blob/1aceebe916de446f69b98ba1745a42f071785730/src/validators/StorageRegistry.sol) --- # Apps / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#VPContent) On this page Apps [​](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#apps) ======================================================================= Using Farcaster requires an Ethereum wallet to register your account and UI to browse the network. If you are new, we recommend starting with the official Farcaster client on [iOS](https://apps.apple.com/us/app/warpcast/id1600555445) or [Android](https://play.google.com/store/apps/details?id=com.farcaster.mobile&hl=en_US&gl=US) There are two kinds of apps: 1. **Wallet App** - allows signing up, adding connected apps, posting and browsing messages. 2. **Connected App** - allows posting and browsing messages only. Wallet Apps [​](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#wallet-apps) ------------------------------------------------------------------------------------- Users must install a wallet app to get started with Farcaster. They can take onchain and offchain actions like signing up, adding connected apps, posting messages and users. A wallet app controls the Ethereum address that owns the account. It has control over the account and can take any action on your behalf, so only use a wallet app that you trust. ### Farcaster client [​](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#farcaster-client) The Farcaster client is a wallet app developed by the Farcaster team. It has a web and mobile app, though signing up is only available on mobile. * Download: [iOS](https://apps.apple.com/us/app/warpcast/id1600555445) , [Android](https://play.google.com/store/apps/details?id=com.farcaster.mobile&hl=en_US&gl=US) Connected Apps [​](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#connected-apps) ------------------------------------------------------------------------------------------- Connected apps can only be added once a user signs up with a wallet app. They can take offchain actions on Farcaster like writing casts, following accounts and browsing. A connected app controls an app key granted by the wallet app. Users can add many connected apps to their account and remove them at any time. A malicious connected app cannot take control of your account and any actions it takes can be reversed by your wallet app. Some popular connected apps include: * [Supercast](https://supercast.xyz/) * [Yup](https://yup.io/) * [Farcord](https://farcord.com/) **Connected apps are not reviewed by Farcaster, use them at your own risk** Resources [​](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#resources) --------------------------------------------------------------------------------- ### Tools [​](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#tools) * [Snapchain](https://snapchain.farcaster.xyz/) - a node for reading and writing messages. * [Replicator](https://github.com/farcasterxyz/hub-monorepo/tree/main/apps/replicator) - a tool to sync a hub to a postgres database. ### Tutorials [​](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#tutorials) * [Set up snapchain](https://snapchain.farcaster.xyz/guides/running-a-node) - run a snapchain node. * [Set up replicator](https://snapchain.farcaster.xyz/guides/syncing-to-db) - sync a hub to postgres for easy querying. * [Schema for replication](https://docs.farcaster.xyz/reference/replicator/schema) - schema for a replicator's postgres tables. ### Services [​](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#services) * [Neynar](https://neynar.com/) - infrastructure and services for building farcaster apps. --- # useSignIn / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/hooks/use-sign-in#VPContent) On this page `useSignIn` [​](https://docs.farcaster.xyz/auth-kit/hooks/use-sign-in#usesignin) ================================================================================= Hook for signing in a user. Connects to the relay server, generates a sign in link to present to the user, and polls the relay server for the user's Farcaster wallet signature. If you want to build your own sign in component with a custom UI, use the `useSignIn` hook. tsx import { useSignIn, QRCode } from '@farcaster/auth-kit'; function App() { const { signIn, url, data: { username }, } = useSignIn({ onSuccess: ({ fid }) => console.log('Your fid:', fid), }); return (
{url && ( Scan this: )} {username && `Hello, ${username}!`}
); } Parameters [​](https://docs.farcaster.xyz/auth-kit/hooks/use-sign-in#parameters) --------------------------------------------------------------------------------- | Parameter | Type | Description | Default | | --- | --- | --- | --- | | `timeout` | `number` | Return an error after polling for this long. | `300_000` (5 minutes) | | `interval` | `number` | Poll the relay server for updates at this interval. | `1500` (1.5 seconds) | | `nonce` | `string` | A random nonce to include in the Sign In With Farcaster message. | None | | `notBefore` | `string` | Time when the SIWF message becomes valid. ISO 8601 datetime string. | None | | `expirationTime` | `string` | Time when the SIWF message expires. ISO 8601 datetime string. | None | | `requestId` | `string` | An optional system-specific ID to include in the SIWF message. | None | | `onSuccess` | `function` | Callback invoked when sign in is complete and the user is authenticated. | None | | `onStatusResponse` | `function` | Callback invoked when the component receives a status update from the relay server. | None | | `onError` | `function` | Error callback function. | None | Returns [​](https://docs.farcaster.xyz/auth-kit/hooks/use-sign-in#returns) --------------------------------------------------------------------------- ts { signIn: () => void; signOut: () => void; connect: () => void; reconnect: () => void; isConnected: boolean; isSuccess: boolean; isPolling: boolean; isError: boolean; error: AuthClientError; channelToken: string; url: string; appClient: AppClient; data: { state: "pending" | "complete"; nonce: string; message: string; signature: Hex; fid: number; username: string; bio: string; displayName: string; pfpUrl: string; custody?: Hex; verifications?: Hex[]; }, validSignature: boolean; }; | Parameter | Description | | --- | --- | | `signIn` | Call this function following `connect` to begin polling for a signature. | | `signOut` | Call this function to clear the AuthKit state and sign out the user. | | `connect` | Connect to the auth relay and create a channel. | | `reconnect` | Reconnect to the relay and try again. Call this in the event of an error. | | `isConnected` | True if AuthKit is connected to the relay server and has an active channel. | | `isSuccess` | True when the relay server returns a valid signature. | | `isPolling` | True when the relay state is `"pending"` and the app is polling the relay server for a response. | | `isError` | True when an error has occurred. | | `error` | `AuthClientError` instance. | | `channelToken` | Connect relay channel token. | | `url` | Sign in With Farcaster URL to present to the user. Links to the Farcaster client in v1. | | `appClient` | Underlying `AppClient` instance. | | `data.state` | Status of the sign in request, either `"pending"` or `"complete"` | | `data.nonce` | Random nonce used in the SIWE message. If you don't provide a custom nonce as an argument to the hook, you should read this value. | | `data.message` | The generated SIWE message. | | `data.signature` | Hex signature produced by the user's Farcaster client app wallet. | | `data.fid` | User's Farcaster ID. | | `data.username` | User's Farcaster username. | | `data.bio` | User's Farcaster bio. | | `data.displayName` | User's Farcaster display name. | | `data.pfpUrl` | User's Farcaster profile picture URL. | | `data.custody` | User's FID custody address. | | `data.verifications` | List of user's verified addresses. | | `validSignature` | True when the signature returned by the relay server is valid. | --- # Architecture / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/architecture/overview#VPContent) On this page Architecture [​](https://docs.farcaster.xyz/learn/architecture/overview#architecture) ====================================================================================== Farcaster has a hybrid architecture that stores identity onchain and data offchain. ![Architecture](https://docs.farcaster.xyz/assets/architecture.BPu0I8Sc.png) Onchain [​](https://docs.farcaster.xyz/learn/architecture/overview#onchain) ---------------------------------------------------------------------------- Farcaster's onchain systems are implemented as [contracts on OP Mainnet](https://docs.farcaster.xyz/learn/architecture/contracts) . Actions are performed onchain only when security and consistency are critical. Use of onchain actions is kept at a minimum to reduce costs and improve performance. Only a handful of actions are performed onchain, including: * Creating an [account](https://docs.farcaster.xyz/learn/what-is-farcaster/accounts) . * Paying rent to [store data](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#storage) . * Adding account keys for [connected apps](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#connected-apps) . Offchain [​](https://docs.farcaster.xyz/learn/architecture/overview#offchain) ------------------------------------------------------------------------------ Farcaster's offchain system is a peer-to-peer network of servers called [Snapchain](https://snapchain.farcaster.xyz/) which store user data. The majority of user actions are performed offchain. These include: * Posting a new public message. * Following another user. * Reacting to a post. * Updating your profile picture. Actions are performed offchain when performance and cost are critical. Use of offchain actions is typically preferred when consistency isn't a strict requirement. Offchain systems achieve security by relying on signatures from onchain systems. --- # useSignInMessage / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/hooks/use-sign-in-message#VPContent) On this page `useSignInMessage` [​](https://docs.farcaster.xyz/auth-kit/hooks/use-sign-in-message#usesigninmessage) ======================================================================================================= Hook for reading the Sign in With Farcaster message and signature used to authenticate the user. If you're providing the message and signature to a backend API, you may want to use this hook. tsx import { useSignInMessage } from '@farcaster/auth-kit'; function App() { const { message, signature } = useSignInMessage(); return (

You signed: {message}

Your signature: {signature}

); } Returns [​](https://docs.farcaster.xyz/auth-kit/hooks/use-sign-in-message#returns) ----------------------------------------------------------------------------------- ts { message: string; signature: Hex; } | Parameter | Description | | --- | --- | | `message` | SIWE message signed by the user. | | `signature` | Signature produced by the user's Farcaster wallet. | --- # Find account by username / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/accounts/find-by-name#VPContent) On this page Find account by username [​](https://docs.farcaster.xyz/developers/guides/accounts/find-by-name#find-account-by-username) ========================================================================================================================== If you have a user's name and want to find their account, you'll need to use one of these methods depending on what type of username they have. Offchain ENS Names (Fnames) [​](https://docs.farcaster.xyz/developers/guides/accounts/find-by-name#offchain-ens-names-fnames) ------------------------------------------------------------------------------------------------------------------------------ If the user has an offchain ENS name like `@alice`, you'll need to call the [Fname Registry](https://docs.farcaster.xyz/reference/fname/api#get-current-fname-or-fid) . bash curl https://fnames.farcaster.xyz/transfers/current?name=farcaster | jq { "transfers": [\ {\ "id": 1,\ "timestamp": 1628882891,\ "username": "farcaster",\ "owner": "0x8773442740c17c9d0f0b87022c722f9a136206ed",\ "from": 0,\ "to": 1,\ "user_signature": "0xa6fdd2a69deab5633636f32a30a54b21b27dff123e6481532746eadca18cd84048488a98ca4aaf90f4d29b7e181c4540b360ba0721b928e50ffcd495734ef8471b",\ "server_signature": "0xb7181760f14eda0028e0b647ff15f45235526ced3b4ae07fcce06141b73d32960d3253776e62f761363fb8137087192047763f4af838950a96f3885f3c2289c41b"\ }\ ] } This returns the most recent transfer associated with the name if it is registered. Note that the creation of an Fname is a transfer from the zero address to the custody address. The `to` field indicates the current FID that owns the name. Onchain ENS Names [​](https://docs.farcaster.xyz/developers/guides/accounts/find-by-name#onchain-ens-names) ------------------------------------------------------------------------------------------------------------ If the user has an onchain ENS name like `@alice.eth`, the easiest way to do it is with the Postgres [replicator](https://snapchain.farcaster.xyz/guides/syncing-to-db) . It indexes onchain and offchain data and lets you easily find what you're looking for. Once you have it set up, query the `fnames` table in the replicator database for the account's FID: sql SELECT username, fid FROM fnames WHERE username = 'farcaster.eth' order by updated_at desc limit 1; See [here](https://docs.farcaster.xyz/reference/replicator/schema#fnames) for more details on the replicator table schema. --- # Frames v2 have been rebranded to Mini Apps / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/frames-redirect#VPContent) Return to top Frames v2 have been rebranded to Mini Apps [​](https://docs.farcaster.xyz/reference/frames-redirect#frames-v2-have-been-rebranded-to-mini-apps) ================================================================================================================================================ As of early 2025, Frames v2 have been rebranded to Mini Apps. All Frames documentation and resources have been migrated to the [Mini Apps](https://miniapps.farcaster.xyz/) ecosystem. Frames v2 have been deprecated and will be supported only until the end of March 2025. We strongly recommend using Mini Apps for all new projects. [Go to Mini Apps Documentation →](https://miniapps.farcaster.xyz/) --- # Direct Casts / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/warpcast/direct-casts#VPContent) Return to top Direct Casts [​](https://docs.farcaster.xyz/reference/warpcast/direct-casts#direct-casts) ========================================================================================== This page documents public APIs provided by the official Farcaster client for direct casts. Direct casts are currently not part of the protocol. There are plans to add direct casts to the protocol later this year. #### Send / write API for direct casts [​](https://docs.farcaster.xyz/reference/warpcast/direct-casts#send-write-api-for-direct-casts) * [Send direct casts via API](https://www.notion.so/warpcast/Public-Programmable-DCs-v1-50d9d99e34ac4d10add55bd26a91804f) * The above link also provides information on how to obtain direct cast API keys #### Direct cast intents [​](https://docs.farcaster.xyz/reference/warpcast/direct-casts#direct-cast-intents) Intents enable developers to direct authenticated users to a pre-filled direct cast composer via a URL. bash https://farcaster.xyz/~/inbox/create/[fid]?text=[message] https://farcaster.xyz/~/inbox/create/1?text=gm --- # Farcaster Client Embeds Reference / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/warpcast/embeds#VPContent) Return to top Farcaster Client Embeds Reference [​](https://docs.farcaster.xyz/reference/warpcast/embeds#farcaster-client-embeds-reference) ============================================================================================================================== The Farcaster client follows the [Open Graph protocol](https://ogp.me/) when rendering rich previews for URL embeds. Developers can reset existing embed caches on the Farcaster client at [https://farcaster.xyz/~/developers/embeds](https://farcaster.xyz/~/developers/embeds) . #### Additional details [​](https://docs.farcaster.xyz/reference/warpcast/embeds#additional-details) * Resetting an embed cache, does not reset Open Graph image caches. If you are experiencing a stale image on your Open Graph, change the image file path served as the `og:image`. * Developers have to be logged in to the Farcaster client to access this page. * To render rich previews of NFTs, follow the [Mini Apps specification](https://miniapps.farcaster.xyz/docs/specification) . --- # Bundler / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/reference/bundler#VPContent) On this page Bundler [​](https://docs.farcaster.xyz/reference/contracts/reference/bundler#bundler) ====================================================================================== The Bundler makes first time sign up easier by allowing a user to register an fid, add a key and rent storage in one transaction. If you want to create a new Farcaster account in a single transaction, use the Bundler. Read [​](https://docs.farcaster.xyz/reference/contracts/reference/bundler#read) -------------------------------------------------------------------------------- ### price [​](https://docs.farcaster.xyz/reference/contracts/reference/bundler#price) Get the price in wei to register an fid, including 1 storage unit. To add additional storage units to the calculation, use the `extraStorage` parameter. | Parameter | type | Description | | --- | --- | --- | | extraStorage | `uint256` | Number of extra units to include in the price | Write [​](https://docs.farcaster.xyz/reference/contracts/reference/bundler#write) ---------------------------------------------------------------------------------- ### register [​](https://docs.farcaster.xyz/reference/contracts/reference/bundler#register) Register an fid, add one or more keys, and rent storage in a single step. For a detailed usage example, see the [signup demo app](https://farcaster-signup-demo.vercel.app/bundler) . | Parameter | type | Description | | --- | --- | --- | | `msg.value` | `wei` | Payment amount for registration | | registerParams | `RegistrationParams` | Registration related parameters and signature | | signerParams | `SignerParams[]` | Key related parameters and signature | | extraStorage | `uint256` | Additional storage units to rent | **RegistrationParams struct** The `RegistrationParams` struct includes registration parameters and an IdGateway [`Register`](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#register-signature) signature from the fid recipient. | Parameter | type | Description | | --- | --- | --- | | to | `address` | Address to register the fid to | | recovery | `address` | Recovery address for the new fid | | deadline | `uint256` | Signature expiration timestamp signature | | sig | `bytes` | EIP-712 [`Register`](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#add-signature)
signature from the `to` address | **SignerParams struct** The `SignerParams` struct includes signer key parameters and a KeyGateway [`Add`](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#add-signature) signature from the fid recipient. Callers may provide multiple `SignerParams` structs to add multiple keys at registration time. | Parameter | type | Description | | --- | --- | --- | | keyType | `uint32` | Must be set to `1`. This is currently the only supported `keyType`. | | key | `bytes` | Public key to add | | metadataType | `uint8` | Must be set to `1`. This is currently the only supported `metadataType`. | | metadata | `bytes` | Encoded [`SignedKeyRequestMetadata`](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequestmetadata-struct) | | deadline | `uint256` | Signature expiration timestamp | | sig | `bytes` | EIP-712 [`Add`](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#add-signature)
signature from `registrationParams.to` address | Errors [​](https://docs.farcaster.xyz/reference/contracts/reference/bundler#errors) ------------------------------------------------------------------------------------ | Error | Selector | Description | | --- | --- | --- | | InvalidPayment | `3c6b4b28` | The caller provided insufficient payment. | | InvalidMetadata | `bcecb64a` | The signed metadata provided with the key is invalid. | | InvalidSignature | `8baa579f` | The provided signature is invalid. It may be incorrectly formatted, or signed by the wrong address. | | SignatureExpired | `0819bdcd` | The provided signature has expired. Collect a new signature from the signer with a later deadline timestamp. | Source [​](https://docs.farcaster.xyz/reference/contracts/reference/bundler#source) ------------------------------------------------------------------------------------ [`Bundler.sol`](https://github.com/farcasterxyz/contracts/blob/1aceebe916de446f69b98ba1745a42f071785730/src/Bundler.sol) --- # How to have your video displayed on the Farcaster client / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/warpcast/videos#VPContent) Return to top How to have your video displayed on the Farcaster client [​](https://docs.farcaster.xyz/reference/warpcast/videos#how-to-have-your-video-displayed-on-the-farcaster-client) ============================================================================================================================================================================ 1. Ensure you serve your video as a streamable `.m3u8` file. This ensures clients only download what they need when viewing, and nothing more, providing a high performance experience. 2. Make sure that `.m3u8` manifest exposes the resolution(s) for your video. The Farcaster client uses this to determine the correct aspect ratio when rendering. A manifest file with resolution data looks something like: #EXTM3U #EXT-X-VERSION:3 #EXT-X-STREAM-INF:BANDWIDTH=2444200,CODECS="avc1.64001f,mp4a.40.2",RESOLUTION=474x842 480p/video.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=4747600,CODECS="avc1.640020,mp4a.40.2",RESOLUTION=720x1280 720p/video.m3u8 3. Ensure that at cast publish time the `.m3u8` file is available. The Farcaster client checks once and if there is no valid data, the cast will show no video. 4. Have the same URL when changed from ending in `/my-video.m3u8` to `/thumbnail.jpg` with a preview/thumbnail image to render before the user has interacted with the video. 5. Reach out to the Farcaster team and ask for us to enable videos for your domain. Tell us the format of your video URLs and we’ll configure our scrapers to display them correctly in the feed. Please make sure to complete all these steps above before reaching out for allowlisting. [Ping @gt on Farcaster](https://farcaster.xyz/~/inbox/create/302?text=Completed%20video%20setup) . --- # Signer Requests / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/warpcast/signer-requests#VPContent) On this page Signer Requests [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#signer-requests) =================================================================================================== If your application wants to write data to Farcaster on behalf of a user, the application must get the user to add a signing key for their application. Guide [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#guide) ------------------------------------------------------------------------------- ### Prerequisites [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#prerequisites) * a registered FID ### 1\. An authenticated user clicks "Connect with Farcaster" in your app [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#_1-an-authenticated-user-clicks-connect-with-farcaster-in-your-app) Once your app can identify and authenticate a user you can present them with the option to connect with Farcaster. ### 2\. Generate a new Ed25519 key pair for the user and SignedKeyRequest signature [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#_2-generate-a-new-ed25519-key-pair-for-the-user-and-signedkeyrequest-signature) Your app should generate and securely store an Ed25519 associated with this user. In the next steps, you will prompt the user to approve this keypair to sign Farcaster messages on their behalf. It's important that: * the private key is stored securely and never exposed * the key pair can be retrieved and used to sign messages when the user returns In addition to generating a new key pair, your application must produce an ECDSA signature using the custody address of its FID. This allows the key to be attributed to the application which is useful for a wide range of things from knowing which apps are being used to filtering content based on the applications that generated them. **Example code:** ts import * as ed from '@noble/ed25519'; import { mnemonicToAccount, signTypedData } from 'viem/accounts'; /*** EIP-712 helper code ***/ const SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN = { name: 'Farcaster SignedKeyRequestValidator', version: '1', chainId: 10, verifyingContract: '0x00000000fc700472606ed4fa22623acf62c60553', } as const; const SIGNED_KEY_REQUEST_TYPE = [\ { name: 'requestFid', type: 'uint256' },\ { name: 'key', type: 'bytes' },\ { name: 'deadline', type: 'uint256' },\ ] as const; /*** Generating a keypair ***/ const privateKey = ed.utils.randomPrivateKey(); const publicKeyBytes = await ed.getPublicKey(privateKey); const key = '0x' + Buffer.from(publicKeyBytes).toString('hex'); /*** Generating a Signed Key Request signature ***/ const appFid = process.env.APP_FID; const account = mnemonicToAccount(process.env.APP_MNENOMIC); const deadline = Math.floor(Date.now() / 1000) + 86400; // signature is valid for 1 day const signature = await account.signTypedData({ domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN, types: { SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE, }, primaryType: 'SignedKeyRequest', message: { requestFid: BigInt(appFid), key, deadline: BigInt(deadline), }, }); **Deadline** This value controls how long the Signed Key Request signature is valid for. It is a Unix timestamp in second (note: JavaScript uses millisecond). We recommend setting this to 24 hours. ### 3\. App uses the public key + SignedKeyRequest signature to initiate a Signed Key Request using the Farcaster client API [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#_3-app-uses-the-public-key-signedkeyrequest-signature-to-initiate-a-signed-key-request-using-the-farcaster-client-api) The app calls the Farcaster client backend which returns a deeplink and a session token that can be used to check the status of the request. ts /*** Creating a Signed Key Request ***/ const farcasterClientApi = 'https://api.farcaster.xyz'; const { token, deeplinkUrl } = await axios .post(`${farcasterClientApi}/v2/signed-key-requests`, { key: publicKey, requestFid: fid, signature, deadline, }) .then((response) => response.data.result.signedKeyRequest); // deeplinkUrl should be presented to the user // token should be used to poll **Redirect URL** If this request is being made from a native mobile application that can open deeplinks, you can include a redirectUrl that the user should be brought back to after they complete the request. Note: if your app is PWA or web app do not include this value as the user will brought to a session that has no state. **\*Sponsorships** You can sponsor the onchain fees for the user. See [Sponsoring a signer](https://docs.farcaster.xyz/reference/warpcast/signer-requests#sponsoring-a-signer) below. ### 4\. Application presents the deep link from the response to the user [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#_4-application-presents-the-deep-link-from-the-response-to-the-user) The app presents the deeplink which will prompt the user to open the Farcaster client app and authorize the signer request (screenshots at the bottom). The app should direct the user to open the link on their mobile device they have the Farcaster client installed on: 1. when on mobile, trigger the deeplink directly 2. when on web, display the deeplink as a QR code to scan **Example code** ts import QRCode from 'react-qr-code'; const DeepLinkQRCode = (deepLinkUrl) => ; ### 5\. Application begins polling Signer Request endpoint using token [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#_5-application-begins-polling-signer-request-endpoint-using-token) Once the user has been presented the deep link, the application must wait for the user to complete the signer request flow. The application can poll the signer request resource and look for the data that indicates the user has completed the request: ts const poll = async (token: string) => { while (true) { // sleep 1s await new Promise((r) => setTimeout(r, 2000)); console.log('polling signed key request'); const signedKeyRequest = await axios .get(`${farcasterClientApi}/v2/signed-key-request`, { params: { token, }, }) .then((response) => response.data.result.signedKeyRequest); if (signedKeyRequest.state === 'completed') { console.log('Signed Key Request completed:', signedKeyRequest); /** * At this point the signer has been registered onchain and you can start submitting * messages to hubs signed by its key: * ``` * const signer = Ed25519Signer.fromPrivateKey(privateKey)._unsafeUnwrap(); * const message = makeCastAdd(..., signer) * ``` */ break; } } }; poll(token); ### 6\. User opens the link and completes Signer Request flow in the Farcaster client app [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#_6-user-opens-the-link-and-completes-signer-request-flow-in-the-farcaster-client-app) When the user approves the request in the client app, an onchain transaction will be made that grants write permissions to that signer. Once that completes your app should indicate success and can being writing messages using the newly added key. ### Reference implementation [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#reference-implementation) ts import * as ed from '@noble/ed25519'; import { Hex } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; import axios from 'axios'; import * as qrcode from 'qrcode-terminal'; /*** EIP-712 helper code ***/ const SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN = { name: 'Farcaster SignedKeyRequestValidator', version: '1', chainId: 10, verifyingContract: '0x00000000fc700472606ed4fa22623acf62c60553', } as const; const SIGNED_KEY_REQUEST_TYPE = [\ { name: 'requestFid', type: 'uint256' },\ { name: 'key', type: 'bytes' },\ { name: 'deadline', type: 'uint256' },\ ] as const; (async () => { /*** Generating a keypair ***/ const privateKey = ed.utils.randomPrivateKey(); const publicKeyBytes = await ed.getPublicKey(privateKey); const key = '0x' + Buffer.from(publicKeyBytes).toString('hex'); /*** Generating a Signed Key Request signature ***/ const appFid = process.env.APP_FID; const account = mnemonicToAccount(process.env.APP_MNEMONIC); const deadline = Math.floor(Date.now() / 1000) + 86400; // signature is valid for 1 day const signature = await account.signTypedData({ domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN, types: { SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE, }, primaryType: 'SignedKeyRequest', message: { requestFid: BigInt(appFid), key, deadline: BigInt(deadline), }, }); /*** Creating a Signed Key Request ***/ const farcasterClientApi = 'https://api.farcaster.xyz'; const { token, deeplinkUrl } = await axios .post(`${farcasterClientApi}/v2/signed-key-requests`, { key, requestFid: appFid, signature, deadline, }) .then((response) => response.data.result.signedKeyRequest); qrcode.generate(deeplinkUrl, console.log); console.log('scan this with your phone'); console.log('deep link:', deeplinkUrl); const poll = async (token: string) => { while (true) { // sleep 1s await new Promise((r) => setTimeout(r, 2000)); console.log('polling signed key request'); const signedKeyRequest = await axios .get(`${farcasterClientApi}/v2/signed-key-request`, { params: { token, }, }) .then((response) => response.data.result.signedKeyRequest); if (signedKeyRequest.state === 'completed') { console.log('Signed Key Request completed:', signedKeyRequest); /** * At this point the signer has been registered onchain and you can start submitting * messages to hubs signed by its key: * ``` * const signer = Ed25519Signer.fromPrivateKey(privateKey)._unsafeUnwrap(); * const message = makeCastAdd(..., signer) * ``` */ break; } } }; await poll(token); })(); API [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#api) --------------------------------------------------------------------------- ### POST /v2/signed-key-request [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#post-v2-signed-key-request) Create a signed key requests. **Body:** * `key` - hex string of the Ed25519 public key * `requestFid` - fid of the requesting application * `deadline` - Unix timestamp signature is valid until * `signature` - [SignedKeyRequest](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signed-key-request-validator) signature from the requesting application * `redirectUrl` - Optional. Url to redirect to after the signer is approved. Note: this should only be used when requesting a signer from a native mobile application. * `sponsorship` - **Sample response:** json { "result": { "signedKeyRequest": { "token": "0xa241e6b1287a07f4d3f9c5bd", "deeplinkUrl": "farcaster://signed-key-request?token=0xa241e6b1287a07f4d3f9c5bd" "key": "0x48b0c7a6deff69bad7673357df43274f3a08163a6440b7a7e3b3cb6b6623faa7", "state": "pending" } } } ### GET /v2/signed-key-request [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#get-v2-signed-key-request) Get the state of a signed key requests. **Query parameters:** * `token` - token identifying the request **Response:** * `token` - token identifying the request * `deeplinkUrl` - URL where the user can complete the request * `key` - requested key to add * `state` - state of the request: `pending` - no action taken by user, `approved` - user has approved but onchain transaction is not confirmed, `completed` - onchain transaction is confirmed **Sample response in pending state:** json { "result": { "signedKeyRequest": { "token": "0xa241e6b1287a07f4d3f9c5bd", "deeplinkUrl": "farcaster://signed-key-request?token=0xa241e6b1287a07f4d3f9c5bd" "key": "0x48b0c7a6deff69bad7673357df43274f3a08163a6440b7a7e3b3cb6b6623faa7", "state": "pending" } } } **Sample response after approval but before transaction is confirmed:** json { "result": { "signedKeyRequest": { "token": "0xa241e6b1287a07f4d3f9c5bd", "deeplinkUrl": "farcaster://signed-key-request?token=0xa241e6b1287a07f4d3f9c5bd" "key": "0x48b0c7a6deff69bad7673357df43274f3a08163a6440b7a7e3b3cb6b6623faa7", "state": "approved", "userFid": 1, } } } **Sample response after after transaction is confirmed:** json { "result": { "signedKeyRequest": { "token": "0xa241e6b1287a07f4d3f9c5bd", "deeplinkUrl": "farcaster://signed-key-request?token=0xa241e6b1287a07f4d3f9c5bd" "key": "0x48b0c7a6deff69bad7673357df43274f3a08163a6440b7a7e3b3cb6b6623faa7", "state": "completed", "userFid": 1, } } } Sponsoring a signer [​](https://docs.farcaster.xyz/reference/warpcast/signer-requests#sponsoring-a-signer) ----------------------------------------------------------------------------------------------------------- An application can sponsor a signer so that the user doesn’t need to pay. The application must be signed up on the Farcaster client app and have a warps ≥ 100. When generating a signed key request an application can indicate it should be sponsored by including an additional `sponsorship` field in the request body. ts type SignedKeyRequestSponsorship = { sponsorFid: number; signature: string; // sponsorship signature by sponsorFid }; type SignedKeyRequestBody = { key: string; requestFid: number; deadline: number; signature: string; // key request signature by requestFid sponsorship?: SignedKeyRequestSponsorship; }; To create a `SignedKeyRequestSponsorship`: 1. create the key pair and have the requesting FID generate a signature 2. create a second signature from the sponsoring FID using the signature generated in step 1 as the raw input to an EIP-191 message ts // sponsoringAccount is Viem account instance for the sponsoring FID's custody address // signedKeyRequestSignature is the EIP-712 signature signed by the requesting FID const sponsorSignature = sponsoringAccount.signMessage({ message: { raw: signedKeyRequestSignature }, }); When the user opens the signed key request in the Farcaster client they will the onchain fees have been sponsored by your application. --- # Sign In with Farcaster / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/siwf/#VPContent) On this page Introduction [​](https://docs.farcaster.xyz/developers/siwf/#introduction) =========================================================================== Sign In with Farcaster (SIWF) is a way for users to sign into any app using their Farcaster identity. When a user signs into your application with Farcaster you'll be able to use public social data like their social graph and profile information to provide a streamlined onboarding experience and social-powered features. ### How does it work? [​](https://docs.farcaster.xyz/developers/siwf/#how-does-it-work) 1. Show a "Sign in with Farcaster" button to the user. 2. Wait for the user to click, scan a QR code and approve the request in Warpcast. 3. Receive and verify a signature from Warpcast. 4. Show the logged in user's profile picture and username. ![Sign In with Farcaste demo](https://docs.farcaster.xyz/assets/siwf_demo.RXQZH9JE.avifs) Next Steps [​](https://docs.farcaster.xyz/developers/siwf/#next-steps) ----------------------------------------------------------------------- * Integrate SIWF to your app today with [AuthKit](https://docs.farcaster.xyz/auth-kit/) . * Read about the underlying standard in [FIP-11: Sign In With Farcaster](https://github.com/farcasterxyz/protocol/discussions/110) . --- # Signed Key Request Validator / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#VPContent) On this page Signed Key Request Validator [​](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signed-key-request-validator) ===================================================================================================================================================== The Signed Key Request Validator validates the signed metadata associated with keys. The Key Registry calls the validator before adding a key, to check that the provided Signed Key Request is valid. If you want to construct or check a Signed Key Request, use the Signed Key Request Validator. What is a Signed Key Request? When a user adds a key to their account (the primary fid), it must include a signature from the person that requested it (the request fid). This enables anyone to identify who requested a specific key. Typically, the primary fid is the end user and the requesting fid is an app the user wishes to connect to. Read [​](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#read) ----------------------------------------------------------------------------------------------------- ### encodeMetadata [​](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#encodemetadata) Convert a [`SignedKeyRequestMetadata`](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequestmetadata-struct) struct into `bytes` to pass into contract functions like [add](https://docs.farcaster.xyz/reference/contracts/reference/key-gateway#add) , [register](https://docs.farcaster.xyz/reference/contracts/reference/bundler#register) . | Parameter | type | Description | | --- | --- | --- | | metadata | `SignedKeyRequestMetadata` | The `SignedKeyRequestMetadata` struct to encode | #### SignedKeyRequestMetadata struct [​](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequestmetadata-struct) The `SignedKeyRequestMetadata` struct contains data to validate authenticity of a Signed Key Request: requesting fid, the requesting fid owner, and an EIP-712 [`SignedKeyRequest`](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequest-signature) signature. | Parameter | type | Description | | --- | --- | --- | | requestFid | `uint256` | requesting fid | | requestSigner | `address` | owner address of the requesting fid | | signature | `bytes` | EIP-712 [`SignedKeyRequest`](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequest-signature)
signature from the `requestSigner` address | | deadline | `uint256` | Expiration timestamp of the signature | See below for code examples that demonstrate two methods of generating encoded `SignedKeyRequestMetadata` — using the `@farcaster/hub-web` library to sign and encode in a single step, or using Viem to sign and encode separately. @farcaster/hub-webViemsignature.tshelpers.tssigner.ts ts import { ViemLocalEip712Signer } from '@farcaster/hub-web'; import { privateKeyToAccount } from 'viem/accounts'; import { getDeadline } from './helpers.ts'; import { getPublicKey } from './signer.ts'; export const appAccount = privateKeyToAccount('0x...'); const key = getPublicKey(); const deadline = getDeadline(); // The getSignedKeyRequestMetadata helper generates a SignedKeyRequest // signature and returns an ABI-encoded SignedKeyRequest metadata struct. const eip712Signer = new ViemLocalEip712Signer(appAccount); const encodedData = await eip712Signer.getSignedKeyRequestMetadata({ requestFid: 9152n, key, deadline, }); ts import { bytesToHex, encodeAbiParameters } from 'viem'; import { signature } from './signature.ts'; import { getDeadline } from './helpers.ts'; const deadline = getDeadline(); // An example of collecting the signature and // encoding the SignedKeyRequest metadata separately. const encodedData = encodeAbiParameters( [\ {\ components: [\ {\ name: 'requestFid',\ type: 'uint256',\ },\ {\ name: 'requestSigner',\ type: 'address',\ },\ {\ name: 'signature',\ type: 'bytes',\ },\ {\ name: 'deadline',\ type: 'uint256',\ },\ ],\ type: 'tuple',\ },\ ], [\ {\ requestFid: 9152n,\ requestSigner: '0x02ef790dd7993a35fd847c053eddae940d055596',\ bytesToHex(signature),\ deadline,\ },\ ] ); ts import { ViemLocalEip712Signer } from '@farcaster/hub-web'; import { privateKeyToAccount } from 'viem/accounts'; import { getDeadline } from './helpers.ts'; import { getPublicKey } from './signer.ts'; export const appAccount = privateKeyToAccount('0x...'); const key = getPublicKey(); const deadline = getDeadline(); const eip712Signer = new ViemLocalEip712Signer(appAccount); const signature = await eip712Signer.signKeyRequest({ requestFid: 9152n, key, deadline, }); ts export const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return now + oneHour; }; ts import * as ed from '@noble/ed25519'; import { NobleEd25519Signer } from '@farcaster/hub-web'; const privateKeyBytes = ed.utils.randomPrivateKey(); export const accountKey = new NobleEd25519Signer(privateKeyBytes); export const getPublicKey = async () => { const accountKeyResult = await accountKey.getSignerKey(); if (accountKeyResult.isOk()) { return accountKeyResult.value; } }; ### validate [​](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#validate) Validate an encoded [`SignedKeyRequestMetadata`](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequestmetadata-struct) . The KeyRegistry calls this function internally when a user adds a key to validate the Signed Key Request. If you are creating keys on behalf of users, you can call this function yourself to validate the Signed Key Request created by your app. | Parameter | type | Description | | --- | --- | --- | | fid | `uint256` | Primary fid that will be associated with the key | | key | `bytes` | Bytes of the public key to validate | | sig | `bytes` | EIP-712 `SignedKeyRequest` signature from the entity requesting the key | #### SignedKeyRequest signature [​](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequest-signature) The `SignedKeyRequest` message is an EIP-712 typed signature signed by the requesting fid owner in the following format: `SignedKeyRequest(uint256 requestFid,bytes key,uint256 deadline)` Why sign metadata? The `SignedKeyRequest` signature proves that the requesting fid asked the primary fid to authorize a key pair. For example, when an app asks a user to add a new key, the app creates a Signed Key Request proving that it made the request and the KeyRegistry emits it in an onchain event. This allows anyone to attribute a signer to the specific person who requested it, which is useful for a wide range of things from knowing which apps are being used to filtering content based on the applications that generated them. | Parameter | type | Description | | --- | --- | --- | | requestFid | `uint256` | fid of the entity | | key | `bytes` | Bytes of the public key | | deadline | `uint256` | Expiration timestamp of the signature | @farcaster/hub-webViemhelpers.tssigner.ts ts import { ViemLocalEip712Signer } from '@farcaster/hub-web'; import { privateKeyToAccount } from 'viem/accounts'; import { getDeadline } from './helpers.ts'; import { getPublicKey } from './signer.ts'; export const appAccount = privateKeyToAccount('0x...'); const key = getPublicKey(); const deadline = getDeadline(); const eip712Signer = new ViemLocalEip712Signer(appAccount); const signature = await eip712Signer.signKeyRequest({ requestFid: 9152n, key, deadline, }); ts import { SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_TYPES } from '@farcaster/hub-web'; import { bytesToHex, privateKeyToAccount } from 'viem/accounts'; import { getDeadline } from './helpers.ts'; import { getPublicKey } from './signer.ts'; export const appAccount = privateKeyToAccount('0x...'); const key = getPublicKey(); const deadline = getDeadline(); const signature = await appAccount.signTypedData({ ...SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_TYPES, primaryType: 'SignedKeyRequest', message: { requestFid: 9152n, key: bytesToHex(key), deadline, }, }); ts export const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return now + oneHour; }; ts import * as ed from '@noble/ed25519'; import { NobleEd25519Signer } from '@farcaster/hub-web'; const privateKeyBytes = ed.utils.randomPrivateKey(); export const accountKey = new NobleEd25519Signer(privateKeyBytes); export const getPublicKey = async () => { const accountKeyResult = await accountKey.getSignerKey(); if (accountKeyResult.isOk()) { return accountKeyResult.value; } }; Source [​](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#source) --------------------------------------------------------------------------------------------------------- [`SignedKeyRequestValidator.sol`](https://github.com/farcasterxyz/contracts/blob/1aceebe916de446f69b98ba1745a42f071785730/src/validators/SignedKeyRequestValidator.sol) --- # Change Farcaster name / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/accounts/change-fname#VPContent) Return to top Change Farcaster name [​](https://docs.farcaster.xyz/developers/guides/accounts/change-fname#change-farcaster-name) ==================================================================================================================== A user can change their offchain ENS name or Fname without affecting their account's history. This can be done at most once in 28 days. WARNING * Fnames may be revoked if you violate the [usage policy](https://docs.farcaster.xyz/learn/architecture/ens-names#offchain-ens-names-fnames) . * Apps may lower your reputation if you change Fnames often. ### Requirements [​](https://docs.farcaster.xyz/developers/guides/accounts/change-fname#requirements) * An ETH wallet that owns the account on OP Mainnet. No ETH is required. ### Change username [​](https://docs.farcaster.xyz/developers/guides/accounts/change-fname#change-username) To transfer an Fname, e.g. `farcaster`, make a POST request to `/transfers` with the following body: yaml { "name": "farcaster", // Name to transfer "from": 123, // FID to transfer from "to": 321, // FID to transfer to "fid": 123, // FID making the request (must match from) "owner": "0x...", // Custody address of FID making the request "timestamp": 1641234567, // Current timestamp in seconds "signature": "0x..." // EIP-712 signature signed by the custody address of the FID } To generate the EIP-712 signature, use the following code: js import { makeUserNameProofClaim, EIP712Signer } from '@farcaster/hub-nodejs'; const accountKey: EIP712Signer = undefined; // Account Key for the custody address (use appropriate subclass from hub-nodejs for ethers or viem) const claim = makeUserNameProofClaim({ name: 'farcaster', owner: '0x...', timestamp: Math.floor(Date.now() / 1000), }); const signature = ( await accountKey.signUserNameProofClaim(claim) )._unsafeUnwrap(); Example request via curl: bash curl -X POST https://fnames.farcaster.xyz/transfers \ -H "Content-Type: application/json" \ -d \ '{"name": "farcaster", "owner": "0x...", "signature": "0x...", "from": 123, "to": 321, "timestamp": 1641234567, fid: 123}' See [here](https://docs.farcaster.xyz/reference/fname/api) for more details on the Fname registry API. --- # Installation / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/installation#VPContent) Return to top Installation [​](https://docs.farcaster.xyz/auth-kit/installation#installation) ================================================================================ Install auth-kit and its peer dependency [viem](https://viem.sh/) . sh npm install @farcaster/auth-kit viem **Note:** auth-kit is a [React](https://react.dev/) library. If you're using a different framework, take a look at the [client library](https://docs.farcaster.xyz/auth-kit/client/introduction) instead. ### 1\. Import the libraries [​](https://docs.farcaster.xyz/auth-kit/installation#_1-import-the-libraries) Import auth-kit and CSS styles. tsx import '@farcaster/auth-kit/styles.css'; import { AuthKitProvider } from '@farcaster/auth-kit'; import { SignInButton } from '@farcaster/auth-kit'; ### 2\. Configure your provider [​](https://docs.farcaster.xyz/auth-kit/installation#_2-configure-your-provider) Configure a provider with an Optimism RPC URL, your app's domain and login URL, and wrap your application in it. tsx const config = { rpcUrl: 'https://mainnet.optimism.io', domain: 'example.com', siweUri: 'https://example.com/login', }; const App = () => { return ( {/* Your App */} ); }; ### 3\. Add a connect button [​](https://docs.farcaster.xyz/auth-kit/installation#_3-add-a-connect-button) Render the `SignInButton` component. When the user clicks this button, they will be prompted to complete sign in using their Farcaster wallet application. tsx export const Login = () => { return ; }; ### 4\. Read user profile [​](https://docs.farcaster.xyz/auth-kit/installation#_4-read-user-profile) Optionally, fetch details about the logged in user anywhere in your app with `useProfile`. tsx import { useProfile } from '@farcaster/auth-kit'; export const UserProfile = () => { const { isAuthenticated, profile: { username, fid }, } = useProfile(); return (
{isAuthenticated ? (

Hello, {username}! Your fid is: {fid}

) : (

You're not signed in.

)}
); }; --- # Overview / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/index#VPContent) Return to top Overview [​](https://docs.farcaster.xyz/reference/index#overview) ================================================================== The reference sections documents API's, standards and protocols used commonly used by Farcaster developers. * [Mini Apps](https://miniapps.farcaster.xyz/) \- A specification for writing and rendering mini apps. * [Farcaster Client APIs](https://docs.farcaster.xyz/reference/warpcast/api) - An overview of Farcaster Client APIs that are publicly available. * [Snapchain](https://snapchain.farcaster.xyz/) - A design overview and API reference for Farcaster Hubs. * [Replicator](https://docs.farcaster.xyz/reference/replicator/schema) - An overview and schema for the replicator. * [Contracts](https://docs.farcaster.xyz/reference/contracts/index) - A design overview and ABI reference for Farcaster contracts. * [FName Registry](https://docs.farcaster.xyz/reference/fname/api) - An overview and API reference for the Farcaster Name Server. * [Neynar](https://docs.farcaster.xyz/reference/third-party/neynar/index) - An overview of Neynar APIs that can help developers get started with Farcaster --- # Schema / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/replicator/schema#VPContent) On this page Schema [​](https://docs.farcaster.xyz/reference/replicator/schema#schema) ========================================================================== The following tables are created in Postgres DB where data from the Hubs are stored: chain\_events [​](https://docs.farcaster.xyz/reference/replicator/schema#chain-events) --------------------------------------------------------------------------------------- All onchain events received from the hub event stream are stored in this table. These events represent any onchain action including registrations, transfers, signer additions/removals, storage rents, etc. Events are never deleted (i.e. this table is append-only). | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as the message timestamp!) | | block\_timestamp | `timestamp with time zone` | Timestamp of the block this event was emitted in UTC. | | fid | `bigint` | FID of the user that signed the message. | | chain\_id | `bigint` | Chain ID. | | block\_number | `bigint` | Block number of the block this event was emitted. | | transaction\_index | `smallint` | Index of the transaction in the block. | | log\_index | `smallint` | Index of the log event in the block. | | type | `smallint` | Type of chain event. | | block\_hash | `bytea` | Hash of the block where this event was emitted. | | transaction\_hash | `bytea` | Hash of the transaction triggering this event. | | body | `json` | JSON representation of the chain event body (changes shape based on `type`). | | raw | `bytea` | Raw bytes representing the serialized `OnChainEvent` [protobuf](https://protobuf.dev/)
. | fids [​](https://docs.farcaster.xyz/reference/replicator/schema#fids) ---------------------------------------------------------------------- Stores all registered FIDs on the Farcaster network. | Column Name | Data Type | Description | | --- | --- | --- | | fid | `bigint` | FID of the user (primary key) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as registration date!) | | updated\_at | `timestamp with time zone` | When the row was last updated. | | registered\_at | `timestamp with time zone` | Timestamp of the block in which the user was registered. | | chain\_event\_id | `uuid` | ID of the row in the `chain_events` table corresponding to this FID's initial registration. | | custody\_address | `bytea` | Address that owns the FID. | | recovery\_address | `bytea` | Address that can initiate a recovery for this FID. | signers [​](https://docs.farcaster.xyz/reference/replicator/schema#signers) ---------------------------------------------------------------------------- Stores all registered account keys (signers). | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as when the key was created on the network!) | | updated\_at | `timestamp with time zone` | When the row was last updated. | | added\_at | `timestamp with time zone` | Timestamp of the block where this signer was added. | | removed\_at | `timestamp with time zone` | Timestamp of the block where this signer was removed. | | fid | `bigint` | FID of the user that authorized this signer. | | requester\_fid | `bigint` | FID of the user/app that requested this signer. | | add\_chain\_event\_id | `uuid` | ID of the row in the `chain_events` table corresponding to the addition of this signer. | | remove\_chain\_event\_id | `uuid` | ID of the row in the `chain_events` table corresponding to the removal of this signer. | | key\_type | `smallint` | Type of key. | | metadata\_type | `smallint` | Type of metadata. | | key | `bytea` | Public key bytes. | | metadata | `bytea` | Metadata bytes as stored on the blockchain. | username\_proofs [​](https://docs.farcaster.xyz/reference/replicator/schema#username-proofs) --------------------------------------------------------------------------------------------- Stores all username proofs that have been seen. This includes proofs that are no longer valid, which are soft-deleted via the `deleted_at` column. When querying usernames, you probably want to query the `fnames` table directly, rather than this table. | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as when the key was created on the network!) | | updated\_at | `timestamp with time zone` | When the row was last updated. | | timestamp | `timestamp with time zone` | Timestamp of the proof message. | | deleted\_at | `timestamp with time zone` | When this proof was revoked or otherwise invalidated. | | fid | `bigint` | FID that the username in the proof belongs to. | | type | `smallint` | Type of proof (either fname or ENS). | | username | `text` | Username, e.g. `dwr` if an fname, or `dwr.eth` if an ENS name. | | signature | `bytea` | Proof signature. | | owner | `bytea` | Address of the wallet that owns the ENS name, or the wallet that provided the proof signature. | fnames [​](https://docs.farcaster.xyz/reference/replicator/schema#fnames) -------------------------------------------------------------------------- Stores all usernames that are currently registered. Note that in the case a username is deregistered, the row is soft-deleted via the `deleted_at` column until a new username is registered for the given FID. | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as when the key was created on the network!) | | updated\_at | `timestamp with time zone` | When the row was last updated. | | registered\_at | `timestamp with time zone` | Timestamp of the username proof message. | | deleted\_at | `timestamp with time zone` | When the proof was revoked or the fname was otherwise deregistered from this user. | | fid | `bigint` | FID the username belongs to. | | type | `smallint` | Type of username (either fname or ENS). | | username | `text` | Username, e.g. `dwr` if an fname, or `dwr.eth` if an ENS name. | messages [​](https://docs.farcaster.xyz/reference/replicator/schema#messages) ------------------------------------------------------------------------------ All Farcaster messages retrieved from the hub are stored in this table. Messages are never deleted, only soft-deleted ( i.e. marked as deleted but not actually removed from the DB). | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as the message timestamp!) | | updated\_at | `timestamp with time zone` | When the row was last updated. | | timestamp | `timestamp with time zone` | Message timestamp in UTC. | | deleted\_at | `timestamp with time zone` | When the message was deleted by the hub (e.g. in response to a `CastRemove` message, etc.) | | pruned\_at | `timestamp with time zone` | When the message was pruned by the hub. | | revoked\_at | `timestamp with time zone` | When the message was revoked by the hub due to revocation of the signer that signed the message. | | fid | `bigint` | FID of the user that signed the message. | | type | `smallint` | Message type. | | hash\_scheme | `smallint` | Message hash scheme. | | signature\_scheme | `smallint` | Message hash scheme. | | hash | `bytea` | Message hash. | | signature | `bytea` | Message signature. | | signer | `bytea` | Signer used to sign this message. | | body | `json` | JSON representation of the body of the message. | | raw | `bytea` | Raw bytes representing the serialized message [protobuf](https://protobuf.dev/)
. | casts [​](https://docs.farcaster.xyz/reference/replicator/schema#casts) ------------------------------------------------------------------------ Represents a cast authored by a user. | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as the message timestamp!) | | updated\_at | `timestamp with time zone` | When the row was last updated. | | timestamp | `timestamp with time zone` | Message timestamp in UTC. | | deleted\_at | `timestamp with time zone` | When the cast was considered deleted/revoked/pruned by the hub (e.g. in response to a `CastRemove` message, etc.) | | fid | `bigint` | FID of the user that signed the message. | | parent\_fid | `bigint` | If this cast was a reply, the FID of the author of the parent cast. `null` otherwise. | | hash | `bytea` | Message hash. | | root\_parent\_hash | `bytea` | If this cast was a reply, the hash of the original cast in the reply chain. `null` otherwise. | | parent\_hash | `bytea` | If this cast was a reply, the hash of the parent cast. `null` otherwise. | | root\_parent\_url | `text` | If this cast was a reply, then the URL that the original cast in the reply chain was replying to. | | parent\_url | `text` | If this cast was a reply to a URL (e.g. an NFT, a web URL, etc.), the URL. `null` otherwise. | | text | `text` | The raw text of the cast with mentions removed. | | embeds | `json` | Array of URLs or cast IDs that were embedded with this cast. | | mentions | `json` | Array of FIDs mentioned in the cast. | | mentions\_positions | `json` | UTF8 byte offsets of the mentioned FIDs in the cast. | reactions [​](https://docs.farcaster.xyz/reference/replicator/schema#reactions) -------------------------------------------------------------------------------- Represents a user reacting (liking or recasting) content. | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as the message timestamp!) | | updated\_at | `timestamp with time zone` | When the row was last updated. | | timestamp | `timestamp with time zone` | Message timestamp in UTC. | | deleted\_at | `timestamp with time zone` | When the reaction was considered deleted by the hub (e.g. in response to a `ReactionRemove` message, etc.) | | fid | `bigint` | FID of the user that signed the message. | | target\_cast\_fid | `bigint` | If target was a cast, the FID of the author of the cast. `null` otherwise. | | type | `smallint` | Type of reaction. | | hash | `bytea` | Message hash. | | target\_cast\_hash | `bytea` | If target was a cast, the hash of the cast. `null` otherwise. | | target\_url | `text` | If target was a URL (e.g. NFT, a web URL, etc.), the URL. `null` otherwise. | links [​](https://docs.farcaster.xyz/reference/replicator/schema#links) ------------------------------------------------------------------------ Represents a link between two FIDs (e.g. a follow, subscription, etc.) | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not when the link itself was created on the network!) | | updated\_at | `timestamp with time zone` | When the row was last updated | | timestamp | `timestamp with time zone` | Message timestamp in UTC. | | deleted\_at | `timestamp with time zone` | When the link was considered deleted by the hub (e.g. in response to a `LinkRemoveMessage` message, etc.) | | fid | `bigint` | Farcaster ID (the user ID). | | target\_fid | `bigint` | Farcaster ID of the target user. | | display\_timestamp | `timestamp with time zone` | When the row was last updated. | | type | `string` | Type of connection between users, e.g. `follow`. | | hash | `bytea` | Message hash. | verifications [​](https://docs.farcaster.xyz/reference/replicator/schema#verifications) ---------------------------------------------------------------------------------------- Represents a user verifying something on the network. Currently, the only verification is proving ownership of an Ethereum wallet address. | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as the message timestamp!) | | updated\_at | `timestamp with time zone` | When the row was last updated. | | timestamp | `timestamp with time zone` | Message timestamp in UTC. | | deleted\_at | `timestamp with time zone` | When the verification was considered deleted by the hub (e.g. in response to a `VerificationRemove` message, etc.) | | fid | `bigint` | FID of the user that signed the message. | | hash | `bytea` | Message hash. | | signer\_address | `bytea` | Address of the wallet being verified. | | block\_hash | `bytea` | Block hash of the latest block at the time the ownership was verified. | | signature | `bytea` | Ownership proof signature. | user\_data [​](https://docs.farcaster.xyz/reference/replicator/schema#user-data) --------------------------------------------------------------------------------- Represents data associated with a user (e.g. profile photo, bio, username, etc.) | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB (not the same as the message timestamp!) | | updated\_at | `timestamp with time zone` | When the row was last updated. | | timestamp | `timestamp with time zone` | Message timestamp in UTC. | | deleted\_at | `timestamp with time zone` | When the data was considered deleted by the hub | | fid | `bigint` | FID of the user that signed the message. | | type | `smallint` | The type of user data (PFP, bio, username, etc.) | | hash | `bytea` | Message hash. | | value | `text` | The string value of the field. | storage\_allocations [​](https://docs.farcaster.xyz/reference/replicator/schema#storage-allocations) ----------------------------------------------------------------------------------------------------- Stores how many units of storage each FID has purchased, and when it expires. | Column Name | Data Type | Description | | --- | --- | --- | | id | `uuid` | Generic identifier specific to this DB (a.k.a. [surrogate key](https://en.wikipedia.org/wiki/Surrogate_key)
) | | created\_at | `timestamp with time zone` | When the row was first created in this DB | | updated\_at | `timestamp with time zone` | When the row was last updated. | | rented\_at | `timestamp with time zone` | Message timestamp in UTC. | | expires\_at | `timestamp with time zone` | When this storage allocation will expire. | | chain\_event\_id | `uuid` | ID of the row in the `chain_events` table representing the onchain event where storage was allocated. | | fid | `bigint` | FID that owns the storage. | | units | `smallint` | Number of storage units allocated. | | payer | `bytea` | Wallet address that paid for the storage. | --- # Contracts / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/architecture/contracts#VPContent) Return to top Contracts [​](https://docs.farcaster.xyz/learn/architecture/contracts#contracts) ================================================================================= A Farcaster account is managed and secured onchain using the Farcaster contracts. This section provides a high level overview and avoids some implementation details. For the full picture, see the [contracts repository](https://github.com/farcasterxyz/contracts/) . There are three main contracts deployed on OP Mainnet: * **Id Registry** - creates new accounts * **Storage Registry** - rents storage to accounts * **Key Registry** - adds and removes app keys from accounts ![Registry Contracts](https://docs.farcaster.xyz/assets/registry-contracts.C93-05Rq.png) The contracts are deployed at the following addresses: | Contract | Address | | --- | --- | | IdRegistry | [0x00000000fc6c5f01fc30151999387bb99a9f489b](https://optimistic.etherscan.io/address/0x00000000fc6c5f01fc30151999387bb99a9f489b) | | StorageRegistry | [0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d](https://optimistic.etherscan.io/address/0x00000000fcce7f938e7ae6d3c335bd6a1a7c593d) | | KeyRegistry | [0x00000000fc1237824fb747abde0ff18990e59b7e](https://optimistic.etherscan.io/address/0x00000000fc1237824fb747abde0ff18990e59b7e) | ### Id Registry [​](https://docs.farcaster.xyz/learn/architecture/contracts#id-registry) The IdRegistry lets users register, transfer and recover Farcaster accounts. An account is identified by a unique number (the fid) which is assigned to an Ethereum address on registration. An Ethereum address may only own one account at a time, though it may transfer it freely to other accounts. It may also specify a recovery address which can transfer the account at any time. ### Storage Registry [​](https://docs.farcaster.xyz/learn/architecture/contracts#storage-registry) The Storage Registry lets accounts rent [storage](https://docs.farcaster.xyz/learn/what-is-farcaster/messages#storage) by making a payment in ETH. The storage prices are set by admins in USD and converted to ETH using a Chainlink oracle. The price increases or decreases based on supply and demand. ### Key Registry [​](https://docs.farcaster.xyz/learn/architecture/contracts#key-registry) The Key Registry lets accounts issue keys to apps, so that they can publish messages on their behalf. Keys can be added or removed at any time. To add a key, an account must submit the public key of an EdDSA key pair along with a requestor signature. The requestor can be the account itself or an app that wants to operate on its behalf. --- # ENS Names / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/architecture/ens-names#VPContent) On this page ENS Names [​](https://docs.farcaster.xyz/learn/architecture/ens-names#ens-names) ================================================================================= Farcaster uses ENS names as human readable identifiers for accounts. Two kinds of ENS names are supported: * **Offchain ENS names**: free and controlled by farcaster. (e.g. @alice) * **Onchain ENS names**: costs money and is controlled by your wallet. (e.g. @alice.eth) ENS names can only be used on Farcaster if they are <= 16 characters and contain only lowercase letters, numbers, and hyphens. ![Usernames](https://docs.farcaster.xyz/assets/usernames.D0JzJrCz.png) Onchain ENS Names [​](https://docs.farcaster.xyz/learn/architecture/ens-names#onchain-ens-names) ------------------------------------------------------------------------------------------------- Users can use onchain ENS names like `@alice.eth` on Farcaster. Onchain ENS names are issued by ENS, end in .eth and must be registered on the Ethereum L1 blockchain. Anyone can register an ENS name by using the [ENS app](https://app.ens.domains/) . Users must pay a fee to register an onchain ENS name, but once registered, it is controlled by the user and cannot be revoked. Offchain ENS Names (Fnames) [​](https://docs.farcaster.xyz/learn/architecture/ens-names#offchain-ens-names-fnames) ------------------------------------------------------------------------------------------------------------------- Users can use offchain ENS names like `@alice` on Farcaster. Offchain ENS names, also called Farcaster Names or Fnames, are compliant with ENS but registered offchain. Fnames are free but are subject to a usage policy to prevent squatting and impersonation. They are also subject to the following requirements: 1. An account can only have one Fname at a time. 2. An account can change its Fname once every 28 days. ### Usage Policy [​](https://docs.farcaster.xyz/learn/architecture/ens-names#usage-policy) Registering an Fname is free but subject to the following policy: 1. Names connected to public figures or entities may be reclaimed (e.g. @google). 2. Names that haven't been used for 60+ days may be reclaimed. 3. Names that are registered for the sole purpose of resale may be reclaimed. Decisions are made by the Farcaster team and often require human judgment. Users who want a name that is fully under their control should use an onchain ENS name. ### Registry [​](https://docs.farcaster.xyz/learn/architecture/ens-names#registry) Fnames are issued as offchain names under the subdomain `fcast.id`. Bob can register the offchain ENS name `bob.fcast.id` and use it on any Farcaster app with the shorthand `@bob`. The name can be registered by making a signed request to the Fname Registry server. See the [FName API reference](https://docs.farcaster.xyz/reference/fname/api) for more details on how to query and create Fnames. To learn more about how Fnames work, see [ENSIP-16](https://docs.ens.domains/ens-improvement-proposals/ensip-16-offchain-metadata) and [ERC-3668](https://eips.ethereum.org/EIPS/eip-3668) . --- # useProfile / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/hooks/use-profile#VPContent) On this page `useProfile` [​](https://docs.farcaster.xyz/auth-kit/hooks/use-profile#useprofile) =================================================================================== Hook for reading information about the authenticated user. You can use this hook to read the authenticated user's profile information from other components inside your app. tsx import { useProfile } from '@farcaster/auth-kit'; function App() { const { isAuthenticated, profile: { username, fid, bio, displayName, pfpUrl }, } = useProfile(); return (
{isAuthenticated ? (

Hello, {username}! Your fid is: {fid}

) : (

You're not signed in.

)}
); } Returns [​](https://docs.farcaster.xyz/auth-kit/hooks/use-profile#returns) --------------------------------------------------------------------------- ts { isAuthenticated: boolean; profile?: { fid?: number; username?: string; bio?: string; displayName?: string; pfpUrl?: string; custody?: Hex; verifications?: Hex[]; }, }; | Parameter | Description | | --- | --- | | `isAuthenticated` | True when the user is logged in. | | `profile.fid` | User's Farcaster ID. | | `profile.username` | User's username. | | `profile.bio` | User's bio text. | | `profile.displayName` | User's display name. | | `profile.pfpUrl` | User's profile picture URL. | | `profile.custody` | User's FID custody address. | | `profile.verifications` | List of user's verified addresses. | --- # Auth client / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/introduction#VPContent) On this page Auth client [​](https://docs.farcaster.xyz/auth-kit/client/introduction#auth-client) ===================================================================================== [![NPM Version](https://img.shields.io/npm/v/@farcaster/auth-client)](https://www.npmjs.com/package/@farcaster/auth-client) The `@farcaster/auth-client` library provides a framework agnostic client for Farcaster Auth. If you're not using React, want greater customizability, or want to interact with the Farcaster Auth relay directly, you can use the Auth client library to build your own Sign in With Farcaster flow. Getting Started [​](https://docs.farcaster.xyz/auth-kit/client/introduction#getting-started) --------------------------------------------------------------------------------------------- ### Installation [​](https://docs.farcaster.xyz/auth-kit/client/introduction#installation) Install the Auth client and its peer dependency [viem](https://viem.sh/) . sh npm install @farcaster/auth-client viem **Note:** This is a low level client library. If you're using React, take a look at [auth-kit](https://docs.farcaster.xyz/auth-kit/) instead. ### Create a client [​](https://docs.farcaster.xyz/auth-kit/client/introduction#create-a-client) Set up a client with a relay server URL and Ethereum connector. tsx import { createAppClient, viemConnector } from '@farcaster/auth-client'; const appClient = createAppClient({ relay: 'https://relay.farcaster.xyz', ethereum: viemConnector(), }); Depending on the type of app you're building, you may use an `AppClient` or a `WalletClient`. If you're building a connected app and logging in users, use an _app client_. If you're building a Farcaster wallet app, use a _wallet client_. ### Consume actions [​](https://docs.farcaster.xyz/auth-kit/client/introduction#consume-actions) Now that your client is set up, you can use it to interact with Farcaster Auth actions. tsx const { data: { channelToken } } = await appClient.createChannel({ siweUri: "https://example.com/login", domain: "example.com"; }); const status = await appClient.watchStatus({ channelToken, }); --- # Register ENS Name / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/accounts/register-ens#VPContent) Return to top Register ENS Name [​](https://docs.farcaster.xyz/developers/guides/accounts/register-ens#register-ens-name) ============================================================================================================ A user can set their farcaster username to an ENS name they own. ### Requirements [​](https://docs.farcaster.xyz/developers/guides/accounts/register-ens#requirements) * An ETH wallet that owns the account on OP Mainnet. No ETH is required. * A valid ENS name that resolves to the custody address of the Farcaster account, or a verified eth address in the Farcaster account. ### Register ENS Name [​](https://docs.farcaster.xyz/developers/guides/accounts/register-ens#register-ens-name-1) First, ensure the ENS name resolves to the custody address of the farcaster account. Or the resolved address is a verified eth address in the Farcaster account. See [here](https://docs.farcaster.xyz/developers/guides/writing/verify-address) for how to verify an ETH address. Then, generate an EIP-712 signature for the ENS name proof claim and submit the message. For more details on how to create messages, see [this guide](https://docs.farcaster.xyz/developers/guides/writing/messages) . js import { makeUserNameProofClaim, EIP712Signer, makeUsernameProof, FarcasterNetwork, makeUserDataAdd, UserNameType, UserDataType, } from '@farcaster/hub-nodejs'; const accountKey: EIP712Signer = undefined; // Account Key for the custody/verified address (use appropriate subclass from hub-nodejs for ethers or viem) const accountEd25519Key = undefined; // Private key for the farcaster account signer const claim = makeUserNameProofClaim({ name: 'farcaster.eth', // ENS name to register owner: '0x...', // Must be the public key of accountKey, and name must resolve to this address timestamp: Math.floor(Date.now() / 1000), }); const signature = ( await accountKey.signUserNameProofClaim(claim) )._unsafeUnwrap(); const dataOptions = { fid: 123, // FID make the request network: FarcasterNetwork.MAINNET, }; const signer = new NobleEd25519Signer(accountEd25519Key); const usernameProofMessage = makeUsernameProof( { name: claim.name, owner: claim.owner, timestamp: claim.timestamp, fid: dataOptions.fid, signature: signature, type: UserNameType.USERNAME_TYPE_ENS_L1, }, dataOptions, signer ); // Submit the message to the node. Note that this only registers the name proof to the account, it does not change the username yet. // await client.submitMessage(usernameProofMessage); // Once it's accepted, you can set the username to the ENS name const usernameMessage = makeUserData( { type: UserDataType.USERNAME, value: claim.name, }, dataOptions, signer ); // Submit the username message to the node // await client.submitMessage(usernameMessage); --- # Change custody address / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/accounts/change-custody#VPContent) Return to top Change custody address [​](https://docs.farcaster.xyz/developers/guides/accounts/change-custody#change-custody-address) ======================================================================================================================== Accounts are owned by custody address which is an Ethereum address on OP Mainnet. A user may want to change this address for security reasons or to transfer ownership of the entire account. ### Requirements [​](https://docs.farcaster.xyz/developers/guides/accounts/change-custody#requirements) * An ETH wallet that owns the account on OP Mainnet, with some ETH. * An Ethereum provider URL for OP Mainnet (e.g. via [Alchemy](https://www.alchemy.com/) , [Infura](https://www.infura.io/) or [QuickNode](https://www.quicknode.com/) ). ### Change Custody Address [​](https://docs.farcaster.xyz/developers/guides/accounts/change-custody#change-custody-address-1) Call the `transfer` function on the Id Registry contract. The receiving address must provide an EIP-712 signature accepting the transfer. @farcaster/hub-webViemhelpers.tsclients.ts ts import { ViemWalletEip712Signer } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const eip712Signer = new ViemWalletEip712Signer(walletClient); const signature = await eip712signer.signTransfer({ fid: 1n, to: account, nonce, deadline, }); const { request: transferRequest } = await publicClient.simulateContract({ ...IdContract, functionName: 'transfer', args: [account, deadline, signature], // to, deadline, signature }); await walletClient.writeContract(transferRequest); ts import { ID_REGISTRY_EIP_712_TYPES, idRegistryABI, ID_GATEWAY_ADDRESS, } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; import { readNonce, getDeadline } from './helpers.ts'; const nonce = await readNonce(); const deadline = getDeadline(); const IdContract = { abi: idRegistryABI, address: ID_GATEWAY_ADDRESS, chain: optimism, }; const signature = await walletClient.signTypedData({ account, ...ID_REGISTRY_EIP_712_TYPES, primaryType: 'Transfer', message: { fid: 1n, to: account, nonce, deadline, }, }); ts import { ID_REGISTRY_ADDRESS, idRegistryABI } from '@farcaster/hub-web'; import { publicClient, account } from './clients.ts'; export const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return now + oneHour; }; export const readNonce = async () => { return await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'nonces', args: [account], }); }; ts import { createWalletClient, createPublicClient, custom, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; export const publicClient = createPublicClient({ chain: optimism, transport: http(), }); export const walletClient = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }); // JSON-RPC Account export const [account] = await walletClient.getAddresses(); // Local Account export const account = privateKeyToAccount('0x...'); WARNING Transferring a FID does not reset its recovery address. To transfer a FID and update its recovery address, call [`transferAndChangeRecovery`](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferandchangerecovery) . See the [Id Registry](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer) section for more details. --- # Deployments / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/deployments#VPContent) On this page Deployments [​](https://docs.farcaster.xyz/reference/contracts/deployments#deployments) ======================================================================================== Addresses [​](https://docs.farcaster.xyz/reference/contracts/deployments#addresses) ------------------------------------------------------------------------------------ The Farcaster contracts are deployed on Optimism and Base mainnet. There are no testnet deployments. Optimism: | Contract | Optimism Address | Etherscan Link | | --- | --- | --- | | Id Registry | `0x00000000fc6c5f01fc30151999387bb99a9f489b` | [link](https://optimistic.etherscan.io/address/0x00000000fc6c5f01fc30151999387bb99a9f489b) | | Key Registry | `0x00000000Fc1237824fb747aBDE0FF18990E59b7e` | [link](https://optimistic.etherscan.io/address/0x00000000Fc1237824fb747aBDE0FF18990E59b7e) | | Storage Registry | `0x00000000fcCe7f938e7aE6D3c335bD6a1a7c593D` | [link](https://optimistic.etherscan.io/address/0x00000000fcCe7f938e7aE6D3c335bD6a1a7c593D) | | IdGateway | `0x00000000fc25870c6ed6b6c7e41fb078b7656f69` | [link](https://optimistic.etherscan.io/address/0x00000000fc25870c6ed6b6c7e41fb078b7656f69) | | KeyGateway | `0x00000000fc56947c7e7183f8ca4b62398caadf0b` | [link](https://optimistic.etherscan.io/address/0x00000000fc56947c7e7183f8ca4b62398caadf0b) | | Bundler | `0x00000000fc04c910a0b5fea33b03e0447ad0b0aa` | [link](https://optimistic.etherscan.io/address/0x00000000fc04c910a0b5fea33b03e0447ad0b0aa) | Base: | Contract | Base Address | Etherscan Link | | --- | --- | --- | | Tier Registry | `0x00000000fc84484d585C3cF48d213424DFDE43FD` | [link](https://basescan.org/address/0x00000000fc84484d585c3cf48d213424dfde43fd) | ABIs [​](https://docs.farcaster.xyz/reference/contracts/deployments#abis) -------------------------------------------------------------------------- The ABIs for the contracts are available via etherscan links above, or from the [`@farcaster/core`](https://github.com/farcasterxyz/hub-monorepo/tree/main/packages/core/src/eth/contracts/abis) package. --- # Tier Registry / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#VPContent) On this page Tier Registry [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#tier-registry) ======================================================================================================== The Tier Registry allows Farcaster accounts to purchase or extend Farcaster Pro subscriptions. Unlike other protocol contracts, the Tier Registry is deployed on Base Mainnet. If you want to purchase a Pro subscription for a Farcaster account, use the Tier Registry. Active Tiers [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#active-tiers) ------------------------------------------------------------------------------------------------------ | Tier ID | Description | Payment Token | Min Days | Price/day | | --- | --- | --- | --- | --- | | 1 | Farcaster Pro | USDC | 30 | `328767` wei USDC | Read [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#read) -------------------------------------------------------------------------------------- ### price [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#price) Get the total price in payment token (`uint256`) to purchase a tier subscription for a given number of days. | Param Name | type | Description | | --- | --- | --- | | tier | `uint256` | The tier ID to calculate price for | | forDays | `uint256` | The number of days to calculate price for | ### tierInfo [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#tierinfo) Get information about a specific tier. Returns a `TierInfo` struct. | Param Name | type | Description | | --- | --- | --- | | tier | `uint256` | The tier ID | `TierInfo` struct parameters: | Param Name | type | Description | | --- | --- | --- | | minDays | `uint256` | Minimum number of days required to purchase | | maxDays | `uint256` | Maximum number of days per purchase | | vault | `address` | Payment destination address | | paymentToken | `IERC20` | ERC20 payment token | | tokenPricePerDay | `uint256` | Price per day in fundamental units of `paymentToken` | | isActive | `bool` | Whether tier is currently active | Write [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#write) ---------------------------------------------------------------------------------------- ### purchaseTier [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#purchasetier) Purchase a subscription tier for a given fid. If the account already has an active subscription, purchasing will extend their total subscription time. | Param Name | type | Description | | --- | --- | --- | | fid | `uint256` | The fid to credit the subscription to | | tier | `uint256` | The tier ID to calculate price for | | forDays | `uint256` | The number of days to calculate price for | ### batchRent [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#batchrent) Rent storage for multiple fids in one transaction. The caller must send enough ether to cover the total cost of all units. Like single-unit rental, extra ether is returned and units are valid for 1 year. | Param Name | type | Description | | --- | --- | --- | | `msg.value` | `wei` | Total payment amount | | fids | `uint256[]` | Array of fids | | forDays | `uint256[]` | Array of unit quantities, corresponding to each fid in the `fids` array | ### batchPurchaseTier [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#batchpurchasetier) Purchase a subscription tier for multiple fids in a single transaction. If an account already has an active subscription, purchasing will extend their total subscription time. | Param Name | type | Description | | --- | --- | --- | | tier | `uint256` | The tier ID to calculate price for | | fids | `uint256[]` | Array of fids | | forDays | `uint256[]` | Array of days, corresponding to each fid in the `fids` array | Events [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#events) ------------------------------------------------------------------------------------------ ### PurchasedTier [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#purchasedtier) Emitted when a tier is purchased for a Farcaster account. | Param Name | type | Description | | --- | --- | --- | | fid | `uint256 indexed` | Farcaster ID the tier was purchased for | | tier | `uint256 indexed` | Tier ID that was purchased | | forDays | `uint256` | Number of days of subscription purchased | | payer | `address indexed` | Caller address that paid | Errors [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#errors) ------------------------------------------------------------------------------------------ | Error | Selector | Description | | --- | --- | --- | | InvalidDuration | `76166401` | The caller attempted to purchase an invalid number of days. | | InvalidTier | `e1423617` | The caller attempted to purchase an invalid or inactive tier. | | InvalidBatchInput | `0a514b99` | The caller provided mismatched arrays of `fids` and `forDays`. | Source [​](https://docs.farcaster.xyz/reference/contracts/reference/tier-registry#source) ------------------------------------------------------------------------------------------ [`TierRegistry.sol`](https://github.com/farcasterxyz/contracts/blob/d0af1b8148db713239f6ac19465efeadb713b58c/src/TierRegistry.sol) --- # Overview / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/index#VPContent) On this page Overview [​](https://docs.farcaster.xyz/reference/contracts/index#overview) ============================================================================ The core Farcaster contracts are deployed on Optimism, an Ethereum layer 2 network. There are three core contracts: Id Registry, Key Registry, and Storage Registry. Write access to the ID and Key registry is gated through the Gateway contracts. There is also a Bundler helper contract to make it easy to register an fid, add a key and rent storage in one transaction. The Tier Registry contract supporting Farcaster Pro subscriptions is deployed on Base, an Ethereum layer 2 network. ![contracts.png](https://docs.farcaster.xyz/assets/contracts.A6r7Ltyn.png) Id Registry [​](https://docs.farcaster.xyz/reference/contracts/index#id-registry) ---------------------------------------------------------------------------------- The Id Registry contract is used to keep track of Farcaster IDs. It maps an fid to an owning Ethereum address. The owner can also designate a "recovery address" which can be used to recover the fid if the owner loses access to the registering address. Registering an fid for the first time must be done through the [ID Gateway](https://docs.farcaster.xyz/reference/contracts/index#idgateway) . Key Registry [​](https://docs.farcaster.xyz/reference/contracts/index#key-registry) ------------------------------------------------------------------------------------ The Key Registry associates a Farcaster id to zero or more ed2559 public keys. Only messages signed by a key registered here are considered valid by the hubs. Registered keys can be revoked by the owner of the fid, but revoked keys can not be added to that fid again. The same key may be registered to multiple fids. Adding a key must be done through the [Key Gateway](https://docs.farcaster.xyz/reference/contracts/index#keygateway) . Storage Registry [​](https://docs.farcaster.xyz/reference/contracts/index#storage-registry) -------------------------------------------------------------------------------------------- The Storage Registry allows an fid to rent one or more "units" of storage on the farcaster network. The current cost of storage is 7$ USD per unit, for one year. This fee must be paid in ETH. The storage registry uses an ETH price oracle to determine the current cost in ETH and exposes functions to query this price. Overpayments are refunded to the caller. Id Gateway [​](https://docs.farcaster.xyz/reference/contracts/index#id-gateway) -------------------------------------------------------------------------------- The ID Gateway handles additional logic required for first time registration of an fid. To prevent spam, the gateway also requires renting 1 unit of storage. Key Gateway [​](https://docs.farcaster.xyz/reference/contracts/index#key-gateway) ---------------------------------------------------------------------------------- Similarly, the Key Gateway exists for the Key Registry. Adding a key to a fid must be done via the gateway. Bundler [​](https://docs.farcaster.xyz/reference/contracts/index#bundler) -------------------------------------------------------------------------- The Bundler makes first time sign up easier by allowing a user to register an fid, add a key and rent storage in one function call. Tier Registry [​](https://docs.farcaster.xyz/reference/contracts/index#tier-registry) -------------------------------------------------------------------------------------- The Tier Registry collects subscription payments for Farcaster Pro, paid in USDC. Unlike other core protocol contracts, it is deployed on Base mainnet. Source code [​](https://docs.farcaster.xyz/reference/contracts/index#source-code) ---------------------------------------------------------------------------------- The contracts source repo can be found [here](https://github.com/farcasterxyz/contracts) , including more low level documentation [here](https://github.com/farcasterxyz/contracts/blob/main/docs/docs.md) . --- # FAQ / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/contracts/faq#VPContent) On this page FAQ [​](https://docs.farcaster.xyz/reference/contracts/faq#faq) ================================================================ Signatures [​](https://docs.farcaster.xyz/reference/contracts/faq#signatures) ------------------------------------------------------------------------------ ### How do I generate an EIP-712 signature? [​](https://docs.farcaster.xyz/reference/contracts/faq#how-do-i-generate-an-eip-712-signature) See the contract reference docs for documentation on each EIP-712 signature, including Typescript [code examples](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway#register-signature) . If you're using Typescript/JS, the [`@farcaster/hub-web`](https://www.npmjs.com/package/@farcaster/hub-web) package includes tools for generating and working with EIP-712 signatures. To ensure you're using the correct addresses and typehashes, we recommend importing the ABIs and EIP-712 types from the [contracts module](https://github.com/farcasterxyz/hub-monorepo/tree/main/packages/core/src/eth/contracts) or using the provided [`Eip712Signer`](https://github.com/farcasterxyz/hub-monorepo/blob/main/packages/core/src/signers/eip712Signer.ts) helper. See the "Working with EIP-712 signatures" [example app](https://github.com/farcasterxyz/hub-monorepo/tree/main/packages/hub-nodejs/examples/contract-signatures) in the hub monorepo for a reference that demonstrates each signature and contract call. ### How can I debug an invalid EIP-712 signature? [​](https://docs.farcaster.xyz/reference/contracts/faq#how-can-i-debug-an-invalid-eip-712-signature) To help debug EIP-712 signing, every contract that uses EIP-712 signatures exposes its [domain separator](https://optimistic.etherscan.io/address/0x00000000fc25870c6ed6b6c7e41fb078b7656f69#readContract#F3) and typehashes as [constants](https://optimistic.etherscan.io/address/0x00000000fc25870c6ed6b6c7e41fb078b7656f69#readContract#F1) along with a [hashTypedDataV4](https://optimistic.etherscan.io/address/0x00000000fc25870c6ed6b6c7e41fb078b7656f69#readContract#F6) helper view. If you're constructing signatures in Solidity or another low level language, you can use these to help debug. Reference [​](https://docs.farcaster.xyz/reference/contracts/faq#reference) ---------------------------------------------------------------------------- ### Where is the full contract source code? [​](https://docs.farcaster.xyz/reference/contracts/faq#where-is-the-full-contract-source-code) The contracts repo is on Github [here](https://github.com/farcasterxyz/contracts) . ### Where do I get contract ABIs? [​](https://docs.farcaster.xyz/reference/contracts/faq#where-do-i-get-contract-abis) Find contract ABIs and deployment addresses [here](https://docs.farcaster.xyz/reference/contracts/deployments#abis) . ### Where can I find audit reports? [​](https://docs.farcaster.xyz/reference/contracts/faq#where-can-i-find-audit-reports) Past audit reports are linked from the [contracts repo](https://github.com/farcasterxyz/contracts/blob/1aceebe916de446f69b98ba1745a42f071785730/README.md#audits) . ### Are the Farcaster contracts deployed to a testnet? [​](https://docs.farcaster.xyz/reference/contracts/faq#are-the-farcaster-contracts-deployed-to-a-testnet) No. Consider using [network forking](https://book.getfoundry.sh/tutorials/forking-mainnet-with-cast-anvil) to test or develop against the OP mainnet contracts. Data [​](https://docs.farcaster.xyz/reference/contracts/faq#data) ------------------------------------------------------------------ ### How do I find a user’s custody address? [​](https://docs.farcaster.xyz/reference/contracts/faq#how-do-i-find-a-user-s-custody-address) Call the [`custodyOf`](https://optimistic.etherscan.io/address/0x00000000fc6c5f01fc30151999387bb99a9f489b#readContract#F5) function on the [IdRegistry](https://docs.farcaster.xyz/reference/contracts/reference/id-registry) . ### How do I find a user’s recovery address? [​](https://docs.farcaster.xyz/reference/contracts/faq#how-do-i-find-a-user-s-recovery-address) Call the [`recoveryOf`](https://optimistic.etherscan.io/address/0x00000000fc6c5f01fc30151999387bb99a9f489b#readContract#F23) function on the [IdRegistry](https://docs.farcaster.xyz/reference/contracts/reference/id-registry) . ### How do I find an account’s fid? [​](https://docs.farcaster.xyz/reference/contracts/faq#how-do-i-find-an-account-s-fid) Call the [`idOf`](https://optimistic.etherscan.io/address/0x00000000fc6c5f01fc30151999387bb99a9f489b#readContract#F14) function on the [IdRegistry](https://docs.farcaster.xyz/reference/contracts/reference/id-registry) . ### How do I look up the account keys for my fid? [​](https://docs.farcaster.xyz/reference/contracts/faq#how-do-i-look-up-the-account-keys-for-my-fid) Call the [`keysOf`](https://optimistic.etherscan.io/address/0x00000000fc1237824fb747abde0ff18990e59b7e#readContract#F16) function on the [KeyRegistry](https://docs.farcaster.xyz/reference/contracts/reference/key-registry) . Other [​](https://docs.farcaster.xyz/reference/contracts/faq#other) -------------------------------------------------------------------- ### What is an app fid? How do I get one? [​](https://docs.farcaster.xyz/reference/contracts/faq#what-is-an-app-fid-how-do-i-get-one) **What is an FID?** An FID (Farcaster ID) is a unique identifier used to distinguish applications and users. With an FID, apps and users can be identified and differentiated. **Why is an FID necessary?** To create or post anything on the Farcaster platform, an FID is essential for identifying your app or user. **How do I get one?** You can register an app fid directly through the [Bundler](https://docs.farcaster.xyz/reference/contracts/reference/bundler) or [IdGateway](https://docs.farcaster.xyz/reference/contracts/reference/id-gateway) , or use a Farcaster client to register an account for your app. Since you'll need to sign [key request metadata](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator) from the wallet that owns your app fid, keep the private key secure. --- # Contributing / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/contributing/overview#VPContent) On this page Contributing [​](https://docs.farcaster.xyz/learn/contributing/overview#contributing) ====================================================================================== Farcaster welcomes contributions of all sizes from the community. The protocol owes thanks to over 100 contributors who have helped us so far. To get involved, try looking up open issues in one of the repos below or join a dev call. Repositories [​](https://docs.farcaster.xyz/learn/contributing/overview#repositories) -------------------------------------------------------------------------------------- | Repository | Description | | --- | --- | | [Protocol](https://github.com/farcasterxyz/protocol) | Specification of the protocol | | [Contracts](https://github.com/farcasterxyz/contracts) | The canonical Farcaster contracts | | [Snapchain](https://github.com/farcasterxyz/snapchain) | A Farcaster node written in Rust | | [FName Registry](https://github.com/farcasterxyz/fname-registry) | The canonical server to register fnames | | [Docs](https://github.com/farcasterxyz/docs) | Documentation for all the above (this site) | Documentation [​](https://docs.farcaster.xyz/learn/contributing/overview#documentation) ---------------------------------------------------------------------------------------- This site serves as the central hub for documentation on the protocol. If you have feedback, please open an issue or create a pull request at [farcasterxyz/docs](https://github.com/farcasterxyz/docs) Dev Calls [​](https://docs.farcaster.xyz/learn/contributing/overview#dev-calls) -------------------------------------------------------------------------------- We host a bi-weekly developer call to discuss upcoming changes to the protocol. The call is open to anyone and is a great way to get involved. * [Calendar Invite](https://calendar.google.com/calendar/u/0?cid=NjA5ZWM4Y2IwMmZiMWM2ZDYyMTkzNWM1YWNkZTRlNWExN2YxOWQ2NDU3NTA3MjQwMTk3YmJlZGFjYTQ3MjZlOEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t) - Calendar invite to join upcoming calls. * [Recordings](https://www.youtube.com/watch?v=lmGXWP5m1_Y&list=PL0eq1PLf6eUeZnPtyKMS6uN9I5iRIlnvq) - watch recordings of previous calls. --- # FName Registry Server API Reference / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/fname/api#VPContent) Return to top FName Registry Server API Reference [​](https://docs.farcaster.xyz/reference/fname/api#fname-registry-server-api-reference) ============================================================================================================================ The [Fname registry](https://github.com/farcasterxyz/fname-registry) server is hosted at [https://fnames.farcaster.xyz](https://fnames.farcaster.xyz/) It's a simple HTTP service that's responsible for issuing and tracking fnames. All Fname changes are recorded as a transfer. Registering an fname is a transfer from FID 0 to the user's fid. Transferring an fname is a transfer from the user's fid to another fid. Unregistering an fname is a transfer from the user's fid to fid 0. Registering an fname Note, when registering a new fname, calling this api is not sufficient. This only reserves the name to your fid. You must also submit a [UserDataAdd](https://snapchain.farcaster.xyz/reference/datatypes/messages#2-userdata) message to the hub to set this name as your username. ### Get Transfer History [​](https://docs.farcaster.xyz/reference/fname/api#get-transfer-history) To get a history of all transfers, make a GET request to `/transfers` bash curl https://fnames.farcaster.xyz/transfers | jq It also accepts the following query parameters: * `from_id` - The transfer id to start from for pagination * `name` - The fname to filter by * `fid` - The fid (either from or to) to filter by * `from_ts` - The timestamp (in seconds) to start from for pagination ### Get current fname or fid [​](https://docs.farcaster.xyz/reference/fname/api#get-current-fname-or-fid) To get the most recent transfer event for an fid or fname, make a GET request to `/transfers/current` e.g. To determine the fid of `@farcaster`, make the following call and use the value from the `to` field in the return value bash curl https://fnames.farcaster.xyz/transfers?name=farcaster | jq To determine the fname of fid `1`, make the following call and use the value from the `username` field in the return value bash curl https://fnames.farcaster.xyz/transfers?fid=1 | jq Both will return the same transfers object: json { "transfers": [\ {\ "id": 1,\ "timestamp": 1628882891,\ "username": "farcaster",\ "owner": "0x8773442740c17c9d0f0b87022c722f9a136206ed",\ "from": 0,\ "to": 1,\ "user_signature": "0xa6fdd2a69deab5633636f32a30a54b21b27dff123e6481532746eadca18cd84048488a98ca4aaf90f4d29b7e181c4540b360ba0721b928e50ffcd495734ef8471b",\ "server_signature": "0xb7181760f14eda0028e0b647ff15f45235526ced3b4ae07fcce06141b73d32960d3253776e62f761363fb8137087192047763f4af838950a96f3885f3c2289c41b"\ }\ ] } ### Register or transfer an fname [​](https://docs.farcaster.xyz/reference/fname/api#register-or-transfer-an-fname) To register a new fid, e.g. `farcaster`, first make sure the fname is not already registered. Then make a POST request to `/transfers` with the following body: yaml { "name": "farcaster", // Name to register "from": 0, // Fid to transfer from (0 for a new registration) "to": 123, // Fid to transfer to (0 to unregister) "fid": 123, // Fid making the request (must match from or to) "owner": "0x...", // Custody address of fid making the request "timestamp": 1641234567, // Current timestamp in seconds "signature": "0x..." // EIP-712 signature signed by the custody address of the fid } To generate the EIP-712 signature, use the following code: js import { makeUserNameProofClaim, EIP712Signer } from '@farcaster/hub-nodejs'; const accountKey: EIP712Signer = undefined; // Account key for the custody address (use appropriate subclass from hub-nodejs for ethers or viem) const claim = makeUserNameProofClaim({ name: 'farcaster', owner: '0x...', timestamp: Math.floor(Date.now() / 1000), }); const signature = ( await accountKey.signUserNameProofClaim(claim) )._unsafeUnwrap(); This is the exact same kind of signature used in the ENS UsernameProofs provided to hubs to prove ownership of an ENS name. e.g. bash curl -X POST https://fnames.farcaster.xyz/transfers \ -H "Content-Type: application/json" \ -d \ '{"name": "farcaster", "owner": "0x...", "signature": "0x...", "from": 0, "to": 1000, "timestamp": 1641234567, fid: 1000}' Once a name is registered, it still needs a [UserData](https://snapchain.farcaster.xyz/reference/datatypes/messages#2-userdata) message to be sent to the hub in order to actually set the username for the user. See examples in the [hub-nodejs](https://github.com/farcasterxyz/hub-monorepo/tree/main/packages/hub-nodejs/examples/hello-world) repo. --- # Examples / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/examples#VPContent) Return to top Examples [​](https://docs.farcaster.xyz/auth-kit/examples#examples) ==================================================================== Client Side [​](https://docs.farcaster.xyz/auth-kit/examples#client-side) -------------------------------------------------------------------------- A frontend-only app that lets users Sign in with Farcaster and shows them their profile picture and username. [Try Demo](https://farcaster-auth-kit-vite-demo.replit.app/) | [View Source](https://github.com/farcasterxyz/connect-monorepo/tree/main/examples/frontend-only) Server Side [​](https://docs.farcaster.xyz/auth-kit/examples#server-side) -------------------------------------------------------------------------- A Next.js app that lets users Sign in with Farcaster and handles sessions server-side. [Try Demo](https://farcaster-auth-kit-next-auth-demo.replit.app/) | [View Source](https://github.com/farcasterxyz/connect-monorepo/tree/main/examples/with-next-auth) --- # Governance / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/contributing/governance#VPContent) Return to top Governance [​](https://docs.farcaster.xyz/learn/contributing/governance#governance) ==================================================================================== Farcaster embraces [rough consensus and running code](https://en.wikipedia.org/wiki/Rough_consensus) as its governance model. Changes happen when someone makes a proposal, gets buy-in and ships running code. Depending on the change, different groups need to be convinced: 1. **Protocol devs**, who choose to merge changes into hubs and contracts. 2. **Hub runners**, who choose to deploy those changes to their hubs. 3. **App devs**, who choose the hubs they read from. 4. **Users**, who choose the apps they want to use. Consensus emerges from people accepting new code or rejecting it. Farcaster will not have a binding voting process, official roles or veto power for anyone. Having too much structure ossifies systems, encourages politicking and slows progress. Rough consensus biases to action, encourages diversity of viewpoints, and maximizes decentralization, which is essential for a long-lived protocol. Most changes happen through the [FIP](https://docs.farcaster.xyz/learn/contributing/fips) process. --- # FIPs / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/learn/contributing/fips#VPContent) Return to top FIPs [​](https://docs.farcaster.xyz/learn/contributing/fips#fips) ================================================================== An FIP, or Farcaster Improvement Proposal, is a process for building consensus around protocol changes. FIP's are inspired by [Ethereum's EIPs](https://eips.ethereum.org/EIPS/eip-1) and [Python's PEPs](https://peps.python.org/pep-0001/) . Anyone can write an FIP to propose a change to: 1. A process, like the protocol's release schedule 2. A standard, like URIs for onchain assets 3. An implementation, like adding a new protocol feature Read more about FIP's in [FIP-0: A proposal for making proposals](https://github.com/farcasterxyz/protocol/discussions/82) . Proposals are made and ratified on the [discussions board](https://github.com/farcasterxyz/protocol/discussions/) . A list of finalized FIPs can be found [here](https://github.com/farcasterxyz/protocol/discussions/categories/fip-stage-4-finalized) . --- # App Client / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/app/client#VPContent) On this page App Client [​](https://docs.farcaster.xyz/auth-kit/client/app/client#app-client) ================================================================================= If you're building a [connected app](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#connected-apps) and want users to sign in with Farcaster, use an `AppClient`. You can use an `AppClient` to create a Farcaster Auth relay channel, generate a deep link to request a signature from the user's Farcaster wallet app, and verify the returned signature. ts import { createAppClient, viemConnector } from '@farcaster/auth-client'; const appClient = createAppClient({ relay: 'https://relay.farcaster.xyz', ethereum: viemConnector(), }); Parameters [​](https://docs.farcaster.xyz/auth-kit/client/app/client#parameters) --------------------------------------------------------------------------------- | Parameter | Type | Description | Required | | --- | --- | --- | --- | | `ethereum` | `EthereumConnector` | An Ethereum connector, used to query the Farcaster contracts and verify smart contract wallet signatures. `@farcaster/auth-client` currently provides only the `viem` connector type.

To use a custom RPC, pass an RPC URL to the viem connector. | Yes | | `relay` | `string` | Relay server URL. Defaults to the public relay at `https://relay.farcaster.xyz` | No | | `version` | `string` | Farcaster Auth version. Defaults to `"v1"` | No | --- # createChannel / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/app/create-channel#VPContent) On this page `createChannel` [​](https://docs.farcaster.xyz/auth-kit/client/app/create-channel#createchannel) ================================================================================================= Create a Farcaster Auth relay channel. Returns a secret token identifying the channel, and a URI to display to the end user as a link or QR code. ts const channel = await appClient.createChannel({ siweUri: 'https://example.com/login', domain: 'example.com', }); Parameters [​](https://docs.farcaster.xyz/auth-kit/client/app/create-channel#parameters) ----------------------------------------------------------------------------------------- | Parameter | Type | Description | Required | Example | | --- | --- | --- | --- | --- | | `siweUri` | `string` | Login URL for your application. | Yes | `https://example.com/login` | | `domain` | `string` | Domain of your application. | Yes | `example.com` | | `nonce` | `string` | A custom nonce. Must be at least 8 alphanumeric characters. | No | `ESsxs6MaFio7OvqWb` | | `notBefore` | `string` | Start time at which the signature becomes valid. ISO 8601 datetime. | No | `2023-12-20T23:21:24.917Z` | | `expirationTime` | `string` | Expiration time at which the signature is no longer valid. ISO 8601 datetime. | No | `2023-12-20T23:21:24.917Z` | | `requestId` | `string` | A system specific ID your app can use to refer to the sign in request. | No | `8d0494d9-e0cf-402b-ab0a-394ac7fe07a0` | | `acceptAuthAddress` | `boolean` | Whether your application accepts signatures from an [auth address](https://github.com/farcasterxyz/protocol/discussions/225)
. | No | `true` | Returns [​](https://docs.farcaster.xyz/auth-kit/client/app/create-channel#returns) ----------------------------------------------------------------------------------- ts { response: Response; data: { channelToken: string; url: string; nonce: string; } isError: boolean; error: Error; } | Parameter | Description | | --- | --- | | `response` | HTTP response from the Connect relay server. | | `data.channelToken` | Connect relay channel token. | | `data.url` | Sign in With Farcaster URL to present to the user. Links to the Farcaster client in v1. | | `data.nonce` | Random nonce included in the Sign in With Farcaster message. | | `isError` | True when an error has occurred. | | `error` | `Error` instance. | --- # status / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/app/status#VPContent) On this page `status` [​](https://docs.farcaster.xyz/auth-kit/client/app/status#status) =========================================================================== Get the current status of a Farcaster Auth request. Returns the current state of the request, either `'pending'` if the user's Farcaster wallet app has not yet sent back a signature, or `'completed'` once the wallet app has returned a response. In `'completed'` state, the response includes the generated Sign in With Farcaster message, a signature from the user's custody address, the user's verified fid, and user profile information. ts const status = await appClient.status({ channelToken: '23W59BKK', }); Parameters [​](https://docs.farcaster.xyz/auth-kit/client/app/status#parameters) --------------------------------------------------------------------------------- | Parameter | Type | Description | Required | Example | | --- | --- | --- | --- | --- | | `channelToken` | `string` | Farcaster Auth channel token. | Yes | `8d0494d9-e0cf-402b-ab0a-394ac7fe07a0` | Returns [​](https://docs.farcaster.xyz/auth-kit/client/app/status#returns) --------------------------------------------------------------------------- ts { response: Response data: { state: "pending"; nonce: string; metadata: { ip: string; userAgent: string; }; acceptAuthAddress: boolean; } | { state: "completed"; nonce: string; url: string; message?: string; signature?: `0x${string}`; authMethod?: "custody" | "authAddress"; fid?: number; username?: string; bio?: string; displayName?: string; pfpUrl?: string; verifications?: string[]; custody?: Hex; signatureParams: { siweUri: string; domain: string; nonce?: string; notBefore?: string; expirationTime?: string; requestId?: string; redirectUrl?: string; }; metadata: { ip: string; userAgent: string; }; acceptAuthAddress: boolean; } isError: boolean error: Error } | Parameter | Description | | --- | --- | | `response` | HTTP response from the Connect relay server. | | `data.state` | Status of the sign in request, either `"pending"` or `"completed"` | | `data.nonce` | Random nonce used in the SIWE message. If you don't provide a custom nonce as an argument to the hook, you should read this value. | | `data.url` | URL of the application. | | `data.message` | The generated SIWE message. | | `data.signature` | Hex signature produced by the user's Farcaster client app wallet. | | `data.authMethod` | Auth method used to sign the message. Either `"custody"` or `"authAddress"`. | | `data.fid` | User's Farcaster ID. | | `data.username` | User's Farcaster username. | | `data.bio` | User's Farcaster bio. | | `data.displayName` | User's Farcaster display name. | | `data.pfpUrl` | User's Farcaster profile picture URL. | | `data.custody` | User's FID custody address. | | `data.verifications` | List of user's verified addresses. | | `data.signatureParams` | SIWF message parameters. | | `data.metadata.ip` | IP address of client request. | | `data.metadata.userAgent` | User agent of client request. | | `data.acceptAuthAddress` | `true` if requesting application accepts auth address signatures. | | `isError` | True when an error has occurred. | | `error` | `Error` instance. | --- # watchStatus / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/app/watch-status#VPContent) On this page `watchStatus` [​](https://docs.farcaster.xyz/auth-kit/client/app/watch-status#watchstatus) =========================================================================================== Poll for the current status of a Farcaster Auth request. When the status changes to `'complete'` this action resolves with the final channel value, including the Sign In With Farcaster message, signature, and user profile information. ts const status = await appClient.watchStatus({ channelToken: '210f1718-427e-46a4-99e3-2207f21f83ec', timeout: 60_000, interval: 1_000, onResponse: ({ response, data }) => { console.log('Response code:', response.status); console.log('Status data:', data); }, }); Parameters [​](https://docs.farcaster.xyz/auth-kit/client/app/watch-status#parameters) --------------------------------------------------------------------------------------- | Parameter | Type | Description | Required | Example | | --- | --- | --- | --- | --- | | `channelToken` | `string` | Farcaster Auth channel token. | Yes | `8d0494d9-e0cf-402b-ab0a-394ac7fe07a0` | | `timeout` | `number` | Polling timeout, in milliseconds. If the connect request is not completed before the timeout, `watchStatus` returns an error. | No | `300_000` | | `interval` | `number` | Polling interval, in milliseconds. The client will check for updates at this frequency. | No | `1_000` | | `onResponse` | `function` | Callback function invoked each time the client polls for an update and receives a response from the relay server. Receives the return value of the latest `status` request. | No | `({ data }) => console.log(data.fid)` | Returns [​](https://docs.farcaster.xyz/auth-kit/client/app/watch-status#returns) --------------------------------------------------------------------------------- ts { response: Response data: { state: "pending"; nonce: string; metadata: { ip: string; userAgent: string; }; acceptAuthAddress: boolean; } | { state: "completed"; nonce: string; url: string; message?: string; signature?: `0x${string}`; authMethod?: "custody" | "authAddress"; fid?: number; username?: string; bio?: string; displayName?: string; pfpUrl?: string; verifications?: string[]; custody?: Hex; signatureParams: { siweUri: string; domain: string; nonce?: string; notBefore?: string; expirationTime?: string; requestId?: string; redirectUrl?: string; }; metadata: { ip: string; userAgent: string; }; acceptAuthAddress: boolean; } isError: boolean error: Error } | Parameter | Description | | --- | --- | | `response` | HTTP response from the Connect relay server. | | `data.state` | Status of the sign in request, either `"pending"` or `"completed"` | | `data.nonce` | Random nonce used in the SIWE message. If you don't provide a custom nonce as an argument, you should read this value. | | `data.url` | URL of the application. | | `data.message` | The generated SIWE message. | | `data.signature` | Hex signature produced by the user's Warpcast wallet. | | `data.authMethod` | Auth method used to sign the message. Either `"custody"` or `"authAddress"`. | | `data.fid` | User's Farcaster ID. | | `data.username` | User's Farcaster username. | | `data.bio` | User's Farcaster bio. | | `data.displayName` | User's Farcaster display name. | | `data.pfpUrl` | User's Farcaster profile picture URL. | | `data.custody` | User's FID custody address. | | `data.verifications` | List of user's verified addresses. | | `data.signatureParams` | SIWF message parameters. | | `data.metadata.ip` | IP address of client request. | | `data.metadata.userAgent` | User agent of client request. | | `data.acceptAuthAddress` | `true` if requesting application accepts auth address signatures. | | `isError` | True when an error has occurred. | | `error` | `Error` instance. | --- # verifySignInMessage / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/app/verify-sign-in-message#VPContent) On this page `verifySignInMessage` [​](https://docs.farcaster.xyz/auth-kit/client/app/verify-sign-in-message#verifysigninmessage) ===================================================================================================================== Verify a Sign In With Farcaster message. Your app should call this function and check that it succeeds after reading the message and signature provided by the user's Farcaster wallet over the Connect channel. Returns the parsed Sign in With Farcaster message, the user's fid, and whether the verification succeeded. ts const { data, success, fid } = await appClient.verifySignInMessage({ nonce: 'abcd1234', domain: 'example.com', message: 'example.com wants you to sign in with your Ethereum account…', signature: '0x9335c3055d47780411a3fdabad293c68c84ea350a11794cd11fd51b…', acceptAuthAddress: true, }); Parameters [​](https://docs.farcaster.xyz/auth-kit/client/app/verify-sign-in-message#parameters) ------------------------------------------------------------------------------------------------- | Parameter | Type | Description | Required | | --- | --- | --- | --- | | `domain` | `string` | Domain of your application. Must match the domain in the provided SIWF message. | Yes | | `nonce` | `string` | A custom nonce. Must match the nonce in the provided SIWF message. | Yes | | `message` | `string` or `SiweMessage` | The Sign in With Farcaster message to verify. This may be either a string or a parsed `SiweMessage`. Your app should read this value from the Connect channel once the request is completed. | Yes | | `signature` | `Hex` | Signature provided by the user's Farcaster wallet. Your app should read this from the Connect channel once the request is completed. | Yes | | `acceptAuthAddress` | `boolean` | Pass `true` to accept an [auth address](https://github.com/farcasterxyz/protocol/discussions/225)
signature in addition to a custody address signature. | No | Returns [​](https://docs.farcaster.xyz/auth-kit/client/app/verify-sign-in-message#returns) ------------------------------------------------------------------------------------------- ts { data: SiweMessage, success: boolean, fid: number isError: boolean error: Error } | Parameter | Description | | --- | --- | | `data` | Parsed SIWF message, as a `SiweMessage` object. | | `success` | True if the provided signature is valid. | | `fid` | FID of the user. | | `isError` | True when an error has occurred. | | `error` | `Error` instance. | --- # Wallet Client / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/wallet/client#VPContent) On this page Wallet Client [​](https://docs.farcaster.xyz/auth-kit/client/wallet/client#wallet-client) ========================================================================================== If you're building a [wallet app](https://docs.farcaster.xyz/learn/what-is-farcaster/apps#wallet-apps) and receiving signature requests, use a `WalletClient`. You can use a `WalletClient` to parse an incoming Sign In With Farcaster request URL, build a Sign In With Farcaster message to present to the user, and submit the signed message to a Farcaster Auth relay channel. ts import { createWalletClient, viemConnector } from '@farcaster/auth-client'; const walletClient = createWalletClient({ relay: 'https://relay.farcaster.xyz', ethereum: viemConnector(), }); Parameters [​](https://docs.farcaster.xyz/auth-kit/client/wallet/client#parameters) ------------------------------------------------------------------------------------ | Parameter | Type | Description | Required | | --- | --- | --- | --- | | `ethereum` | `EthereumConnector` | An Ethereum connector, used to query the Farcaster contracts and verify smart contract wallet signatures. `@farcaster/auth-client` currently provides only the `viem` connector type.

To use a custom RPC, pass an RPC URL to the viem connector. | Yes | | `relay` | `string` | Relay server URL. Defaults to the public relay at `https://relay.farcaster.xyz` | No | | `version` | `string` | Farcaster Auth version. Defaults to `"v1"` | No | --- # parseSignInURI / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/wallet/parse-sign-in-uri#VPContent) On this page `parseSignInURI` [​](https://docs.farcaster.xyz/auth-kit/client/wallet/parse-sign-in-uri#parsesigninuri) ========================================================================================================= Parse the Sign In With Farcaster URI provided by a connected app user. Returns the parsed parameters. Your app should use these to construct a Sign In With Farcaster message. Returns an error if URI is invalid. ts const params = walletClient.parseSignInURI({ uri: 'farcaster://connect?channelToken=23W59BKK&nonce=ESsxs6MaFio7OvqWb&siweUri=https%3A%2F%2Fexample.com%2Flogin&domain=example.com', }); Parameters [​](https://docs.farcaster.xyz/auth-kit/client/wallet/parse-sign-in-uri#parameters) ----------------------------------------------------------------------------------------------- | Parameter | Type | Description | Required | | --- | --- | --- | --- | | `uri` | `string` | Sign In With Farcaster URI. | Yes | Returns [​](https://docs.farcaster.xyz/auth-kit/client/wallet/parse-sign-in-uri#returns) ----------------------------------------------------------------------------------------- ts { channelToken: string params: { domain: string uri: string nonce: string notBefore?: string expirationTime?: string requestId?: string } isError: boolean error: Error } | Parameter | Description | | --- | --- | | `channelToken` | Connect relay channel token. | | `params.uri` | Login URI of the relying connected app. | | `params.domain` | Domain of the relying app. | | `params.nonce` | Random nonce provided by the relying app. | | `params.notBefore` | Time at which this message becomes valid. | | `params.expirationTime` | Time at which this message expires. | | `params.requestId` | A system specific identifier provided by the relying application. | | `isError` | True when an error has occurred. | | `error` | `Error` instance. | --- # buildSignInMessage / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/wallet/build-sign-in-message#VPContent) On this page `buildSignInMessage` [​](https://docs.farcaster.xyz/auth-kit/client/wallet/build-sign-in-message#buildsigninmessage) ===================================================================================================================== Build a Sign In With Farcaster message to present to the end user for signing. Adds required Sign In With Farcaster message attributes to any provided parameters. You should parse most of these parameters from a provided protocol URI. Your wallet app must provide the user's custody address and fid. Returns a `SiweMessage` object and the message as a string. ts const { siweMessage, message } = walletClient.buildSignInMessage({ address: '0x63C378DDC446DFf1d831B9B96F7d338FE6bd4231', fid: 1, uri: 'https://example.com/login', domain: 'example.com', nonce: 'ESsxs6MaFio7OvqWb', }); Parameters [​](https://docs.farcaster.xyz/auth-kit/client/wallet/build-sign-in-message#parameters) --------------------------------------------------------------------------------------------------- | Parameter | Type | Description | Required | | --- | --- | --- | --- | | `address` | `Hex` | Wallet user's custody address. This address must be the same account that signs the generated Sign In With Farcaster message. Your wallet app should provide the custody address of the authenticated user. | Yes | | `fid` | `string` | Wallet user's fid. Your wallet app should provide the fid of the authenticated user. | Yes | | `uri` | `string` | Login URL of the relying connected app. Parse this from the provided Sign In With Farcaster URI. | Yes | | `domain` | `string` | Domain of the relying connected app. Parse this from the provided Sign In With Farcaster URI. | Yes | | `nonce` | `string` | Random nonce to include in the Sign In With Farcaster message. Must be at least 8 alphanumeric characters. Parse this from the provided Sign In With Farcaster URI. | Yes | | `notBefore` | `string` | Start time at which the SIWE signature becomes valid. ISO 8601 datetime. Parse this from the provided Sign In With Farcaster URI. | No | | `expirationTime` | `string` | Expiration time after which the SIWE signature becomes valid. ISO 8601 datetime. Parse this from the provided Sign In With Farcaster URI. | No | | `requestId` | `string` | A system specific ID provided by the relying app. Parse this from the provided Sign In With Farcaster URI. | No | Returns [​](https://docs.farcaster.xyz/auth-kit/client/wallet/build-sign-in-message#returns) --------------------------------------------------------------------------------------------- ts { siweMessage: SiweMessage; message: string; isError: boolean; error: Error; } | Parameter | Description | | --- | --- | | `siweMessage` | Constructed Sign In With Ethereum message object. | | `message` | SIWE message serialized as a string. | | `isError` | True when an error has occurred. | | `error` | `Error` instance. | --- # authenticate / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/auth-kit/client/wallet/authenticate#VPContent) On this page `authenticate` [​](https://docs.farcaster.xyz/auth-kit/client/wallet/authenticate#authenticate) ================================================================================================ Submit a Sign In With Farcaster message, user signature, and profile data to the Connect relay server. ts const params = await walletClient.authenticate({ message: 'example.com wants you to sign in with your Ethereum account…', signature: '0x9335c3055d47780411a3fdabad293c68c84ea350a11794cdc811fd5…', authMethod: 'authAddress', fid: 1, username: 'alice', bio: "I'm a little teapot who didn't fill out my bio", displayName: 'Alice Teapot', pfpUrl: 'https://images.example.com/profile.png', }); Parameters [​](https://docs.farcaster.xyz/auth-kit/client/wallet/authenticate#parameters) ------------------------------------------------------------------------------------------ | Parameter | Type | Description | Required | | --- | --- | --- | --- | | `authKey` | `string` | Farcaster Auth API key. Farcaster Auth v1 restricts calls to `/authenticate` to the official Farcaster client. | Yes | | `channelToken` | `string` | Farcaster Auth channel token. | Yes | | `message` | `string` | The Sign in With Farcaster message produced by your wallet app and signed by the user. | Yes | | `signature` | `Hex` | SIWE signature created by the wallet user's account. | Yes | | `authMethod` | `string` | Method used to sign the SIWF message. Either `"custody"` or `"authAddress"`. | Yes | | `fid` | `number` | Wallet user's fid. | Yes | | `username` | `string` | Wallet user's Farcaster username. | Yes | | `bio` | `string` | Wallet user's bio. | Yes | | `displayName` | `string` | Wallet user's display name. | Yes | | `pfpUrl` | `string` | Wallet user's profile photo URL. | Yes | Returns [​](https://docs.farcaster.xyz/auth-kit/client/wallet/authenticate#returns) ------------------------------------------------------------------------------------ ts { response: Response data: { state: 'completed' nonce: string message?: string signature?: `Hex` fid?: number username?: string bio?: string displayName?: string pfpUrl?: string } isError: boolean error: Error } | Parameter | Description | | --- | --- | | `response` | HTTP response from the Connect relay server. | | `data.state` | Status of the sign in request, either `"pending"` or `"complete"` | | `data.nonce` | Random nonce used in the SIWE message. | | `data.message` | The generated SIWE message. | | `data.signature` | Hex signature produced by the user's Farcaster client app wallet. | | `data.fid` | User's Farcaster ID. | | `data.username` | User's Farcaster username. | | `data.bio` | User's Farcaster bio. | | `data.displayName` | User's Farcaster display name. | | `data.pfpUrl` | User's Farcaster profile picture URL. | | `isError` | True when an error has occurred. | | `error` | `Error` instance. | --- # Change recovery address / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/accounts/change-recovery#VPContent) Return to top Change recovery address [​](https://docs.farcaster.xyz/developers/guides/accounts/change-recovery#change-recovery-address) =========================================================================================================================== Accounts can configure a recovery address to protect against the loss of the custody address. The recovery address will change the custody address of the account. ### Requirements [​](https://docs.farcaster.xyz/developers/guides/accounts/change-recovery#requirements) * An ETH wallet that has an FID on OP Mainnet, with some ETH for gas costs. * An ETH RPC URL for OP Mainnet (e.g. via [Alchemy](https://www.alchemy.com/) , [Infura](https://www.infura.io/) or [QuickNode](https://www.quicknode.com/) ). ### Change Address [​](https://docs.farcaster.xyz/developers/guides/accounts/change-recovery#change-address) Call the `changeRecovery` function on the Id Registry contract. @farcaster/hub-webclients.ts ts import { walletClient, account, IdContract } from './clients.ts'; const newRecoveryAddress = '0x...'; const { request: transferRequest } = await walletClient.simulateContract({ ...IdContract, functionName: 'changeRecovery', args: [newRecoveryAddress], // New recovery address }); await walletClient.writeContract(transferRequest); ts import { ID_REGISTRY_EIP_712_TYPES, idRegistryABI, ID_GATEWAY_ADDRESS, } from '@farcaster/hub-web'; import { walletClient, account } from './clients.ts'; const IdContract = { abi: idRegistryABI, address: ID_GATEWAY_ADDRESS, chain: optimism, }; import { createWalletClient, createPublicClient, custom, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; export const publicClient = createPublicClient({ chain: optimism, transport: http(), }); export const walletClient = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }); // JSON-RPC Account export const [account] = await walletClient.getAddresses(); // Local Account export const account = privateKeyToAccount('0x...'); See the [Id Registry](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transfer) section for more details. --- # Get account messages / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/querying/fetch-casts#VPContent) Return to top Get account messages [​](https://docs.farcaster.xyz/developers/guides/querying/fetch-casts#get-account-messages) ================================================================================================================= Pre-requisites * Read only access to a Snapchain node See [Snapchain installation](https://snapchain.farcaster.xyz/getting-started#sync-a-node) for more information on how to set up a local Snapchain instance. To query all the casts for a particular FID, you can use the castsByFid HTTP endpoint: bash # Default http port is 3381 $ curl http://localhost:3381/v1/castsByFid\?fid\=1 | jq ".messages[].data.castAddBody.text | select( . != null)" "testing" "test" "another test" "another testy test" This returns all the cast related messages for the fid. There are similar endpoints for reactions and follows. See the [http api reference](https://snapchain.farcaster.xyz/reference/httpapi/httpapi) for more details. If you have the [hub-monorepo](https://github.com/farcasterxyz/hub-monorepo) installed from source, you can use the built in `console`. This will use the grpc APIs bash # Ensure you are in the hubble sub directory $ cd apps/hubble # Remove `--insecure` if the host is using TLS $ yarn console --insecure -s localhost:3383 > res = await rpcClient.getCastsByFid({fid: 1}) Ok { value: { messages: [ [Object], [Object], [Object], [Object] ], nextPageToken: } } > res.value.messages.map( m => m.data.castAddBody.text) [ 'testing', 'test', 'another test', 'another testy test' ] For more details on the GRPC API, see the [grpc api reference](https://snapchain.farcaster.xyz/reference/grpcapi/grpcapi) . --- # Get account profile / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/querying/fetch-profile#VPContent) Return to top Get account profile [​](https://docs.farcaster.xyz/developers/guides/querying/fetch-profile#get-account-profile) ================================================================================================================= Pre-requisites * Read only access to a Snapchain instance To fetch profile details, use the bash $ curl http://localhost:3381/v1/userDataByFid\?fid\=1 | jq ".messages[].data.userDataBody" { "type": "USER_DATA_TYPE_PFP", "value": "https://i.imgur.com/I2rEbPF.png" } { "type": "USER_DATA_TYPE_BIO", "value": "A sufficiently decentralized social network. farcaster.xyz" } { "type": "USER_DATA_TYPE_DISPLAY", "value": "Farcaster" } { "type": "USER_DATA_TYPE_USERNAME", "value": "farcaster" } See the [http api reference](https://snapchain.farcaster.xyz/reference/httpapi/userdata) for more details. If you have the [hub-monorepo](https://github.com/farcasterxyz/hub-monorepo) installed from source, you can use the built in `console`. This will use the grpc APIs bash # Ensure you are in the hubble sub directory $ cd apps/hubble # Remove `--insecure` if the host is using TLS $ yarn console --insecure -s localhost:3383 > res = await rpcClient.getUserDataByFid({fid: 1}) Ok { value: { messages: [ [Object], [Object], [Object], [Object] ], nextPageToken: } } > res.value.messages.map(m => m.data.userDataBody) [\ { type: 1, value: 'https://i.imgur.com/I2rEbPF.png' },\ {\ type: 3,\ value: 'A sufficiently decentralized social network. farcaster.xyz'\ },\ { type: 2, value: 'Farcaster' },\ { type: 6, value: 'farcaster' }\ ] For more details on the GRPC API, see the [grpc api reference](https://snapchain.farcaster.xyz/reference/grpcapi/grpcapi) . --- # Fetch casts from a channel / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/querying/fetch-channel-casts#VPContent) Return to top Fetch casts from a channel [​](https://docs.farcaster.xyz/developers/guides/querying/fetch-channel-casts#fetch-casts-from-a-channel) ===================================================================================================================================== Pre-requisites * Read access to a Snapchain instance To fetch casts from a channel, Snapchain provides a `getCastsByParent` api call. For example, to query all casts to the `ethereum` channel: bash $ curl http://localhost:3381/v1/castsByParent\?fid\=1\&url\="https://ethereum.org" | jq " .messages | limit(10;.[]) | .data.castAddBody.text" "cue " "Commandeering this channel for the one true Ethereum, Ethereum classic." "Pretty amazing that even during a bear market, the 30 day average burn gives us deflationary ETH. \n\nSource: Ultrasound.Money" "So, Ethereum is frequently called the Base Layer or L1 when talking about scalability.\n\nBut how can you call the whole Ethereum + Ethereum L2s + L3s that commit to Ethereum L2s?\n\nWe’re building a unified multi-chain explorer for that, but we don’t know how to call it: https://ethereum.routescan.io" "This, we're already doing.\n\nBut we call it, the Full-Index Ecosystem Explorer." ". By subdomains do you mean more specific namespaces, e.g. Farcaster fnames being name.farcaster.eth?\n\nMy guess is if the root .eth namespace is available it will command higher status and value.\n\nhttps://x.com/0xfoobar/status/1687209523239604230" "what are the best examples of DAOs with independent core working groups/multisigs?" "Anyone here use Rabby?\n\nhttps://x.com/0xfoobar/status/1687474090150416385" "Who needs stablecoins when we have ETH" "782,672 active + pending validators! 🤯 \n\n(also... I haven't proposed a block for a few months)" Using the GRPC API: bash > res = await rpcClient.getCastsByParent({parentUrl: "https://ethereum.org"}) > res.value.messages.slice(0, 10).map( m => m.data.castAddBody.text) [\ 'cue ',\ 'Commandeering this channel for the one true Ethereum, Ethereum classic.',\ 'Pretty amazing that even during a bear market, the 30 day average burn gives us deflationary ETH. \n' +\ '\n' +\ 'Source: Ultrasound.Money',\ 'So, Ethereum is frequently called the Base Layer or L1 when talking about scalability.\n' +\ '\n' +\ 'But how can you call the whole Ethereum + Ethereum L2s + L3s that commit to Ethereum L2s?\n' +\ '\n' +\ 'We’re building a unified multi-chain explorer for that, but we don’t know how to call it: https://ethereum.routescan.io',\ "This, we're already doing.\n" +\ '\n' +\ 'But we call it, the Full-Index Ecosystem Explorer.',\ '. By subdomains do you mean more specific namespaces, e.g. Farcaster fnames being name.farcaster.eth?\n' +\ '\n' +\ 'My guess is if the root .eth namespace is available it will command higher status and value.\n' +\ '\n' +\ 'https://x.com/0xfoobar/status/1687209523239604230',\ 'what are the best examples of DAOs with independent core working groups/multisigs?',\ 'Anyone here use Rabby?\n' +\ '\n' +\ 'https://x.com/0xfoobar/status/1687474090150416385',\ 'Who needs stablecoins when we have ETH',\ '782,672 active + pending validators! 🤯 \n' +\ '\n' +\ "(also... I haven't proposed a block for a few months)"\ ] --- # Create casts / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/writing/casts#VPContent) On this page Create casts [​](https://docs.farcaster.xyz/developers/guides/writing/casts#create-casts) ========================================================================================== Creating simple casts is covered in the [messages](https://docs.farcaster.xyz/developers/guides/writing/messages) tutorial. This tutorial covers advanced topics like mentions, embeds, emojis, replies and channels. Setup [​](https://docs.farcaster.xyz/developers/guides/writing/casts#setup) ---------------------------------------------------------------------------- * See the setup instructions in [creating messages](https://docs.farcaster.xyz/developers/guides/writing/messages) Mentions [​](https://docs.farcaster.xyz/developers/guides/writing/casts#mentions) ---------------------------------------------------------------------------------- Users can be tagged in a cast with an @username mention (e.g. "Hello @bob!") which causes clients to send notifications. A mention is not included in the text of the cast. The target fid and the position of the mention in the text are specified in the `mentions` and `mentionPositions` array, and are populated into the cast by clients at render-time. typescript /** * "@dwr and @v are big fans of @farcaster" */ const castWithMentions = await makeCastAdd( { text: ' and are big fans of ', embeds: [], embedsDeprecated: [], mentions: [3, 2, 1], mentionsPositions: [0, 5, 22], // The position in bytes (not characters) }, dataOptions, ed25519Signer ); Embeds [​](https://docs.farcaster.xyz/developers/guides/writing/casts#embeds) ------------------------------------------------------------------------------ URLs can be embedded in the cast which instructs clients to render a preview. A cast can have up to 2 embeds which can each be up to 256 bytes long. No other validation is performed on embeds. typescript /** * A cast with a mention and an attachment * * "Hey @dwr, check this out!" */ const castWithMentionsAndAttachment = await makeCastAdd( { text: 'Hey, check this out!', embeds: [{ url: 'https://farcaster.xyz' }], embedsDeprecated: [], mentions: [3], mentionsPositions: [4], }, dataOptions, ed25519Signer ); Emoji [​](https://docs.farcaster.xyz/developers/guides/writing/casts#emoji) ---------------------------------------------------------------------------- Emojis can be included directly in the text of a cast and be rendered by clients. Since an emoji character often takes up multiple bytes but renders as a single character, it has an impact on how mention positions and cast length should be calculated. typescript /** * A cast with emojis and mentions * * "🤓@farcaster can mention immediately after emoji" */ const castWithEmojiAndMentions = await makeCastAdd( { text: '🤓 can mention immediately after emoji', embeds: [], embedsDeprecated: [], mentions: [1], mentionsPositions: [4], }, dataOptions, ed25519Signer ); Reply [​](https://docs.farcaster.xyz/developers/guides/writing/casts#reply) ---------------------------------------------------------------------------- A cast can be a reply to another cast, URL or NFT. A reply to another cast is treated as a thread, while a reply to a URL or NFT can be treated as a comment or a [channel](https://docs.farcaster.xyz/developers/guides/writing/casts#channels) . The optional parentUrl property can be set to a URL or to an fid-hash value of the cast being replied to, as shown in th example below. See [FIP-2](https://github.com/farcasterxyz/protocol/discussions/71) for more details on reply types. typescript /** * A cast that replies to a URL * * "I think this is a great protocol 🚀" */ const castReplyingToAUrl = await makeCastAdd( { text: 'I think this is a great protocol 🚀', embeds: [], embedsDeprecated: [], mentions: [], mentionsPositions: [], parentUrl: 'https://www.farcaster.xyz/', }, dataOptions, ed25519Signer ); Channels [​](https://docs.farcaster.xyz/developers/guides/writing/casts#channels) ---------------------------------------------------------------------------------- A cast can be sent into a channel, which instructs clients that it is related to a specific topic. Clients may choose to use this metadata in different ways, like grouping channel casts together or recommending them to certain users. A channel is simply a `parentURL` that is either a URL or NFT, which all clients recognize as a channel by loose consensus. There is no protocol level agreement on the list of channels yet, but the `casts` table in the replicator database can be used to get a list of commonly used channels. sql /* Query for a list of channels */ select parent_url, count(*) as count from casts where parent_url is not null group by parent_url order by count desc; typescript // Cast into the Farcaster channel const channelCast = await makeCastAdd( { text: 'I love farcaster', embeds: [], embedsDeprecated: [], mentions: [], mentionsPositions: [], parentUrl: 'https://www.farcaster.xyz/', // This is illustrative, and is not an actual channel URL }, dataOptions, ed25519Signer ); --- # Create messages / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/writing/messages#VPContent) On this page Create messages [​](https://docs.farcaster.xyz/developers/guides/writing/messages#create-messages) =================================================================================================== A message represents an action taken by a user (e.g. alice says "hello world") There are many types of messages, and this tutorial will walk you through the most common ones. Other tutorials will cover the more advanced message types. Setup [​](https://docs.farcaster.xyz/developers/guides/writing/messages#setup) ------------------------------------------------------------------------------- You will need: * Write access to a Snapchain node * Private key of a signer registered to an fid * `hub-nodejs` and helper functions imported and shown below ts import { makeCastAdd, makeCastRemove, makeLinkAdd, makeLinkRemove, makeReactionAdd, makeReactionRemove, makeUserDataAdd, NobleEd25519Signer, FarcasterNetwork, } from '@farcaster/hub-nodejs'; const ACCOUNT_PRIVATE_KEY: Hex = '0x...'; // Your account key's private key const FID = -1; // Your fid const ed25519Signer = new NobleEd25519Signer(ACCOUNT_PRIVATE_KEY); const dataOptions = { fid: FID, network: FC_NETWORK, }; const FC_NETWORK = FarcasterNetwork.MAINNET; Casts [​](https://docs.farcaster.xyz/developers/guides/writing/messages#casts) ------------------------------------------------------------------------------- Casts are public messages created by a user. A cast is created by issuing a CastAdd message with the text of the cast and optional embeds, mentions, and emoji. The example below shows the creation of a simple cast. typescript const cast = await makeCastAdd( { text: 'This is a cast!', // Text can be up to 320 bytes long embeds: [], embedsDeprecated: [], mentions: [], mentionsPositions: [], }, dataOptions, ed25519Signer ); A cast can be removed by issuing a CastRemove message with the hash of the CastAdd message and a later timestamp. typescript const castRemove = await makeCastRemove( { targetHash: castReplyingToAUrl._unsafeUnwrap().hash, }, dataOptions, ed25519Signer ); To create casts with embeds, mentions, channels emoji, see the [casts](https://docs.farcaster.xyz/developers/guides/writing/casts) tutorial. Reactions [​](https://docs.farcaster.xyz/developers/guides/writing/messages#reactions) --------------------------------------------------------------------------------------- Reactions are strongly typed relationships between a user and a cast (e.g. a like). A user "likes" a cast by producing a ReactionAdd message with type set to `like` and the target set to the hash of the cast and the fid of its author. typescript const reactionAdd = await makeReactionAdd( { type: ReactionType.LIKE, targetCastId: { fid: createdCast.data.fid, hash: createdCast.hash }, }, dataOptions, ed25519Signer ); The like can be negated by broadcasting a ReactionRemove message with the information and a later timestamp. typescript const reactionRemove = await makeReactionRemove( { type: ReactionType.LIKE, targetCastId: { fid: createdCast.data.fid, hash: createdCast.hash }, }, dataOptions, // Timestamp provided must be higher ed25519Signer ); A user can "recast" with a very similar process. typescript const recastAdd = await makeReactionAdd( { type: ReactionType.RECAST, targetCastId: { fid: createdCast.data.fid, hash: createdCast.hash }, }, dataOptions, ed25519Signer ); const recastRemove = await makeReactionRemove( { type: ReactionType.RECAST, targetCastId: { fid: createdCast.data.fid, hash: createdCast.hash }, }, dataOptions, ed25519Signer ); User Data [​](https://docs.farcaster.xyz/developers/guides/writing/messages#user-data) --------------------------------------------------------------------------------------- UserData is a strongly typed set of messages that represent metadata about a user (e.g. bio, profile picture). A `UserData` message has a type and a string value which can be set. The example below shows a user updating their bio. typescript // Update user bio. Other fields are similar, just change the type. Value is always a string. const bioUpdate = await makeUserDataAdd( { type: UserDataType.BIO, value: 'new bio', }, dataOptions, ed25519Signer ); Links [​](https://docs.farcaster.xyz/developers/guides/writing/messages#links) ------------------------------------------------------------------------------- Links are loosely typed relationships between users (e.g. alice follows bob). A user creates a Link by issuing a LinkAdd message with a string type and the target user's fid. The most commonly supported link on all clients is 'follow'. typescript const follow = await makeLinkAdd( { type: 'follow', targetFid: 1, }, dataOptions, ed25519Signer ); const unfollow = await makeLinkRemove( { type: 'unfollow', targetFid: 1, }, dataOptions, ed25519Signer ); --- # Create an address verification / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/writing/verify-address#VPContent) Return to top Create an address verification [​](https://docs.farcaster.xyz/developers/guides/writing/verify-address#create-an-address-verification) ======================================================================================================================================= Pre-requisites * Write access to a Snapchain instance * Private key of an account key registered to an fid * An Ethereum provider URL for OP Mainnet (e.g. via [Alchemy](https://www.alchemy.com/) ,[Infura](https://www.infura.io/) or [QuickNode](https://www.quicknode.com/) ). To create a [Verification](https://snapchain.farcaster.xyz/reference/datatypes/messages#6-verification) proving ownership of an external Ethereum address, you can use an `Eip712Signer` to sign a verification message, and the `makeVerificationAddEthAddress` function to construct a message to send to a Hub. First, set up clients and account keys: ts import { NobleEd25519Signer, ViemLocalEip712Signer, FarcasterNetwork, makeVerificationAddEthAddress, } from '@farcaster/hub-nodejs'; import { createPublicClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; const publicClient = createPublicClient({ chain: optimism, transport: http(), }); const alice = privateKeyToAccount('0xab..12'); const eip712Signer = new ViemLocalEip712Signer(alice); const ed25519Signer = new NobleEd25519Signer('0xcd..34'); Then create a verification signature using an `Eip712Signer`. You'll need to query the latest blockhash on OP mainnet and include it in the signed message. ts const latestBlock = await publicClient.getBlock(); const fid = 1n; const network = FarcasterNetwork.MAINNET; const address = alice.address; const blockHash = latestBlock.blockhash; const ethSignature = eip712Signer.signVerificationEthAddressClaim({ fid, address, network, blockHash, }); Finally, use `makeVerificationAddEthAddress` to construct a [`Verification`](https://snapchain.farcaster.xyz/reference/datatypes/messages#6-verification) message you can send to a Hub. ts if (ethSignature.isOk()) { const message = await makeVerificationAddEthAddress( { address, blockHash, ethSignature, }, { fid, network }, ed25519Signer ); } ### Full code example [​](https://docs.farcaster.xyz/developers/guides/writing/verify-address#full-code-example) @farcaster/hub-nodejs ts import { NobleEd25519Signer, ViemLocalEip712Signer, FarcasterNetwork, makeVerificationAddEthAddress, } from '@farcaster/hub-nodejs'; import { createPublicClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { optimism } from 'viem/chains'; const publicClient = createPublicClient({ chain: optimism, transport: http(), }); const alice = privateKeyToAccount('0xab..12'); const eip712Signer = new ViemLocalEip712Signer(alice); const ed25519Signer = new NobleEd25519Signer('0xcd..34'); const latestBlock = await publicClient.getBlock(); const fid = 1n; const network = FarcasterNetwork.MAINNET; const address = alice.address; const blockHash = latestBlock.blockhash; const ethSignature = eip712Signer.signVerificationEthAddressClaim({ fid, address, network, blockHash, }); if (ethSignature.isOk()) { const message = await makeVerificationAddEthAddress( { address, blockHash, ethSignature, }, { fid, network }, ed25519Signer ); } --- # Submit data to the hub / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/writing/submit-messages#VPContent) Return to top Submit data to the hub [​](https://docs.farcaster.xyz/developers/guides/writing/submit-messages#submit-data-to-the-hub) ======================================================================================================================== Pre-requisites * Write access to a Snapchain node * Private key of an account key registered to an fid * Local typescript development environment To see a full example of how to submit different kinds of messages to the hub, see [here](https://github.com/farcasterxyz/hub-monorepo/tree/main/packages/hub-nodejs/examples/write-data) --- # Neynar / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/reference/third-party/neynar/index#VPContent) Return to top Neynar [​](https://docs.farcaster.xyz/reference/third-party/neynar/index#neynar) ================================================================================= [Neynar](https://neynar.com/) is an independent 3rd party provider that offers the following services for Farcaster data: * [Hosted hubs](https://docs.neynar.com/docs/create-a-stream-of-casts) * [REST APIs](https://docs.neynar.com/reference/quickstart) * [Signer management](https://docs.neynar.com/docs/which-signer-should-you-use-and-why) * [New account creation](https://docs.neynar.com/docs/how-to-create-a-new-farcaster-account-with-neynar) * [Webhooks](https://docs.neynar.com/docs/how-to-create-webhooks-on-the-go-using-the-sdk) * [Data ingestion pipelines](https://docs.neynar.com/docs/how-to-choose-the-right-data-product-for-you) 🪐 --- # Counting signups by day / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/advanced/query-signups#VPContent) Return to top Counting signups by day [​](https://docs.farcaster.xyz/developers/guides/advanced/query-signups#counting-signups-by-day) ========================================================================================================================= Pre-requisites * Read access to a replicator database To count the number of signups by day, we can use the `chain_events` table to query the number of [`ID_REGISTER`](https://snapchain.farcaster.xyz/reference/datatypes/events#onchaineventtype) events and group by day. sql SELECT DATE_TRUNC('day', created_at) AS day, COUNT(*) AS count FROM chain_events WHERE type = 3 -- ID_REGISTER (see event types reference page) GROUP BY day ORDER BY day desc; For more information on the schema, see the [replicator schema](https://docs.farcaster.xyz/reference/replicator/schema) . --- # Decode key metadata / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/advanced/decode-key-metadata#VPContent) Return to top Decode key metadata [​](https://docs.farcaster.xyz/developers/guides/advanced/decode-key-metadata#decode-key-metadata) ======================================================================================================================= When users add a new key to the Key Registry, the contract emits [encoded metadata](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequestmetadata-struct) in an event. You can use this metadata to determine who requested the key. To decode key metadata, you can use Viem's `decodeAbiParameters` function: Viem ts import { decodeAbiParameters } from 'viem'; const encodedMetadata = '0x' + '0000000000000000000000000000000000000000000000000000000000000020' + '00000000000000000000000000000000000000000000000000000000000023C0' + '00000000000000000000000002EF790DD7993A35FD847C053EDDAE940D055596' + '0000000000000000000000000000000000000000000000000000000000000080' + '00000000000000000000000000000000000000000000000000000000657C8AF5' + '0000000000000000000000000000000000000000000000000000000000000041' + 'F18569F4364BB2455DB27A4E8464A83AD8555416B1AAB21FF26E8501168F471F' + '7EC17BB096EA4438E5BF82CE01224B2F67EF9042737B7B101C41A1A05CB469DC' + '1B00000000000000000000000000000000000000000000000000000000000000'; const decoded = decodeAbiParameters( [\ {\ components: [\ {\ name: 'requestFid',\ type: 'uint256',\ },\ {\ name: 'requestSigner',\ type: 'address',\ },\ {\ name: 'signature',\ type: 'bytes',\ },\ {\ name: 'deadline',\ type: 'uint256',\ },\ ],\ type: 'tuple',\ },\ ], encodedMetadata ); console.log(decoded); /* [\ {\ requestFid: 9152n,\ requestSigner: '0x02ef790Dd7993A35fD847C053EDdAE940D055596',\ signature: '0xF185...69F4',\ deadline: 1702660853n\ }\ ] */ See the [Signed Key Request Validator](https://docs.farcaster.xyz/reference/contracts/reference/signed-key-request-validator#signedkeyrequestmetadata-struct) reference for more details. --- # Hello World / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/guides/basics/hello-world#VPContent) On this page Hello World [​](https://docs.farcaster.xyz/developers/guides/basics/hello-world#hello-world) ============================================================================================= Create your Farcaster account programmatically and publish your first "Hello World" message. The example shows you how to: * Make onchain transactions to create an account * Rent a storage unit so you can publish messages * Add an account key to sign messages * Acquire an fname for your account * Create, sign and publish messages This example can be checked out as a fully functional repository [here](https://github.com/farcasterxyz/hub-monorepo/tree/main/packages/hub-nodejs/examples/hello-world) . ### Requirements [​](https://docs.farcaster.xyz/developers/guides/basics/hello-world#requirements) * Write access to a hub (either your own, or a 3rd party hub) * An ETH wallet with about ~10$ USD of ETH bridged to [Optimism](https://www.optimism.io/) * An ETH RPC URL for OP Mainnet (e.g. via [Alchemy](https://www.alchemy.com/) , [Infura](https://www.infura.io/) or [QuickNode](https://www.quicknode.com/) ). 1\. Set up constants [​](https://docs.farcaster.xyz/developers/guides/basics/hello-world#_1-set-up-constants) -------------------------------------------------------------------------------------------------------------- typescript import { ID_GATEWAY_ADDRESS, idGatewayABI, KEY_GATEWAY_ADDRESS, keyGatewayABI, ID_REGISTRY_ADDRESS, idRegistryABI, FarcasterNetwork, } from '@farcaster/hub-web'; import { zeroAddress } from 'viem'; import { optimism } from 'viem/chains'; /** * Populate the following constants with your own values */ const MNEMONIC = ''; const OP_PROVIDER_URL = ''; // Alchemy or Infura url const RECOVERY_ADDRESS = zeroAddress; // Optional, using the default value means the account will not be recoverable later if the mnemonic is lost const ACCOUNT_KEY_PRIVATE_KEY: Hex = zeroAddress; // Optional, using the default means a new account key will be created each time // Note: hoyt is the Farcaster team's mainnet hub, which is password protected to prevent abuse. Use a 3rd party hub // provider like https://neynar.com/ Or, run your own mainnet hub and broadcast to it permissionlessly. const HUB_URL = 'hoyt.farcaster.xyz:3383'; // URL + Port of the Hub const HUB_USERNAME = ''; // Username for auth, leave blank if not using TLS const HUB_PASS = ''; // Password for auth, leave blank if not using TLS const USE_SSL = false; // set to true if talking to a hub that uses SSL (3rd party hosted hubs or hubs that require auth) const FC_NETWORK = FarcasterNetwork.MAINNET; // Network of the Hub const CHAIN = optimism; const IdGateway = { abi: idGatewayABI, address: ID_GATEWAY_ADDRESS, chain: CHAIN, }; const IdContract = { abi: idRegistryABI, address: ID_REGISTRY_ADDRESS, chain: CHAIN, }; const KeyContract = { abi: keyGatewayABI, address: KEY_GATEWAY_ADDRESS, chain: CHAIN, }; 2\. Register and pay for storage [​](https://docs.farcaster.xyz/developers/guides/basics/hello-world#_2-register-and-pay-for-storage) -------------------------------------------------------------------------------------------------------------------------------------- Create a function to register an FID and pay for storage. This function will check if the account already has an FID and return early if so. typescript const getOrRegisterFid = async (): Promise => { const balance = await walletClient.getBalance({ address: account.address }); // Check if we already have an fid const existingFid = (await walletClient.readContract({ ...IdContract, functionName: 'idOf', args: [account.address], })) as bigint; if (existingFid > 0n) { return parseInt(existingFid.toString()); } const price = await walletClient.readContract({ ...IdGateway, functionName: 'price', }); if (balance < price) { throw new Error( `Insufficient balance to rent storage, required: ${price}, balance: ${balance}` ); } const { request: registerRequest } = await walletClient.simulateContract({ ...IdGateway, functionName: 'register', args: [RECOVERY_ADDRESS], value: price, }); const registerTxHash = await walletClient.writeContract(registerRequest); const registerTxReceipt = await walletClient.waitForTransactionReceipt({ hash: registerTxHash, }); // Now extract the FID from the logs const registerLog = decodeEventLog({ abi: idRegistryABI, data: registerTxReceipt.logs[0].data, topics: registerTxReceipt.logs[0].topics, }); const fid = parseInt(registerLog.args['id']); return fid; }; const fid = await getOrRegisterFid(); 3\. Add an account key [​](https://docs.farcaster.xyz/developers/guides/basics/hello-world#_3-add-an-account-key) ------------------------------------------------------------------------------------------------------------------ Now, we will add an account key to the key registry. Every account key must have a signed metadata field from the fid of the app requesting it. In our case, we will use our own fid. Note, this requires you to sign a message with the private key of the address holding the fid. If this is not possible, register a separate fid for the app first and use that. typescript const getOrRegisterAccountKey = async (fid: number) => { if (ACCOUNT_KEY_PRIVATE_KEY !== zeroAddress) { // If a private key is provided, we assume the account key is already in the key registry const privateKeyBytes = fromHex(ACCOUNT_KEY_PRIVATE_KEY, 'bytes'); const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes); return privateKeyBytes; } const privateKey = ed25519.utils.randomPrivateKey(); const publicKey = toHex(ed25519.getPublicKey(privateKey)); const eip712signer = new ViemLocalEip712Signer(appAccount); // To add a key, we need to sign the metadata with the fid of the app we're adding the key on behalf of // Use your personal fid, or register a separate fid for the app const metadata = await eip712signer.getSignedKeyRequestMetadata({ requestFid: APP_FID, key: APP_PRIVATE_KEY, deadline: Math.floor(Date.now() / 1000) + 60 * 60, // 1 hour from now }); const { request: signerAddRequest } = await walletClient.simulateContract({ ...KeyContract, functionName: 'add', args: [1, publicKey, 1, metadata], // keyType, publicKey, metadataType, metadata }); const accountKeyAddTxHash = await walletClient.writeContract( signerAddRequest ); await walletClient.waitForTransactionReceipt({ hash: accountKeyAddTxHash }); // Sleeping 30 seconds to allow hubs to pick up the accountKey tx await new Promise((resolve) => setTimeout(resolve, 30000)); return privateKey; }; const accountPrivateKey = await getOrRegisterAccountKey(fid); 4\. Register an fname [​](https://docs.farcaster.xyz/developers/guides/basics/hello-world#_4-register-an-fname) ---------------------------------------------------------------------------------------------------------------- Now that the onchain actions are complete, let's register an fname using the farcaster offchain fname registry. Registering an fname requires a signature from the custody address of the fid. typescript const registerFname = async (fid: number) => { try { // First check if this fid already has an fname const response = await axios.get( `https://fnames.farcaster.xyz/transfers/current?fid=${fid}` ); const fname = response.data.transfer.username; return fname; } catch (e) { // No username, ignore and continue with registering } const fname = `fid-${fid}`; const timestamp = Math.floor(Date.now() / 1000); const userNameProofSignature = await walletClient.signTypedData({ domain: USERNAME_PROOF_DOMAIN, types: USERNAME_PROOF_TYPE, primaryType: 'UserNameProof', message: { name: fname, timestamp: BigInt(timestamp), owner: account.address, }, }); const response = await axios.post('https://fnames.farcaster.xyz/transfers', { name: fname, // Name to register from: 0, // Fid to transfer from (0 for a new registration) to: fid, // Fid to transfer to (0 to unregister) fid: fid, // Fid making the request (must match from or to) owner: account.address, // Custody address of fid making the request timestamp: timestamp, // Current timestamp in seconds signature: userNameProofSignature, // EIP-712 signature signed by the current custody address of the fid }); return fname; }; const fname = await registerFname(fid); Note that this only associated the name to our fid, we still need to set it as our username. 5\. Write to the hub [​](https://docs.farcaster.xyz/developers/guides/basics/hello-world#_5-write-to-the-hub) -------------------------------------------------------------------------------------------------------------- Finally, we're now ready to submit messages to the hub. First, we shall set the fname as our username. And then post a cast. typescript const submitMessage = async (resultPromise: HubAsyncResult) => { const result = await resultPromise; if (result.isErr()) { throw new Error(`Error creating message: ${result.error}`); } await hubClient.submitMessage(result.value); }; const accountKey = new NobleEd25519Signer(accountPrivateKey); const dataOptions = { fid: fid, network: FC_NETWORK, }; const userDataUsernameBody = { type: UserDataType.USERNAME, value: fname, }; // Set the username await submitMessage( makeUserDataAdd(userDataUsernameBody, dataOptions, accountKey) ); // Post a cast await submitMessage( makeCastAdd( { text: 'Hello World!', }, dataOptions, accountKey ) ); Now, you can view your profile on any farcaster client. To see it on Warpcast, visit `https://warpcast.com/@` --- # Farcaster Docs / Farcaster Docs [Skip to content](https://docs.farcaster.xyz/developers/index#VPContent) On this page Join the conversation Ask questions and hang out with other Farcaster developers in the [/fc-devs](https://warpcast.com/~/channel/fc-devs) channel on Farcaster. Create mini apps [​](https://docs.farcaster.xyz/developers/index#create-mini-apps) ----------------------------------------------------------------------------------- Learn how to build mini apps (previously called Frames) that run inside a Farcaster feed. * [Introduction](https://miniapps.farcaster.xyz/) \- understand what a mini app is and how it works * [Getting Started](https://miniapps.farcaster.xyz/docs/getting-started) \- Build your first mini app Sign in with Farcaster [​](https://docs.farcaster.xyz/developers/index#sign-in-with-farcaster) ----------------------------------------------------------------------------------------------- Make it easy for users to sign in to your app with their Farcaster account. * [Examples](https://docs.farcaster.xyz/auth-kit/examples) - see Sign in with Farcaster (SIWF) in action * [AuthKit](https://docs.farcaster.xyz/auth-kit/installation) - a React toolkit to integrate SIWF * [FIP-11](https://github.com/farcasterxyz/protocol/discussions/110) - the formal standard for SIWF Analyze Farcaster data [​](https://docs.farcaster.xyz/developers/index#analyze-farcaster-data) ----------------------------------------------------------------------------------------------- Sync the Farcaster network to a local machine so you can run queries on the data. * [Run a Snapchain node](https://snapchain.farcaster.xyz/getting-started#getting-started) - get realtime access to Farcaster data * [Write your first Snapchain query](https://snapchain.farcaster.xyz/getting-started#query-a-node) - get an account's casts from a Snapchain node * [Set up the replicator](https://snapchain.farcaster.xyz/guides/syncing-to-db) - sync a Snapchain node to a postgres database to run advanced queries Write to Farcaster [​](https://docs.farcaster.xyz/developers/index#write-to-farcaster) --------------------------------------------------------------------------------------- * [Hello World](https://docs.farcaster.xyz/developers/guides/basics/hello-world) - programmatically create an account and publish a cast ---