Back to Guides
guide10 min readDecember 4, 2024GasX Team

Stop Gas Draining: How to Set Up Sybil-Resistant Gas Sponsorship

Comprehensive guide to preventing abuse and gas draining in your sponsorship campaigns. Learn to implement rate limits, wallet whitelists, Gitcoin Passport verification, and smart contract allowlists.

sybil resistancegas draining preventionpaymaster securityWeb3 securityrate limitingGitcoin Passportwallet verification

Stop Gas Draining: How to Set Up Sybil-Resistant Gas Sponsorship

You've created a gas sponsorship campaign. Congratulations! Now comes the hard part: preventing bad actors from draining your entire budget in minutes.

This guide covers battle-tested strategies to protect your gas sponsorship from Sybil attacks, bots, and abuse.

The Gas Draining Problem

Without protection, your sponsorship campaign is vulnerable to:

  1. Sybil Attacks: One person creates hundreds of wallets
  2. Bot Farming: Automated scripts repeatedly claiming sponsored gas
  3. Wash Trading: Users cycling funds through sponsored transactions
  4. Contract Exploitation: Calling expensive functions to drain gas

Real Example: A project launched a "free mint" campaign. Within 2 hours, one attacker with 500 wallets drained $15,000 in sponsored gas by minting and immediately selling NFTs.

Defense Layers

Effective protection requires multiple layers:

Layer 1: Rate Limiting (Per Wallet)
       ↓
Layer 2: Identity Verification (Gitcoin Passport, etc.)
       ↓
Layer 3: Contract Allowlisting (Only approved contracts)
       ↓
Layer 4: Transaction Analysis (Pattern detection)
       ↓
Layer 5: Budget Controls (Hard caps, alerts)

Layer 1: Rate Limiting

Per-Wallet Limits

The simplest defense. Set maximum sponsored transactions per wallet:

// GasX Campaign Configuration
{
  "rateLimits": {
    "perWallet": {
      "maxTransactionsPerHour": 10,
      "maxTransactionsPerDay": 50,
      "maxGasPerDay": "0.01", // In ETH equivalent
      "cooldownMinutes": 5    // Time between transactions
    }
  }
}

Why It Works

  • Legitimate users rarely need more than 10-20 transactions/day
  • Forces attackers to create many more wallets (costly)
  • Easy to implement, low false positive rate

GasX Implementation

In your campaign settings:

  1. Go to CampaignsEdit Campaign
  2. Navigate to Security tab
  3. Set Transaction Limits:
    • Transactions per hour: 10
    • Transactions per day: 50
    • Gas per day: 0.01 ETH

Layer 2: Identity Verification

Rate limits aren't enough when attackers can create unlimited wallets. Add identity verification.

Option A: Gitcoin Passport Score

Gitcoin Passport provides a "humanity score" based on verified credentials:

// Check Gitcoin Passport score before sponsoring
interface PassportCheck {
  address: string;
  score: number;
  threshold: number;
}

async function checkPassportScore(address: string): Promise<boolean> {
  const response = await fetch(
    `https://api.scorer.gitcoin.co/registry/score/${SCORER_ID}/${address}`,
    {
      headers: {
        'X-API-Key': GITCOIN_API_KEY,
      },
    }
  );

  const { score } = await response.json();

  // Require minimum score of 15 (adjustable)
  return score >= 15;
}

Score Guidelines:

ScoreTrust LevelRecommendation
< 10LowBlock or heavily limit
10-20MediumStandard limits
20-30HighRelaxed limits
> 30Very HighMinimal restrictions

Option B: On-Chain Reputation

Check if wallets have meaningful on-chain history:

async function checkOnChainReputation(address: string): Promise<ReputationScore> {
  const [balance, txCount, tokenHoldings, nftHoldings] = await Promise.all([
    provider.getBalance(address),
    provider.getTransactionCount(address),
    getERC20Holdings(address),
    getNFTHoldings(address),
  ]);

  // Score based on activity
  let score = 0;

  if (balance > parseEther('0.01')) score += 10;
  if (txCount > 10) score += 10;
  if (txCount > 100) score += 20;
  if (tokenHoldings.length > 0) score += 10;
  if (nftHoldings.length > 0) score += 10;

  // Check account age (proxy: first transaction date)
  const firstTx = await getFirstTransaction(address);
  const ageInDays = (Date.now() - firstTx.timestamp) / (1000 * 60 * 60 * 24);

  if (ageInDays > 30) score += 10;
  if (ageInDays > 180) score += 20;
  if (ageInDays > 365) score += 30;

  return { score, details: { balance, txCount, ageInDays } };
}

Option C: Social Verification

Require connection to social accounts:

  • Twitter/X verification
  • GitHub account linking
  • Discord server membership
  • Email verification

GasX supports integrating with:

  • Privy for social logins
  • Dynamic for wallet + social
  • Worldcoin for proof of personhood

Layer 3: Contract Allowlisting

Never sponsor transactions to arbitrary contracts. Maintain a strict allowlist:

// Campaign contract allowlist
{
  "allowedContracts": [
    {
      "address": "0x1234...",
      "name": "MyNFT Contract",
      "allowedFunctions": ["mint", "transfer"],
      "maxGasPerCall": 200000
    },
    {
      "address": "0x5678...",
      "name": "Governance",
      "allowedFunctions": ["vote", "delegate"],
      "maxGasPerCall": 150000
    }
  ],
  "blockUnknownContracts": true
}

Function-Level Controls

Don't just allowlist contracts—allowlist specific functions:

function validateTransaction(tx: Transaction): ValidationResult {
  const contract = allowedContracts.find(
    (c) => c.address.toLowerCase() === tx.to.toLowerCase()
  );

  if (!contract) {
    return { valid: false, reason: 'Contract not in allowlist' };
  }

  // Decode function selector
  const selector = tx.data.slice(0, 10);
  const functionName = decodeFunctionSelector(selector, contract.abi);

  if (!contract.allowedFunctions.includes(functionName)) {
    return { valid: false, reason: `Function ${functionName} not allowed` };
  }

  // Check gas limit
  if (tx.gasLimit > contract.maxGasPerCall) {
    return { valid: false, reason: 'Gas limit exceeds maximum' };
  }

  return { valid: true };
}

Layer 4: Transaction Analysis

Detect suspicious patterns in real-time:

Pattern Detection Rules

const suspiciousPatterns = [
  {
    name: 'rapid-fire',
    check: (wallet: WalletHistory) =>
      wallet.recentTxCount(5 * 60 * 1000) > 5, // 5+ tx in 5 minutes
    action: 'flag',
  },
  {
    name: 'new-wallet-burst',
    check: (wallet: WalletHistory) =>
      wallet.ageInHours < 24 && wallet.sponsoredTxCount > 10,
    action: 'block',
  },
  {
    name: 'gas-maximizer',
    check: (tx: Transaction, avgGas: number) =>
      tx.gasUsed > avgGas * 3, // 3x average gas usage
    action: 'flag',
  },
  {
    name: 'funding-source-analysis',
    check: (wallet: WalletHistory) =>
      wallet.fundingSource.isKnownAttacker,
    action: 'block',
  },
];

Clustering Detection

Identify wallets controlled by the same entity:

async function detectWalletClusters(
  wallets: string[]
): Promise<ClusterResult[]> {
  const clusters: Map<string, string[]> = new Map();

  for (const wallet of wallets) {
    // Check common funding sources
    const fundingTxs = await getFundingTransactions(wallet);

    for (const tx of fundingTxs) {
      const source = tx.from;
      const existing = clusters.get(source) || [];
      existing.push(wallet);
      clusters.set(source, existing);
    }
  }

  // Flag clusters with many wallets
  return Array.from(clusters.entries())
    .filter(([, wallets]) => wallets.length > 5)
    .map(([source, wallets]) => ({
      fundingSource: source,
      wallets,
      riskLevel: wallets.length > 20 ? 'critical' : 'high',
    }));
}

Layer 5: Budget Controls

Your last line of defense: hard budget limits and alerts.

Budget Configuration

{
  "budget": {
    "total": 1000,              // Total campaign budget (USD)
    "dailyLimit": 100,          // Max spend per day
    "hourlyLimit": 20,          // Max spend per hour
    "perWalletLimit": 5,        // Max per wallet lifetime

    "alerts": {
      "warningThreshold": 0.7,  // Alert at 70% spent
      "criticalThreshold": 0.9, // Critical alert at 90%
      "channels": ["email", "slack", "webhook"]
    },

    "autoActions": {
      "onDailyLimitReached": "pause_until_reset",
      "onTotalExhausted": "pause_campaign",
      "onSuspiciousActivity": "require_manual_approval"
    }
  }
}

Real-Time Monitoring

GasX provides real-time dashboards showing:

  • Spending rate (gas/hour)
  • Unique wallets vs. transactions
  • Geographic distribution (via IP)
  • Contract function distribution
  • Anomaly alerts

Complete Security Configuration

Here's a production-ready GasX campaign configuration:

{
  "name": "Secure NFT Mint Campaign",
  "network": "arbitrum",

  "security": {
    "rateLimits": {
      "perWallet": {
        "maxTransactionsPerHour": 5,
        "maxTransactionsPerDay": 20,
        "maxGasPerDay": "0.005",
        "cooldownMinutes": 10
      }
    },

    "verification": {
      "gitcoinPassport": {
        "enabled": true,
        "minimumScore": 15,
        "refreshIntervalHours": 24
      },
      "onChainReputation": {
        "enabled": true,
        "minimumAge": 7,
        "minimumTransactions": 5
      }
    },

    "contractAllowlist": [
      {
        "address": "0x...",
        "functions": ["mint"],
        "maxGas": 200000
      }
    ],

    "patternDetection": {
      "enabled": true,
      "rules": ["rapid-fire", "new-wallet-burst", "clustering"]
    }
  },

  "budget": {
    "total": 500,
    "dailyLimit": 50,
    "alerts": {
      "email": "team@example.com",
      "slack": "https://hooks.slack.com/..."
    }
  }
}

Incident Response Playbook

When you detect an attack:

1. Immediate Actions (< 5 minutes)

  • Pause the campaign
  • Block identified attacker wallets
  • Capture transaction logs

2. Analysis (< 1 hour)

  • Identify attack vector
  • Trace wallet clusters
  • Estimate damage

3. Recovery (< 24 hours)

  • Update security rules
  • Implement additional verification
  • Resume with tighter limits

4. Post-Mortem

  • Document the attack
  • Update allowlists
  • Share learnings (anonymized)

Balancing Security vs. UX

More security = more friction. Find the right balance:

Campaign TypeSecurity LevelRationale
Public AirdropMaximumHigh abuse risk
Event/HackathonMediumControlled audience
Private BetaLowTrusted users
DAO GovernanceMedium-HighImportant but familiar users

Progressive Trust

Start strict, relax for returning users:

function getSecurityLevel(wallet: WalletData): SecurityLevel {
  if (wallet.isFirstTimeUser) return 'strict';
  if (wallet.successfulTx < 5) return 'medium';
  if (wallet.successfulTx >= 5 && wallet.flagCount === 0) return 'relaxed';
  if (wallet.flagCount > 0) return 'strict';

  return 'medium';
}

Conclusion

Gas sponsorship is powerful but requires robust security. By implementing multiple layers of defense:

  1. Rate limits stop casual abuse
  2. Identity verification stops Sybil attacks
  3. Contract allowlists prevent exploitation
  4. Pattern detection catches sophisticated attackers
  5. Budget controls limit maximum damage

GasX builds all of these protections into our platform. Create a campaign and configure security settings in minutes—no code required.

Further Reading

Ready to eliminate gas friction?

Create your first gas sponsorship campaign in under 5 minutes. No coding required.

Create Campaign