mirror of
https://github.com/pezkuwichain/pezkuwi-telegram-miniapp.git
synced 2026-06-14 23:51:09 +00:00
fix: tiki score, staking lookup, LP balance, teleport, DOT swap
- Fix tiki: use userTikis storage instead of userRoles - Add tiki name to score mapping (welati=10, serok=50, etc) - Improve staking ledger lookup with debug logging - Fix LP balance fetching using poolId directly - Change teleport placeholder from 0.5 to empty - Add DOT token to swap list with 10 decimals
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pezkuwi-telegram-miniapp",
|
||||
"version": "1.0.126",
|
||||
"version": "1.0.127",
|
||||
"type": "module",
|
||||
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
|
||||
"author": "Pezkuwichain Team",
|
||||
|
||||
@@ -45,7 +45,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
const { hapticImpact, showAlert } = useTelegram();
|
||||
|
||||
const [targetChain, setTargetChain] = useState<TargetChain>('asset-hub');
|
||||
const [amount, setAmount] = useState('0.5');
|
||||
const [amount, setAmount] = useState('');
|
||||
const [isTransferring, setIsTransferring] = useState(false);
|
||||
const [txStatus, setTxStatus] = useState<'idle' | 'signing' | 'pending' | 'success' | 'error'>(
|
||||
'idle'
|
||||
@@ -244,7 +244,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
|
||||
// Reset after success
|
||||
setTimeout(() => {
|
||||
setAmount('0.5');
|
||||
setAmount('');
|
||||
setTxStatus('idle');
|
||||
onClose();
|
||||
}, 2000);
|
||||
@@ -405,7 +405,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
step="0.0001"
|
||||
value={amount}
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
placeholder="0.5"
|
||||
placeholder="Mîqdar"
|
||||
className="w-full px-4 py-3 bg-muted rounded-xl text-lg font-mono"
|
||||
disabled={isTransferring}
|
||||
/>
|
||||
|
||||
@@ -67,7 +67,8 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const poolData = value.toJSON() as any;
|
||||
|
||||
const lpTokenId = poolData.stakedAssetId?.interior?.x2?.[1]?.generalIndex ?? poolId;
|
||||
// LP token ID in poolAssets pallet matches the pool ID (0, 1, 2)
|
||||
const lpTokenId = poolId;
|
||||
|
||||
let userStaked = '0';
|
||||
let pendingRewards = '0';
|
||||
@@ -84,16 +85,23 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
const stakeData = stakeInfo.unwrap().toJSON();
|
||||
userStaked = stakeData.amount || '0';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching stake info:', err);
|
||||
}
|
||||
|
||||
// Fetch LP balance
|
||||
// Fetch LP balance from poolAssets pallet
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const lpBal = await (assetHubApi.query.poolAssets as any).account(lpTokenId, address);
|
||||
if (lpBal && lpBal.isSome) {
|
||||
const lpData = lpBal.unwrap().toJSON();
|
||||
lpBalance = lpData.balance || '0';
|
||||
if (lpBal) {
|
||||
// Handle both Option<AccountData> and direct AccountData
|
||||
const lpData = lpBal.isSome ? lpBal.unwrap().toJSON() : lpBal.toJSON();
|
||||
if (lpData && lpData.balance) {
|
||||
lpBalance = lpData.balance.toString();
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
} catch (err) {
|
||||
console.error('Error fetching LP balance for pool', poolId, ':', err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ const TOKENS = [
|
||||
{ symbol: 'HEZ', name: 'Hezkurd', assetId: -1, decimals: 12, icon: '/tokens/HEZ.png' },
|
||||
{ symbol: 'PEZ', name: 'Pezkuwi', assetId: 1, decimals: 12, icon: '/tokens/PEZ.png' },
|
||||
{ symbol: 'USDT', name: 'Tether', assetId: 1000, decimals: 6, icon: '/tokens/USDT.png' },
|
||||
{ symbol: 'DOT', name: 'Polkadot', assetId: 1001, decimals: 10, icon: '/tokens/DOT.png' },
|
||||
];
|
||||
|
||||
// Native token ID for relay chain HEZ
|
||||
@@ -51,6 +52,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
HEZ: '0',
|
||||
PEZ: '0',
|
||||
USDT: '0',
|
||||
DOT: '0',
|
||||
});
|
||||
|
||||
// Fetch balances from Asset Hub (where swaps happen)
|
||||
@@ -84,11 +86,19 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
? (parseInt(usdtResult.unwrap().balance.toString()) / 1e6).toFixed(2)
|
||||
: '0.00';
|
||||
|
||||
// DOT balance (Asset 1001, 10 decimals)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const dotResult = await (assetHubApi.query.assets as any).account(1001, keypair.address);
|
||||
const dotBalance = dotResult.isSome
|
||||
? (parseInt(dotResult.unwrap().balance.toString()) / 1e10).toFixed(4)
|
||||
: '0.0000';
|
||||
|
||||
// Update all balances at once
|
||||
setBalances({
|
||||
HEZ: hezBalance,
|
||||
PEZ: pezBalance,
|
||||
USDT: usdtBalance,
|
||||
DOT: dotBalance,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch balances:', err);
|
||||
@@ -127,8 +137,14 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
|
||||
if (poolInfo && !poolInfo.isEmpty) {
|
||||
// Get quote from runtime API
|
||||
const decimals1 = asset1 === 1000 ? 6 : 12;
|
||||
const decimals2 = asset2 === 1000 ? 6 : 12;
|
||||
// USDT has 6 decimals, DOT has 10 decimals, others have 12
|
||||
const getDecimals = (id: number) => {
|
||||
if (id === 1000) return 6; // USDT
|
||||
if (id === 1001) return 10; // DOT
|
||||
return 12; // HEZ, PEZ
|
||||
};
|
||||
const decimals1 = getDecimals(asset1);
|
||||
const decimals2 = getDecimals(asset2);
|
||||
const oneUnit = BigInt(Math.pow(10, decimals1));
|
||||
|
||||
const quote = await (
|
||||
|
||||
+101
-22
@@ -64,6 +64,7 @@ function saveStakingTrackingData(data: StakingTrackingData): void {
|
||||
|
||||
/**
|
||||
* Fetch staking details directly from Relay Chain
|
||||
* In newer Substrate versions, ledger is keyed by stash address
|
||||
*/
|
||||
export async function fetchRelayStakingDetails(
|
||||
relayApi: ApiPromise,
|
||||
@@ -71,36 +72,64 @@ export async function fetchRelayStakingDetails(
|
||||
): Promise<{ stakedAmount: bigint; nominationsCount: number } | null> {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if (!(relayApi?.query as any)?.staking) return null;
|
||||
if (!(relayApi?.query as any)?.staking) {
|
||||
console.log('[Staking] staking pallet not found');
|
||||
return null;
|
||||
}
|
||||
|
||||
let stashAddress = address;
|
||||
let active = 0n;
|
||||
|
||||
// In newer Substrate, ledger is keyed by stash address directly
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let ledger = await (relayApi.query.staking as any).ledger?.(address);
|
||||
let stashAddress = address;
|
||||
|
||||
// If no ledger, check if this is a stash account
|
||||
if (!ledger || ledger.isEmpty || ledger.isNone) {
|
||||
// Check if ledger exists and has data
|
||||
if (ledger && !ledger.isEmpty && !ledger.isNone) {
|
||||
// Ledger might be wrapped in Option
|
||||
const unwrapped = ledger.isSome ? ledger.unwrap() : ledger;
|
||||
const ledgerJson = unwrapped.toJSON() as { active?: string | number; stash?: string };
|
||||
console.log('[Staking] Ledger found for', address, ':', ledgerJson);
|
||||
active = BigInt(ledgerJson?.active || 0);
|
||||
if (ledgerJson?.stash) {
|
||||
stashAddress = ledgerJson.stash;
|
||||
}
|
||||
} else {
|
||||
// Fallback: check if this is a stash account with a controller
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const bonded = await (relayApi.query.staking as any).bonded?.(address);
|
||||
if (bonded && !bonded.isEmpty && !bonded.isNone) {
|
||||
const controller = bonded.toString();
|
||||
console.log('[Staking] Address', address, 'is stash, controller:', controller);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
ledger = await (relayApi.query.staking as any).ledger?.(controller);
|
||||
stashAddress = address;
|
||||
if (ledger && !ledger.isEmpty && !ledger.isNone) {
|
||||
const unwrapped = ledger.isSome ? ledger.unwrap() : ledger;
|
||||
const ledgerJson = unwrapped.toJSON() as { active?: string | number };
|
||||
console.log('[Staking] Ledger from controller:', ledgerJson);
|
||||
active = BigInt(ledgerJson?.active || 0);
|
||||
}
|
||||
} else {
|
||||
console.log('[Staking] No ledger or bonded found for', address);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ledger || ledger.isEmpty || ledger.isNone) {
|
||||
if (active === 0n) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ledgerJson = ledger.toJSON() as { active?: string | number };
|
||||
const active = BigInt(ledgerJson?.active || 0);
|
||||
|
||||
// Get nominations
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const nominations = await (relayApi.query.staking as any).nominators?.(stashAddress);
|
||||
const nominationsJson = nominations?.toJSON() as { targets?: unknown[] } | null;
|
||||
const nominationsCount = nominationsJson?.targets?.length || 0;
|
||||
|
||||
console.log(
|
||||
'[Staking] Final result - active:',
|
||||
active.toString(),
|
||||
'nominations:',
|
||||
nominationsCount
|
||||
);
|
||||
return {
|
||||
stakedAmount: active,
|
||||
nominationsCount,
|
||||
@@ -224,25 +253,68 @@ const TIKI_ROLE_SCORES: Record<number, number> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch user's tiki roles from People Chain
|
||||
* Tiki role name to score mapping
|
||||
* Welati (citizen) is the basic role with score 10
|
||||
*/
|
||||
const TIKI_NAME_SCORES: Record<string, number> = {
|
||||
welati: 10,
|
||||
parlementer: 30,
|
||||
serokimeclise: 40,
|
||||
serok: 50,
|
||||
wezir: 40,
|
||||
endamediwane: 30,
|
||||
dadger: 35,
|
||||
dozger: 35,
|
||||
mamoste: 25,
|
||||
perwerdekar: 25,
|
||||
bazargan: 20,
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch user's tikis from People Chain
|
||||
* Storage: tiki.userTikis(address) -> Vec<TikiRole>
|
||||
*/
|
||||
export async function fetchUserTikis(peopleApi: ApiPromise, address: string): Promise<TikiInfo[]> {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if (!(peopleApi?.query as any)?.tiki) return [];
|
||||
if (!(peopleApi?.query as any)?.tiki) {
|
||||
console.log('[Tiki] tiki pallet not found');
|
||||
return [];
|
||||
}
|
||||
|
||||
// Try userTikis first (actual storage name)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const result = await (peopleApi.query.tiki as any).userRoles?.(address);
|
||||
let result = await (peopleApi.query.tiki as any).userTikis?.(address);
|
||||
|
||||
if (!result || result.isEmpty) return [];
|
||||
// Fallback to userRoles if userTikis doesn't exist
|
||||
if (!result && (peopleApi.query.tiki as any).userRoles) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
result = await (peopleApi.query.tiki as any).userRoles?.(address);
|
||||
}
|
||||
|
||||
if (!result || result.isEmpty) {
|
||||
console.log('[Tiki] No tikis found for', address);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Result is Vec<TikiRole> which are enum variants as strings
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const roles = result.toJSON() as any[];
|
||||
return roles.map((role) => ({
|
||||
roleId: role.roleId || role.role_id || 0,
|
||||
level: role.level || 0,
|
||||
name: role.name || 'Unknown',
|
||||
}));
|
||||
const tikis = result.toJSON() as any[];
|
||||
console.log('[Tiki] Raw tikis for', address, ':', tikis);
|
||||
|
||||
return tikis.map((tiki, index) => {
|
||||
// Tiki can be a string (enum variant name) or object
|
||||
const name = typeof tiki === 'string' ? tiki : tiki.name || tiki.role || 'Unknown';
|
||||
const nameLower = name.toLowerCase();
|
||||
const score = TIKI_NAME_SCORES[nameLower] || 10; // Default to 10 if unknown
|
||||
|
||||
return {
|
||||
roleId: index + 1,
|
||||
level: 1,
|
||||
name: name,
|
||||
score: score,
|
||||
};
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch tiki roles:', err);
|
||||
return [];
|
||||
@@ -250,7 +322,8 @@ export async function fetchUserTikis(peopleApi: ApiPromise, address: string): Pr
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate tiki score from user's roles
|
||||
* Calculate tiki score from user's tikis
|
||||
* Uses the score property set during fetch, or looks up by name
|
||||
*/
|
||||
export function calculateTikiScore(tikis: TikiInfo[]): number {
|
||||
if (!tikis.length) return 0;
|
||||
@@ -258,10 +331,16 @@ export function calculateTikiScore(tikis: TikiInfo[]): number {
|
||||
// Get highest role score
|
||||
let maxScore = 0;
|
||||
for (const tiki of tikis) {
|
||||
const roleScore = TIKI_ROLE_SCORES[tiki.roleId] || 0;
|
||||
maxScore = Math.max(maxScore, roleScore);
|
||||
// Use score from tiki if available, otherwise lookup by roleId or name
|
||||
const tikiScore =
|
||||
(tiki as TikiInfo & { score?: number }).score ||
|
||||
TIKI_ROLE_SCORES[tiki.roleId] ||
|
||||
TIKI_NAME_SCORES[tiki.name.toLowerCase()] ||
|
||||
10; // Default welati score
|
||||
maxScore = Math.max(maxScore, tikiScore);
|
||||
}
|
||||
|
||||
console.log('[Tiki] Calculated score:', maxScore, 'from tikis:', tikis);
|
||||
return Math.min(maxScore, 50); // Capped at 50
|
||||
}
|
||||
|
||||
|
||||
@@ -101,12 +101,16 @@ export function RewardsSection() {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[Scores] Fetching scores for', address);
|
||||
console.log('[Scores] API connected:', !!api, 'People API:', !!peopleApi);
|
||||
|
||||
setScoresLoading(true);
|
||||
try {
|
||||
const [scores, staking] = await Promise.all([
|
||||
getAllScoresWithFallback(peopleApi, api, address),
|
||||
api ? getFrontendStakingScore(api, address) : Promise.resolve(null),
|
||||
]);
|
||||
console.log('[Scores] Results:', { scores, staking });
|
||||
setUserScores(scores);
|
||||
setStakingDetails(staking);
|
||||
} catch (err) {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0.126",
|
||||
"buildTime": "2026-02-06T23:07:37.278Z",
|
||||
"buildNumber": 1770419257279
|
||||
"version": "1.0.127",
|
||||
"buildTime": "2026-02-06T23:20:04.537Z",
|
||||
"buildNumber": 1770420004537
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user