mirror of
https://github.com/pezkuwichain/pezkuwi-telegram-miniapp.git
synced 2026-07-24 12:55:40 +00:00
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:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pezkuwi-telegram-miniapp",
|
"name": "pezkuwi-telegram-miniapp",
|
||||||
"version": "1.0.177",
|
"version": "1.0.181",
|
||||||
"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",
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ interface Deposit {
|
|||||||
interface Props {
|
interface Props {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
userId: string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DepositUSDTModal({ isOpen, onClose }: Props) {
|
export function DepositUSDTModal({ isOpen, onClose }: Props) {
|
||||||
|
|||||||
@@ -954,11 +954,7 @@ export function TokensCard({ onSendToken }: Props) {
|
|||||||
<FundFeesModal isOpen={showFundFeesModal} onClose={() => setShowFundFeesModal(false)} />
|
<FundFeesModal isOpen={showFundFeesModal} onClose={() => setShowFundFeesModal(false)} />
|
||||||
|
|
||||||
{/* Deposit USDT Modal */}
|
{/* Deposit USDT Modal */}
|
||||||
<DepositUSDTModal
|
<DepositUSDTModal isOpen={showDepositModal} onClose={() => setShowDepositModal(false)} />
|
||||||
isOpen={showDepositModal}
|
|
||||||
onClose={() => setShowDepositModal(false)}
|
|
||||||
userId={null}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { SwapModal } from './SwapModal';
|
|||||||
import { PoolsModal } from './PoolsModal';
|
import { PoolsModal } from './PoolsModal';
|
||||||
import { LPStakingModal } from './LPStakingModal';
|
import { LPStakingModal } from './LPStakingModal';
|
||||||
import { HEZStakingModal } from './HEZStakingModal';
|
import { HEZStakingModal } from './HEZStakingModal';
|
||||||
|
import { DepositUSDTModal } from './DepositUSDTModal';
|
||||||
import { useWallet } from '@/contexts/WalletContext';
|
import { useWallet } from '@/contexts/WalletContext';
|
||||||
import { useTelegram } from '@/hooks/useTelegram';
|
import { useTelegram } from '@/hooks/useTelegram';
|
||||||
import { formatAddress } from '@/lib/wallet-service';
|
import { formatAddress } from '@/lib/wallet-service';
|
||||||
@@ -62,6 +63,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
|||||||
const [isLPStakingModalOpen, setIsLPStakingModalOpen] = useState(false);
|
const [isLPStakingModalOpen, setIsLPStakingModalOpen] = useState(false);
|
||||||
const [isHEZStakingModalOpen, setIsHEZStakingModalOpen] = useState(false);
|
const [isHEZStakingModalOpen, setIsHEZStakingModalOpen] = useState(false);
|
||||||
const [isStakingSelectorOpen, setIsStakingSelectorOpen] = useState(false);
|
const [isStakingSelectorOpen, setIsStakingSelectorOpen] = useState(false);
|
||||||
|
const [isDepositUSDTModalOpen, setIsDepositUSDTModalOpen] = useState(false);
|
||||||
|
|
||||||
// Subscribe to PEZ balance (Asset ID: 1) - Uses Asset Hub API
|
// Subscribe to PEZ balance (Asset ID: 1) - Uses Asset Hub API
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -189,13 +191,35 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
|||||||
const { method, signer } = extrinsic;
|
const { method, signer } = extrinsic;
|
||||||
const fromAddress = signer.toString();
|
const fromAddress = signer.toString();
|
||||||
|
|
||||||
// Parse assets.transfer and transferKeepAlive (PEZ)
|
// Parse assets.transfer and transferKeepAlive (PEZ/wUSDT)
|
||||||
if (
|
if (
|
||||||
method.section === 'assets' &&
|
method.section === 'assets' &&
|
||||||
(method.method === 'transfer' || method.method === 'transferKeepAlive')
|
(method.method === 'transfer' || method.method === 'transferKeepAlive')
|
||||||
) {
|
) {
|
||||||
const [assetId, dest, value] = method.args;
|
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 isFromUs = fromAddress === address;
|
||||||
const isToUs = toAddress === address;
|
const isToUs = toAddress === address;
|
||||||
@@ -363,7 +387,50 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
|||||||
};
|
};
|
||||||
}, [api, address, hapticNotification]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
if (!assetHubApi || !address) return;
|
if (!assetHubApi || !address) return;
|
||||||
|
|
||||||
@@ -390,7 +457,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
|||||||
(method.method === 'transfer' || method.method === 'transferKeepAlive')
|
(method.method === 'transfer' || method.method === 'transferKeepAlive')
|
||||||
) {
|
) {
|
||||||
const [assetId, dest, value] = method.args;
|
const [assetId, dest, value] = method.args;
|
||||||
const toAddress = dest.toString();
|
const toAddress = extractAddress(dest);
|
||||||
|
|
||||||
const isFromUs = fromAddress === address;
|
const isFromUs = fromAddress === address;
|
||||||
const isToUs = toAddress === address;
|
const isToUs = toAddress === address;
|
||||||
@@ -457,8 +524,19 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
|||||||
fetchRecentTransactions();
|
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 formatAmount = (amount: string, decimals: number = 12) => {
|
||||||
const value = parseInt(amount) / Math.pow(10, decimals);
|
const value = parseInt(amount) / Math.pow(10, decimals);
|
||||||
|
if (decimals <= 6) return value.toFixed(2); // wUSDT
|
||||||
return value.toFixed(4);
|
return value.toFixed(4);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -635,6 +713,28 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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 */}
|
{/* Recent Activity */}
|
||||||
<div className="px-4 pb-4">
|
<div className="px-4 pb-4">
|
||||||
<div className="bg-muted/50 border border-border rounded-xl p-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'}`}
|
className={`text-sm font-mono ${tx.direction === 'sent' ? 'text-yellow-400' : 'text-green-400'}`}
|
||||||
>
|
>
|
||||||
{tx.direction === 'sent' ? '-' : '+'}
|
{tx.direction === 'sent' ? '-' : '+'}
|
||||||
{formatAmount(tx.amount || '0')}
|
{formatAmount(tx.amount || '0', getDecimalsForAsset(tx.section, tx.assetId))}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-400">
|
<div className="text-xs text-gray-400">
|
||||||
{tx.section === 'balances'
|
{tx.section === 'balances'
|
||||||
? 'HEZ'
|
? 'HEZ'
|
||||||
: tx.assetId === '1'
|
: tx.assetId === '1'
|
||||||
? 'PEZ'
|
? '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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -735,6 +843,10 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
|||||||
isOpen={isHEZStakingModalOpen}
|
isOpen={isHEZStakingModalOpen}
|
||||||
onClose={() => setIsHEZStakingModalOpen(false)}
|
onClose={() => setIsHEZStakingModalOpen(false)}
|
||||||
/>
|
/>
|
||||||
|
<DepositUSDTModal
|
||||||
|
isOpen={isDepositUSDTModalOpen}
|
||||||
|
onClose={() => setIsDepositUSDTModalOpen(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Staking Selector */}
|
{/* Staking Selector */}
|
||||||
{isStakingSelectorOpen && (
|
{isStakingSelectorOpen && (
|
||||||
@@ -1296,8 +1408,19 @@ function HistoryTab({
|
|||||||
}) {
|
}) {
|
||||||
const { hapticImpact } = useTelegram();
|
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 formatAmount = (amount: string, decimals: number = 12) => {
|
||||||
const value = parseInt(amount) / Math.pow(10, decimals);
|
const value = parseInt(amount) / Math.pow(10, decimals);
|
||||||
|
if (decimals <= 6) return value.toFixed(2); // wUSDT
|
||||||
return value.toFixed(4);
|
return value.toFixed(4);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1366,7 +1489,7 @@ function HistoryTab({
|
|||||||
className={`font-mono font-semibold ${tx.direction === 'sent' ? 'text-yellow-400' : 'text-green-400'}`}
|
className={`font-mono font-semibold ${tx.direction === 'sent' ? 'text-yellow-400' : 'text-green-400'}`}
|
||||||
>
|
>
|
||||||
{tx.direction === 'sent' ? '-' : '+'}
|
{tx.direction === 'sent' ? '-' : '+'}
|
||||||
{formatAmount(tx.amount || '0')}
|
{formatAmount(tx.amount || '0', getDecimalsForAsset(tx.section, tx.assetId))}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-400">
|
<div className="text-xs text-gray-400">
|
||||||
{tx.section === 'balances'
|
{tx.section === 'balances'
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.177",
|
"version": "1.0.181",
|
||||||
"buildTime": "2026-02-08T01:24:06.896Z",
|
"buildTime": "2026-02-08T02:31:45.624Z",
|
||||||
"buildNumber": 1770513846897
|
"buildNumber": 1770517905625
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user