wisdom-sdk
Version:
Core business logic and data access layer for prediction markets
210 lines (208 loc) • 7.85 kB
JavaScript
import { getTopFromSortedSet, getScoresFromSortedSet, addToSortedSet, storeEntity, getEntity } from './chunk-FIJAO3BQ.js';
// src/user-stats-store.ts
var userStatsStore = {
// Helper to calculate user score consistently across the app
calculateUserScore(stats) {
const accuracyComponent = stats.totalPredictions >= 5 ? stats.accuracy : 0;
const normalizedEarnings = stats.totalEarnings / 100;
const volumeFactor = stats.totalPredictions > 0 ? Math.log10(stats.totalPredictions + 1) * 10 : 0;
const consistencyFactor = stats.totalPredictions >= 10 ? accuracyComponent * Math.min(stats.totalPredictions / 20, 1.5) : accuracyComponent;
return consistencyFactor * 0.4 + normalizedEarnings * 0.3 + Math.min(volumeFactor, 25) * 0.3;
},
// Get user stats for a specific user
async getUserStats(userId) {
try {
if (!userId) return null;
const stats = await getEntity("USER_STATS", userId);
return stats || null;
} catch (error) {
console.error(`Error getting user stats for ${userId}:`, error);
return null;
}
},
// Update user stats when a prediction is made
async updateStatsForNewPrediction(userId, prediction) {
try {
const currentStats = await this.getUserStats(userId) || {
userId,
totalPredictions: 0,
correctPredictions: 0,
accuracy: 0,
totalAmount: 0,
totalEarnings: 0,
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
};
const updatedStats = {
...currentStats,
totalPredictions: currentStats.totalPredictions + 1,
totalAmount: currentStats.totalAmount + prediction.amount,
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
};
updatedStats.accuracy = updatedStats.totalPredictions > 0 ? updatedStats.correctPredictions / updatedStats.totalPredictions * 100 : 0;
await storeEntity("USER_STATS", userId, updatedStats);
await this.updateLeaderboardEntries(updatedStats);
return updatedStats;
} catch (error) {
console.error(`Error updating stats for user ${userId}:`, error);
throw error;
}
},
// Update user stats when a prediction is resolved
async updateStatsForResolvedPrediction(userId, prediction, isCorrect, earnings) {
try {
if (!userId || userId === "anonymous") {
console.log("Skipping stats update for anonymous user");
return {
userId,
totalPredictions: 1,
correctPredictions: isCorrect ? 1 : 0,
accuracy: isCorrect ? 100 : 0,
totalAmount: prediction?.amount || 0,
totalEarnings: earnings,
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
};
}
const currentStats = await this.getUserStats(userId);
if (!currentStats) {
console.log(`Creating new stats for user ${userId}`);
const newStats = {
userId,
totalPredictions: 1,
correctPredictions: isCorrect ? 1 : 0,
accuracy: isCorrect ? 100 : 0,
totalAmount: prediction?.amount || 0,
totalEarnings: earnings,
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
};
await storeEntity("USER_STATS", userId, newStats);
await this.updateLeaderboardEntries(newStats);
return newStats;
}
const updatedStats = {
...currentStats,
correctPredictions: isCorrect ? currentStats.correctPredictions + 1 : currentStats.correctPredictions,
totalEarnings: currentStats.totalEarnings + earnings,
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
};
updatedStats.accuracy = updatedStats.totalPredictions > 0 ? updatedStats.correctPredictions / updatedStats.totalPredictions * 100 : 0;
await storeEntity("USER_STATS", userId, updatedStats);
await this.updateLeaderboardEntries(updatedStats);
return updatedStats;
} catch (error) {
console.error(`Error updating stats for resolved prediction, user ${userId}:`, error);
throw error;
}
},
// Update user's username (when available from auth provider)
async updateUsername(userId, username) {
try {
const stats = await this.getUserStats(userId);
if (!stats) return null;
const updatedStats = {
...stats,
username,
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
};
await storeEntity("USER_STATS", userId, updatedStats);
await this.updateLeaderboardEntries(updatedStats);
return updatedStats;
} catch (error) {
console.error(`Error updating username for user ${userId}:`, error);
return null;
}
},
// Update leaderboard sorted sets for efficient querying
async updateLeaderboardEntries(stats) {
try {
await addToSortedSet(
"LEADERBOARD_EARNINGS",
stats.userId,
stats.totalEarnings
);
const accuracyScore = stats.totalPredictions >= 5 ? stats.accuracy : 0;
await addToSortedSet(
"LEADERBOARD_ACCURACY",
stats.userId,
accuracyScore
);
const compositeScore = this.calculateUserScore(stats);
await addToSortedSet(
"LEADERBOARD",
stats.userId,
compositeScore
);
} catch (error) {
console.error("Error updating leaderboard entries:", error);
throw error;
}
},
// Get top leaderboard entries by earnings
async getTopEarners(limit = 10) {
try {
const userIds = await getTopFromSortedSet("LEADERBOARD_EARNINGS", limit);
const leaderboard = await this.getUserStatsForIds(userIds);
const scoresMap = await getScoresFromSortedSet("LEADERBOARD_EARNINGS", userIds);
return leaderboard.map((entry, index) => {
return {
...entry,
rank: index + 1,
score: scoresMap[entry.userId] || this.calculateUserScore(entry)
};
});
} catch (error) {
console.error("Error getting top earners:", error);
return [];
}
},
// Get top leaderboard entries by accuracy
async getTopAccurate(limit = 10) {
try {
const userIds = await getTopFromSortedSet("LEADERBOARD_ACCURACY", limit);
const leaderboard = await this.getUserStatsForIds(userIds);
const scoresMap = await getScoresFromSortedSet("LEADERBOARD_ACCURACY", userIds);
return leaderboard.map((entry, index) => {
return {
...entry,
rank: index + 1,
score: scoresMap[entry.userId] || this.calculateUserScore(entry)
};
});
} catch (error) {
console.error("Error getting top accuracy:", error);
return [];
}
},
// Get top leaderboard entries by combined score
async getTopUsers(limit = 10) {
try {
const userIds = await getTopFromSortedSet("LEADERBOARD", limit);
const leaderboard = await this.getUserStatsForIds(userIds);
const scoresMap = await getScoresFromSortedSet("LEADERBOARD", userIds);
return leaderboard.map((entry, index) => {
return {
...entry,
rank: index + 1,
score: scoresMap[entry.userId] || this.calculateUserScore(entry)
};
});
} catch (error) {
console.error("Error getting leaderboard:", error);
return [];
}
},
// Helper to get multiple user stats by IDs
async getUserStatsForIds(userIds) {
try {
if (userIds.length === 0) return [];
const statsPromises = userIds.map((id) => this.getUserStats(id));
const statsResults = await Promise.all(statsPromises);
return statsResults.filter(Boolean);
} catch (error) {
console.error("Error getting user stats for IDs:", error);
return [];
}
}
};
export { userStatsStore };
//# sourceMappingURL=chunk-ZJ2UKXRV.js.map
//# sourceMappingURL=chunk-ZJ2UKXRV.js.map