User API
Initialize user profiles and manage user data across the FarFISH ecosystem.
Base URL
https://app.farfish.xyz/api/user
POST /api/user/init
Initialize user profile on first wallet connection. Creates refCode cache entry and leaderboard record.
Request
Endpoint: POST /api/user/init
Headers:
Content-Type: application/json
Body Schema:
{
wallet: string // Ethereum wallet address (42 chars, 0x-prefixed)
}
Example Request:
{
"wallet": "0x1234567890abcdef1234567890abcdef12345678"
}
Response
Success - New User (200):
{
"success": true
}
Success - Existing User (200):
{
"success": true,
"alreadyExists": true
}
Error (400):
{
"error": "Invalid wallet address format"
}
Operations Performed
When a new user is initialized, the API performs:
-
Extract RefCode: Last 8 characters of wallet address (lowercase)
typescriptconst refCode = wallet.slice(-8).toLowerCase(); // Example: "12345678" -
Create RefCode Cache: Maps refCode → wallet for referral lookups
-
Initialize Leaderboard: Add user to leaderboard with score 0
Idempotency
Calling /api/user/init multiple times with same wallet is safe.
- First call: Creates cache and leaderboard entry
- Subsequent calls: Returns
{success: true, alreadyExists: true} - No data is overwritten or duplicated
RefCode Generation
RefCode is deterministic - same wallet always generates same code:
Wallet: 0x1234567890ABCDEF1234567890ABCDEF12345678
RefCode: 12345678 (lowercase)
Wallet: 0x742d35cc6634c0532925a3b844bc9e7595f0beb9
RefCode: 95f0beb9
Collision Probability: 1 in 4.3 billion (2^32 combinations)
cURL Example
curl -X POST https://app.farfish.xyz/api/user/init \
-H "Content-Type: application/json" \
-d '{"wallet":"0x1234567890abcdef1234567890abcdef12345678"}'
JavaScript Example
async function initializeUser(wallet: string) {
const response = await fetch('https://app.farfish.xyz/api/user/init', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wallet })
});
const data = await response.json();
if (data.success) {
if (data.alreadyExists) {
console.log('User already initialized');
} else {
console.log('New user created');
}
} else {
console.error('Error:', data.error);
}
}
Auto-Initialization on Wallet Connection
Typically called automatically when user connects wallet:
// Get wallet address from your Web3 connection library
const { address } = useWalletConnection();
import { useEffect } from 'react';
function useAutoInit() {
const { address, isConnected } = useAccount();
useEffect(() => {
if (isConnected && address) {
// Initialize user on wallet connection
fetch('/api/user/init', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wallet: address })
});
}
}, [isConnected, address]);
}
User Data Structure
Backend Keys Created
When user initializes, the following data entries are created or updated:
| Key Pattern | Type | Value | Purpose |
|---|---|---|---|
refcode:{last8} | String | Full wallet address | RefCode → wallet lookup |
leaderboard | Sorted Set | Member: wallet, Score: 0 | Leaderboard ranking |
Additional Keys (Created by Other APIs)
| Key Pattern | Type | Value | Purpose |
|---|---|---|---|
referral:{wallet} | JSON | {referrer, createdAt} | Referral attribution |
refcount:{wallet} | Integer | Count of referrals | Referral count |
user:{wallet} | JSON | User profile | Steam tasks, preferences |
Identity Model
FarFISH uses wallet-based identity - no usernames, passwords, or emails required.
- Primary ID: Wallet address (42 characters, 0x-prefixed)
- Secondary ID: RefCode (last 8 characters, deterministic)
- Farcaster Mini App: FID (Farcaster ID) as additional identifier
Wallet Address Properties
- Unique: Ethereum address space (2^160 possible addresses)
- Immutable: Cannot be changed once created
- Public: Visible on blockchain and in leaderboard
- Self-sovereign: User controls via private key
Cross-App Identity
Same wallet address works across all FarFISH applications:
Web App: app.farfish.xyz → Shared backend database
Base App: baseapp.farfish.xyz → Shared backend database
Mini App: miniapp.farfish.xyz → Shared backend database
User data (referrals, leaderboard, tasks) is automatically synchronized across all apps.
Profile Data (Future Enhancement)
Current Status: User profiles are minimal (wallet + refCode only).
Future enhancements may include customizable profiles with display names, avatars, social links, etc.
Potential Future Fields:
{
wallet: string;
refCode: string;
displayName?: string; // Custom username
avatar?: string; // IPFS/CDN URL
bio?: string; // Profile description
twitter?: string; // Social link
farcaster?: number; // FID
createdAt: number; // Unix timestamp
lastActive: number; // Last activity timestamp
}
Error Handling
| Error | HTTP Status | Meaning | Action |
|---|---|---|---|
Invalid wallet address format | 400 | Malformed address | Validate format before calling |
Missing wallet parameter | 400 | Request body incomplete | Include wallet field |
| Server error | 500 | Backend connection issue | Retry with exponential backoff |
Wallet Format Validation
Validate wallet client-side before API call:
function isValidWallet(wallet: string): boolean {
return /^0x[a-fA-F0-9]{40}$/.test(wallet);
}
async function initUser(wallet: string) {
if (!isValidWallet(wallet)) {
throw new Error('Invalid wallet address format');
}
// Proceed with API call...
}
Integration Example
Complete user initialization flow:
// Get wallet address from your Web3 connection library
const { address } = useWalletConnection();
import { useEffect, useState } from 'react';
function useUserInit() {
const { address, isConnected } = useAccount();
const [initialized, setInitialized] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!isConnected || !address) {
setInitialized(false);
return;
}
async function initUser() {
try {
const response = await fetch('/api/user/init', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wallet: address })
});
const data = await response.json();
if (data.success) {
setInitialized(true);
setError(null);
} else {
setError(data.error);
}
} catch (err) {
setError('Failed to initialize user');
}
}
initUser();
}, [isConnected, address]);
return { initialized, error };
}
// Usage
function App() {
const { initialized, error } = useUserInit();
if (error) return <div>Error: {error}</div>;
if (!initialized) return <div>Initializing...</div>;
return <div>Welcome to FarFISH!</div>;
}
Related Documentation
- Referral API - Uses refCode created by user init
- Leaderboard API - Queries leaderboard entry created here
- Getting Started - Learn how wallet-based identity works