Real-Time Sybil Detection On Base With GoldRush - (Part 3)

Rajdeep Singha
Content Writer
Sybil Resistance (Part 3): Building a Fairer Future with On-Chain Gamification on Base with GoldRush

Welcome back! In Part 1 and Part 2 , we established the crucial need for real-time Sybil detection on Layer 2 networks like Base, driven by the low cost of bot farming. We designed a sophisticated architecture that uses the GoldRush Streaming API to generate instant Sybil Risk Scores.

Now, we close the loop. A score is useless if it doesn’t inform action. This final part focuses on the action layer: transforming that real-time security score into a mechanism that rewards real users, boosts community identity, and builds a sustainable, fraud-resistant ecosystem on Base.


 Designing Sybil-Resistant On-chain Gamification

Gamification is the engine of Web3 growth, turning simple interactions into fun, rewarding experiences. But if the gates are left open, that engine quickly starts burning through bot fuel.

The Goals of Fair Gamification

When designed correctly, on-chain gamification achieves three vital goals:

  • Engagement: Turning passive observers into active participants.

  • Retention: Encouraging long-term commitment by rewarding continued interaction.  

  • Community Identity: Creating a sense of belonging and establishing reputation within the ecosystem.

The core elements are straightforward: Quests (tasks), Badges/Achievements (proof of completion), and Leaderboards (social ranking). The challenge is to ensure these elements serve humans, not scripts.  

How to Build Resistance into the Rules

We can strategically bake Sybil resistance directly into the gamification mechanics, focusing on quality of participation over sheer quantity of transactions:

  1. Require Provenance (Wallet Age & History): Instead of allowing any newly minted wallet to start a quest, protocols can require a minimum wallet age or demand a history of specific, non-farmed on-chain interactions on Base or L1. This forces an attacker to invest time—a cost that dramatically increases the Attacker Cost to Succeed (ACS).  

  2. Use Dynamic Reward Weighting: The system must not rely on a simple binary outcome (pass/fail). The Sybil Risk Score (SRS) generated in real-time should dynamically adjust the reward weight. A wallet with a slightly elevated score might receive a reduced reward, while a verified human receives a full bonus. This progressive approach is fairer and highly adaptive.  

  3. Lock Achievements with Soulbound Tokens (SBTs): SBTs are non-transferable tokens that bind an achievement or reputation status to a specific wallet address. They are ideal for gamification badges because they prevent Sybils from consolidating valuable reputation or credentials into a single master wallet and then liquidating them. If a wallet earns a "Top Governance Contributor" SBT, that credential remains locked to its history, cementing a genuine, non-transferable identity. 

 


 A Fair Quest System on Base

Let’s apply this architecture to a practical scenario: a dApp launching a new Quest System on Base designed to incentivize healthy, long-term protocol engagement.

The Quests 

The dApp wants to reward users for actions that build genuine utility and depth in the ecosystem, such as:

  • Financial Commitment: Execute 5 unique swaps on a decentralized exchange.

  • Long-Term Provision: Provide liquidity in a specific pool for 7 continuous days.

  • Governance Participation: Engage with a governance vote or proposal (requires token-gating/stake).  

The Sybil-Resistant Workflow

The entire process is automated and secured by the GoldRush/Covalent data streams:

  1. Real-Time Data Streams Triggered: When a user executes a swap or provides liquidity, the transaction immediately flows into the GoldRush Streaming API.  

  2. Quest Completion Verification: The off-chain service uses Covalent data to verify that the user's history shows the quest criteria have been met (e.g., confirming five unique swaps, not five micro-swaps in quick succession).

  3. Real-Time Sybil Scoring: Simultaneously, the detection engine calculates the Sybil Risk Score (SRS) for that wallet. This score checks for synchronized activity, identical transaction patterns, and funding fan-out.  

  4. Reward Gatekeeper: Only if the quest is completed AND the SRS is below the acceptable threshold (verified legitimate activity) does the system proceed.

  5. Reward Attestation & Payout: The Off-Chain Oracle generates a signed attestation for the legitimate user. The on-chain reward contract on Base releases the reward (or mints the SBT badge) only after verifying this cryptographic signature.

This workflow ensures that bot farms, even if they execute the quest action perfectly, are filtered out by the security layer, preventing them from draining the incentive budget and ensuring rewards are funneled exclusively to verifiable community members.  


 Best Practices for Designing Sybil-Resistant Incentives

To stay ahead of continuously adapting attackers, system design requires continuous ethical and technical maintenance.

  • Combine On-Chain Behavioral Analysis with Off-Chain Friction Sparingly: Behavioral analysis of transaction patterns is the most privacy-preserving and scalable defense. Only in extreme cases, where very high integrity is needed (like critical governance votes), should friction-based validation, such as optional identity verification or Proof-of-Personhood, be considered. This balance preserves the privacy ethos of Base.  

  • Use Dynamic Scoring, Not Binary Logic: Avoid hard, binary "allow/deny" filters that are easy to game and prone to False Positives. Instead, use the continuous Sybil Risk Score (SRS) to drive nuanced responses. Flagged users can be put in a Human-in-the-Loop (HITL) review queue instead of being instantly banned, allowing for human judgment in complex cases and maintaining fairness.  

  • Continuously Adapt Heuristics (HITL is Mandatory): Attackers continuously modify their scripts to evade static filters. The security system must feature an adaptive feedback loop. Human reviewers analyze borderline flagged accounts and provide feedback, training the algorithm to identify newly observed adversarial behaviors and adjust the detection model accordingly.  

Reward Genuine Community Contribution, Not Volume: The incentives themselves should be designed to increase the Attacker Cost to Succeed (ACS). Focus rewards on non-replicable, valuable contributions—governance, long-term locked liquidity, unique creative acts—rather than easily automated, high-volume transactions that simply inflate metrics like Daily Active Wallets. 

Let’s move on to the Coding part : 

Let’s create a more interactive simulation of Sybil-resistant gamification with:

  1. Multiple quests

  2. Dynamic Sybil Risk Score (SRS) calculation

  3. Badge system (like SBTs)

  4. Leaderboard showing rewards 

IV. Technical Implementation

Step 1: Environment Setup

Begin by installing Node.js (version 16+) and npm. Visit nodejs.org and download the LTS version. Verify installation:

node --version npm --version

Step 2: Project Initialization

2.1) Create a new directory for your project:

mkdir sybil_attack cd sybil_attack

What this does: Creates a new folder for your project and enters it. All your files will be organized here.

2.2) Initialize npm and install dependencies:

npm init -y npm install ws graphql-ws graphql axios ethers npm install dotenv

What this does:

  • npm init -y creates a package.json file (package configuration file) with default settings

  • npm install dotenv installs the dotenv package to safely load environment variables from .env file

2.3) Create a .env file in the root directory:

GOLDRUSH_KEY=your_api_key_here BASE_RPC_URL=https://base-goerli.publicnode.com

What this does: Stores your Goldrush Streaming API key securely in a .env file. This file is ignored by Git (never committed) so your credentials stay private.

Obtain your Goldrush API key from goldrush.dev by registering as a developer. This key authenticates your requests to the Goldrush Streaming API.

Step 3: Project Structure

Organize your project with this directory structure:

sybil_attack/

├── .env

├── .gitignore

├── package.json

├── package-lock.json

├── sybil-gamification-cli.js

Step 4: Let’s deep dive into Coding

Create sybil-gamification-cli.js:


import 'dotenv/config'; import WebSocket from 'ws'; import { createClient } from 'graphql-ws'; import { GoldRushClient, StreamingChain } from '@covalenthq/client-sdk'; const API_KEY = process.env.GOLDRUSH_API_KEY; // Use GoldRush SDK for streaming const client = new GoldRushClient(API_KEY, {}, { onConnecting: () => console.log("[GoldRush] Connecting..."), onOpened: () => console.log("[GoldRush] Connected"), onError: (e) => console.error("[GoldRush] Error", e), onClosed: () => console.log("[GoldRush] Disconnected"), }); // In-memory state for risk scoring & gamification const walletState = new Map(); // Gamification state const MIN_SRS_TO_PARTICIPATE = 0.6; // Minimum SRS to earn rewards const questProgress = new Map(); // Track quest progress const badges = new Map(); // Track earned badges const reputation = new Map(); // Track reputation points // Simple scoring: count txs in short window function scoreWallet(addr) { const state = walletState.get(addr); if (!state) return 0.5; const now = Date.now(); // count txs in last minute const recent = state.lastTxs.filter(t => now - t < 60 * 1000).length; // fewer txs → less risk in this simple model const score = Math.max(0, 1 - recent / 10); state.score = score; return score; } // Check quests and award badges function checkQuestsAndBadges(addr, srs) { // Only reward wallets with good SRS if (srs < MIN_SRS_TO_PARTICIPATE) { return; } const state = walletState.get(addr); const txCount = state.lastTxs.length; const walletBadges = badges.get(addr) || new Set(); const progress = questProgress.get(addr) || { txCount: 0 }; let reputationGained = 0; // Quest 1: First 5 Transactions (Bronze) if (txCount >= 5 && !walletBadges.has('bronze_active')) { walletBadges.add('bronze_active'); reputationGained += 50; console.log(`🥉 ${addr.slice(0, 10)}... earned BRONZE BADGE: Active Wallet`); } // Quest 2: 20 Transactions (Silver) if (txCount >= 20 && !walletBadges.has('silver_trader')) { walletBadges.add('silver_trader'); reputationGained += 100; console.log(`🥈 ${addr.slice(0, 10)}... earned SILVER BADGE: Trader`); } // Quest 3: 50 Transactions (Gold) if (txCount >= 50 && !walletBadges.has('gold_veteran')) { walletBadges.add('gold_veteran'); reputationGained += 250; console.log(`🥇 ${addr.slice(0, 10)}... earned GOLD BADGE: Veteran`); } // Quest 4: High SRS (0.8+) with 30+ txs (Platinum) if (srs >= 0.8 && txCount >= 30 && !walletBadges.has('platinum_trusted')) { walletBadges.add('platinum_trusted'); reputationGained += 500; console.log(`💎 ${addr.slice(0, 10)}... earned PLATINUM BADGE: Trusted`); } // Update state badges.set(addr, walletBadges); questProgress.set(addr, { txCount }); // Add reputation with SRS multiplier if (reputationGained > 0) { const srsMultiplier = srs; // 0.6-1.0 = 60-100% rewards const finalReputation = Math.floor(reputationGained * srsMultiplier); const currentRep = reputation.get(addr) || 0; reputation.set(addr, currentRep + finalReputation); console.log(` +${finalReputation} reputation (SRS multiplier: ${srsMultiplier.toFixed(2)}x)`); } } // Display leaderboard function showLeaderboard() { const topWallets = Array.from(reputation.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 5); if (topWallets.length === 0) return; console.log('\n╔════════════════════ LEADERBOARD ═══════════════════╗'); topWallets.forEach(([addr, rep], index) => { const badgeCount = (badges.get(addr) || new Set()).size; const srs = walletState.get(addr)?.score || 0; const rank = ['🥇', '🥈', '🥉', '4️⃣', '5️⃣'][index]; console.log(`${rank} ${addr.slice(0, 12)}... | Rep: ${rep} | Badges: ${badgeCount} | SRS: ${srs.toFixed(2)}`); }); console.log('╚════════════════════════════════════════════════════╝\n'); } // Handle each wallet activity event function handleEvent(evt) { const from = evt.from_address?.toLowerCase(), to = evt.to_address?.toLowerCase(); for (const addr of [from, to].filter(Boolean)) { // Initialize wallet state if (!walletState.has(addr)) { walletState.set(addr, { lastTxs: [], score: 0.5, firstSeen: Date.now() }); } const st = walletState.get(addr); st.lastTxs.push(Date.now()); // Keep only last ~5 min events st.lastTxs = st.lastTxs.filter(t => Date.now() - t < 5 * 60 * 1000); // Calculate SRS const newScore = scoreWallet(addr); const walletAge = Math.floor((Date.now() - st.firstSeen) / (24 * 60 * 60 * 1000)); // days console.log(`\n📍 ${addr.slice(0, 12)}... | SRS: ${newScore.toFixed(2)} | Age: ${walletAge}d | Txs: ${st.lastTxs.length}`); // Gamification: Check quests and award badges checkQuestsAndBadges(addr, newScore); // Warning for low SRS if (newScore < MIN_SRS_TO_PARTICIPATE) { console.log(` ⚠️ SRS too low for rewards (need ${MIN_SRS_TO_PARTICIPATE}+)`); } } } // Try ultra-active addresses on Base (token contracts with high volume) const MONITORED_WALLETS = [ '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base (extremely active) '0x4200000000000000000000000000000000000006', // WETH on Base '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', // Aerodrome Router '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', // BaseName (.base domains) ]; console.log(`\n╔════════════════════════════════════════════════════════════╗`); console.log(`║ SYBIL-RESISTANT GAMIFICATION SYSTEM ║`); console.log(`╚════════════════════════════════════════════════════════════╝`); console.log(`\n📊 Monitoring ${MONITORED_WALLETS.length} high-activity addresses on Base Mainnet`); console.log(`🎮 Gamification enabled with ${MIN_SRS_TO_PARTICIPATE}+ SRS required\n`); console.log(`Quest Tiers:`); console.log(` 🥉 Bronze: 5 txs → +50 rep`); console.log(` 🥈 Silver: 20 txs → +100 rep`); console.log(` 🥇 Gold: 50 txs → +250 rep`); console.log(` 💎 Platinum: 30 txs + 0.8 SRS → +500 rep\n`); console.log(`Waiting for transactions...\n`); // Keep track of whether we receive any data let receivedData = false; // Subscribe to Wallet Activity stream client.StreamingService.rawQuery( ` subscription WalletTxs($chainName: ChainName!, $walletAddresses: [String!]!) { walletTxs(chain_name: $chainName, wallet_addresses: $walletAddresses) { tx_hash from_address to_address value block_signed_at successful } } `, { chainName: StreamingChain.BASE_MAINNET, walletAddresses: MONITORED_WALLETS }, { next: (data) => { receivedData = true; console.log('Received data:', JSON.stringify(data, null, 2)); const ev = data.data?.walletTxs; if (ev) { console.log(`Processing ${ev.length} transaction(s)`); for (const tx of ev) handleEvent(tx); } else { console.log('No walletTxs in data'); } }, error: (err) => console.error("Stream error:", err), complete: () => console.log("Stream completed"), } ); // Show leaderboard every 30 seconds setInterval(() => { if (reputation.size > 0) { showLeaderboard(); } }, 30000); // Show summary statistics every minute setInterval(() => { if (walletState.size > 0) { const totalWallets = walletState.size; const qualifiedWallets = Array.from(walletState.values()) .filter(s => s.score >= MIN_SRS_TO_PARTICIPATE).length; const totalBadges = Array.from(badges.values()) .reduce((sum, set) => sum + set.size, 0); const avgSRS = Array.from(walletState.values()) .reduce((sum, s) => sum + s.score, 0) / totalWallets; console.log(`\n📈 Stats: ${totalWallets} wallets tracked | ${qualifiedWallets} qualified (SRS ${MIN_SRS_TO_PARTICIPATE}+) | ${totalBadges} badges earned | Avg SRS: ${avgSRS.toFixed(2)}\n`); } }, 60000); // Graceful shutdown - show final stats process.on('SIGINT', () => { console.log('\n\n👋 Shutting down...\n'); if (walletState.size > 0) { console.log('═══════════════ FINAL STATISTICS ═══════════════'); console.log(`Total wallets tracked: ${walletState.size}`); console.log(`Total badges awarded: ${Array.from(badges.values()).reduce((sum, set) => sum + set.size, 0)}`); console.log(`Total reputation distributed: ${Array.from(reputation.values()).reduce((sum, rep) => sum + rep, 0)}`); showLeaderboard(); // Show top badge earners const topBadgeEarners = Array.from(badges.entries()) .sort((a, b) => b[1].size - a[1].size) .slice(0, 3); if (topBadgeEarners.length > 0) { console.log('🏆 Top Badge Collectors:'); topBadgeEarners.forEach(([addr, badgeSet], i) => { const srs = walletState.get(addr)?.score || 0; console.log(` ${i + 1}. ${addr.slice(0, 12)}... → ${badgeSet.size} badges (SRS: ${srs.toFixed(2)})`); }); } console.log('\n'); } process.exit(0); }); // Add a timeout check setTimeout(() => { if (!receivedData) { console.log('\n⚠️ No transactions received after 30 seconds.'); console.log('This might indicate:'); console.log(' 1. The streaming API requires specific subscription setup'); console.log(' 2. These addresses might not be configured for streaming'); console.log(' 3. There might be API limitations or delays'); } }, 30000);

Now , once we run : node sybil-gamification-cli.js

We will get :

How It Works:

  1. Dynamic SRS: Each wallet gets a Sybil Risk Score based on wallet age and activity.

  2. Quest System: Each quest has a type, requirement, max SRS, and a badge.

  3. Eligibility Check: Only wallets meeting both activity and SRS thresholds get rewards.

  4. Badge System: Simulates SBT-like non-transferable achievements.

  5. Leaderboard: Ranks wallets by number of badges earned.

This simulation visually shows Sybil-resistance in action:

  • Low SRS → reward granted

  • High SRS → reward denied

  • Leaderboard demonstrates community ranking

  • Badges simulate on-chain SBT achievements

Conclusion

The future of Base, and indeed all high-speed Layer 2s, depends on its ability to turn the economic advantage of low fees into a sustainable growth environment.

Fair incentive systems require two powerful components working in synchronization:

  1. Real-time Sybil/Bot Detection: Instantly identifying fake activity using low-latency data streams.

  2. Intentionally Designed, Sybil-Resistant On-chain Gamification Models: Gating rewards and reputation only to verified human behavior.

By integrating the real-time detection power of the GoldRush Wallet Activity Streams with carefully architected, gas-optimized gamification contracts on Base, builders gain a robust defense. This integrated approach protects protocol treasuries, prevents the draining of airdrops, and ensures that the exponential growth on Base translates into genuine, verifiable long-term community growth.

Ready to secure your incentives? Try integrating this real-time pipeline with your next Base deployment. The full technical implementation, including the Node.js streaming consumer and the smart contract integration patterns, is available in the dedicated code repository. Let’s build a fairer, stronger, and more human-centric Web3.

Get Started

Get started with GoldRush API in minutes. Sign up for a free API key and start building.

Support

Explore multiple support options! From FAQs for self-help to real-time interactions on Discord.

Contact Sales

Interested in our professional or enterprise plans? Contact our sales team to learn more.