TON Connect React SDK
TON Connect React SDK — это готовый набор компонентов и хуков для интеграции кошельков в React-приложения. Использование официального SDK вместо самописных решений экономит недели разработки и гарантирует совместимость со всеми основными кошельками экосистемы TON.
В предыдущих уроках мы использовали отдельные хуки и компоненты SDK. Теперь соберём полную картину: все инструменты @tonconnect/ui-react, их возвращаемые типы, сценарии использования и паттерны.
Обзор SDK
Пакет @tonconnect/ui-react — это React-обёртка над @tonconnect/ui, предоставляющая:
- Компоненты:
TonConnectUIProvider,TonConnectButton - Хуки: для управления подключением, отправки транзакций, отслеживания состояния
- UI-кастомизацию: язык, тема, поведение модальных окон
Справочник хуков
useTonConnectUI
Главный хук — предоставляет прямой доступ к экземпляру SDK:
const [tonConnectUI, setOptions] = useTonConnectUI();
Возвращает: [TonConnectUI, (options: TonConnectUiOptions) => void]
Возможности tonConnectUI:
| Метод | Описание |
|---|---|
sendTransaction(tx) | Отправить транзакцию через кошелёк |
disconnect() | Отключить текущий кошелёк |
openModal() | Открыть модальное окно выбора кошелька |
closeModal() | Закрыть модальное окно |
onStatusChange(cb) | Подписка на изменения состояния подключения |
setConnectRequestParameters(p) | Настроить параметры подключения (ton_proof) |
connected | Boolean — подключён ли кошелёк |
wallet | Текущий подключённый кошелёк (или null) |
// Пример: программное открытие модального окна
function CustomConnectButton() {
const [tonConnectUI] = useTonConnectUI();
return (
<button onClick={() => tonConnectUI.openModal()}>
Подключить кошелёк
</button>
);
}
useTonWallet
Реактивное состояние подключённого кошелька:
const wallet = useTonWallet();
Возвращает: Wallet | null
// Структура Wallet (упрощённо)
interface Wallet {
account: {
address: string; // raw-адрес (0:ca6e321c...)
chain: string; // '-239' (mainnet) или '-3' (testnet)
walletStateInit: string; // BOC stateInit кошелька
publicKey?: string; // публичный ключ (hex)
};
device: {
platform: string; // 'android' | 'ios' | 'linux' | 'windows' | 'mac'
appName: string; // 'Tonkeeper' | 'MyTonWallet' | ...
appVersion: string; // '3.5.0'
};
connectItems?: {
tonProof?: TonProofItem; // результат ton_proof (если запрошен)
};
}
function WalletDetails() {
const wallet = useTonWallet();
if (!wallet) return null;
return (
<div>
<p>Приложение: {wallet.device.appName} {wallet.device.appVersion}</p>
<p>Платформа: {wallet.device.platform}</p>
<p>Адрес: {wallet.account.address}</p>
<p>Сеть: {wallet.account.chain === '-239' ? 'Mainnet' : 'Testnet'}</p>
</div>
);
}
useTonAddress
Сокращённый доступ к адресу кошелька:
const address = useTonAddress(); // raw-адрес или ''
const userFriendly = useTonAddress(true); // user-friendly формат или ''
Возвращает: string (пустая строка если не подключён)
function AddressDisplay() {
const rawAddress = useTonAddress();
const friendlyAddress = useTonAddress(true);
if (!rawAddress) return <p>Не подключён</p>;
return (
<div>
<p>Raw: {rawAddress}</p>
<p>Friendly: {friendlyAddress}</p>
</div>
);
}
useIsConnectionRestored
Статус восстановления сессии из localStorage:
const isRestored = useIsConnectionRestored();
Возвращает: boolean
false— SDK ещё проверяет localStoragetrue— проверка завершена (кошелёк может быть подключён или нет)
Всегда проверяйте useIsConnectionRestored() перед отображением UI, зависящего от кошелька
Без этой проверки пользователь увидит “flash” — кратковременное отображение состояния “не подключён”, которое сменится на “подключён” после восстановления сессии из localStorage.
UI-кастомизация
Изменение языка
const [_, setOptions] = useTonConnectUI();
setOptions({
language: 'ru', // 'en' | 'ru' | 'zh' | ...
});
Настройка действий
setOptions({
actionsConfiguration: {
// Куда перенаправить для просмотра транзакции
twaReturnUrl: 'https://myapp.com',
},
});
Настройка через Provider
<TonConnectUIProvider
manifestUrl="https://myapp.com/tonconnect-manifest.json"
uiPreferences={{
theme: 'DARK', // 'DARK' | 'LIGHT' | 'SYSTEM'
}}
language="ru"
actionsConfiguration={{
twaReturnUrl: 'https://myapp.com',
}}
>
<App />
</TonConnectUIProvider>
Паттерны управления состоянием
Паттерн 1: условный рендеринг
function DashboardPage() {
const wallet = useTonWallet();
const connectionRestored = useIsConnectionRestored();
// 1. Ждём восстановления сессии
if (!connectionRestored) {
return <LoadingSpinner />;
}
// 2. Если не подключён -- показываем страницу входа
if (!wallet) {
return <ConnectWalletPage />;
}
// 3. Подключён -- показываем дашборд
return <Dashboard wallet={wallet} />;
}
Паттерн 2: обработка отключения
function useWalletGuard() {
const [tonConnectUI] = useTonConnectUI();
useEffect(() => {
const unsubscribe = tonConnectUI.onStatusChange((wallet) => {
if (!wallet) {
// Кошелёк отключён -- очищаем авторизацию
localStorage.removeItem('auth_token');
// Перенаправляем на страницу входа
window.location.href = '/login';
}
});
return () => unsubscribe();
}, [tonConnectUI]);
}
Паттерн 3: отправка транзакции с обработкой ошибок
function useSendTransaction() {
const [tonConnectUI] = useTonConnectUI();
const [sending, setSending] = useState(false);
const [error, setError] = useState<string | null>(null);
const send = async (to: string, amount: string) => {
setSending(true);
setError(null);
try {
const result = await tonConnectUI.sendTransaction({
validUntil: Math.floor(Date.now() / 1000) + 300,
messages: [{ address: to, amount }],
});
return result.boc;
} catch (err) {
if (err instanceof Error) {
setError(err.message);
} else {
setError('Неизвестная ошибка');
}
return null;
} finally {
setSending(false);
}
};
return { send, sending, error };
}
Полный пример: мини-dApp
Объединим все концепции в одном компоненте:
import {
TonConnectUIProvider,
TonConnectButton,
useTonConnectUI,
useTonWallet,
useTonAddress,
useIsConnectionRestored,
} from '@tonconnect/ui-react';
import { toNano } from '@ton/core';
import { useState } from 'react';
function MiniDApp() {
const wallet = useTonWallet();
const address = useTonAddress(true);
const connectionRestored = useIsConnectionRestored();
const [tonConnectUI] = useTonConnectUI();
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const [status, setStatus] = useState('');
if (!connectionRestored) {
return <p>Загрузка...</p>;
}
const handleSend = async () => {
if (!recipient || !amount) return;
setStatus('Ожидаем подтверждение в кошельке...');
try {
const result = await tonConnectUI.sendTransaction({
validUntil: Math.floor(Date.now() / 1000) + 300,
messages: [{
address: recipient,
amount: toNano(amount).toString(),
}],
});
setStatus('Транзакция отправлена!');
} catch (error) {
setStatus('Ошибка: ' + (error instanceof Error ? error.message : 'отклонено'));
}
};
return (
<div>
<header>
<h1>Mini dApp</h1>
<TonConnectButton />
</header>
{wallet ? (
<main>
<p>Подключён: {address}</p>
<p>Кошелёк: {wallet.device.appName}</p>
<div>
<input
placeholder="Адрес получателя"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
/>
<input
placeholder="Сумма (TON)"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<button onClick={handleSend}>Отправить</button>
</div>
{status && <p>{status}</p>}
</main>
) : (
<main>
<p>Подключите кошелёк для начала работы</p>
</main>
)}
</div>
);
}
// Точка входа
function App() {
return (
<TonConnectUIProvider
manifestUrl="https://myapp.example.com/tonconnect-manifest.json"
uiPreferences={{ theme: 'DARK' }}
language="ru"
>
<MiniDApp />
</TonConnectUIProvider>
);
}
Обработка восстановления сессии при загрузке
При загрузке страницы SDK асинхронно восстанавливает сессию. Важно правильно обработать этот момент:
function AppWithSessionRestore() {
const connectionRestored = useIsConnectionRestored();
const wallet = useTonWallet();
const [tonConnectUI] = useTonConnectUI();
useEffect(() => {
if (!connectionRestored) return; // ещё загружается
if (wallet) {
// Сессия восстановлена -- загружаем данные пользователя
fetchUserData(wallet.account.address);
} else {
// Сессия не найдена -- показываем экран входа
showLoginScreen();
}
}, [connectionRestored, wallet]);
if (!connectionRestored) {
return <LoadingScreen />;
}
return <MainApp />;
}
Сводная таблица хуков
| Хук | Возвращает | Когда использовать |
|---|---|---|
useTonConnectUI() | [TonConnectUI, setOptions] | Отправка транзакций, управление подключением, кастомизация |
useTonWallet() | Wallet | null | Информация о кошельке: устройство, адрес, сеть, ton_proof |
useTonAddress() | string | Только адрес кошелька (raw или user-friendly) |
useIsConnectionRestored() | boolean | Проверка завершения восстановления сессии из localStorage |
Итоги модуля M07
В этом модуле мы прошли полный путь от теории протокола до практической интеграции:
- Протокол TON Connect 2.0 — bridge-архитектура, SSE, X25519 шифрование
- Интеграция кошельков — manifest.json, TonConnectButton, сохранение сессии
- Отправка транзакций — sendTransaction, nanoTON, payload, multi-message
- Аутентификация ton_proof — off-chain Ed25519 подпись, верификация на бэкенде
- React SDK — все хуки, компоненты, паттерны управления состоянием
Для практики интеграции TON Connect в реальном приложении обратитесь к лабораторной работе по созданию Mini App, где вы подключите кошелёк, отправите транзакцию и реализуете аутентификацию.
Частые ошибки
- Не оборачивают приложение в TonConnectUIProvider: без провайдера хуки useTonConnectUI и useTonWallet не работают.
- Используют несколько экземпляров TonConnectUI: SDK должен инициализироваться один раз; дублирование создаёт конфликты состояния.
- Забывают установить manifestUrl: без манифеста кошельки не смогут отобразить информацию о вашем dApp при подключении.
- Не обрабатывают состояние loading при восстановлении сессии: при обновлении страницы SDK пытается восстановить подключение, и UI должен это отражать.
Проверка знанийКакие 4 хука предоставляет @tonconnect/ui-react, и в каких сценариях используется каждый?
Check Your Understanding
Finished the lesson?
Mark it as complete to track your progress
Войдите чтобы оценить урок