Skip to main content

Overview

Implement score-based access control to gate features, content, or functionality based on user ZKScore.

Basic Score Gating

Simple Score Check

import { ZKScoreClient } from '@zkscore/sdk';

const client = new ZKScoreClient({
  apiKey: process.env.ZKSCORE_API_KEY
});

async function checkAccess(userAddress, requiredScore) {
  try {
    const score = await client.getScore(userAddress);
    return score.total >= requiredScore;
  } catch (error) {
    console.error('Error checking score:', error);
    return false;
  }
}

// Usage
const hasAccess = await checkAccess(userAddress, 500);
if (hasAccess) {
  // Grant access to premium feature
  showPremiumContent();
} else {
  // Show upgrade prompt
  showUpgradePrompt();
}

Category-Based Gating

async function checkCategoryAccess(userAddress, category, requiredScore) {
  try {
    const breakdown = await client.getScoreBreakdown(userAddress);
    return breakdown[category] >= requiredScore;
  } catch (error) {
    console.error('Error checking category score:', error);
    return false;
  }
}

// Check DeFi access
const hasDeFiAccess = await checkCategoryAccess(userAddress, 'defi', 200);
if (hasDeFiAccess) {
  showDeFiFeatures();
}

Advanced Gating

Multi-Criteria Gating

class ScoreGate {
  constructor(client) {
    this.client = client;
  }
  
  async checkAccess(userAddress, criteria) {
    try {
      const score = await client.getScore(userAddress);
      const breakdown = await client.getScoreBreakdown(userAddress);
      
      // Check total score
      if (criteria.totalScore && score.total < criteria.totalScore) {
        return { access: false, reason: 'Insufficient total score' };
      }
      
      // Check category scores
      for (const [category, requiredScore] of Object.entries(criteria.categories || {})) {
        if (breakdown[category] < requiredScore) {
          return { access: false, reason: `Insufficient ${category} score` };
        }
      }
      
      // Check achievements
      if (criteria.achievements) {
        const achievements = await client.getAchievements(userAddress);
        const achievementIds = achievements.map(a => a.id);
        
        for (const requiredAchievement of criteria.achievements) {
          if (!achievementIds.includes(requiredAchievement)) {
            return { access: false, reason: 'Missing required achievement' };
          }
        }
      }
      
      return { access: true };
    } catch (error) {
      return { access: false, reason: 'Error checking access' };
    }
  }
}

// Usage
const gate = new ScoreGate(client);
const result = await gate.checkAccess(userAddress, {
  totalScore: 600,
  categories: {
    defi: 200,
    nft: 100
  },
  achievements: ['defi-pioneer', 'nft-collector']
});

if (result.access) {
  grantAccess();
} else {
  showAccessDenied(result.reason);
}

React Component Example

Score Gate Component

import React, { useState, useEffect } from 'react';
import { ZKScoreClient } from '@zkscore/sdk';

const ScoreGate = ({ 
  userAddress, 
  requiredScore, 
  children, 
  fallback,
  category,
  requiredCategoryScore 
}) => {
  const [hasAccess, setHasAccess] = useState(false);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    checkAccess();
  }, [userAddress, requiredScore, category, requiredCategoryScore]);
  
  const checkAccess = async () => {
    try {
      setLoading(true);
      setError(null);
      
      const client = new ZKScoreClient({
        apiKey: process.env.REACT_APP_ZKSCORE_API_KEY
      });
      
      if (category && requiredCategoryScore) {
        const breakdown = await client.getScoreBreakdown(userAddress);
        setHasAccess(breakdown[category] >= requiredCategoryScore);
      } else {
        const score = await client.getScore(userAddress);
        setHasAccess(score.total >= requiredScore);
      }
    } catch (err) {
      setError(err.message);
      setHasAccess(false);
    } finally {
      setLoading(false);
    }
  };
  
  if (loading) {
    return <div>Checking access...</div>;
  }
  
  if (error) {
    return <div>Error: {error}</div>;
  }
  
  if (hasAccess) {
    return children;
  }
  
  return fallback || <div>Access denied</div>;
};

// Usage
function PremiumFeature() {
  return (
    <ScoreGate 
      userAddress={userAddress}
      requiredScore={500}
      fallback={<div>You need a score of 500+ to access this feature</div>}
    >
      <div>Premium content here!</div>
    </ScoreGate>
  );
}

Vue Component Example

Score Gate Directive

// score-gate.js
import { ZKScoreClient } from '@zkscore/sdk';

const client = new ZKScoreClient({
  apiKey: process.env.VUE_APP_ZKSCORE_API_KEY
});

export const scoreGate = {
  async inserted(el, binding) {
    const { userAddress, requiredScore, category, requiredCategoryScore } = binding.value;
    
    try {
      let hasAccess = false;
      
      if (category && requiredCategoryScore) {
        const breakdown = await client.getScoreBreakdown(userAddress);
        hasAccess = breakdown[category] >= requiredCategoryScore;
      } else {
        const score = await client.getScore(userAddress);
        hasAccess = score.total >= requiredScore;
      }
      
      if (!hasAccess) {
        el.style.display = 'none';
      }
    } catch (error) {
      console.error('Score gate error:', error);
      el.style.display = 'none';
    }
  }
};

// Usage in Vue component
export default {
  directives: { scoreGate },
  template: `
    <div v-score-gate="{ userAddress, requiredScore: 500 }">
      Premium content
    </div>
  `
};

Server-Side Gating

Express.js Middleware

// middleware/scoreGate.js
import { ZKScoreClient } from '@zkscore/sdk';

const client = new ZKScoreClient({
  apiKey: process.env.ZKSCORE_API_KEY
});

export const scoreGate = (requiredScore, category, requiredCategoryScore) => {
  return async (req, res, next) => {
    try {
      const userAddress = req.user.address; // From auth middleware
      
      if (category && requiredCategoryScore) {
        const breakdown = await client.getScoreBreakdown(userAddress);
        if (breakdown[category] < requiredCategoryScore) {
          return res.status(403).json({ 
            error: 'Insufficient score', 
            required: requiredCategoryScore,
            current: breakdown[category]
          });
        }
      } else {
        const score = await client.getScore(userAddress);
        if (score.total < requiredScore) {
          return res.status(403).json({ 
            error: 'Insufficient score', 
            required: requiredScore,
            current: score.total
          });
        }
      }
      
      next();
    } catch (error) {
      res.status(500).json({ error: 'Score check failed' });
    }
  };
};

// Usage
app.get('/premium-content', 
  scoreGate(500), 
  (req, res) => {
    res.json({ content: 'Premium content here' });
  }
);

Best Practices

  1. Graceful Degradation: Always provide fallbacks for failed checks
  2. Caching: Cache score data to reduce API calls
  3. User Feedback: Clearly communicate access requirements
  4. Security: Never trust client-side score checks alone
  5. Performance: Use batch operations when checking multiple users