Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 08.01 · 25 мин
Средний
TON ConnectBridge ProtocolWalletsdAppsTON

Протокол TON Connect 2.0

TON Connect — это стандартный протокол для подключения кошельков к dApps в экосистеме TON, аналог WalletConnect в Ethereum. Без TON Connect ваше приложение не сможет безопасно взаимодействовать с кошельками пользователей. Понимание протокола на уровне архитектуры позволяет реализовать надёжную интеграцию, правильно обрабатывать edge-cases и обеспечить безопасность пользовательских средств.

В предыдущих модулях мы изучили архитектуру TON, написали смарт-контракты на Tact и разобрали токены Jetton 2.0. Теперь перейдём к интеграции: как пользовательские приложения (dApps) взаимодействуют с кошельками для подписания транзакций?

TON Connect 2.0 — это открытый протокол, который решает эту задачу. Он позволяет dApp безопасно соединяться с кошельком пользователя, даже если они работают на разных устройствах — например, dApp в браузере ноутбука, а кошелёк на телефоне.

WARNING

TON Connect 1.0 устарел

TON Connect 1.0 использовал простые deeplinks и требовал, чтобы dApp и кошелёк были на одном устройстве. Версия 2.0 полностью заменила его, добавив bridge-архитектуру и end-to-end шифрование. Используйте только TON Connect 2.0.


Зачем нужен bridge?

Главная проблема: dApp (веб-сайт в браузере) и кошелёк (мобильное приложение) не имеют прямого сетевого соединения. Они работают в разных средах:

Проблема прямого соединения
dApp
(браузер)
desktop
◄── ??? ──►
нет прямого соединения
Кошелёк
(телефон)
mobile

Bridge (мост) решает эту проблему. Это HTTP-сервер, который ретранслирует сообщения между dApp и кошельком через Server-Sent Events (SSE):

Решение через Bridge
dApp
(браузер)
desktop
SSE подписка
Bridge
(HTTP-сервер)
relay only
SSE подписка
Кошелёк
(телефон)
mobile

Bridge видит только зашифрованные данные. Он ничего не знает о содержимом сообщений — просто передаёт байты от одного Client ID к другому.

NOTE

Каждый кошелёк использует свой bridge

Tonkeeper использует bridge.tonapi.io, MyTonWallet — свой endpoint. Нет единого “центрального” bridge-сервера. dApp подключается к bridge того кошелька, с которым устанавливает соединение.


Client ID и X25519

Для идентификации сторон TON Connect использует криптографические ключи, а не логины или токены.

Как работает идентификация

Каждая сторона (dApp и кошелёк) генерирует пару ключей X25519 (Curve25519 Diffie-Hellman):

  • Публичный ключ = Client ID (идентификатор на bridge)
  • Приватный ключ = для расшифровки входящих сообщений
dApp:
  KeyPair A = X25519.generate()
  Client ID dApp = hex(publicKey_A)

Кошелёк:
  KeyPair B = X25519.generate()
  Client ID Wallet = hex(publicKey_B)

Шифрование через общий секрет

Когда обе стороны знают публичные ключи друг друга, они вычисляют общий секрет (shared secret) через X25519 Diffie-Hellman:

shared_secret = X25519(privateKey_A, publicKey_B)
              = X25519(privateKey_B, publicKey_A)  // тот же результат

Все сообщения шифруются с помощью NaCl box (Curve25519-XSalsa20-Poly1305) на основе этого общего секрета. Bridge не может расшифровать сообщения, потому что не имеет ни одного приватного ключа.


Первый шаг подключения — dApp должен передать кошельку свой Client ID и параметры подключения. Для этого используется QR-код или Universal Link:

tc://connect?v=2&id=<hex(pubkeyA)>&r=<ConnectRequest>

Параметры URL:

ПараметрОписание
vВерсия протокола (2)
idClient ID dApp (hex публичного ключа A)
rConnectRequest — JSON с запрашиваемыми данными

ConnectRequest

dApp указывает, какие данные хочет получить от кошелька:

{
  "manifestUrl": "https://myapp.com/tonconnect-manifest.json",
  "items": [
    { "name": "ton_addr" },
    { "name": "ton_proof", "payload": "random-nonce-from-backend" }
  ]
}
  • ton_addr — адрес кошелька (запрашивается всегда)
  • ton_proof — криптографическое доказательство владения кошельком (опционально, для аутентификации)

SSE-подписка

После отображения QR-кода dApp подписывается на SSE-поток bridge, чтобы получать входящие сообщения:

GET https://bridge.tonapi.io/bridge/events?client_id=<hex(pubkeyA)>

Ответ: Server-Sent Events поток
  event: message
  data: {"from": "<client_id_B>", "message": "<encrypted_base64>"}

Кошелёк, отсканировав QR-код, также подключается к bridge по своему Client ID. Теперь обе стороны слушают bridge и могут обмениваться зашифрованными сообщениями.


5-шаговый handshake

Рассмотрим полный процесс установления соединения:

TON Connect 2.0: bridge handshake
dApp
Приложение
Bridge
HTTP-сервер
Кошелёк
Tonkeeper / MyTonWallet
1
dApp
(отображение)
QR / Universal Link
2
dApp
Bridge
SSE Subscribe (A)
3
Кошелёк
Bridge
Scan + KeyPair B
4
Кошелёк
dApp
ConnectResponse
5
dApp
Кошелёк
Сессия установлена
Handshake -- Шаг 1 / 5
dApp генерирует KeyPair A (X25519) и отображает QR-код или Universal Link с Client ID (публичный ключ A) и ConnectRequest.
Типvisual
Шифрованиенет (открытый)
Bridge не видит содержимое сообщений -- все данные зашифрованы end-to-end через X25519 (Diffie-Hellman). dApp и кошелёк могут быть на разных устройствах.
Шаг 1 / 5

Шаг за шагом

1. dApp генерирует KeyPair A и формирует QR-код / Universal Link с Client ID и ConnectRequest.

2. dApp подписывается на SSE bridge по своему Client ID. Bridge начинает ожидать сообщения для этого ID.

3. Кошелёк сканирует QR-код, извлекает Client ID dApp и ConnectRequest. Генерирует собственный KeyPair B и подключается к bridge.

4. Кошелёк отправляет ConnectResponse — зашифрованный ответ с адресом кошелька и запрошенными данными. Bridge ретранслирует его в SSE-поток dApp.

5. Сессия установлена. Обе стороны имеют общий секрет (X25519). Дальнейшие запросы (SendTransactionRequest и др.) передаются через тот же bridge, зашифрованные end-to-end.

ConnectResponse

{
  "items": [
    {
      "name": "ton_addr",
      "address": "0:ca6e321c...",
      "network": "-239",
      "walletStateInit": "..."
    }
  ],
  "device": {
    "platform": "android",
    "appName": "Tonkeeper",
    "appVersion": "3.5.0"
  }
}

Сохранение сессии

После успешного handshake SDK сохраняет параметры сессии в localStorage браузера:

  • Client ID dApp (KeyPair A)
  • Client ID кошелька (публичный ключ B)
  • Bridge URL
  • Адрес кошелька

При повторном визите dApp восстанавливает сессию без нового QR-сканирования. Пользователь видит, что кошелёк уже подключён.


Сравнение с Ethereum

АспектTON Connect 2.0WalletConnect (Ethereum)
ТранспортBridge + SSERelay server + WebSocket
ШифрованиеX25519 + NaCl boxX25519 + ChaCha20-Poly1305
ИдентификацияClient ID (публичный ключ)Pairing key (shared secret)
QR-кодtc://connect?...wc:...
Cross-deviceДа (через bridge)Да (через relay)

Оба протокола решают одну задачу — безопасное кросс-девайсное подключение кошелька к dApp. Архитектурные решения схожи: relay-сервер, end-to-end шифрование, QR-код для первичного соединения.


Итоги

КомпонентРоль
BridgeHTTP-сервер, ретранслирует зашифрованные сообщения через SSE
Client IDПубличный ключ X25519 — идентификатор стороны на bridge
X25519Алгоритм обмена ключами (Diffie-Hellman на Curve25519)
NaCl boxШифрование сообщений (XSalsa20-Poly1305)
ConnectRequestЗапрос dApp на подключение (через QR / Universal Link)
ConnectResponseОтвет кошелька с адресом и запрошенными данными

В следующем уроке мы перейдём от теории к практике — настроим TON Connect в React-приложении, создадим manifest и добавим кнопку подключения кошелька.


Частые ошибки

  1. Путают TON Connect 1.0 и 2.0: версия 2.0 использует другой протокол коммуникации (HTTP bridge вместо JS bridge) и несовместима с 1.0.
  2. Не сохраняют сессию подключения, из-за чего пользователю приходится переподключать кошелёк при каждом обновлении страницы.
  3. Хардкодят список поддерживаемых кошельков вместо использования динамического списка из wallets-list, и новые кошельки не будут поддерживаться.
  4. Не обрабатывают отклонение транзакции пользователем: UI зависает в состоянии «ожидание подтверждения» без возможности отмены.

Проверка знанийKnowledge check
Почему bridge-сервер не может прочитать содержимое сообщений между dApp и кошельком?
ОтветAnswer
Все сообщения зашифрованы end-to-end с помощью NaCl box на основе общего секрета, вычисленного через X25519 Diffie-Hellman. Bridge имеет только Client ID (публичные ключи) для маршрутизации, но не имеет приватных ключей -- поэтому не может вычислить общий секрет и расшифровать данные.

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5