Learn how to effectively manage points and currencies in your DRIP realm. This guide covers everything from basic point operations to advanced currency management strategies.

Overview

DRIP’s point system is highly flexible, allowing you to create multiple currencies, manage complex reward structures, and build engaging gamification experiences for your community.
Points in DRIP are called “Realm Points” and can be customized with names, emojis, and branding to match your community’s theme.

Understanding Realm Points

Point Types

Primary Currency

Your main point system (e.g., XP, Coins, Tokens)
  • Usually the most visible to members
  • Used for major rewards and purchases
  • Often tied to overall member ranking

Secondary Currencies

Additional point types for specific purposes
  • Event-specific points (e.g., “Summer Points”)
  • Category-specific rewards (e.g., “Art Coins”)
  • Premium currencies (e.g., “Gems”)

Point Properties

Each Realm Point has these key properties:
const realmPoint = {
  id: "507f1f77bcf86cd799439015",
  name: "Community XP",
  emoji: "⭐",
  description: "Experience points for community participation",
  isDefault: true,
  isTransferable: true,
  minimumBalance: 0,
  maximumBalance: 1000000
};

Basic Point Operations

Awarding Points

async function awardPoints(client, memberId, points, reason = '') {
  try {
    const result = await client.request('PATCH', 
      `/realm/${client.realmId}/members/${memberId}/point-balance`,
      { tokens: points }
    );
    
    console.log(`✅ Awarded ${points} points to member ${memberId}`);
    if (reason) console.log(`Reason: ${reason}`);
    
    return result;
  } catch (error) {
    console.error(`❌ Failed to award points: ${error.message}`);
    throw error;
  }
}

// Examples
await awardPoints(client, 'member123', 100, 'Completed daily quest');
await awardPoints(client, 'member456', 50, 'Helpful community contribution');

Deducting Points

// Deduct points (negative amount)
await awardPoints(client, 'member123', -25, 'Minor rule violation penalty');

// Or use a dedicated deduction function
async function deductPoints(client, memberId, points, reason = '') {
  return await awardPoints(client, memberId, -Math.abs(points), reason);
}

Checking Balances

async function getPointBalance(client, memberId) {
  const members = await client.searchMembers('drip-id', memberId);
  
  if (members.data && members.data.length > 0) {
    const member = members.data[0];
    return member.pointBalances || [];
  }
  
  throw new Error('Member not found');
}

// Usage
const balances = await getPointBalance(client, 'member123');
balances.forEach(balance => {
  console.log(`${balance.realmPoint.name}: ${balance.balance} ${balance.realmPoint.emoji}`);
});

Advanced Point Management

Multiple Currency Systems

Manage different types of points for different purposes:
class MultiCurrencyManager {
  constructor(client) {
    this.client = client;
    this.currencies = new Map();
  }

  async loadCurrencies() {
    // Load available realm points
    const realmPoints = await this.client.getRealmPoints();
    
    for (const point of realmPoints.data) {
      this.currencies.set(point.name.toLowerCase(), {
        id: point.id,
        name: point.name,
        emoji: point.emoji,
        isDefault: point.isDefault
      });
    }
  }

  async awardCurrency(memberId, currencyName, amount, reason = '') {
    const currency = this.currencies.get(currencyName.toLowerCase());
    if (!currency) {
      throw new Error(`Currency '${currencyName}' not found`);
    }

    return await this.client.request('PATCH',
      `/realm/${this.client.realmId}/members/${memberId}/point-balance`,
      { 
        tokens: amount,
        realmPointId: currency.id
      }
    );
  }

  async transferBetweenCurrencies(memberId, fromCurrency, toCurrency, amount) {
    // Convert points between different currencies
    // This would need custom business logic for exchange rates
    
    const exchangeRate = this.getExchangeRate(fromCurrency, toCurrency);
    const convertedAmount = Math.floor(amount * exchangeRate);

    // Deduct from source currency
    await this.awardCurrency(memberId, fromCurrency, -amount, 'Currency conversion');
    
    // Add to target currency
    await this.awardCurrency(memberId, toCurrency, convertedAmount, 'Currency conversion');
    
    return { originalAmount: amount, convertedAmount, exchangeRate };
  }

  getExchangeRate(fromCurrency, toCurrency) {
    // Define your exchange rates
    const rates = {
      'xp_to_coins': 0.1,      // 10 XP = 1 Coin
      'coins_to_gems': 0.01,   // 100 Coins = 1 Gem
      'gems_to_coins': 100,    // 1 Gem = 100 Coins
    };

    const rateKey = `${fromCurrency}_to_${toCurrency}`;
    return rates[rateKey] || 1;
  }
}

Batch Point Operations

Efficiently update multiple members at once:
async function batchPointAward(client, awards) {
  // awards = [{ memberId, points, reason }, ...]
  
  const updates = awards.map(award => ({
    memberId: award.memberId,
    tokens: award.points
  }));

  try {
    const result = await client.request('PATCH',
      `/realm/${client.realmId}/members/transaction`,
      { updates }
    );

    console.log(`✅ Batch awarded points to ${awards.length} members`);
    return result;
  } catch (error) {
    console.error('❌ Batch point award failed:', error.message);
    throw error;
  }
}

// Usage examples
await batchPointAward(client, [
  { memberId: 'member1', points: 100, reason: 'Event participation' },
  { memberId: 'member2', points: 150, reason: 'Contest winner' },
  { memberId: 'member3', points: 75, reason: 'Helpful member' }
]);

Point Transfer Between Members

Enable peer-to-peer point transfers:
async function transferPoints(client, senderId, recipientId, amount, currency = null) {
  try {
    const transferData = {
      tokens: amount,
      recipientId: recipientId
    };

    if (currency) {
      transferData.realmPointId = currency;
    }

    const result = await client.request('PATCH',
      `/realm/${client.realmId}/members/${senderId}/transfer`,
      transferData
    );

    console.log(`✅ Transferred ${amount} points from ${senderId} to ${recipientId}`);
    return result;
  } catch (error) {
    console.error(`❌ Transfer failed: ${error.message}`);
    throw error;
  }
}

// Usage
await transferPoints(client, 'sender123', 'recipient456', 50);

Gamification Strategies

Point Earning Systems

Leaderboards and Rankings

Create competitive elements with point-based rankings:
class LeaderboardManager {
  constructor(client) {
    this.client = client;
  }

  async getTopMembers(limit = 10, currencyId = null) {
    const members = await this.client.searchMembers('drip-id', 'all');
    
    return members.data
      .filter(member => member.pointBalances && member.pointBalances.length > 0)
      .map(member => {
        const balance = currencyId 
          ? member.pointBalances.find(b => b.realmPoint.id === currencyId)
          : member.pointBalances[0];
          
        return {
          id: member.id,
          name: member.displayName || member.username,
          points: balance?.balance || 0,
          currency: balance?.realmPoint.name || 'Unknown'
        };
      })
      .sort((a, b) => b.points - a.points)
      .slice(0, limit)
      .map((member, index) => ({ ...member, rank: index + 1 }));
  }

  async getMemberRank(memberId, currencyId = null) {
    const leaderboard = await this.getTopMembers(1000, currencyId);
    const memberRank = leaderboard.find(entry => entry.id === memberId);
    
    return memberRank ? memberRank.rank : null;
  }

  async getSeasonalLeaderboard(season, currencyId = null) {
    // This would require tracking seasonal points separately
    // Implementation depends on your seasonal point tracking system
    
    const seasonalPoints = await this.getSeasonalPoints(season, currencyId);
    return seasonalPoints
      .sort((a, b) => b.points - a.points)
      .map((entry, index) => ({ ...entry, rank: index + 1 }));
  }
}

Point Economy Design

Balancing Your Economy

Inflation Control

Prevent point inflation by:
  • Setting reasonable earning rates
  • Creating point sinks (ways to spend points)
  • Implementing diminishing returns
  • Regular economy reviews

Engagement Optimization

Optimize for engagement by:
  • Rewarding diverse activities
  • Providing clear progression paths
  • Balancing effort vs. reward
  • Regular feedback and adjustments

Point Sink Strategies

Create ways for members to spend their points:
const pointSinks = {
  store_items: {
    'premium_role': { cost: 1000, duration: '30 days' },
    'custom_emoji': { cost: 500, permanent: true },
    'channel_access': { cost: 250, duration: '7 days' }
  },
  
  services: {
    'priority_support': { cost: 100, duration: '24 hours' },
    'custom_title': { cost: 300, permanent: true },
    'event_priority': { cost: 150, duration: 'next event' }
  },
  
  gambling: {
    'daily_lottery': { cost: 10, chance_to_win: 0.1, payout: 200 },
    'point_doubler': { cost: 50, chance_to_win: 0.3, payout: 100 }
  }
};

Analytics and Monitoring

Point Flow Tracking

Monitor how points move through your economy:
class PointAnalytics {
  constructor(client) {
    this.client = client;
    this.transactions = [];
  }

  recordTransaction(type, memberId, amount, reason) {
    this.transactions.push({
      timestamp: Date.now(),
      type, // 'award', 'deduct', 'transfer'
      memberId,
      amount,
      reason
    });
  }

  getEconomyStats(timeframe = '7d') {
    const cutoff = Date.now() - this.getTimeframeMs(timeframe);
    const recentTransactions = this.transactions.filter(t => t.timestamp > cutoff);

    return {
      totalAwarded: recentTransactions
        .filter(t => t.type === 'award')
        .reduce((sum, t) => sum + t.amount, 0),
      
      totalDeducted: recentTransactions
        .filter(t => t.type === 'deduct')
        .reduce((sum, t) => sum + Math.abs(t.amount), 0),
      
      totalTransfers: recentTransactions
        .filter(t => t.type === 'transfer')
        .reduce((sum, t) => sum + t.amount, 0),
      
      netPointsCreated: recentTransactions
        .reduce((sum, t) => {
          if (t.type === 'award') return sum + t.amount;
          if (t.type === 'deduct') return sum - Math.abs(t.amount);
          return sum; // transfers don't create/destroy points
        }, 0)
    };
  }

  getTimeframeMs(timeframe) {
    const units = {
      'd': 24 * 60 * 60 * 1000,
      'h': 60 * 60 * 1000,
      'm': 60 * 1000
    };
    
    const value = parseInt(timeframe);
    const unit = timeframe.slice(-1);
    
    return value * (units[unit] || units.d);
  }
}

Best Practices

Next Steps

Building a point economy? Join our Discord community to discuss strategies and get feedback from other developers! 💰