feat: add USDT deposit card and fix transaction history parsing

- Add prominent 'USDT Zêde Bike' card above recent activity
- Fix MultiAddress parsing for Asset Hub transactions
- Add proper token names for wUSDT, DOT, ETH, BTC
- Use correct decimals per asset type
- Remove unused userId prop from DepositUSDTModal
This commit is contained in:
2026-02-08 05:31:45 +03:00
parent bf8a3cc06c
commit c45f95fa31
5 changed files with 135 additions and 17 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "pezkuwi-telegram-miniapp",
"version": "1.0.177",
"version": "1.0.181",
"type": "module",
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
"author": "Pezkuwichain Team",
@@ -80,7 +80,6 @@ interface Deposit {
interface Props {
isOpen: boolean;
onClose: () => void;
userId: string | null;
}
export function DepositUSDTModal({ isOpen, onClose }: Props) {
+1 -5
View File
@@ -954,11 +954,7 @@ export function TokensCard({ onSendToken }: Props) {
<FundFeesModal isOpen={showFundFeesModal} onClose={() => setShowFundFeesModal(false)} />
{/* Deposit USDT Modal */}
<DepositUSDTModal
isOpen={showDepositModal}
onClose={() => setShowDepositModal(false)}
userId={null}
/>
<DepositUSDTModal isOpen={showDepositModal} onClose={() => setShowDepositModal(false)} />
</div>
);
}
+130 -7
View File
@@ -26,6 +26,7 @@ import { SwapModal } from './SwapModal';
import { PoolsModal } from './PoolsModal';
import { LPStakingModal } from './LPStakingModal';
import { HEZStakingModal } from './HEZStakingModal';
import { DepositUSDTModal } from './DepositUSDTModal';
import { useWallet } from '@/contexts/WalletContext';
import { useTelegram } from '@/hooks/useTelegram';
import { formatAddress } from '@/lib/wallet-service';
@@ -62,6 +63,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
const [isLPStakingModalOpen, setIsLPStakingModalOpen] = useState(false);
const [isHEZStakingModalOpen, setIsHEZStakingModalOpen] = useState(false);
const [isStakingSelectorOpen, setIsStakingSelectorOpen] = useState(false);
const [isDepositUSDTModalOpen, setIsDepositUSDTModalOpen] = useState(false);
// Subscribe to PEZ balance (Asset ID: 1) - Uses Asset Hub API
useEffect(() => {
@@ -189,13 +191,35 @@ export function WalletDashboard({ onDisconnect }: Props) {
const { method, signer } = extrinsic;
const fromAddress = signer.toString();
// Parse assets.transfer and transferKeepAlive (PEZ)
// Parse assets.transfer and transferKeepAlive (PEZ/wUSDT)
if (
method.section === 'assets' &&
(method.method === 'transfer' || method.method === 'transferKeepAlive')
) {
const [assetId, dest, value] = method.args;
const toAddress = dest.toString();
// Extract address from MultiAddress format
let toAddress = '';
try {
const destStr = String(dest);
if (destStr.startsWith('5') || destStr.startsWith('1')) {
toAddress = destStr;
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const destObj = dest as any;
if (destObj.isId) {
toAddress = destObj.asId.toString();
} else if (destObj.Id) {
toAddress = destObj.Id.toString();
} else if (typeof destObj.toHuman === 'function') {
const human = destObj.toHuman();
toAddress = typeof human === 'string' ? human : human?.Id || destStr;
} else {
toAddress = destStr;
}
}
} catch {
toAddress = String(dest);
}
const isFromUs = fromAddress === address;
const isToUs = toAddress === address;
@@ -363,7 +387,50 @@ export function WalletDashboard({ onDisconnect }: Props) {
};
}, [api, address, hapticNotification]);
// Real-time subscription to Asset Hub for PEZ transactions
// Helper function to extract address from MultiAddress format
const extractAddress = (dest: unknown): string => {
if (!dest) return '';
// If it's already a string that looks like an address
const destStr = String(dest);
if (destStr.startsWith('5') || destStr.startsWith('1')) {
return destStr;
}
// Try to parse as MultiAddress object
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const destObj = dest as any;
// Handle { Id: '5xxx...' } format
if (destObj.isId) {
return destObj.asId.toString();
}
if (destObj.Id) {
return destObj.Id.toString();
}
// Handle direct object with id property
if (typeof destObj.toHuman === 'function') {
const human = destObj.toHuman();
if (typeof human === 'string') return human;
if (human?.Id) return human.Id;
}
// Try JSON parse for string representation
if (destStr.startsWith('{')) {
const parsed = JSON.parse(destStr);
if (parsed.Id) return parsed.Id;
if (parsed.id) return parsed.id;
}
} catch {
// Fall back to string
}
return destStr;
};
// Real-time subscription to Asset Hub for PEZ/wUSDT transactions
useEffect(() => {
if (!assetHubApi || !address) return;
@@ -390,7 +457,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
(method.method === 'transfer' || method.method === 'transferKeepAlive')
) {
const [assetId, dest, value] = method.args;
const toAddress = dest.toString();
const toAddress = extractAddress(dest);
const isFromUs = fromAddress === address;
const isToUs = toAddress === address;
@@ -457,8 +524,19 @@ export function WalletDashboard({ onDisconnect }: Props) {
fetchRecentTransactions();
};
const getDecimalsForAsset = (section: string, assetId?: string): number => {
if (section === 'balances') return 12; // HEZ
if (assetId === '1') return 12; // PEZ
if (assetId === '1000') return 6; // wUSDT
if (assetId === '1001') return 10; // DOT
if (assetId === '1002') return 18; // ETH
if (assetId === '1003') return 8; // BTC
return 12; // Default
};
const formatAmount = (amount: string, decimals: number = 12) => {
const value = parseInt(amount) / Math.pow(10, decimals);
if (decimals <= 6) return value.toFixed(2); // wUSDT
return value.toFixed(4);
};
@@ -635,6 +713,28 @@ export function WalletDashboard({ onDisconnect }: Props) {
</button>
</div>
{/* Add USDT Card */}
<div className="px-4 pb-4">
<button
onClick={() => {
hapticImpact('light');
setIsDepositUSDTModalOpen(true);
}}
className="w-full p-4 bg-gradient-to-r from-emerald-600/20 to-teal-500/20 border border-emerald-500/40 rounded-xl flex items-center gap-4 hover:border-emerald-400/60 transition-all"
>
<div className="w-12 h-12 bg-emerald-500/20 rounded-full flex items-center justify-center">
<img src="/tokens/USDT.png" alt="USDT" className="w-8 h-8 rounded-full" />
</div>
<div className="flex-1 text-left">
<div className="font-semibold text-emerald-400">USDT Zêde Bike</div>
<div className="text-xs text-muted-foreground">
TON, Polkadot an TRC20 ji zincîrên din
</div>
</div>
<ArrowDownLeft className="w-5 h-5 text-emerald-400" />
</button>
</div>
{/* Recent Activity */}
<div className="px-4 pb-4">
<div className="bg-muted/50 border border-border rounded-xl p-4">
@@ -702,14 +802,22 @@ export function WalletDashboard({ onDisconnect }: Props) {
className={`text-sm font-mono ${tx.direction === 'sent' ? 'text-yellow-400' : 'text-green-400'}`}
>
{tx.direction === 'sent' ? '-' : '+'}
{formatAmount(tx.amount || '0')}
{formatAmount(tx.amount || '0', getDecimalsForAsset(tx.section, tx.assetId))}
</div>
<div className="text-xs text-gray-400">
{tx.section === 'balances'
? 'HEZ'
: tx.assetId === '1'
? 'PEZ'
: `Asset #${tx.assetId}`}
: tx.assetId === '1000'
? 'wUSDT'
: tx.assetId === '1001'
? 'DOT'
: tx.assetId === '1002'
? 'ETH'
: tx.assetId === '1003'
? 'BTC'
: `Asset #${tx.assetId}`}
</div>
</div>
</div>
@@ -735,6 +843,10 @@ export function WalletDashboard({ onDisconnect }: Props) {
isOpen={isHEZStakingModalOpen}
onClose={() => setIsHEZStakingModalOpen(false)}
/>
<DepositUSDTModal
isOpen={isDepositUSDTModalOpen}
onClose={() => setIsDepositUSDTModalOpen(false)}
/>
{/* Staking Selector */}
{isStakingSelectorOpen && (
@@ -1296,8 +1408,19 @@ function HistoryTab({
}) {
const { hapticImpact } = useTelegram();
const getDecimalsForAsset = (section: string, assetId?: string): number => {
if (section === 'balances') return 12; // HEZ
if (assetId === '1') return 12; // PEZ
if (assetId === '1000') return 6; // wUSDT
if (assetId === '1001') return 10; // DOT
if (assetId === '1002') return 18; // ETH
if (assetId === '1003') return 8; // BTC
return 12; // Default
};
const formatAmount = (amount: string, decimals: number = 12) => {
const value = parseInt(amount) / Math.pow(10, decimals);
if (decimals <= 6) return value.toFixed(2); // wUSDT
return value.toFixed(4);
};
@@ -1366,7 +1489,7 @@ function HistoryTab({
className={`font-mono font-semibold ${tx.direction === 'sent' ? 'text-yellow-400' : 'text-green-400'}`}
>
{tx.direction === 'sent' ? '-' : '+'}
{formatAmount(tx.amount || '0')}
{formatAmount(tx.amount || '0', getDecimalsForAsset(tx.section, tx.assetId))}
</div>
<div className="text-xs text-gray-400">
{tx.section === 'balances'
+3 -3
View File
@@ -1,5 +1,5 @@
{
"version": "1.0.177",
"buildTime": "2026-02-08T01:24:06.896Z",
"buildNumber": 1770513846897
"version": "1.0.181",
"buildTime": "2026-02-08T02:31:45.624Z",
"buildNumber": 1770517905625
}