mirror of
https://github.com/pezkuwichain/pezkuwi-telegram-miniapp.git
synced 2026-06-13 04:41:03 +00:00
feat: add staking and presale buttons to wallet quick actions
- Change quick actions grid from 2x2 to 2x3 with smaller buttons - Add LP Staking modal with stake/unstake/claim rewards functionality - Add Presale button with coming soon message
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pezkuwi-telegram-miniapp",
|
||||
"version": "1.0.123",
|
||||
"version": "1.0.124",
|
||||
"type": "module",
|
||||
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
|
||||
"author": "Pezkuwichain Team",
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
/**
|
||||
* LP Staking Modal for Telegram Mini App
|
||||
* Allows users to stake LP tokens and earn PEZ rewards
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { X, Lock, Unlock, Gift, AlertCircle, Loader2 } from 'lucide-react';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface StakingPool {
|
||||
poolId: number;
|
||||
stakedAsset: string;
|
||||
rewardAsset: string;
|
||||
rewardRatePerBlock: string;
|
||||
totalStaked: string;
|
||||
userStaked: string;
|
||||
pendingRewards: string;
|
||||
lpTokenId: number;
|
||||
lpBalance: string;
|
||||
}
|
||||
|
||||
interface LPStakingModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const LP_TOKEN_NAMES: Record<number, string> = {
|
||||
0: 'HEZ-PEZ LP',
|
||||
1: 'HEZ-USDT LP',
|
||||
2: 'HEZ-DOT LP',
|
||||
};
|
||||
|
||||
export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
const { assetHubApi, keypair, address } = useWallet();
|
||||
const { hapticImpact, hapticNotification, showAlert } = useTelegram();
|
||||
|
||||
const [pools, setPools] = useState<StakingPool[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [selectedPool, setSelectedPool] = useState<number | null>(null);
|
||||
const [stakeAmount, setStakeAmount] = useState('');
|
||||
const [unstakeAmount, setUnstakeAmount] = useState('');
|
||||
const [activeTab, setActiveTab] = useState<'stake' | 'unstake' | 'rewards'>('stake');
|
||||
|
||||
useEffect(() => {
|
||||
if (!assetHubApi || !isOpen) return;
|
||||
|
||||
const fetchPools = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const poolEntries = await (assetHubApi.query.assetRewards as any)?.pools?.entries();
|
||||
|
||||
if (!poolEntries) {
|
||||
setError('Staking palleti amade nîne');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const stakingPools: StakingPool[] = [];
|
||||
|
||||
for (const [key, value] of poolEntries) {
|
||||
const poolId = parseInt(key.args[0].toString());
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const poolData = value.toJSON() as any;
|
||||
|
||||
const lpTokenId = poolData.stakedAssetId?.interior?.x2?.[1]?.generalIndex ?? poolId;
|
||||
|
||||
let userStaked = '0';
|
||||
let pendingRewards = '0';
|
||||
let lpBalance = '0';
|
||||
|
||||
if (address) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stakeInfo = await (assetHubApi.query.assetRewards as any).poolStakers([
|
||||
poolId,
|
||||
address,
|
||||
]);
|
||||
if (stakeInfo && stakeInfo.isSome) {
|
||||
const stakeData = stakeInfo.unwrap().toJSON();
|
||||
userStaked = stakeData.amount || '0';
|
||||
}
|
||||
|
||||
// Fetch LP balance
|
||||
// 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';
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
stakingPools.push({
|
||||
poolId,
|
||||
stakedAsset: LP_TOKEN_NAMES[lpTokenId] || `LP Token #${lpTokenId}`,
|
||||
rewardAsset: 'PEZ',
|
||||
rewardRatePerBlock: poolData.rewardRatePerBlock || '0',
|
||||
totalStaked: poolData.totalTokensStaked || '0',
|
||||
userStaked,
|
||||
pendingRewards,
|
||||
lpTokenId,
|
||||
lpBalance,
|
||||
});
|
||||
}
|
||||
|
||||
setPools(stakingPools);
|
||||
if (stakingPools.length > 0 && selectedPool === null) {
|
||||
setSelectedPool(stakingPools[0].poolId);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching staking pools:', err);
|
||||
setError('Staking pools bar nekirin');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPools();
|
||||
}, [assetHubApi, isOpen, address, selectedPool]);
|
||||
|
||||
const formatAmount = (amount: string, decimals: number = 12): string => {
|
||||
const value = Number(amount) / Math.pow(10, decimals);
|
||||
return value.toLocaleString(undefined, { maximumFractionDigits: 4 });
|
||||
};
|
||||
|
||||
const handleStake = async () => {
|
||||
if (!assetHubApi || !keypair || selectedPool === null || !stakeAmount) return;
|
||||
|
||||
setIsProcessing(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const pool = pools.find((p) => p.poolId === selectedPool);
|
||||
if (!pool) throw new Error('Pool not found');
|
||||
|
||||
const amountBN = BigInt(Math.floor(parseFloat(stakeAmount) * 1e12));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const tx = (assetHubApi.tx.assetRewards as any).stake(selectedPool, amountBN.toString());
|
||||
|
||||
const hash = await tx.signAndSend(keypair);
|
||||
hapticNotification('success');
|
||||
showAlert(`Stake serket! Hash: ${hash.toString().slice(0, 16)}...`);
|
||||
setStakeAmount('');
|
||||
|
||||
// Refresh pools
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Stake error:', err);
|
||||
setError(err instanceof Error ? err.message : 'Stake neserketî');
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnstake = async () => {
|
||||
if (!assetHubApi || !keypair || selectedPool === null || !unstakeAmount) return;
|
||||
|
||||
setIsProcessing(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const amountBN = BigInt(Math.floor(parseFloat(unstakeAmount) * 1e12));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const tx = (assetHubApi.tx.assetRewards as any).unstake(selectedPool, amountBN.toString());
|
||||
|
||||
const hash = await tx.signAndSend(keypair);
|
||||
hapticNotification('success');
|
||||
showAlert(`Unstake serket! Hash: ${hash.toString().slice(0, 16)}...`);
|
||||
setUnstakeAmount('');
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Unstake error:', err);
|
||||
setError(err instanceof Error ? err.message : 'Unstake neserketî');
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClaimRewards = async () => {
|
||||
if (!assetHubApi || !keypair || selectedPool === null) return;
|
||||
|
||||
setIsProcessing(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const tx = (assetHubApi.tx.assetRewards as any).harvestRewards(selectedPool);
|
||||
|
||||
const hash = await tx.signAndSend(keypair);
|
||||
hapticNotification('success');
|
||||
showAlert(`Xelat hat stendin! Hash: ${hash.toString().slice(0, 16)}...`);
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Claim error:', err);
|
||||
setError(err instanceof Error ? err.message : 'Xelat stendin neserketî');
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const currentPool = pools.find((p) => p.poolId === selectedPool);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 bg-black/80 flex items-end justify-center">
|
||||
<div className="bg-background w-full max-w-lg rounded-t-3xl max-h-[90vh] overflow-hidden flex flex-col animate-in slide-in-from-bottom duration-300">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-border">
|
||||
<h2 className="text-lg font-semibold">LP Staking</h2>
|
||||
<button onClick={onClose} className="p-2 hover:bg-secondary rounded-lg transition-colors">
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 overflow-y-auto p-4">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-primary" />
|
||||
</div>
|
||||
) : pools.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<AlertCircle className="w-12 h-12 text-muted-foreground mx-auto mb-4" />
|
||||
<p className="text-muted-foreground">Hêj staking pool tune ne</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Pool Selector */}
|
||||
<div className="mb-4">
|
||||
<label className="text-sm text-muted-foreground mb-2 block">Pool Hilbijêre</label>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{pools.map((pool) => (
|
||||
<button
|
||||
key={pool.poolId}
|
||||
onClick={() => {
|
||||
hapticImpact('light');
|
||||
setSelectedPool(pool.poolId);
|
||||
}}
|
||||
className={cn(
|
||||
'p-3 rounded-xl border text-center transition-all',
|
||||
selectedPool === pool.poolId
|
||||
? 'border-primary bg-primary/10'
|
||||
: 'border-border bg-secondary/50'
|
||||
)}
|
||||
>
|
||||
<div className="text-xs font-medium">{pool.stakedAsset}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pool Stats */}
|
||||
{currentPool && (
|
||||
<div className="bg-secondary/50 rounded-xl p-4 mb-4 border border-border">
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<div className="text-muted-foreground">Giştî Staked</div>
|
||||
<div className="font-medium">{formatAmount(currentPool.totalStaked)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground">Te Stake Kiriye</div>
|
||||
<div className="font-medium text-green-400">
|
||||
{formatAmount(currentPool.userStaked)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground">LP Bakiye</div>
|
||||
<div className="font-medium">{formatAmount(currentPool.lpBalance)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground">Xelat</div>
|
||||
<div className="font-medium text-yellow-400">
|
||||
{formatAmount(currentPool.pendingRewards)} PEZ
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-1 bg-secondary/50 rounded-lg p-1 mb-4">
|
||||
{[
|
||||
{ id: 'stake' as const, label: 'Stake', icon: Lock },
|
||||
{ id: 'unstake' as const, label: 'Unstake', icon: Unlock },
|
||||
{ id: 'rewards' as const, label: 'Xelat', icon: Gift },
|
||||
].map(({ id, label, icon: Icon }) => (
|
||||
<button
|
||||
key={id}
|
||||
onClick={() => {
|
||||
hapticImpact('light');
|
||||
setActiveTab(id);
|
||||
}}
|
||||
className={cn(
|
||||
'flex-1 py-2 rounded-md text-sm flex items-center justify-center gap-1 transition-colors',
|
||||
activeTab === id
|
||||
? 'bg-background text-foreground shadow-sm'
|
||||
: 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Error */}
|
||||
{error && (
|
||||
<div className="bg-red-500/10 border border-red-500/30 rounded-lg p-3 mb-4 text-sm text-red-400">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stake Tab */}
|
||||
{activeTab === 'stake' && currentPool && (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">
|
||||
Mîqdar ({currentPool.stakedAsset})
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="number"
|
||||
value={stakeAmount}
|
||||
onChange={(e) => setStakeAmount(e.target.value)}
|
||||
placeholder="0.0000"
|
||||
className="w-full bg-secondary border border-border rounded-xl p-4 pr-20 text-lg"
|
||||
/>
|
||||
<button
|
||||
onClick={() => setStakeAmount(formatAmount(currentPool.lpBalance))}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-primary"
|
||||
>
|
||||
MAX
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
Bakiye: {formatAmount(currentPool.lpBalance)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleStake}
|
||||
disabled={isProcessing || !stakeAmount}
|
||||
className="w-full py-4 bg-primary text-primary-foreground rounded-xl font-medium disabled:opacity-50 flex items-center justify-center gap-2"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
) : (
|
||||
<Lock className="w-5 h-5" />
|
||||
)}
|
||||
{isProcessing ? 'Tê stake kirin...' : 'Stake Bike'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Unstake Tab */}
|
||||
{activeTab === 'unstake' && currentPool && (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">
|
||||
Mîqdar ({currentPool.stakedAsset})
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="number"
|
||||
value={unstakeAmount}
|
||||
onChange={(e) => setUnstakeAmount(e.target.value)}
|
||||
placeholder="0.0000"
|
||||
className="w-full bg-secondary border border-border rounded-xl p-4 pr-20 text-lg"
|
||||
/>
|
||||
<button
|
||||
onClick={() => setUnstakeAmount(formatAmount(currentPool.userStaked))}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-primary"
|
||||
>
|
||||
MAX
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
Staked: {formatAmount(currentPool.userStaked)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleUnstake}
|
||||
disabled={isProcessing || !unstakeAmount}
|
||||
className="w-full py-4 bg-orange-500 text-white rounded-xl font-medium disabled:opacity-50 flex items-center justify-center gap-2"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
) : (
|
||||
<Unlock className="w-5 h-5" />
|
||||
)}
|
||||
{isProcessing ? 'Tê unstake kirin...' : 'Unstake Bike'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Rewards Tab */}
|
||||
{activeTab === 'rewards' && currentPool && (
|
||||
<div className="space-y-4">
|
||||
<div className="bg-gradient-to-r from-yellow-500/20 to-orange-500/20 border border-yellow-500/30 rounded-xl p-6 text-center">
|
||||
<Gift className="w-12 h-12 text-yellow-400 mx-auto mb-3" />
|
||||
<div className="text-2xl font-bold text-yellow-400 mb-1">
|
||||
{formatAmount(currentPool.pendingRewards)} PEZ
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">Xelatên li bendê</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleClaimRewards}
|
||||
disabled={isProcessing || currentPool.pendingRewards === '0'}
|
||||
className="w-full py-4 bg-gradient-to-r from-yellow-500 to-orange-500 text-white rounded-xl font-medium disabled:opacity-50 flex items-center justify-center gap-2"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
) : (
|
||||
<Gift className="w-5 h-5" />
|
||||
)}
|
||||
{isProcessing ? 'Tê stendin...' : 'Xelatan Bistîne'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -17,11 +17,14 @@ import {
|
||||
ScanLine,
|
||||
ArrowLeftRight,
|
||||
Droplets,
|
||||
Coins,
|
||||
Rocket,
|
||||
} from 'lucide-react';
|
||||
import QRCode from 'qrcode';
|
||||
import { TokensCard } from './TokensCard';
|
||||
import { SwapModal } from './SwapModal';
|
||||
import { PoolsModal } from './PoolsModal';
|
||||
import { LPStakingModal } from './LPStakingModal';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import { formatAddress } from '@/lib/wallet-service';
|
||||
@@ -45,7 +48,7 @@ interface Transaction {
|
||||
|
||||
export function WalletDashboard({ onDisconnect }: Props) {
|
||||
const { address, balance, api, assetHubApi, disconnect, isLoading } = useWallet();
|
||||
const { hapticImpact, hapticNotification } = useTelegram();
|
||||
const { hapticImpact, hapticNotification, showAlert } = useTelegram();
|
||||
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<'main' | 'send' | 'receive' | 'history'>('main');
|
||||
@@ -55,6 +58,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
const [isLoadingTxs, setIsLoadingTxs] = useState(false);
|
||||
const [isSwapModalOpen, setIsSwapModalOpen] = useState(false);
|
||||
const [isPoolsModalOpen, setIsPoolsModalOpen] = useState(false);
|
||||
const [isStakingModalOpen, setIsStakingModalOpen] = useState(false);
|
||||
|
||||
// Subscribe to PEZ balance (Asset ID: 1) - Uses Asset Hub API
|
||||
useEffect(() => {
|
||||
@@ -547,20 +551,19 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions - 2x2 Grid */}
|
||||
<div className="px-4 pb-4 grid grid-cols-2 gap-3">
|
||||
{/* Quick Actions - 2x3 Grid */}
|
||||
<div className="px-4 pb-4 grid grid-cols-3 gap-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
hapticImpact('light');
|
||||
setActiveTab('send');
|
||||
}}
|
||||
className="flex flex-col items-center gap-1 p-4 bg-gradient-to-r from-green-600/20 to-yellow-500/20 border border-green-500/30 rounded-xl"
|
||||
className="flex flex-col items-center gap-1 p-3 bg-gradient-to-r from-green-600/20 to-yellow-500/20 border border-green-500/30 rounded-xl"
|
||||
>
|
||||
<div className="w-10 h-10 bg-green-500/20 rounded-full flex items-center justify-center">
|
||||
<Send className="w-5 h-5 text-green-400" />
|
||||
<div className="w-8 h-8 bg-green-500/20 rounded-full flex items-center justify-center">
|
||||
<Send className="w-4 h-4 text-green-400" />
|
||||
</div>
|
||||
<span className="text-sm font-medium">Bişîne</span>
|
||||
<span className="text-xs text-gray-500">(send)</span>
|
||||
<span className="text-xs font-medium">Bişîne</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -568,13 +571,12 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
hapticImpact('light');
|
||||
setActiveTab('receive');
|
||||
}}
|
||||
className="flex flex-col items-center gap-1 p-4 bg-muted rounded-xl border border-border"
|
||||
className="flex flex-col items-center gap-1 p-3 bg-muted rounded-xl border border-border"
|
||||
>
|
||||
<div className="w-10 h-10 bg-cyan-500/20 rounded-full flex items-center justify-center">
|
||||
<QrCode className="w-5 h-5 text-cyan-400" />
|
||||
<div className="w-8 h-8 bg-cyan-500/20 rounded-full flex items-center justify-center">
|
||||
<QrCode className="w-4 h-4 text-cyan-400" />
|
||||
</div>
|
||||
<span className="text-sm font-medium">Werbigire</span>
|
||||
<span className="text-xs text-gray-500">(receive)</span>
|
||||
<span className="text-xs font-medium">Werbigire</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -582,13 +584,12 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
hapticImpact('light');
|
||||
setIsSwapModalOpen(true);
|
||||
}}
|
||||
className="flex flex-col items-center gap-1 p-4 bg-gradient-to-r from-blue-600/20 to-purple-500/20 border border-blue-500/30 rounded-xl"
|
||||
className="flex flex-col items-center gap-1 p-3 bg-gradient-to-r from-blue-600/20 to-purple-500/20 border border-blue-500/30 rounded-xl"
|
||||
>
|
||||
<div className="w-10 h-10 bg-blue-500/20 rounded-full flex items-center justify-center">
|
||||
<ArrowLeftRight className="w-5 h-5 text-blue-400" />
|
||||
<div className="w-8 h-8 bg-blue-500/20 rounded-full flex items-center justify-center">
|
||||
<ArrowLeftRight className="w-4 h-4 text-blue-400" />
|
||||
</div>
|
||||
<span className="text-sm font-medium">Swap</span>
|
||||
<span className="text-xs text-gray-500">(guhertin)</span>
|
||||
<span className="text-xs font-medium">Swap</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -596,13 +597,38 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
hapticImpact('light');
|
||||
setIsPoolsModalOpen(true);
|
||||
}}
|
||||
className="flex flex-col items-center gap-1 p-4 bg-gradient-to-r from-purple-600/20 to-pink-500/20 border border-purple-500/30 rounded-xl"
|
||||
className="flex flex-col items-center gap-1 p-3 bg-gradient-to-r from-purple-600/20 to-pink-500/20 border border-purple-500/30 rounded-xl"
|
||||
>
|
||||
<div className="w-10 h-10 bg-purple-500/20 rounded-full flex items-center justify-center">
|
||||
<Droplets className="w-5 h-5 text-purple-400" />
|
||||
<div className="w-8 h-8 bg-purple-500/20 rounded-full flex items-center justify-center">
|
||||
<Droplets className="w-4 h-4 text-purple-400" />
|
||||
</div>
|
||||
<span className="text-sm font-medium">Pools</span>
|
||||
<span className="text-xs text-gray-500">(hewz)</span>
|
||||
<span className="text-xs font-medium">Pools</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
hapticImpact('light');
|
||||
setIsStakingModalOpen(true);
|
||||
}}
|
||||
className="flex flex-col items-center gap-1 p-3 bg-gradient-to-r from-yellow-600/20 to-orange-500/20 border border-yellow-500/30 rounded-xl"
|
||||
>
|
||||
<div className="w-8 h-8 bg-yellow-500/20 rounded-full flex items-center justify-center">
|
||||
<Coins className="w-4 h-4 text-yellow-400" />
|
||||
</div>
|
||||
<span className="text-xs font-medium">Staking</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
hapticImpact('light');
|
||||
showAlert('Presale tê de ye! Zûtirîn demekê de dê bête vekirin.');
|
||||
}}
|
||||
className="flex flex-col items-center gap-1 p-3 bg-gradient-to-r from-pink-600/20 to-red-500/20 border border-pink-500/30 rounded-xl"
|
||||
>
|
||||
<div className="w-8 h-8 bg-pink-500/20 rounded-full flex items-center justify-center">
|
||||
<Rocket className="w-4 h-4 text-pink-400" />
|
||||
</div>
|
||||
<span className="text-xs font-medium">Presale</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -698,6 +724,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
{/* Modals */}
|
||||
<SwapModal isOpen={isSwapModalOpen} onClose={() => setIsSwapModalOpen(false)} />
|
||||
<PoolsModal isOpen={isPoolsModalOpen} onClose={() => setIsPoolsModalOpen(false)} />
|
||||
<LPStakingModal isOpen={isStakingModalOpen} onClose={() => setIsStakingModalOpen(false)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0.123",
|
||||
"buildTime": "2026-02-06T22:10:09.600Z",
|
||||
"buildNumber": 1770415809601
|
||||
"version": "1.0.124",
|
||||
"buildTime": "2026-02-06T22:20:16.985Z",
|
||||
"buildNumber": 1770416416986
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user