| topic | farcaster |
|---|---|
| type | technical-guide |
| status | research-complete |
| last-validated | 2026-05-21 |
| original-query | How to register a Farcaster FID for agents, comparing direct on-chain vs fid-forge vs Neynar (reconstructed) |
| tier | production |
Status: Research complete Date: 2026-04-05 Goal: Map every path to registering a Farcaster FID for CASTER agent, understand why fid-forge x402 failed, and determine the cheapest/fastest approach
| Decision | Recommendation |
|---|---|
| Registration method | USE direct on-chain via rishavmukherji/farcaster-agent pattern — $7 storage + ~$0.25 gas on Optimism, no intermediary, full control. We already have the wallet and viem. |
| Why NOT fid-forge | SKIP fid-forge — their x402 implementation returns empty {} on 402 (missing x402Version and accepts fields the spec requires). Their Stripe fallback works but adds unnecessary dependency for a $7 on-chain call. |
| Why NOT Neynar register | SKIP Neynar account registration — requires x-wallet-id (Neynar wallet pre-funding), $5+ ETH on Optimism, mnemonic-based signing, and their custody model. Over-engineered for an agent FID. |
| Signer approach | USE self-signed key request via KeyGateway — generates Ed25519 keypair locally, registers via EIP-712 signed metadata. No Neynar dependency for the signer itself. |
| x402 for API calls | USE x402-fetch (v1.1.0, already installed) for Neynar hub API calls AFTER registration — $0.001 USDC/call on Base, standard spec works fine with Neynar's hub. fid-forge was the broken one, not x402 itself. |
| Funding | SEND ~$10 ETH to 0x3D04... on Optimism (not Base). The $2 USDC already on Base stays for x402 hub calls later. Registration is ETH on Optimism, not USDC on Base. |
| Hub for profile setup | USE crackle.farcaster.xyz:3383 — official Farcaster hub, well-synced. Pinata hub lags on new FID recognition. |
| Method | Cost | Chain | Dependencies | x402 Support | Agent-Friendly | ZAO Fit |
|---|---|---|---|---|---|---|
| Direct on-chain (IdGateway.register) | $7 storage + ~$0.25 gas | Optimism | viem, @farcaster/hub-nodejs | N/A (no x402 needed) | YES — full control, no intermediary | BEST |
| fid-forge (fidforge.11211.me) | ~$2 Stripe or x402 | Optimism (via service) | fid-forge API | BROKEN — 402 returns {}, missing spec fields |
PARTIAL — good API but x402 non-standard | SKIP |
| Neynar register (POST /v2/farcaster/user) | $5+ ETH pre-fund | Optimism (via Neynar) | Neynar API key, wallet-id, mnemonic | N/A | NO — requires mnemonic, custody model | SKIP |
| Warpcast app (manual) | $7 | Optimism | Manual browser flow | N/A | NO — not programmatic | SKIP |
The x402 spec (Coinbase, v2) requires the 402 response body to contain:
{
"x402Version": 2,
"accepts": [
{
"scheme": "exact",
"network": "base",
"maxAmountRequired": "1000000",
"resource": "...",
"payTo": "0x...",
...
}
]
}fid-forge's 402 response returns:
{}The x402-fetch library (line 39 of dist/cjs/index.js) does const { x402Version, accepts } = await response.json() — and then tries accepts.map(...) on undefined, which throws TypeError: Cannot read properties of undefined (reading 'map').
fid-forge advertises x402 support but doesn't implement the standard response format. Their x402 endpoint is effectively broken for any standard x402 client.
1. Fund wallet with ~$10 ETH on Optimism
2. Call IdGateway.register(recovery) → get FID
3. Generate Ed25519 signer keypair
4. Sign EIP-712 metadata with custody wallet
5. Call KeyGateway.add(keyType, key, metadataType, metadata) → signer registered
6. Register fname via fnames.farcaster.xyz/transfers (EIP-712 sign)
7. Set profile via hub messages (UserDataAdd)
8. Post welcome cast via hub (CastAdd)
| Contract | Address | Purpose |
|---|---|---|
| IdGateway | 0x00000000Fc25870C6eD6b6c7E41Fb078b7656f69 |
FID registration |
| IdRegistry | 0x00000000Fc6c5F01Fc30151999387Bb99A9f489b |
FID ownership lookup |
| KeyGateway | 0x00000000fC56947c7E7183f8Ca4B62398CaAdf0B |
Signer key registration |
| KeyRegistry | 0x00000000Fc1237824fb747aBDE0FF18990E59b7e |
Signer key lookup |
| SignedKeyRequestValidator | 0x00000000FC700472606ED4fA22623Acf62c60553 |
Metadata encoding for self-signed keys |
| Item | Cost | Notes |
|---|---|---|
| Storage (1 unit, required) | $7 USD in ETH | Price from IdGateway.price(), includes 1-year storage |
| IdGateway.register() gas | ~$0.05 | Optimism L2 gas is cheap |
| KeyGateway.add() gas | ~$0.05 | Second transaction |
| fname registration | $0 | Free API call + EIP-712 signature |
| Profile setup | $0 | Hub messages, no gas |
| Total | ~$7.30 ETH on Optimism | One-time cost, FID is permanent |
{
"viem": "^2.x",
"@farcaster/hub-nodejs": "^0.12.x",
"@noble/ed25519": "^1.6.x"
}All three are already installed in ZAO OS.
x402 works correctly for its intended use: paying for API calls after registration. The breakdown:
| Use Case | x402 Status | Notes |
|---|---|---|
| Neynar hub API (hub-api.neynar.com) | WORKS | $0.001 USDC/call on Base, standard spec |
| fid-forge registration | BROKEN | Non-standard 402 response body |
| publish.new marketplace | WORKS | Agent buying/selling digital goods |
| Future Neynar MCP | EXPECTED | Neynar planning x402 MCP server support |
The x402-fetch package (v1.1.0) already installed works with any spec-compliant x402 server. The $2 USDC Zaal sent to Base is ready for Neynar hub calls once CASTER has a FID.
// 1. Make initial request
const response = await fetch(input, init);
if (response.status !== 402) return response; // Not paywalled
// 2. Parse payment requirements from 402 body
const { x402Version, accepts } = await response.json();
// ↑ This is where fid-forge fails — returns {} instead
// 3. Select matching payment scheme for our network
const selectedRequirements = selectPaymentRequirements(accepts, network, "exact");
// 4. Sign the payment authorization (EIP-3009 for USDC)
const paymentHeader = await createPaymentHeader(walletClient, x402Version, selectedRequirements);
// 5. Retry with X-PAYMENT header
return fetch(input, { ...init, headers: { "X-PAYMENT": paymentHeader } });scripts/generate-wallet.ts— already generated the wallet (0x3D04...)src/lib/fc-identity.ts— FC identity gating (quality score + FID resolver)src/lib/farcaster/neynar.ts— Neynar SDK client (for post-registration API calls)community.config.ts— app FID 19640 (Zaal's FID, CASTER needs its own)node_modules/x402-fetch— v1.1.0 installed, works for standard x402 serversnode_modules/@noble/ed25519— v1.6.1 installed for signer key generation
scripts/register-caster-fid-onchain.ts — Direct on-chain registration:
// Phase 1: Register FID
// - Call IdGateway.price() to get current cost
// - Call IdGateway.register(recoveryAddress) with msg.value = price
// - Read IdRegistry.idOf(address) to get assigned FID
// Phase 2: Add Signer Key
// - Generate Ed25519 keypair
// - Sign EIP-712 SignedKeyRequest with custody wallet
// - Encode metadata via SignedKeyRequestValidator
// - Call KeyGateway.add(1, pubkey, 1, metadata)
// Phase 3: Register Username
// - Sign EIP-712 UserNameProof (chainId: 1, Ethereum mainnet)
// - POST to fnames.farcaster.xyz/transfers
// Phase 4: Set Profile + Welcome Cast
// - Connect to crackle.farcaster.xyz:3383
// - Submit UserDataAdd messages (USERNAME, DISPLAY, PFP, BIO, URL)
// - Submit CastAdd with welcome text to /agents channelAfter CASTER has a FID + signer, use x402-fetch for Neynar hub API calls:
import { wrapFetchWithPayment, createSigner } from 'x402-fetch';
const signer = await createSigner('base', AGENT_WALLET_PRIVATE_KEY);
const payingFetch = wrapFetchWithPayment(fetch, signer);
// This auto-handles 402 and pays $0.001 USDC per call
const response = await payingFetch('https://hub-api.neynar.com/v1/submitMessage', {
method: 'POST',
body: castMessage,
});- Bridge ~$10 ETH to Optimism for the wallet address
0x3D041C14Eb8c803D2bE39d7576C27D6Bd1966604- Use the Optimism Bridge or any cross-chain bridge
- The $7 storage fee is the bulk; rest is gas
- Tell me when the ETH is on Optimism — I'll run the on-chain registration script
The $2 USDC on Base is NOT wasted — it stays in the wallet for x402 Neynar hub API calls ($0.001/call = 2,000 casts).
| Project | License | Key Pattern | What We Borrow |
|---|---|---|---|
| rishavmukherji/farcaster-agent | MIT | Direct IdGateway.register() + KeyGateway.add() | Full registration flow, self-signed key metadata encoding |
| coinbase/x402 | Apache-2.0 | x402-fetch client, EIP-3009 payment signing | Post-registration hub API payments |
| farcasterxyz/hub-monorepo | MIT | @farcaster/hub-nodejs, NobleEd25519Signer | Profile setup + cast submission via hub |