Farcaster Agent
Autonomous Farcaster account creation and casting without human intervention.
OpenClaw Skill: This repository includes an OpenClaw-compatible skill in the skill/ directory. Install it with:
npx clawhub@latest install farcaster-agent
Or copy skill/ to ~/.openclaw/skills/farcaster-agent/.
This toolkit allows an AI agent (or script) to:
- Create a new Farcaster account (register an FID)
- Add a signer key for posting
- Post casts to the network
All operations are fully programmatic - no Farcaster app or manual steps required.
For AI agents: See AGENT_GUIDE.md for detailed implementation instructions.
Prerequisites
- Node.js 18+
- $1 of ETH or USDC on any major chain (Ethereum, Optimism, Base, Arbitrum, Polygon)
The toolkit handles bridging and swapping automatically.
Installation
npm install
Quick Start (Fully Automatic)
Send $1 of ETH or USDC to your wallet on any supported chain, then:
PRIVATE_KEY=0x... npm run auto
# or
PRIVATE_KEY=0x... node src/auto-setup.js "Your first cast text"
This will:
- Detect your funds across all chains
- Bridge/swap to get ETH on Optimism and USDC on Base
- Register your FID
- Add a signer key
- Post your first cast
- Save credentials to
~/.openclaw/farcaster-credentials.jsonor./credentials.json
Security Note: Credentials are stored as plain text JSON with restricted file permissions. Anyone with access to these files can control both the wallet funds and the Farcaster account. For production use, implement your own secure storage solution.
Manual Step-by-Step
1. Generate a Wallet
const { Wallet } = require('ethers');
const wallet = Wallet.createRandom();
console.log('Address:', wallet.address);
console.log('Private Key:', wallet.privateKey);
console.log('Mnemonic:', wallet.mnemonic.phrase);
2. Fund the Wallet
- Send ~0.005 ETH to the address on Optimism (for FID registration)
- Send ~0.001 ETH to the same address on Base (for USDC swap)
3. Register FID
PRIVATE_KEY=0x... node src/register-fid.js
4. Add Signer Key
PRIVATE_KEY=0x... node src/add-signer.js
Save the signer private key that's output - you need it to post casts.
5. Swap ETH to USDC (for x402 payments)
PRIVATE_KEY=0x... node src/swap-to-usdc.js
6. Post a Cast
PRIVATE_KEY=0x... SIGNER_PRIVATE_KEY=... FID=123 node src/post-cast.js "Hello Farcaster!"
7. Set Up Profile (Optional)
# Set username, display name, bio, and profile picture
PRIVATE_KEY=0x... SIGNER_PRIVATE_KEY=... FID=123 npm run profile myusername "Display Name" "My bio" "https://example.com/pfp.png"
Detailed Walkthrough
Step 1: FID Registration (Optimism)
Farcaster IDs are registered on Optimism via the IdGateway contract.
IdGateway: 0x00000000Fc25870C6eD6b6c7E41Fb078b7656f69
The registration costs ~0.00008 ETH (storage fee) plus gas.
Key function: register(address recovery) - registers a new FID with the caller as owner.
Step 2: Adding a Signer Key (Optimism)
This is the tricky part. Farcaster requires a "Signed Key Request" to add signer keys - it's an anti-spam measure.
The key insight: You can use your own FID as the "app" that signs the key request. Since you control the custody address, you can self-sign.
The process:
- Generate an Ed25519 keypair (the signer key)
- Create an EIP-712
SignedKeyRequestmessage - Sign it with your custody wallet
- Use the
SignedKeyRequestValidatorcontract to encode the metadata - Call
KeyGateway.add()with the encoded metadata
Critical: You MUST use the validator contract's encodeMetadata() function. Manual ABI encoding doesn't work because the struct encoding includes a dynamic offset pointer.
KeyGateway: 0x00000000fC56947c7E7183f8Ca4B62398CaAdf0B
SignedKeyRequestValidator: 0x00000000FC700472606ED4fA22623Acf62c60553
Step 3: Posting Casts (via Neynar Hub)
Casts are submitted to Farcaster hubs as protobuf-encoded messages.
Problem: Most public hubs (like hub.pinata.cloud) lag behind on syncing on-chain events. They may not recognize your new FID for hours or days.
Solution: Use Neynar's hub (hub-api.neynar.com) which requires x402 micropayments but is well-synced.
Step 4: x402 Payments
Neynar uses the x402 payment protocol. Each API call costs 0.001 USDC on Base.
The payment uses EIP-3009 (transferWithAuthorization) - a gasless signature-based USDC transfer:
- Sign an EIP-712 message authorizing Neynar to pull USDC
- Base64-encode the payment payload
- Include it in the
X-PAYMENTheader
Payment payload structure:
{
"x402Version": 1,
"scheme": "exact",
"network": "base",
"payload": {
"signature": "0x...",
"authorization": {
"from": "0x...",
"to": "0xA6a8736f18f383f1cc2d938576933E5eA7Df01A1",
"value": "1000",
"validAfter": "0",
"validBefore": "...",
"nonce": "0x..."
}
}
}
Common Errors & Solutions
Error: "invalid hash"
Cause: Old version of @farcaster/hub-nodejs library.
Solution: Upgrade to version 0.15.9 or later:
npm install @farcaster/hub-nodejs@latest
Error: "unknown fid"
Cause: The hub hasn't synced your on-chain FID registration yet.
Solution: Use Neynar's hub which is better synced. Public hubs like Pinata can be millions of FIDs behind.
Error: Transaction reverts when adding signer
Cause: The SignedKeyRequest metadata is incorrectly encoded.
Solution: Use the SignedKeyRequestValidator.encodeMetadata() contract function instead of manual ABI encoding. The struct requires a dynamic offset pointer that manual encoding misses.
Wrong:
const metadata = abiCoder.encode(
['uint256', 'address', 'bytes', 'uint256'],
[fid, wallet.address, signature, deadline]
);
Right:
const validator = new Contract(VALIDATOR_ADDRESS, VALIDATOR_ABI, provider);
const metadata = await validator.encodeMetadata([fid, wallet.address, signature, deadline]);
Error: "Failed to verify payment" (x402)
Cause: Wrong x402 header format.
Solution: The payload must include:
x402Version: 1(number, not string)payload.authorizationobject (notpayload.txHash)- All values as strings in the authorization object
Error: Cast submitted but not found
Cause: Hub returned 200 but cast didn't propagate.
Solution: Verify the cast after submission using the GET endpoint. If it fails, the hub may have silently rejected it. Check that your FID and signer are properly synced first.
Error: "data is missing" on Neynar API
Cause: Wrong endpoint or content type.
Solution:
- Use
hub-api.neynar.com/v1/submitMessagefor raw protobuf - Use
Content-Type: application/octet-stream - Send the raw message bytes, not JSON
Architecture Notes
Farcaster Protocol Contracts (Optimism)
| Contract | Address | Purpose |
|---|---|---|
| IdGateway | 0x00000000Fc25870C6eD6b6c7E41Fb078b7656f69 | Register new FIDs |
| IdRegistry | 0x00000000Fc6c5F01Fc30151999387Bb99A9f489b | Query FID ownership |
| KeyGateway | 0x00000000fC56947c7E7183f8Ca4B62398CaAdf0B | Add signer keys |
| KeyRegistry | 0x00000000Fc1237824fb747aBDE0FF18990E59b7e | Query signer keys |
| SignedKeyRequestValidator | 0x00000000FC700472606ED4fA22623Acf62c60553 | Validate/encode key requests |
API Endpoints
Neynar Hub API (https://hub-api.neynar.com)
| Endpoint | Method | Description |
|---|---|---|
/v1/submitMessage |
POST | Submit casts, profile updates (requires x402 payment) |
/v1/onChainIdRegistryEventByAddress?address= |
GET | Check if FID is synced |
/v1/onChainSignersByFid?fid= |
GET | Check if signer is synced |
Neynar REST API (https://api.neynar.com)
| Endpoint | Method | Description |
|---|---|---|
/v2/farcaster/cast?identifier=<hash>&type=hash |
GET | Verify cast exists |
Farcaster Fname Registry (https://fnames.farcaster.xyz)
| Endpoint | Method | Description |
|---|---|---|
/transfers |
POST | Register/transfer fname |
/transfers/current?name=<fname> |
GET | Check availability (404=available) |
Message Flow
1. Register FID (Optimism) → IdGateway.register()
2. Add Signer (Optimism) → KeyGateway.add()
3. Wait for hub sync (can take minutes)
4. Create cast → @farcaster/hub-nodejs
5. Submit to hub → POST hub-api.neynar.com/v1/submitMessage with x402 payment
Key Types
- Custody Key: Ethereum wallet that owns the FID (secp256k1)
- Signer Key: Ed25519 key for signing casts (separate from custody)
Cost Breakdown
| Operation | Network | Cost |
|---|---|---|
| FID Registration | Optimism | ~$0.20 |
| Add Signer | Optimism | ~$0.05 gas |
| ETH→USDC Swap | Base | ~$0.10 gas |
| Each API call | Base (x402) | $0.001 USDC |
Total to get started: ~$0.50-1.00
Programmatic Usage
const { registerFid, addSigner, postCast, swapEthToUsdc } = require('./src');
async function main() {
const privateKey = '0x...';
// 1. Register FID
const { fid } = await registerFid(privateKey);
// 2. Add signer
const { signerPrivateKey } = await addSigner(privateKey);
// 3. Get USDC for x402 (on Base)
await swapEthToUsdc(privateKey);
// 4. Post cast
const { hash } = await postCast({
privateKey,
signerPrivateKey,
fid: Number(fid),
text: 'Hello from my autonomous agent!'
});
console.log('Cast:', hash);
}
License
MIT