Overview
This example demonstrates how to integrate IOTA Repstation into a gaming platform to build comprehensive player reputation systems. Gaming is an ideal use case because player behavior significantly impacts the experience for others, and reputation can solve many common gaming problems.Use Cases for Gaming Reputation
Player Behavior & Matchmaking
- Skill-based matching: Combine skill level with reputation for better matches
- Toxicity prevention: Track player behavior to reduce toxic interactions
- Team reliability: Rate players on teamwork, communication, and commitment
Gaming Services & Trading
- Coaching services: Rate coaches on teaching ability and professionalism
- Boosting services: Track reliability of players offering account boosting
- Item trading: Build trust for in-game item exchanges
- Tournament organization: Verify player reliability for competitive events
Platform Integration
1. Player Reputation Engine
Copy
// gaming-reputation.ts
import { RepstationClient } from '@repstation/sdk';
interface PlayerProfile {
walletAddress: string;
gamertag: string;
overallReputation: number;
skillLevel: number;
categories: {
teamwork: number;
communication: number;
reliability: number;
sportsmanship: number;
skill: number;
};
badges: string[];
riskLevel: 'low' | 'medium' | 'high';
matchesPlayed: number;
ratingsReceived: number;
}
interface Match {
id: string;
gameType: string;
players: string[];
duration: number;
result: 'completed' | 'abandoned' | 'forfeited';
reputationDealId?: string;
startTime: Date;
endTime?: Date;
}
class GamingReputationEngine {
private reputation: RepstationClient;
constructor() {
this.reputation = new RepstationClient({
network: 'mainnet',
packageId: process.env.REPSTATION_PACKAGE_ID!
});
}
async createMatch(
gameType: string,
players: string[],
organizer: string
): Promise<Match> {
// Create reputation deal for this match
const dealResult = await this.reputation.openDealAdmin({
signer: organizer,
admin_cap_id: process.env.GAMING_ADMIN_CAP!,
party_b: players[1], // In team games, might need multiple deals
subject_ref: {
type: 'GAMING_MATCH',
id: `${gameType}-${Date.now()}`
},
amount: '0' // No monetary value, just for reputation tracking
});
const match: Match = {
id: generateMatchId(),
gameType,
players,
duration: 0,
result: 'completed',
reputationDealId: dealResult.deal_id,
startTime: new Date()
};
return await this.saveMatch(match);
}
async completeMatch(
matchId: string,
result: 'completed' | 'abandoned' | 'forfeited',
duration: number
): Promise<void> {
const match = await this.getMatch(matchId);
// Update match record
match.result = result;
match.duration = duration;
match.endTime = new Date();
await this.updateMatch(match);
// Close reputation deal to enable rating
if (match.reputationDealId) {
await this.reputation.closeDeal({
signer: process.env.GAMING_ADMIN_WALLET!,
deal_id: match.reputationDealId
});
}
// Auto-rate based on match behavior
await this.autoRateMatchBehavior(match);
// Send rating requests to players
await this.sendPlayerRatingRequests(match);
}
async calculatePlayerProfile(walletAddress: string): Promise<PlayerProfile> {
// Get reputation from blockchain
const reputationResult = await this.reputation.getReputationProfile({
walletAddress
});
if (!reputationResult.success || !reputationResult.profile) {
return this.getDefaultPlayerProfile(walletAddress);
}
const profile = reputationResult.profile;
// Get gaming-specific reputation
const gamingReputation = profile.appScoped.find(
app => app.appOwner === process.env.GAMING_ADMIN_WALLET
);
// Get detailed stats from database
const playerStats = await this.getPlayerStats(walletAddress);
const recentRatings = await this.getRecentRatings(walletAddress, 30);
return {
walletAddress,
gamertag: playerStats.gamertag,
overallReputation: profile.global.average,
skillLevel: await this.calculateSkillLevel(walletAddress),
categories: {
teamwork: this.getCategoryAverage(recentRatings, 'teamwork'),
communication: this.getCategoryAverage(recentRatings, 'communication'),
reliability: this.getCategoryAverage(recentRatings, 'reliability'),
sportsmanship: this.getCategoryAverage(recentRatings, 'sportsmanship'),
skill: this.getCategoryAverage(recentRatings, 'skill')
},
badges: this.calculatePlayerBadges(profile, playerStats),
riskLevel: this.calculateRiskLevel(profile, playerStats),
matchesPlayed: playerStats.totalMatches,
ratingsReceived: profile.global.count
};
}
private async autoRateMatchBehavior(match: Match): Promise<void> {
// Automatically rate players based on match behavior
for (const playerWallet of match.players) {
let behaviorScore = 85; // Base score
// Penalize for abandoning matches
if (match.result === 'abandoned') {
behaviorScore = 30;
} else if (match.result === 'forfeited') {
behaviorScore = 50;
}
// Bonus for completing full matches
if (match.result === 'completed' && match.duration >= this.getExpectedDuration(match.gameType)) {
behaviorScore = 95;
}
// Submit behavioral rating
if (match.reputationDealId) {
await this.reputation.rate({
signer: process.env.GAMING_ADMIN_WALLET!,
deal_id: match.reputationDealId,
score: behaviorScore,
category: 'match_completion'
});
}
}
}
private calculatePlayerBadges(profile: any, stats: any): string[] {
const badges = [];
// Experience badges
if (stats.totalMatches >= 1000) badges.push('Veteran Player');
else if (stats.totalMatches >= 500) badges.push('Experienced Player');
else if (stats.totalMatches >= 100) badges.push('Regular Player');
else if (stats.totalMatches < 10) badges.push('Newcomer');
// Reputation badges
if (profile.global.average >= 95 && profile.global.count >= 50) {
badges.push('Legendary Player');
} else if (profile.global.average >= 90 && profile.global.count >= 25) {
badges.push('Elite Player');
} else if (profile.global.average >= 85 && profile.global.count >= 10) {
badges.push('Trusted Player');
}
// Behavior badges
if (stats.completionRate >= 98) badges.push('Reliable');
if (stats.toxicityReports === 0 && stats.totalMatches >= 50) badges.push('Good Sport');
if (stats.helpfulVotes >= 20) badges.push('Helpful');
// Skill badges
if (stats.winRate >= 80) badges.push('Champion');
else if (stats.winRate >= 70) badges.push('Skilled');
return badges;
}
}
2. Matchmaking System
Copy
// matchmaking-service.ts
class SmartMatchmaking {
private reputation: GamingReputationEngine;
constructor() {
this.reputation = new GamingReputationEngine();
}
async findMatch(
playerWallet: string,
gameType: string,
preferredSkillRange: [number, number]
): Promise<MatchmakingResult> {
const playerProfile = await this.reputation.calculatePlayerProfile(playerWallet);
// Get available players in queue
const availablePlayers = await this.getQueuedPlayers(gameType);
// Filter by skill and reputation compatibility
const compatiblePlayers = await this.filterCompatiblePlayers(
playerProfile,
availablePlayers,
preferredSkillRange
);
// Score potential matches
const scoredMatches = await this.scorePlayerMatches(
playerProfile,
compatiblePlayers
);
// Return best match
return this.selectBestMatch(scoredMatches);
}
private async filterCompatiblePlayers(
player: PlayerProfile,
availablePlayers: string[],
skillRange: [number, number]
): Promise<PlayerProfile[]> {
const compatible = [];
for (const playerWallet of availablePlayers) {
const profile = await this.reputation.calculatePlayerProfile(playerWallet);
// Skill level compatibility
if (profile.skillLevel < skillRange[0] || profile.skillLevel > skillRange[1]) {
continue;
}
// Reputation compatibility - avoid toxic players
if (profile.riskLevel === 'high' && player.riskLevel === 'low') {
continue; // Don't match high-risk with low-risk players
}
// Minimum reputation threshold
if (profile.overallReputation < 60) {
continue;\n }\n \n compatible.push(profile);\n }\n \n return compatible;\n }\n \n private async scorePlayerMatches(\n player: PlayerProfile,\n candidates: PlayerProfile[]\n ): Promise<ScoredMatch[]> {\n return candidates.map(candidate => {\n let score = 0;\n \n // Skill similarity (prefer close skill levels)\n const skillDiff = Math.abs(player.skillLevel - candidate.skillLevel);\n score += Math.max(100 - skillDiff * 2, 0);\n \n // Reputation compatibility\n const repDiff = Math.abs(player.overallReputation - candidate.overallReputation);\n score += Math.max(100 - repDiff, 0);\n \n // Teamwork compatibility\n if (player.categories.teamwork >= 80 && candidate.categories.teamwork >= 80) {\n score += 50; // Bonus for both being good teammates\n }\n \n // Communication compatibility\n if (player.categories.communication >= 80 && candidate.categories.communication >= 80) {\n score += 30;\n }\n \n // Avoid repeatedly matching with toxic players\n if (candidate.riskLevel === 'high') {\n score -= 100;\n }\n \n return {\n player: candidate,\n compatibilityScore: score\n };\n }).sort((a, b) => b.compatibilityScore - a.compatibilityScore);\n }\n}\n\ninterface ScoredMatch {\n player: PlayerProfile;\n compatibilityScore: number;\n}\n\ninterface MatchmakingResult {\n match?: {\n players: PlayerProfile[];\n estimatedQuality: number;\n };\n waitTime?: number;\n reason?: string;\n}\n```\n\n### 3. Gaming Services Marketplace\n\n```typescript\n// gaming-services.ts\ninterface GamingService {\n id: string;\n provider: string;\n type: 'coaching' | 'boosting' | 'item_trading' | 'tournament_org';\n title: string;\n description: string;\n price: number;\n gameTypes: string[];\n requirements?: {\n minReputation?: number;\n maxRiskLevel?: 'low' | 'medium' | 'high';\n };\n}\n\ninterface ServiceBooking {\n id: string;\n serviceId: string;\n client: string;\n provider: string;\n status: 'pending' | 'active' | 'completed' | 'disputed';\n reputationDealId?: string;\n agreedTerms: string;\n startTime?: Date;\n endTime?: Date;\n}\n\nclass GamingServicesMarketplace {\n private reputation: RepstationClient;\n \n constructor() {\n this.reputation = new RepstationClient({\n network: 'mainnet',\n packageId: process.env.REPSTATION_PACKAGE_ID!\n });\n }\n \n async createService(\n providerWallet: string,\n service: Omit<GamingService, 'id' | 'provider'>\n ): Promise<GamingService> {\n // Verify provider reputation meets service requirements\n const providerProfile = await this.getPlayerProfile(providerWallet);\n \n if (!this.meetsServiceRequirements(providerProfile, service.type)) {\n throw new Error('Insufficient reputation to offer this service type');\n }\n \n const newService: GamingService = {\n id: generateServiceId(),\n provider: providerWallet,\n ...service\n };\n \n return await this.saveService(newService);\n }\n \n async bookService(\n serviceId: string,\n clientWallet: string,\n terms: string\n ): Promise<ServiceBooking> {\n const service = await this.getService(serviceId);\n \n // Create reputation deal for the service\n const dealResult = await this.reputation.openDealAdmin({\n signer: process.env.GAMING_ADMIN_WALLET!,\n admin_cap_id: process.env.GAMING_ADMIN_CAP!,\n party_b: clientWallet,\n subject_ref: {\n type: 'GAMING_SERVICE',\n id: service.type + '-' + serviceId\n },\n amount: service.price.toString()\n });\n \n const booking: ServiceBooking = {\n id: generateBookingId(),\n serviceId,\n client: clientWallet,\n provider: service.provider,\n status: 'pending',\n reputationDealId: dealResult.deal_id,\n agreedTerms: terms\n };\n \n return await this.saveBooking(booking);\n }\n \n async startService(bookingId: string, clientSignature: string): Promise<void> {\n const booking = await this.getBooking(bookingId);\n \n // Accept the reputation deal\n await this.reputation.acceptDeal({\n signer: booking.client,\n deal_id: booking.reputationDealId!\n });\n \n // Update booking status\n booking.status = 'active';\n booking.startTime = new Date();\n await this.updateBooking(booking);\n }\n \n async completeService(\n bookingId: string,\n completionDetails: any\n ): Promise<void> {\n const booking = await this.getBooking(bookingId);\n \n // Close the reputation deal\n await this.reputation.closeDeal({\n signer: process.env.GAMING_ADMIN_WALLET!,\n deal_id: booking.reputationDealId!\n });\n \n // Update booking\n booking.status = 'completed';\n booking.endTime = new Date();\n await this.updateBooking(booking);\n \n // Send rating requests\n await this.sendServiceRatingRequests(booking);\n }\n \n private meetsServiceRequirements(profile: PlayerProfile, serviceType: string): boolean {\n const requirements = {\n 'coaching': {\n minReputation: 85,\n minSkill: 80,\n maxRiskLevel: 'low'\n },\n 'boosting': {\n minReputation: 80,\n minSkill: 90,\n maxRiskLevel: 'medium'\n },\n 'item_trading': {\n minReputation: 75,\n maxRiskLevel: 'medium'\n },\n 'tournament_org': {\n minReputation: 90,\n minReliability: 95,\n maxRiskLevel: 'low'\n }\n };\n \n const req = requirements[serviceType];\n if (!req) return true;\n \n if (req.minReputation && profile.overallReputation < req.minReputation) {\n return false;\n }\n \n if (req.minSkill && profile.skillLevel < req.minSkill) {\n return false;\n }\n \n if (req.minReliability && profile.categories.reliability < req.minReliability) {\n return false;\n }\n \n const riskLevels = ['low', 'medium', 'high'];\n if (req.maxRiskLevel && riskLevels.indexOf(profile.riskLevel) > riskLevels.indexOf(req.maxRiskLevel)) {\n return false;\n }\n \n return true;\n }\n}\n```\n\n### 4. Player Rating System\n\n```typescript\n// player-rating.ts\ninterface PlayerRating {\n matchId?: string;\n serviceId?: string;\n rater: string;\n ratee: string;\n categories: {\n teamwork?: number;\n communication?: number;\n reliability?: number;\n sportsmanship?: number;\n skill?: number;\n teaching?: number; // For coaches\n professionalism?: number; // For service providers\n };\n comment?: string;\n context: 'match' | 'coaching' | 'boosting' | 'trading' | 'tournament';\n}\n\nclass PlayerRatingSystem {\n private reputation: RepstationClient;\n \n constructor() {\n this.reputation = new RepstationClient({\n network: 'mainnet',\n packageId: process.env.REPSTATION_PACKAGE_ID!\n });\n }\n \n async ratePlayer(\n rating: PlayerRating,\n dealId: string\n ): Promise<void> {\n // Submit ratings for each category\n for (const [category, score] of Object.entries(rating.categories)) {\n if (score !== undefined) {\n await this.reputation.rate({\n signer: rating.rater,\n deal_id: dealId,\n score,\n category: `${rating.context}_${category}`\n });\n }\n }\n \n // Store detailed rating in database\n await this.saveDetailedRating(rating);\n }\n \n async getPlayerReputationSummary(walletAddress: string): Promise<ReputationSummary> {\n const profile = await this.reputation.getReputationProfile({ walletAddress });\n \n if (!profile.success || !profile.profile) {\n return this.getDefaultReputationSummary(walletAddress);\n }\n \n // Get category breakdowns\n const categories = await this.getCategoryBreakdowns(walletAddress);\n const badges = await this.getPlayerBadges(walletAddress);\n const recentTrend = await this.getReputationTrend(walletAddress, 30);\n \n return {\n overall: profile.profile.global,\n categories,\n badges,\n trend: recentTrend,\n riskAssessment: this.assessPlayerRisk(profile.profile)\n };\n }\n}\n\ninterface ReputationSummary {\n overall: { average: number; count: number };\n categories: {\n [key: string]: { average: number; count: number };\n };\n badges: string[];\n trend: 'improving' | 'stable' | 'declining';\n riskAssessment: {\n level: 'low' | 'medium' | 'high';\n factors: string[];\n };\n}\n```\n\n### 5. Gaming UI Components\n\n```tsx\n// components/PlayerCard.tsx\nimport React, { useState, useEffect } from 'react';\n\ninterface PlayerCardProps {\n walletAddress: string;\n showDetailed?: boolean;\n context: 'matchmaking' | 'service' | 'tournament';\n}\n\nexport function PlayerCard({ walletAddress, showDetailed = false, context }: PlayerCardProps) {\n const [profile, setProfile] = useState<PlayerProfile | null>(null);\n const [loading, setLoading] = useState(true);\n \n useEffect(() => {\n loadPlayerProfile();\n }, [walletAddress]);\n \n const loadPlayerProfile = async () => {\n try {\n const reputation = new GamingReputationEngine();\n const playerProfile = await reputation.calculatePlayerProfile(walletAddress);\n setProfile(playerProfile);\n } catch (error) {\n console.error('Failed to load player profile:', error);\n } finally {\n setLoading(false);\n }\n };\n \n if (loading) return <div className=\"player-card loading\">Loading...</div>;\n if (!profile) return <div className=\"player-card error\">Failed to load</div>;\n \n return (\n <div className={`player-card ${profile.riskLevel}-risk`}>\n <div className=\"player-header\">\n <div className=\"gamertag\">{profile.gamertag}</div>\n <div className=\"reputation-score\">\n <span className=\"score\">{profile.overallReputation}</span>\n <span className=\"max\">/100</span>\n </div>\n </div>\n \n <div className=\"player-badges\">\n {profile.badges.slice(0, 3).map((badge, index) => (\n <span key={index} className={`badge ${badge.toLowerCase().replace(' ', '-')}`}>\n {badge}\n </span>\n ))}\n </div>\n \n {showDetailed && (\n <div className=\"detailed-stats\">\n <div className=\"skill-level\">\n <label>Skill Level:</label>\n <div className=\"skill-bar\">\n <div className=\"fill\" style={{ width: `${profile.skillLevel}%` }} />\n <span>{profile.skillLevel}/100</span>\n </div>\n </div>\n \n <div className=\"category-breakdown\">\n {Object.entries(profile.categories).map(([category, score]) => (\n <div key={category} className=\"category\">\n <label>{category.charAt(0).toUpperCase() + category.slice(1)}:</label>\n <span className=\"score\">{score}/100</span>\n </div>\n ))}\n </div>\n \n <div className=\"match-stats\">\n <div className=\"stat\">\n <label>Matches:</label>\n <span>{profile.matchesPlayed}</span>\n </div>\n <div className=\"stat\">\n <label>Ratings:</label>\n <span>{profile.ratingsReceived}</span>\n </div>\n </div>\n </div>\n )}\n \n <TrustIndicator profile={profile} context={context} />\n </div>\n );\n}\n\nfunction TrustIndicator({ profile, context }: { profile: PlayerProfile; context: string }) {\n const getTrustMessage = () => {\n switch (context) {\n case 'matchmaking':\n if (profile.riskLevel === 'low') return '✓ Recommended teammate';\n if (profile.riskLevel === 'medium') return '⚠ Average reliability';\n return '⚠ Potential match issues';\n \n case 'service':\n if (profile.overallReputation >= 90) return '✓ Highly trusted provider';\n if (profile.overallReputation >= 75) return '✓ Reliable service';\n return '⚠ New or unproven provider';\n \n case 'tournament':\n if (profile.categories.reliability >= 90) return '✓ Tournament ready';\n return '⚠ May have commitment issues';\n \n default:\n return '';\n }\n };\n \n return (\n <div className={`trust-indicator ${profile.riskLevel}`}>\n {getTrustMessage()}\n </div>\n );\n}\n```\n\n## Key Benefits\n\n### For Gaming Platforms\n- **Better Matchmaking**: Match players based on skill AND behavior\n- **Toxicity Reduction**: Track and prevent toxic player interactions\n- **Service Quality**: Verify coaches, boosters, and service providers\n- **Tournament Organization**: Ensure reliable players for competitive events\n\n### For Players\n- **Better Matches**: Play with similarly skilled and well-behaved players\n- **Trusted Services**: Find reliable coaches and service providers\n- **Recognition**: Build reputation that follows across games\n- **Opportunities**: Access to tournaments and premium services\n\n### For Game Developers\n- **Community Health**: Improve overall player experience\n- **Retention**: Players stay longer in positive environments\n- **Monetization**: Enable trusted player-to-player services\n- **Competitive Integrity**: Better tournament and ranked play\n\nThis gaming platform example shows how IOTA Repstation can solve real trust and behavior problems in gaming communities, creating better experiences for all players.\n