Files
pwap/shared/lib/scores.ts
T
Claude c59f5c3391 Centralize common code in shared folder
This commit reorganizes the codebase to eliminate duplication between web and mobile frontends by moving all commonly used files to the shared folder.

Changes:
- Moved lib files to shared/lib/:
  * wallet.ts, staking.ts, tiki.ts, identity.ts
  * multisig.ts, usdt.ts, scores.ts, citizenship-workflow.ts

- Moved utils to shared/utils/:
  * auth.ts, dex.ts
  * Created format.ts (extracted formatNumber from web utils)

- Created shared/theme/:
  * colors.ts (Kurdistan and App color definitions)

- Updated web configuration:
  * Added @pezkuwi/* path aliases in tsconfig.json and vite.config.ts
  * Updated all imports to use @pezkuwi/lib/*, @pezkuwi/utils/*, @pezkuwi/theme/*
  * Removed duplicate files from web/src/lib and web/src/utils

- Updated mobile configuration:
  * Added @pezkuwi/* path aliases in tsconfig.json
  * Updated theme/colors.ts to re-export from shared
  * Mobile already uses relative imports to shared (no changes needed)

Architecture Benefits:
- Single source of truth for common code
- No duplication between frontends
- Easier maintenance and consistency
- Clear separation of shared vs platform-specific code

Web-specific files kept:
- web/src/lib/supabase.ts
- web/src/lib/utils.ts (cn function for Tailwind, re-exports formatNumber from shared)

All imports updated and tested. Both web and mobile now use the centralized shared folder.
2025-11-14 22:46:39 +00:00

356 lines
8.7 KiB
TypeScript

// ========================================
// Score Systems Integration
// ========================================
// Centralized score fetching from blockchain pallets
import type { ApiPromise } from '@polkadot/api';
// ========================================
// TYPE DEFINITIONS
// ========================================
export interface UserScores {
trustScore: number;
referralScore: number;
stakingScore: number;
tikiScore: number;
totalScore: number;
}
export interface TrustScoreDetails {
totalScore: number;
stakingPoints: number;
referralPoints: number;
tikiPoints: number;
activityPoints: number;
historyLength: number;
}
// ========================================
// TRUST SCORE (pallet_trust)
// ========================================
/**
* Fetch user's trust score from blockchain
* pallet_trust::TrustScores storage
*/
export async function getTrustScore(
api: ApiPromise,
address: string
): Promise<number> {
try {
if (!api?.query?.trust) {
console.warn('Trust pallet not available');
return 0;
}
const score = await api.query.trust.trustScores(address);
if (score.isEmpty) {
return 0;
}
return Number(score.toString());
} catch (error) {
console.error('Error fetching trust score:', error);
return 0;
}
}
/**
* Fetch detailed trust score breakdown
* pallet_trust::ScoreHistory storage
*/
export async function getTrustScoreDetails(
api: ApiPromise,
address: string
): Promise<TrustScoreDetails | null> {
try {
if (!api?.query?.trust) {
return null;
}
const totalScore = await getTrustScore(api, address);
// Get score history to show detailed breakdown
const historyResult = await api.query.trust.scoreHistory(address);
if (historyResult.isEmpty) {
return {
totalScore,
stakingPoints: 0,
referralPoints: 0,
tikiPoints: 0,
activityPoints: 0,
historyLength: 0
};
}
const history = historyResult.toJSON() as any[];
// Calculate points from history
// History format: [{blockNumber, score, reason}]
let stakingPoints = 0;
let referralPoints = 0;
let tikiPoints = 0;
let activityPoints = 0;
for (const entry of history) {
const reason = entry.reason || '';
const score = entry.score || 0;
if (reason.includes('Staking')) stakingPoints += score;
else if (reason.includes('Referral')) referralPoints += score;
else if (reason.includes('Tiki') || reason.includes('Role')) tikiPoints += score;
else activityPoints += score;
}
return {
totalScore,
stakingPoints,
referralPoints,
tikiPoints,
activityPoints,
historyLength: history.length
};
} catch (error) {
console.error('Error fetching trust score details:', error);
return null;
}
}
// ========================================
// REFERRAL SCORE (pallet_trust)
// ========================================
/**
* Fetch user's referral score
* pallet_trust::ReferralScores storage
*/
export async function getReferralScore(
api: ApiPromise,
address: string
): Promise<number> {
try {
if (!api?.query?.trust?.referralScores) {
console.warn('Referral scores not available in trust pallet');
return 0;
}
const score = await api.query.trust.referralScores(address);
if (score.isEmpty) {
return 0;
}
return Number(score.toString());
} catch (error) {
console.error('Error fetching referral score:', error);
return 0;
}
}
/**
* Get referral count for user
* pallet_trust::Referrals storage
*/
export async function getReferralCount(
api: ApiPromise,
address: string
): Promise<number> {
try {
if (!api?.query?.trust?.referrals) {
return 0;
}
const referrals = await api.query.trust.referrals(address);
if (referrals.isEmpty) {
return 0;
}
const referralList = referrals.toJSON() as any[];
return Array.isArray(referralList) ? referralList.length : 0;
} catch (error) {
console.error('Error fetching referral count:', error);
return 0;
}
}
// ========================================
// STAKING SCORE (pallet_staking_score)
// ========================================
/**
* Get staking score from pallet_staking_score
* This is already implemented in lib/staking.ts
* Re-exported here for consistency
*/
export async function getStakingScoreFromPallet(
api: ApiPromise,
address: string
): Promise<number> {
try {
if (!api?.query?.stakingScore) {
console.warn('Staking score pallet not available');
return 0;
}
// Check if user has started score tracking
const scoreResult = await api.query.stakingScore.stakingStartBlock(address);
if (scoreResult.isNone) {
return 0;
}
// Get staking info from staking pallet
const ledger = await api.query.staking.ledger(address);
if (ledger.isNone) {
return 0;
}
const ledgerData = ledger.unwrap().toJSON() as any;
const stakedAmount = Number(ledgerData.total || 0) / 1e12; // Convert to HEZ
// Get duration
const startBlock = Number(scoreResult.unwrap().toString());
const currentBlock = Number((await api.query.system.number()).toString());
const durationInBlocks = currentBlock - startBlock;
// Calculate score based on amount and duration
// Amount-based score (20-50 points)
let amountScore = 20;
if (stakedAmount <= 100) amountScore = 20;
else if (stakedAmount <= 250) amountScore = 30;
else if (stakedAmount <= 750) amountScore = 40;
else amountScore = 50;
// Duration multiplier
const MONTH_IN_BLOCKS = 30 * 24 * 60 * 10; // ~30 days
let durationMultiplier = 1.0;
if (durationInBlocks >= 12 * MONTH_IN_BLOCKS) durationMultiplier = 2.0;
else if (durationInBlocks >= 6 * MONTH_IN_BLOCKS) durationMultiplier = 1.7;
else if (durationInBlocks >= 3 * MONTH_IN_BLOCKS) durationMultiplier = 1.4;
else if (durationInBlocks >= MONTH_IN_BLOCKS) durationMultiplier = 1.2;
return Math.min(100, Math.floor(amountScore * durationMultiplier));
} catch (error) {
console.error('Error fetching staking score:', error);
return 0;
}
}
// ========================================
// TIKI SCORE (from lib/tiki.ts)
// ========================================
/**
* Calculate Tiki score from user's roles
* Import from lib/tiki.ts
*/
import { fetchUserTikis, calculateTikiScore } from './tiki';
export async function getTikiScore(
api: ApiPromise,
address: string
): Promise<number> {
try {
const tikis = await fetchUserTikis(api, address);
return calculateTikiScore(tikis);
} catch (error) {
console.error('Error fetching tiki score:', error);
return 0;
}
}
// ========================================
// COMPREHENSIVE SCORE FETCHING
// ========================================
/**
* Fetch all scores for a user in one call
*/
export async function getAllScores(
api: ApiPromise,
address: string
): Promise<UserScores> {
try {
if (!api || !address) {
return {
trustScore: 0,
referralScore: 0,
stakingScore: 0,
tikiScore: 0,
totalScore: 0
};
}
// Fetch all scores in parallel
const [trustScore, referralScore, stakingScore, tikiScore] = await Promise.all([
getTrustScore(api, address),
getReferralScore(api, address),
getStakingScoreFromPallet(api, address),
getTikiScore(api, address)
]);
const totalScore = trustScore + referralScore + stakingScore + tikiScore;
return {
trustScore,
referralScore,
stakingScore,
tikiScore,
totalScore
};
} catch (error) {
console.error('Error fetching all scores:', error);
return {
trustScore: 0,
referralScore: 0,
stakingScore: 0,
tikiScore: 0,
totalScore: 0
};
}
}
// ========================================
// SCORE DISPLAY HELPERS
// ========================================
/**
* Get color class based on score
*/
export function getScoreColor(score: number): string {
if (score >= 200) return 'text-purple-500';
if (score >= 150) return 'text-pink-500';
if (score >= 100) return 'text-blue-500';
if (score >= 70) return 'text-cyan-500';
if (score >= 40) return 'text-teal-500';
if (score >= 20) return 'text-green-500';
return 'text-gray-500';
}
/**
* Get score rating label
*/
export function getScoreRating(score: number): string {
if (score >= 250) return 'Legendary';
if (score >= 200) return 'Excellent';
if (score >= 150) return 'Very Good';
if (score >= 100) return 'Good';
if (score >= 70) return 'Average';
if (score >= 40) return 'Fair';
if (score >= 20) return 'Low';
return 'Very Low';
}
/**
* Format score for display
*/
export function formatScore(score: number): string {
return score.toFixed(0);
}