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.
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.
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.
We can strategically bake Sybil resistance directly into the gamification mechanics, focusing on quality of participation over sheer quantity of transactions:
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).
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.
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.

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 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 entire process is automated and secured by the GoldRush/Covalent data streams:
Real-Time Data Streams Triggered: When a user executes a swap or provides liquidity, the transaction immediately flows into the GoldRush Streaming API.
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).
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.
Reward Gatekeeper: Only if the quest is completed AND the SRS is below the acceptable threshold (verified legitimate activity) does the system proceed.
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.
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 create a more interactive simulation of Sybil-resistant gamification with:
Multiple quests
Dynamic Sybil Risk Score (SRS) calculation
Badge system (like SBTs)
Leaderboard showing rewards
Begin by installing Node.js (version 16+) and npm. Visit nodejs.org and download the LTS version. Verify installation:
node --version
npm --version2.1) Create a new directory for your project:
mkdir sybil_attack
cd sybil_attackWhat 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 dotenvWhat 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.comWhat 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.
Organize your project with this directory structure:
sybil_attack/
├── .env
├── .gitignore
├── package.json
├── package-lock.json
├── sybil-gamification-cli.js
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 :



Dynamic SRS: Each wallet gets a Sybil Risk Score based on wallet age and activity.
Quest System: Each quest has a type, requirement, max SRS, and a badge.
Eligibility Check: Only wallets meeting both activity and SRS thresholds get rewards.
Badge System: Simulates SBT-like non-transferable achievements.
Leaderboard: Ranks wallets by number of badges earned.
Low SRS → reward granted
High SRS → reward denied
Leaderboard demonstrates community ranking
Badges simulate on-chain SBT achievements
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:
Real-time Sybil/Bot Detection: Instantly identifying fake activity using low-latency data streams.
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.