mirror of
https://github.com/pezkuwichain/pezkuwi-telegram-miniapp.git
synced 2026-04-22 03:07:55 +00:00
fix: show real staking score from CachedStakingDetails instead of hardcoded 0
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pezkuwi-telegram-miniapp",
|
"name": "pezkuwi-telegram-miniapp",
|
||||||
"version": "1.0.205",
|
"version": "1.0.206",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
|
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
|
||||||
"author": "Pezkuwichain Team",
|
"author": "Pezkuwichain Team",
|
||||||
|
|||||||
+75
-42
@@ -28,7 +28,8 @@ export interface UserScores {
|
|||||||
|
|
||||||
export interface StakingScoreStatus {
|
export interface StakingScoreStatus {
|
||||||
isTracking: boolean;
|
isTracking: boolean;
|
||||||
hasCachedData: boolean; // Whether noter has submitted staking data
|
hasCachedData: boolean;
|
||||||
|
score: number; // 0-100 computed from CachedStakingDetails + duration
|
||||||
startBlock: number | null;
|
startBlock: number | null;
|
||||||
currentBlock: number;
|
currentBlock: number;
|
||||||
durationBlocks: number;
|
durationBlocks: number;
|
||||||
@@ -98,23 +99,34 @@ export async function getReferralScore(peopleApi: ApiPromise, address: string):
|
|||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check staking score tracking status
|
* Check staking score tracking status and compute actual staking score.
|
||||||
* Storage: stakingScore.stakingStartBlock(address)
|
* Queries CachedStakingDetails from People Chain and calculates score
|
||||||
|
* using the same formula as pallet_staking_score::get_staking_score().
|
||||||
|
*
|
||||||
|
* Score Formula:
|
||||||
|
* 1. Amount Score (20-50 points based on staked HEZ)
|
||||||
|
* - 0-100 HEZ: 20, 101-250: 30, 251-750: 40, 751+: 50
|
||||||
|
* 2. Duration Multiplier (time since startScoreTracking)
|
||||||
|
* - <1mo: x1.0, 1-2mo: x1.2, 3-5mo: x1.4, 6-11mo: x1.7, 12+mo: x2.0
|
||||||
|
* 3. Final = min(100, floor(amountScore * durationMultiplier))
|
||||||
*/
|
*/
|
||||||
export async function getStakingScoreStatus(
|
export async function getStakingScoreStatus(
|
||||||
peopleApi: ApiPromise,
|
peopleApi: ApiPromise,
|
||||||
address: string
|
address: string
|
||||||
): Promise<StakingScoreStatus> {
|
): Promise<StakingScoreStatus> {
|
||||||
|
const empty: StakingScoreStatus = {
|
||||||
|
isTracking: false,
|
||||||
|
hasCachedData: false,
|
||||||
|
score: 0,
|
||||||
|
startBlock: null,
|
||||||
|
currentBlock: 0,
|
||||||
|
durationBlocks: 0,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
if (!(peopleApi?.query as any)?.stakingScore?.stakingStartBlock) {
|
if (!(peopleApi?.query as any)?.stakingScore?.stakingStartBlock) {
|
||||||
return {
|
return empty;
|
||||||
isTracking: false,
|
|
||||||
hasCachedData: false,
|
|
||||||
startBlock: null,
|
|
||||||
currentBlock: 0,
|
|
||||||
durationBlocks: 0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -122,20 +134,15 @@ export async function getStakingScoreStatus(
|
|||||||
const currentBlock = Number((await peopleApi.query.system.number()).toString());
|
const currentBlock = Number((await peopleApi.query.system.number()).toString());
|
||||||
|
|
||||||
if (startBlockResult.isEmpty || startBlockResult.isNone) {
|
if (startBlockResult.isEmpty || startBlockResult.isNone) {
|
||||||
return {
|
return { ...empty, currentBlock };
|
||||||
isTracking: false,
|
|
||||||
hasCachedData: false,
|
|
||||||
startBlock: null,
|
|
||||||
currentBlock,
|
|
||||||
durationBlocks: 0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const startBlock = Number(startBlockResult.toString());
|
const startBlock = Number(startBlockResult.toString());
|
||||||
const durationBlocks = currentBlock - startBlock;
|
const durationBlocks = currentBlock - startBlock;
|
||||||
|
|
||||||
// Check if noter has submitted cached staking data
|
// Query CachedStakingDetails for both sources
|
||||||
let hasCachedData = false;
|
let hasCachedData = false;
|
||||||
|
let totalStakeWei = BigInt(0);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
if ((peopleApi.query as any).stakingScore?.cachedStakingDetails) {
|
if ((peopleApi.query as any).stakingScore?.cachedStakingDetails) {
|
||||||
try {
|
try {
|
||||||
@@ -143,38 +150,62 @@ export async function getStakingScoreStatus(
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(peopleApi.query as any).stakingScore
|
(peopleApi.query as any).stakingScore
|
||||||
.cachedStakingDetails(address, 'RelayChain')
|
.cachedStakingDetails(address, 'RelayChain')
|
||||||
.catch(() => ({ isSome: false, isEmpty: true })),
|
.catch(() => null),
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(peopleApi.query as any).stakingScore
|
(peopleApi.query as any).stakingScore
|
||||||
.cachedStakingDetails(address, 'AssetHub')
|
.cachedStakingDetails(address, 'AssetHub')
|
||||||
.catch(() => ({ isSome: false, isEmpty: true })),
|
.catch(() => null),
|
||||||
]);
|
]);
|
||||||
hasCachedData =
|
if (relayResult && !relayResult.isEmpty && relayResult.isSome) {
|
||||||
relayResult.isSome ||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
!relayResult.isEmpty ||
|
const json = (relayResult.unwrap() as any).toJSON();
|
||||||
assetHubResult.isSome ||
|
totalStakeWei += BigInt(json.stakedAmount ?? json.staked_amount ?? '0');
|
||||||
!assetHubResult.isEmpty;
|
hasCachedData = true;
|
||||||
|
}
|
||||||
|
if (assetHubResult && !assetHubResult.isEmpty && assetHubResult.isSome) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const json = (assetHubResult.unwrap() as any).toJSON();
|
||||||
|
totalStakeWei += BigInt(json.stakedAmount ?? json.staked_amount ?? '0');
|
||||||
|
hasCachedData = true;
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
hasCachedData = false;
|
// keep defaults
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate staking score from cached data
|
||||||
|
let score = 0;
|
||||||
|
if (hasCachedData && totalStakeWei > BigInt(0)) {
|
||||||
|
const stakedHEZ = Number(totalStakeWei / BigInt(10 ** 12));
|
||||||
|
|
||||||
|
// Amount tier
|
||||||
|
let amountScore = 20;
|
||||||
|
if (stakedHEZ > 750) amountScore = 50;
|
||||||
|
else if (stakedHEZ > 250) amountScore = 40;
|
||||||
|
else if (stakedHEZ > 100) amountScore = 30;
|
||||||
|
|
||||||
|
// Duration multiplier
|
||||||
|
const MONTH = 30 * 24 * 60 * 10; // ~432,000 blocks
|
||||||
|
let mult = 1.0;
|
||||||
|
if (durationBlocks >= 12 * MONTH) mult = 2.0;
|
||||||
|
else if (durationBlocks >= 6 * MONTH) mult = 1.7;
|
||||||
|
else if (durationBlocks >= 3 * MONTH) mult = 1.4;
|
||||||
|
else if (durationBlocks >= MONTH) mult = 1.2;
|
||||||
|
|
||||||
|
score = Math.min(100, Math.floor(amountScore * mult));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isTracking: true,
|
isTracking: true,
|
||||||
hasCachedData,
|
hasCachedData,
|
||||||
|
score,
|
||||||
startBlock,
|
startBlock,
|
||||||
currentBlock,
|
currentBlock,
|
||||||
durationBlocks,
|
durationBlocks,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching staking score status:', error);
|
console.error('Error fetching staking score status:', error);
|
||||||
return {
|
return empty;
|
||||||
isTracking: false,
|
|
||||||
hasCachedData: false,
|
|
||||||
startBlock: null,
|
|
||||||
currentBlock: 0,
|
|
||||||
durationBlocks: 0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,21 +394,23 @@ export async function getAllScores(
|
|||||||
if (!peopleApi || !address) return empty;
|
if (!peopleApi || !address) return empty;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [trustScore, referralScore, tikiScore, perwerdeScore, isCitizen] = await Promise.all([
|
const [trustScore, referralScore, tikiScore, perwerdeScore, isCitizen, stakingStatus] =
|
||||||
getTrustScore(peopleApi, address),
|
await Promise.all([
|
||||||
getReferralScore(peopleApi, address),
|
getTrustScore(peopleApi, address),
|
||||||
getTikiScore(peopleApi, address),
|
getReferralScore(peopleApi, address),
|
||||||
getPerwerdeScore(peopleApi, address),
|
getTikiScore(peopleApi, address),
|
||||||
checkCitizenshipStatus(peopleApi, address),
|
getPerwerdeScore(peopleApi, address),
|
||||||
]);
|
checkCitizenshipStatus(peopleApi, address),
|
||||||
|
getStakingScoreStatus(peopleApi, address),
|
||||||
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
trustScore,
|
trustScore,
|
||||||
referralScore,
|
referralScore,
|
||||||
stakingScore: 0, // Trust pallet already includes staking in composite
|
stakingScore: stakingStatus.score,
|
||||||
tikiScore,
|
tikiScore,
|
||||||
perwerdeScore,
|
perwerdeScore,
|
||||||
totalScore: trustScore, // Trust score = composite score (on-chain calculated)
|
totalScore: trustScore,
|
||||||
isCitizen,
|
isCitizen,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
Target,
|
Target,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
GraduationCap,
|
GraduationCap,
|
||||||
Clock,
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { cn, formatAddress } from '@/lib/utils';
|
import { cn, formatAddress } from '@/lib/utils';
|
||||||
import { useTelegram } from '@/hooks/useTelegram';
|
import { useTelegram } from '@/hooks/useTelegram';
|
||||||
@@ -608,25 +607,14 @@ export function RewardsSection() {
|
|||||||
<span className="text-xs text-muted-foreground">{t('rewards.staking')}</span>
|
<span className="text-xs text-muted-foreground">{t('rewards.staking')}</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-2xl font-bold text-blue-400">
|
<p className="text-2xl font-bold text-blue-400">
|
||||||
{stakingStatus?.isTracking ? (
|
{userScores?.stakingScore ?? 0}
|
||||||
stakingStatus.hasCachedData ? (
|
|
||||||
<span className="flex items-center gap-1">
|
|
||||||
<Clock className="w-4 h-4" />
|
|
||||||
{formatDuration(stakingStatus.durationBlocks)}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span className="text-sm text-yellow-400">
|
|
||||||
{t('rewards.stakingWaitingData')}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<span className="text-sm text-muted-foreground">
|
|
||||||
{t('rewards.stakingNotStarted')}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
{t('rewards.stakingCountedInTrust')}
|
{stakingStatus?.isTracking
|
||||||
|
? stakingStatus.hasCachedData
|
||||||
|
? formatDuration(stakingStatus.durationBlocks)
|
||||||
|
: t('rewards.stakingWaitingData')
|
||||||
|
: t('rewards.stakingNotStarted')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.205",
|
"version": "1.0.206",
|
||||||
"buildTime": "2026-02-16T22:56:49.789Z",
|
"buildTime": "2026-02-16T23:13:53.931Z",
|
||||||
"buildNumber": 1771282609790
|
"buildNumber": 1771283633932
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user