11 октября вышел TON Connect 5.0.0, набор библиотек, через которые сайты и мини-приложения подключают TON-кошельки. В окне выбора кошелька встроенный кошелёк Telegram теперь стоит первым, отдельной кнопкой «Подключить Telegram», а бывший @wallet подписан как Walt. Разработчикам, у которых вход построен на подписи ton_proof, команда рекомендует сверить сервер с документацией: многие мини-приложения не умеют проверять подпись кошелька Telegram, и пользователь с ним не может войти.

Пост в Telegram — @toncore
Анонс TON Connect 5.0.0

Что сделать разработчику

Обновить пакеты. @tonconnect/sdk, @tonconnect/ui и @tonconnect/ui-react опубликованы в версии 5.0.0, к вечеру того же дня интерфейсные пакеты получили 5.0.1. Смена старшей цифры не означает переписанного кода: по журналу изменений 5.0.0 это тот же код, что sdk 4.1.0 и ui 3.1.0, а номера просто выровняли, чтобы три пакета, которые ставятся вместе, шли одной версией. Если у вас стоит ui 3.0.x, переход на 5.0 равен переходу на 3.1 со всеми его изменениями (они ниже).

Проверить серверную часть ton_proof. Это подпись, которой кошелёк при подключении доказывает серверу приложения, что адрес принадлежит ему: сервер выдаёт одноразовую строку, кошелёк подписывает её ключом, сервер находит открытый ключ кошелька и сверяет подпись. Типичная реализация ищет ключ в walletStateInit, начальном состоянии контракта, которое кошелёк присылает вместе с адресом: распознаёт по хешу кода стандартный кошелёк (v3, v4, v5) и читает ключ из его данных. У кошелька Telegram такой разбор ключа не находит, и проверка отказывает.

По обновлённой документации для кошелька Telegram нужна отдельная ветка:

  1. Сравнить хеш кода в walletStateInit с хешем контракта-трамплина WalletTg.
  2. Запросить из сети состояние аккаунта и убедиться, что он активен и в нём развёрнут тот же трамплин.
  3. Только после этого вызвать у контракта get-метод get_public_key и проверять подпись возвращённым ключом.

Неактивный аккаунт, несовпадающий код или упавший вызов документация велит отклонять. Отдельно подчёркнуто, что наличие у контракта метода get_public_key само по себе ничего не доказывает: вызывать его можно только у кода, который сверен со списком проверенных реализаций. Если SDK передал в account.publicKey ключ, он должен совпасть с найденным; если не передал, это не повод отвергать подпись, проверенную ключом из сети.

Предусмотреть неактивный кошелёк. Аккаунт в TON становится активным с первой исходящей транзакцией: до неё на адресе может лежать баланс, но контракта там нет. Кошелёк Telegram, который пользователю завёл чужой перевод и с которого ещё ничего не отправлялось, по правилам документации войти через ton_proof не сможет. Сообщение об ошибке в этом месте стоит писать по-человечески, а не общим «подпись отклонена».

Пересобрать отлов ошибок. В sdk 4.1.0 у всех ошибок библиотеки error.name стал именем класса (UserRejectsError и т. д.) вместо прежнего "Error" и не искажается минификацией. Трекеры ошибок, которые группируют по имени и тексту, после обновления заведут новые группы. Ответ кошелька с кодом 400 теперь приходит как новый MethodNotSupportedError, а не UnknownError; непонятный ответ встроенного кошелька — как WalletTransportError с исходным значением в cause.

Учесть новый адрес загрузки. В ui 5.0.1 над кнопкой Telegram на телефоне появился объёмный кристалл. Модель весит около 540 КБ и загружается не из пакета, а с s.ton-connect.io; на слабом устройстве, если анимация опускается ниже 20 кадров в секунду, вместо неё остаётся статичная картинка. Приложениям со строгой политикой загрузки (CSP) этот адрес нужно разрешить или смириться с картинкой.

Что поменялось в окне выбора кошелька

Список кошельков библиотека не хранит в себе, а при каждом открытии загружает из общего реестра config.ton.org/wallets-v2.json; в пакете лежит только запасная копия на случай, если реестр недоступен. Поэтому порядок кошельков сменился 11 октября для всех приложений сразу, правкой реестра, без обновления кода. Новые пакеты добавили к этому раскладку: кошелёк Telegram и Gram Wallet вынесены в отдельные кнопки над общим рядом кошельков, а не стоят плиткой среди прочих.

Место в реестреИмя в спискеЧто этоКуда ведёт
1Telegramкошелёк, встроенный в сам мессенджерt.me/sendgrams, мост tonbridge.telegram.org
2Gram Walletотдельное приложение для iOS и Android с сайта gramwallet.ioconnect.gramwallet.io
3Walt (ex. Wallet)бот бывшего @wallett.me/walt
4Keeperбывший Tonkeeperapp.tonkeeper.com

До 11 октября первым в реестре стоял бот @wallet, а запись «Telegram» добавили 15 сентября в самый конец списка. Старые версии ui рисовали первой захардкоженную кнопку «Wallet in Telegram», которая вела в бот. В новых версиях фиксированы только места двух кнопок, а имя, значок, мост и ссылка берутся из реестра, как у любого другого кошелька.

Названия стоит различать. В статьях и разговорах встроенный кошелёк Telegram называют Gram Wallet, и анонс TON Connect тоже пишет «Gram Wallet in Telegram». Но в реестре TON Connect встроенный кошелёк записан как Telegram, а под именем Gram Wallet там отдельное мобильное приложение со своим мостом. Кнопка «Подключить Telegram» ведёт во встроенный кошелёк, прозрачная кнопка под ней — в приложение.

Подпись «Walt (ex. Wallet)» — это не новое переименование. Бот кошелька TOP стал Walt ещё 28 сентября, реестр сменил имя 29 сентября. 11 октября к имени дописали пометку «ex. Wallet», а ссылку перевели на t.me/walt: тот же бот открывается и по @wallet. Новый интерфейс переносит пометку в скобках на вторую строку плитки, так что пользователь видит «Walt» и под ним «ex. Wallet».

Попутно исправлена ссылка, по которой интерфейс открывает кошелёк Telegram: для адреса без имени мини-приложения, как t.me/sendgrams, старые версии подставляли в ссылку буквальную строку appname=undefined. Это исправление вошло в ui 3.1.0, то есть и в 5.0.

Почему кошелёк Telegram не проходит старую проверку

Кошелёк Telegram работает на контракте WalletTg, и он устроен иначе, чем стандартные кошельки. На каждом адресе лежит не сам кошелёк, а крошечный неизменяемый трамплин: он передаёт управление коду WalletTg, который хранится в конфигурации сети под индексом −123 и меняется только голосованием валидаторов. Новая версия кошелька так достаётся всем пользователям сразу, без обновления каждого контракта.

Отсюда и поломка проверки. В walletStateInit кошелька Telegram лежит код трамплина, а не известный разборщику код кошелька v4 или v5, поэтому таблица версий ключ не находит. К тому же WalletTg позволяет сменить ключ подписи: в хранилище контракта ключ перезаписывается, и единственный источник текущего ключа — get-метод get_public_key у живого аккаунта. Документация TON Connect описала эту ветку 12 октября, на следующий день после выхода пакетов.

Справочный сервер из репозитория SDK в демо-приложении уже устроен так: сначала пытается разобрать walletStateInit, а если версия не опознана, спрашивает ключ у сети. Приложения, чей сервер написан по старым примерам только с первой половиной, подпись кошелька Telegram отвергают. Именно это нарушение спецификации анонс называет причиной неудачных входов.

Как встроенный кошелёк получает запросы приложений

TON Connect передаёт запросы между приложением и кошельком через мост — сервер, через который ходят зашифрованные сообщения. У кошелька Telegram мост свой, tonbridge.telegram.org, а запросы до клиента доходят по протоколу самого Telegram. В схеме MTProto слоя 230 у кошелька есть целый набор методов wallet.tonConnect*: создать сессию, получить ожидающие запросы, забрать запрос на одно устройство, отправить ответ. Запрос приложения приходит в клиент сообщением с именем приложения и темой, и TDLib описывает его как messageTonConnectRequest. В той же схеме есть wallet.getExistingWaltBalance: TDLib поясняет, что это проверка, есть ли у пользователя баланс в боте t.me/walt.

Где подключение уже работает

К 10 октября подключить кошелёк Telegram пробовали в Getgems, DeDust, STON.fi, Tonstakers, Tonnel и TON DNS. Запись «Telegram» приходит в окно выбора из реестра, поэтому появляется в списке и у приложения на старой версии библиотеки. Отдельная кнопка над рядом кошельков и исправленная ссылка — только с ui 3.1.0 и новее, а пройдёт ли вход с подписью, решает сервер приложения.