From df9c96b2384d5b72812bfff973cbc565f6a9d3fb Mon Sep 17 00:00:00 2001 From: Satoshi Qazi Muhammed Date: Sun, 25 Oct 2026 19:32:06 +0400 Subject: [PATCH] rewards: claim what the chain pays, without recording a score first The People chain has no pezRewards.recordTrustScore and no userEpochScores since the payroll started holding the roll still through each claim window: a claim pays the trust score at claim time times the epoch's rate, plus the seat share for a Parlementer seat taken by the time of the roll. The app still asked for the recorded score, so the claimable list was always empty, and its Record Trust Score button signed a call that does not exist. The button, its handler and its texts are removed, and the claimable amount is worked out as the chain's entitlement does. --- src/i18n/translations/ar.ts | 6 -- src/i18n/translations/ckb.ts | 6 -- src/i18n/translations/en.ts | 6 -- src/i18n/translations/fa.ts | 6 -- src/i18n/translations/krd.ts | 6 -- src/i18n/translations/tr.ts | 6 -- src/i18n/types.ts | 6 -- src/lib/scores.ts | 151 ++++++++++------------------------- src/sections/Rewards.tsx | 50 +----------- 9 files changed, 46 insertions(+), 197 deletions(-) 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 */}