diff --git a/src/i18n/translations/ar.ts b/src/i18n/translations/ar.ts index e42d749..c60dd7f 100644 --- a/src/i18n/translations/ar.ts +++ b/src/i18n/translations/ar.ts @@ -140,11 +140,6 @@ const ar: Translations = { startingTracking: 'جاري بدء التتبع...', trackingStarted: 'بدأ التتبع!', trackingFailed: 'فشل بدء التتبع', - recordTrustScore: 'تسجيل نقاط الثقة', - recordTrustDesc: 'سجل نقاطك لمكافآت هذه الفترة', - recordingTrustScore: 'جاري تسجيل نقاط الثقة...', - trustScoreRecorded: 'تم تسجيل نقاط الثقة!', - trustScoreRecordFailed: 'فشل تسجيل النقاط', citizenCountTitle: 'الأكراد في العالم', citizenCountDesc: 'المواطنون المسجلون على PezkuwiChain', beCitizen: 'كن مواطناً', @@ -153,7 +148,6 @@ const ar: Translations = { claimPeriod: 'فترة المطالبة', epochClosed: 'مغلقة', epochOpen: 'مفتوحة', - scoreRecorded: 'مسجّل', claimablePez: 'PEZ قابل للمطالبة', claim: 'طالب', claimAll: 'طالب بالكل', diff --git a/src/i18n/translations/ckb.ts b/src/i18n/translations/ckb.ts index dbacb5b..dabd6b7 100644 --- a/src/i18n/translations/ckb.ts +++ b/src/i18n/translations/ckb.ts @@ -141,11 +141,6 @@ const ckb: Translations = { startingTracking: 'شوێنکەوتن دەست پێ دەکات...', trackingStarted: 'شوێنکەوتن دەست پێ کرد!', trackingFailed: 'دەست پێکردنی شوێنکەوتن سەرنەکەوت', - recordTrustScore: 'خاڵی متمانە تۆمار بکە', - recordTrustDesc: 'خاڵەکەت بۆ خەڵاتەکانی ئەم سەردەمە تۆمار بکە', - recordingTrustScore: 'خاڵ تۆمار دەکرێت...', - trustScoreRecorded: 'خاڵی متمانە تۆمار کرا!', - trustScoreRecordFailed: 'تۆمارکردنی خاڵ سەرنەکەوت', citizenCountTitle: 'ژمارەی کورد لە جیهان', citizenCountDesc: 'هاوڵاتییانی تۆمارکراو لە PezkuwiChain', beCitizen: 'ببە هاوڵاتی', @@ -154,7 +149,6 @@ const ckb: Translations = { claimPeriod: 'ماوەی داواکردن', epochClosed: 'داخراوە', epochOpen: 'کراوەیە', - scoreRecorded: 'تۆمارکرا', claimablePez: 'PEZ ی داواکراو', claim: 'داوا بکە', claimAll: 'هەموو داوا بکە', diff --git a/src/i18n/translations/en.ts b/src/i18n/translations/en.ts index 1875e71..b509a6c 100644 --- a/src/i18n/translations/en.ts +++ b/src/i18n/translations/en.ts @@ -140,11 +140,6 @@ const en: Translations = { startingTracking: 'Starting tracking...', trackingStarted: 'Tracking started!', trackingFailed: 'Failed to start tracking', - recordTrustScore: 'Record Trust Score', - recordTrustDesc: "Record your score for this epoch's rewards", - recordingTrustScore: 'Recording trust score...', - trustScoreRecorded: 'Trust score recorded!', - trustScoreRecordFailed: 'Failed to record score', citizenCountTitle: 'Kurds in the World', citizenCountDesc: 'Citizens registered on PezkuwiChain', beCitizen: 'Be Citizen', @@ -153,7 +148,6 @@ const en: Translations = { claimPeriod: 'Claim Period', epochClosed: 'Closed', epochOpen: 'Open', - scoreRecorded: 'Recorded', claimablePez: 'Claimable PEZ', claim: 'Claim', claimAll: 'Claim All', diff --git a/src/i18n/translations/fa.ts b/src/i18n/translations/fa.ts index f96ce2f..eaa43a2 100644 --- a/src/i18n/translations/fa.ts +++ b/src/i18n/translations/fa.ts @@ -140,11 +140,6 @@ const fa: Translations = { startingTracking: 'در حال شروع ردیابی...', trackingStarted: 'ردیابی شروع شد!', trackingFailed: 'شروع ردیابی ناموفق بود', - recordTrustScore: 'ثبت امتیاز اعتماد', - recordTrustDesc: 'امتیاز خود را برای پاداش‌های این دوره ثبت کنید', - recordingTrustScore: 'در حال ثبت امتیاز اعتماد...', - trustScoreRecorded: 'امتیاز اعتماد ثبت شد!', - trustScoreRecordFailed: 'ثبت امتیاز ناموفق بود', citizenCountTitle: 'کوردها در جهان', citizenCountDesc: 'شهروندان ثبت‌شده در PezkuwiChain', beCitizen: 'شهروند شوید', @@ -153,7 +148,6 @@ const fa: Translations = { claimPeriod: 'دوره مطالبه', epochClosed: 'بسته', epochOpen: 'باز', - scoreRecorded: 'ثبت شد', claimablePez: 'PEZ قابل مطالبه', claim: 'مطالبه', claimAll: 'مطالبه همه', diff --git a/src/i18n/translations/krd.ts b/src/i18n/translations/krd.ts index c8f5fa5..1c04ed4 100644 --- a/src/i18n/translations/krd.ts +++ b/src/i18n/translations/krd.ts @@ -145,11 +145,6 @@ const krd: Translations = { startingTracking: 'Şopandin tê destpêkirin...', trackingStarted: 'Şopandin dest pê kir!', trackingFailed: 'Destpêkirina şopandinê biserneket', - recordTrustScore: 'Pûana Pêbaweriyê Tomar Bike', - recordTrustDesc: 'Pûana xwe ji bo xelatên vê serdemê tomar bike', - recordingTrustScore: 'Pûan tê tomarkirin...', - trustScoreRecorded: 'Pûana pêbaweriyê hat tomarkirin!', - trustScoreRecordFailed: 'Tomarkirina pûanê biserneket', citizenCountTitle: 'Hejmara Kurd Le Cîhanê', citizenCountDesc: 'Welatiyên ku li ser PezkuwiChain qeyd bûne', beCitizen: 'Bibe Welatî', @@ -158,7 +153,6 @@ const krd: Translations = { claimPeriod: 'Dema Daxwazkirinê', epochClosed: 'Girtî', epochOpen: 'Vekirî', - scoreRecorded: 'Hat tomarkirin', claimablePez: 'PEZ yên Daxwazkir', claim: 'Daxwaz Bike', claimAll: 'Hemûyan Daxwaz Bike', diff --git a/src/i18n/translations/tr.ts b/src/i18n/translations/tr.ts index 579d111..fca3a9c 100644 --- a/src/i18n/translations/tr.ts +++ b/src/i18n/translations/tr.ts @@ -140,11 +140,6 @@ const tr: Translations = { startingTracking: 'Takip başlatılıyor...', trackingStarted: 'Takip başlatıldı!', trackingFailed: 'Takip başlatılamadı', - recordTrustScore: 'Güven Puanını Kaydet', - recordTrustDesc: 'Bu dönemin ödülleri için puanınızı kaydedin', - recordingTrustScore: 'Güven puanı kaydediliyor...', - trustScoreRecorded: 'Güven puanı kaydedildi!', - trustScoreRecordFailed: 'Puan kaydedilemedi', citizenCountTitle: 'Dünyadaki Kürt Sayısı', citizenCountDesc: "PezkuwiChain'de kayıtlı vatandaşlar", beCitizen: 'Vatandaş Ol', @@ -153,7 +148,6 @@ const tr: Translations = { claimPeriod: 'Talep Dönemi', epochClosed: 'Kapalı', epochOpen: 'Açık', - scoreRecorded: 'Kaydedildi', claimablePez: 'Talep Edilebilir PEZ', claim: 'Talep Et', claimAll: 'Tümünü Talep Et', diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 8361877..0acf689 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -142,11 +142,6 @@ export interface Translations { startingTracking: string; trackingStarted: string; trackingFailed: string; - recordTrustScore: string; - recordTrustDesc: string; - recordingTrustScore: string; - trustScoreRecorded: string; - trustScoreRecordFailed: string; citizenCountTitle: string; citizenCountDesc: string; beCitizen: string; @@ -155,7 +150,6 @@ export interface Translations { claimPeriod: string; epochClosed: string; epochOpen: string; - scoreRecorded: string; claimablePez: string; claim: string; claimAll: string; diff --git a/src/lib/scores.ts b/src/lib/scores.ts index a3855df..e3f1478 100644 --- a/src/lib/scores.ts +++ b/src/lib/scores.ts @@ -41,8 +41,6 @@ export type EpochStatus = 'Open' | 'ClaimPeriod' | 'Closed'; export interface PezRewardInfo { currentEpoch: number; epochStatus: EpochStatus; - hasRecordedThisEpoch: boolean; - userScoreCurrentEpoch: number; claimableRewards: { epoch: number; amount: string }[]; totalClaimable: string; hasPendingClaim: boolean; @@ -508,66 +506,6 @@ export async function startScoreTracking( } } -/** - * Record trust score for current epoch on People Chain. - * Calls pezRewards.recordTrustScore() - no arguments. - * Required for PEZ epoch rewards. - */ -export async function recordTrustScore( - peopleApi: ApiPromise, - keypair: KeyringPair -): Promise<{ success: boolean; error?: string }> { - try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const tx = peopleApi.tx as any; - if (!tx?.pezRewards?.recordTrustScore) { - return { success: false, error: 'pezRewards pallet not available' }; - } - - const result = await new Promise<{ success: boolean; error?: string }>((resolve) => { - tx.pezRewards - .recordTrustScore() - .signAndSend( - keypair, - { nonce: -1 }, - ({ - status, - dispatchError, - }: { - status: { - isInBlock: boolean; - isFinalized: boolean; - }; - dispatchError?: { isModule: boolean; asModule: unknown; toString: () => string }; - }) => { - if (status.isInBlock || status.isFinalized) { - if (dispatchError) { - let errorMessage = 'recordTrustScore failed'; - if (dispatchError.isModule) { - const decoded = peopleApi.registry.findMetaError( - dispatchError.asModule as Parameters[0] - ); - errorMessage = `${decoded.section}.${decoded.name}`; - } - resolve({ success: false, error: errorMessage }); - return; - } - resolve({ success: true }); - } - } - ) - .catch((error: Error) => resolve({ success: false, error: error.message })); - }); - - return result; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - }; - } -} - // ======================================== // PEZ REWARDS (pezRewards pallet on People Chain) // ======================================== @@ -616,66 +554,65 @@ export async function getPezRewards( // Default to Open } - // Check if user has recorded their score this epoch - let hasRecordedThisEpoch = false; - let userScoreCurrentEpoch = 0; - try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const userScoreResult = await (peopleApi.query as any).pezRewards.userEpochScores( - currentEpoch, - address - ); - if (userScoreResult.isSome) { - hasRecordedThisEpoch = true; - const scoreCodec = userScoreResult.unwrap() as { toString: () => string }; - userScoreCurrentEpoch = Number(scoreCodec.toString()); - } - } catch { - // User hasn't recorded - } - - // Check for claimable rewards from completed epochs + // What a claim pays, worked out the way the chain does (`pezRewards::entitlement`). There + // is no score to record beforehand: the roll is held still from an epoch's finalisation + // to the end of its claim window, so the trust score now is the one the rate was set + // against, and a seat is paid if it is held now and was taken by the time of the roll. const claimableRewards: { epoch: number; amount: string }[] = []; let totalClaimable = BigInt(0); + const who = peopleApi.createType('AccountId', address).toString(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const query = peopleApi.query as any; + const trustScore = BigInt((await query.trust.trustScores(address)).toString()); + + // Seat: the Parlementer tiki, not expired, and the date the seat was taken. + let seatTakenAt: number | null = null; + try { + const tikis = ((await query.tiki.userTikis(address)).toJSON() ?? []) as string[]; + if (tikis.includes('Parlementer')) { + const expiry = (await query.tiki.tikiExpiry(address, 'Parlementer')).toJSON() as + | number + | null; + const now = (await peopleApi.rpc.chain.getHeader()).number.toNumber(); + if (expiry === null || now <= expiry) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const members = (await query.welati.parliamentMembers()).toJSON() as any[]; + const member = members.find( + (m) => peopleApi.createType('AccountId', m.account).toString() === who + ); + if (member) seatTakenAt = Number(member.electedAt ?? member.elected_at); + } + } + } catch { + // No seat + } + for (let i = Math.max(0, currentEpoch - 3); i < currentEpoch; i++) { try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const pastStatusResult = await (peopleApi.query as any).pezRewards.epochStatus(i); - const pastStatus = pastStatusResult.toString(); + const pastStatus = (await query.pezRewards.epochStatus(i)).toString(); if (pastStatus !== 'ClaimPeriod') continue; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const claimedResult = await (peopleApi.query as any).pezRewards.claimedRewards(i, address); + const claimedResult = await query.pezRewards.claimedRewards(i, address); if (claimedResult.isSome) continue; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const userScoreResult = await (peopleApi.query as any).pezRewards.userEpochScores( - i, - address - ); - if (!userScoreResult.isSome) continue; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const epochPoolResult = await (peopleApi.query as any).pezRewards.epochRewardPools(i); + const epochPoolResult = await query.pezRewards.epochRewardPools(i); if (!epochPoolResult.isSome) continue; // eslint-disable-next-line @typescript-eslint/no-explicit-any - const epochPool = (epochPoolResult.unwrap() as any).toJSON(); - const userScore = BigInt( - (userScoreResult.unwrap() as { toString: () => string }).toString() - ); + const pool = (epochPoolResult.unwrap() as any).toJSON(); const rewardPerPoint = BigInt( - epochPool.rewardPerTrustPoint || epochPool.reward_per_trust_point || '0' + pool.rewardPerTrustPoint ?? pool.reward_per_trust_point ?? '0' ); + const seatShare = BigInt(pool.seatShare ?? pool.seat_share ?? '0'); + const finalizedAt = Number(pool.finalizedAt ?? pool.finalized_at ?? 0); - const rewardAmount = userScore * rewardPerPoint; - const rewardFormatted = formatBalancePlanck(rewardAmount.toString()); + let rewardAmount = trustScore * rewardPerPoint; + if (seatTakenAt !== null && seatTakenAt <= finalizedAt) rewardAmount += seatShare; + if (rewardAmount === BigInt(0)) continue; - if (parseFloat(rewardFormatted) > 0) { - claimableRewards.push({ epoch: i, amount: rewardFormatted }); - totalClaimable += rewardAmount; - } + claimableRewards.push({ epoch: i, amount: formatBalancePlanck(rewardAmount.toString()) }); + totalClaimable += rewardAmount; } catch (err) { console.warn(`Error checking epoch ${i} rewards:`, err); } @@ -684,8 +621,6 @@ export async function getPezRewards( return { currentEpoch, epochStatus, - hasRecordedThisEpoch, - userScoreCurrentEpoch, claimableRewards, totalClaimable: formatBalancePlanck(totalClaimable.toString()), hasPendingClaim: claimableRewards.length > 0, diff --git a/src/sections/Rewards.tsx b/src/sections/Rewards.tsx index da7f38a..feee2b7 100644 --- a/src/sections/Rewards.tsx +++ b/src/sections/Rewards.tsx @@ -22,7 +22,6 @@ import { Sparkles, GraduationCap, Play, - PenTool, Globe2, Clock, Loader2, @@ -38,7 +37,6 @@ import { getAllScores, getStakingScoreStatus, startScoreTracking, - recordTrustScore, getPezRewards, claimPezReward, formatDuration, @@ -229,29 +227,6 @@ export function RewardsSection() { } }; - const handleRecordTrustScore = async () => { - if (!peopleApi || !keypair) return; - setTrackingAnimationText(t('rewards.recordingTrustScore')); - setShowTrackingAnimation(true); - hapticImpact('medium'); - try { - const result = await recordTrustScore(peopleApi, keypair); - if (result.success) { - hapticNotification('success'); - showAlert(t('rewards.trustScoreRecorded')); - fetchUserScores(); - } else { - hapticNotification('error'); - showAlert(result.error || t('rewards.trustScoreRecordFailed')); - } - } catch (err) { - hapticNotification('error'); - showAlert(err instanceof Error ? err.message : t('rewards.trustScoreRecordFailed')); - } finally { - setShowTrackingAnimation(false); - } - }; - const handleClaimReward = async (epochIndex: number) => { if (!peopleApi || !keypair) return; setClaimingEpoch(epochIndex); @@ -1055,7 +1030,8 @@ export function RewardsSection() { - {/* Start Tracking / Record Trust Score Button */} + {/* Start Tracking Button. Once tracking, there is nothing to do each month: the + chain holds every score still while an epoch is claimed. */} {!stakingStatus?.isTracking ? (
- ) : ( -
- -

- {t('rewards.recordTrustDesc')} -

-
- )} + ) : null} {/* PEZ Epoch Rewards */}
@@ -1114,12 +1076,6 @@ export function RewardsSection() { ? t('rewards.epochClosed') : t('rewards.epochOpen')} - {pezRewards?.hasRecordedThisEpoch && ( - - - {t('rewards.scoreRecorded')} - - )}
{/* Claimable PEZ Rewards */}