Skip to content

Commit 676b5e7

Browse files
committed
Add real wallet balances for TON and RUBLE
1 parent be09156 commit 676b5e7

4 files changed

Lines changed: 147 additions & 61 deletions

File tree

client/src/App.jsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,18 @@ function App() {
7676
useEffect(() => {
7777
const unsubscribe = tonConnectUI.onStatusChange(async (wallet) => {
7878
if (!wallet) {
79+
console.log('Wallet disconnected, resetting state');
7980
authService.clearAuth();
8081
setUser(null);
8182
setError(null);
83+
setIsLoading(false); // Сбрасываем состояние загрузки
8284
setCurrentScreen('splash');
85+
86+
// Принудительно обновляем TonConnect UI
87+
setTimeout(() => {
88+
tonConnectUI.setConnectRequestParameters(null);
89+
}, 100);
90+
8391
return;
8492
}
8593

@@ -126,6 +134,13 @@ function App() {
126134
}
127135
};
128136

137+
// Дополнительная логика для корректного отображения SplashScreen
138+
useEffect(() => {
139+
if (currentScreen === 'splash' && !isLoading && !user) {
140+
console.log('SplashScreen should be fully rendered now');
141+
}
142+
}, [currentScreen, isLoading, user]);
143+
129144
console.log('Current screen:', currentScreen);
130145
console.log('User:', user);
131146
console.log('Is loading:', isLoading);
@@ -134,6 +149,7 @@ function App() {
134149
<div className="min-h-screen bg-dark-bg">
135150
{currentScreen === 'splash' && (
136151
<SplashScreen
152+
key={`splash-${user ? 'authenticated' : 'not-authenticated'}`}
137153
onNavigate={navigateToMain}
138154
isLoading={isLoading}
139155
error={error}
Lines changed: 109 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,122 @@
11
import { useState, useEffect } from 'react';
2-
import { useTonWallet } from '@tonconnect/ui-react';
3-
import { TonClient, Address } from 'ton';
4-
import { fromNano } from 'ton-core';
5-
import { Buffer } from 'buffer';
2+
import { useTonConnectUI } from '@tonconnect/ui-react';
63

7-
// Polyfill for browser environment
8-
window.Buffer = Buffer;
9-
10-
const JETTON_MINTER_ADDRESS = 'EQA5QopV0455mb09Nz6iPL3JsX_guIGf77a6l-DtqSQh0aE-';
11-
12-
// Create a single TonClient instance
13-
const client = new TonClient({
14-
endpoint: 'https://toncenter.com/api/v2/jsonRPC',
15-
});
4+
// Jetton Master Contract для RUBLE
5+
const RUBLE_JETTON_MASTER = 'EQA5QopV0455mb09Nz6iPL3JsX_guIGf77a6l-DtqSQh0aE-';
166

177
export const useWalletBalances = () => {
18-
const wallet = useTonWallet();
19-
const [balanceTon, setBalanceTon] = useState(0);
20-
const [balanceRuble, setBalanceRuble] = useState(0);
21-
const [isLoading, setIsLoading] = useState(false);
8+
const [tonConnectUI] = useTonConnectUI();
9+
const [balances, setBalances] = useState({
10+
ton: 0,
11+
ruble: 0,
12+
loading: true,
13+
error: null
14+
});
2215

23-
useEffect(() => {
24-
const fetchBalances = async () => {
25-
if (!wallet) {
26-
setBalanceTon(0);
27-
setBalanceRuble(0);
28-
return;
29-
}
16+
const fetchBalances = async () => {
17+
if (!tonConnectUI.account) {
18+
setBalances({ ton: 0, ruble: 0, loading: false, error: null });
19+
return;
20+
}
3021

31-
setIsLoading(true);
32-
try {
33-
// Fetch TON balance
34-
const address = Address.parse(wallet.account.address);
35-
const tonBalance = await client.getBalance(address);
36-
setBalanceTon(Number(fromNano(tonBalance)));
22+
try {
23+
setBalances(prev => ({ ...prev, loading: true, error: null }));
3724

38-
// Fetch Jetton (RUBLE) balance
39-
const minterAddress = Address.parse(JETTON_MINTER_ADDRESS);
40-
41-
// 1. Get user's Jetton wallet address from the minter
42-
const result = await client.runMethod(minterAddress, 'get_wallet_address', [
43-
{ type: 'slice', cell: new Address(address.workChain, address.hash).toCell() },
44-
]);
45-
const jettonWalletAddress = result.stack.readAddress();
25+
const walletAddress = tonConnectUI.account.address;
26+
27+
// Получаем баланс TON
28+
const tonBalance = await fetchTonBalance(walletAddress);
29+
30+
// Получаем баланс RUBLE (Jetton)
31+
const rubleBalance = await fetchJettonBalance(walletAddress, RUBLE_JETTON_MASTER);
4632

47-
// 2. Get Jetton data from the user's Jetton wallet
48-
const jettonData = await client.runMethod(
49-
jettonWalletAddress,
50-
'get_jetton_data'
51-
);
52-
53-
const jettonBalance = jettonData.stack.readBigNumber();
54-
// Assuming the Jetton has 9 decimals, like TON
55-
setBalanceRuble(Number(fromNano(jettonBalance)));
33+
setBalances({
34+
ton: tonBalance,
35+
ruble: rubleBalance,
36+
loading: false,
37+
error: null
38+
});
39+
} catch (error) {
40+
console.error('Failed to fetch balances:', error);
41+
setBalances(prev => ({
42+
...prev,
43+
loading: false,
44+
error: error.message
45+
}));
46+
}
47+
};
5648

57-
} catch (error) {
58-
console.error('Failed to fetch balances:', error);
59-
setBalanceTon(0);
60-
setBalanceRuble(0);
61-
} finally {
62-
setIsLoading(false);
49+
// Получение баланса TON
50+
const fetchTonBalance = async (address) => {
51+
try {
52+
const url = new URL('https://toncenter.com/api/v2/getAddressBalance');
53+
url.searchParams.append('address', address);
54+
55+
const tonResponse = await fetch(url);
56+
const tonData = await tonResponse.json();
57+
58+
if (tonData.ok) {
59+
// Конвертируем из nanotons в TON (1 TON = 10^9 nanotons)
60+
return parseFloat(tonData.result) / 1000000000;
6361
}
64-
};
62+
return 0;
63+
} catch (error) {
64+
console.error('Failed to fetch TON balance:', error);
65+
return 0;
66+
}
67+
};
6568

69+
// Получение баланса Jetton
70+
const fetchJettonBalance = async (address, jettonMaster) => {
71+
try {
72+
// Сначала получаем адрес Jetton Wallet
73+
const walletUrl = new URL('https://toncenter.com/api/v2/getJettonWalletAddress');
74+
walletUrl.searchParams.append('owner_address', address);
75+
walletUrl.searchParams.append('jetton_master', jettonMaster);
76+
77+
const walletResponse = await fetch(walletUrl);
78+
const walletData = await walletResponse.json();
79+
80+
if (!walletData.ok || !walletData.result) {
81+
return 0;
82+
}
83+
84+
const jettonWalletAddress = walletData.result;
85+
86+
// Теперь получаем баланс Jetton Wallet
87+
const balanceUrl = new URL('https://toncenter.com/api/v2/getAddressBalance');
88+
balanceUrl.searchParams.append('address', jettonWalletAddress);
89+
90+
const balanceResponse = await fetch(balanceUrl);
91+
const balanceData = await balanceResponse.json();
92+
93+
if (balanceData.ok && balanceData.result) {
94+
// Конвертируем из jetton units в обычные единицы
95+
// RUBLE Jetton имеет 9 знаков после запятой
96+
return parseFloat(balanceData.result) / 1000000000;
97+
}
98+
return 0;
99+
} catch (error) {
100+
console.error('Failed to fetch Jetton balance:', error);
101+
return 0;
102+
}
103+
};
104+
105+
// Обновляем балансы при изменении кошелька
106+
useEffect(() => {
66107
fetchBalances();
67-
}, [wallet]);
108+
}, [tonConnectUI.account?.address]);
109+
110+
// Автоматическое обновление балансов каждые 30 секунд
111+
useEffect(() => {
112+
if (!tonConnectUI.account) return;
113+
114+
const interval = setInterval(fetchBalances, 30000);
115+
return () => clearInterval(interval);
116+
}, [tonConnectUI.account?.address]);
68117

69-
return { balanceTon, balanceRuble, isLoading };
70-
};
118+
return {
119+
...balances,
120+
refetch: fetchBalances
121+
};
122+
};

client/src/screens/MainMenu/components/ProfileCard.jsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import PropTypes from 'prop-types';
2+
import { useWalletBalances } from '../../../hooks/useWalletBalances';
23
import '../../../styles/ProfileCard.css';
34

45
// Функция для форматирования баланса (1234 -> 1.2k, 1234567 -> 1.2M)
@@ -14,6 +15,8 @@ const formatBalance = (num) => {
1415

1516
// Компонент профиля пользователя
1617
const ProfileCard = ({ user }) => {
18+
const { ton, ruble, loading: balancesLoading, error: balancesError } = useWalletBalances();
19+
1720
// Показываем заглушку, если данные пользователя еще не загружены
1821
if (!user) {
1922
return (
@@ -28,8 +31,6 @@ const ProfileCard = ({ user }) => {
2831
}
2932

3033
const { username, winrate, wins, loses, avatar } = user;
31-
const balanceTon = 0; // Заглушка, пока баланс не приходит с бэка
32-
const balanceRuble = 0; // Заглушка
3334

3435
const truncatedUsername =
3536
username && username.length > 15 ? `${username.substring(0, 15)}...` : username;
@@ -48,13 +49,22 @@ const ProfileCard = ({ user }) => {
4849
<div className="balances-container">
4950
<div className="balance-item">
5051
<span className="currency-badge currency-ton">TON</span>
51-
<span className="balance-amount">{formatBalance(balanceTon)}</span>
52+
<span className="balance-amount">
53+
{balancesLoading ? '...' : formatBalance(ton)}
54+
</span>
5255
</div>
5356
<div className="balance-item">
5457
<span className="currency-badge currency-ruble">RUBLE</span>
55-
<span className="balance-amount">{formatBalance(balanceRuble)}</span>
58+
<span className="balance-amount">
59+
{balancesLoading ? '...' : formatBalance(ruble)}
60+
</span>
5661
</div>
5762
</div>
63+
{balancesError && (
64+
<div className="balance-error">
65+
<small>Failed to load balances</small>
66+
</div>
67+
)}
5868
</div>
5969
</div>
6070

@@ -88,3 +98,4 @@ ProfileCard.propTypes = {
8898
};
8999

90100
export default ProfileCard;
101+

client/src/styles/ProfileCard.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@
8080
font-family: 'Roboto Mono', monospace;
8181
}
8282

83+
.balance-error {
84+
margin-top: 4px;
85+
color: #ff4444;
86+
font-size: 10px;
87+
opacity: 0.8;
88+
}
89+
8390
.stats-grid {
8491
display: grid;
8592
grid-template-columns: repeat(4, 1fr);

0 commit comments

Comments
 (0)