13 октября команда TON Core опубликовала рекомендации для экосистемы по кошельку Telegram. Отдельным приложениям-кошелькам предложено новые кошельки по-прежнему создавать на контракте W5 (wallet-v5r1), потому что контракт кошелька Telegram, WalletTg, сделан под сам Telegram, но научиться открывать уже существующие кошельки Telegram по их секретной фразе. Это касается всех, кто хочет держать кошелёк Telegram ещё и в отдельном приложении: My Wallet и Gram Wallet такой импорт уже поддерживают, код для него появился в версии 26.10.0 от 6 октября.

Пост в Telegram — @toncore
Рекомендации TON Core для приложений, обозревателей и кошельков

Как открыть кошелёк Telegram в другом приложении

  1. Запишите адрес кошелька из Telegram. По нему вы потом сверите, тот ли кошелёк открылся.
  2. Возьмите текущую фразу. По документации в Telegram Desktop она лежит в меню кошелька: Keys & Backup → Show Recovery Phrase. Если ключ не меняли, слов 12. После смены ключа фраза становится из 24 слов, и вводить нужно все 24. Как хранить фразу, разобрано отдельно.
  3. Импортируйте фразу в My Wallet или Gram Wallet: Import Existing Wallet → Secret Words. Обе программы собраны из одного открытого кода, а в App Store и на wallet.ton.org сейчас стоит версия 26.10.4, в которой поддержка уже есть.
  4. Сверьте адрес. Совпал с адресом из Telegram — кошелёк открыт целиком, с балансом и историей. Не совпал — приложение открыло другой кошелёк на том же ключе, и причина обычно одна из трёх ниже.

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

Ключ меняли, а введены первые 12 слов. После смены ключа первая половина фразы по-прежнему задаёт адрес, но подписывать уже не может. Приложение видит, что ключ в контракте другой, и показывает пустой кошелёк W5 вместо вашего. Нужны все 24 слова текущей фразы.

Фраза устарела. Если ключ меняли больше одного раза, а у вас записана предыдущая версия фразы, приложение ответит, что публичный ключ не совпадает с фразой и кошелёк, возможно, сменил ключ. Помогает только свежая фраза из Telegram.

Приложение, которое кошелёк Telegram не поддерживает, 12 слов чаще всего примет как обычную фразу BIP-39 и покажет кошелёк с другим адресом и нулевым балансом, а 24 слова из двух половин может не принять вовсе. Монеты при этом остаются на адресе Telegram: приложение просто не умеет считать этот адрес из фразы и подписывать переводы в формате WalletTg.

После импорта ключ живёт в двух местах. Telegram по умолчанию держит зашифрованную копию ключа, разделённую на три части в дата-центрах на трёх континентах, а теперь ключ лежит ещё и в приложении. Если цель — переехать на самостоятельное хранение, документация описывает выход: Keys & Backup → Disable Backup с отметкой Update Secret Phrase. Telegram сменит ключ подписи, покажет новую фразу из 24 слов и отключит облачную копию только после того, как смена прошла в сети. За смену ключа берётся комиссия в Gram. Адрес остаётся прежним, старая фраза перестаёт подписывать переводы, и в приложение после этого нужно заново ввести новые 24 слова.

Одна фраза, три адреса

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

Ключ из фразы кошелька Telegram выводится по общему стандарту BIP-39 с путём m/44'/607'/0', тем же, по которому My Wallet выводит ключ из своей фразы из 12 слов. Дальше пути расходятся. В начальном состоянии кошелька Telegram лежит код трамплина и четыре поля: номер ревизии хранилища, счётчик seqno, номер подкошелька 0x7FFF7F11 и открытый ключ. У W5 код другой и свой идентификатор кошелька.

Как это выглядит на деле, проверено на одноразовой фразе из 12 слов, сгенерированной для этой статьи: по алгоритму из документации получен один ключ и из него три адреса.

КонтрактАдрес из одной фразы
WalletTg, кошелёк TelegramUQAgG-WT…P_gXad
W5 (wallet-v5r1)UQA7s7tH…boDdkIc
v4R2UQBVSR_U…djLee01

Какой из адресов покажет приложение, решает его код. My Wallet и Gram Wallet для фразы из 12 слов перебирают все известные им версии контракта, от самых старых до W5 и telegram, на двух первых индексах пути. Кошелёк Telegram попадает в этот список, только если по адресу работает контракт с трамплином, номер подкошелька равен 0x7FFF7F11, а ключ из get-метода get_public_key совпадает с ключом из фразы. Из найденного выбирается W5, если у него есть история; иначе адрес с наибольшим балансом; иначе адрес с последней транзакцией; если пусто везде — W5. Отсюда и пустой W5 в ловушках выше: когда кошелёк Telegram не прошёл проверку, его просто нет среди кандидатов.

Фраза из 24 слов идёт отдельной веткой. Если каждая половина по 12 слов проходит проверку BIP-39, первая половина даёт ключ-якорь и из него адрес, а вторая — ключ подписи, который сверяется с ключом в контракте. Нужна эта ветка потому, что 24 слова бывают и у старых кошельков TON, выведенных по собственным правилам сети. Документация велит в спорном случае показать пользователю оба кандидата и не выбирать молча; My Wallet сначала пробует ветку Telegram и, если контракта Telegram по адресу нет, а фраза годится как обычная, импортирует её как обычную.

Как кошелёк Telegram меняет ключ, не меняя адрес

В хранилище контракта WalletTg ключ подписи — изменяемое поле, а адрес навсегда вычислен из начального состояния, где стоял первый ключ, ключ-якорь. Смена ключа — это запрос, подписанный текущим ключом, с доказательством, подписанным новым. Контракт перезаписывает поле, и get-метод get_public_key с этого момента возвращает новый ключ. Ключ-якорь по-прежнему нужен, чтобы пересчитать адрес, но подписывать уже не может. Чтобы зашифрованные комментарии, полученные до смены ключа, оставались читаемыми, контракт публикует при каждой смене прежний закрытый ключ, зашифрованный новым: по цепочке таких событий приложение восстанавливает все предыдущие ключи, имея на руках только текущий.

Почему новые кошельки остаются на W5

Обоснование в рекомендации TON Core одно: WalletTg сделан специально для Telegram. Устройство контракта показывает, что за этим стоит.

Код кошелька общий и меняется голосованием. На каждом адресе кошелька Telegram лежит неизменяемый трамплин, который передаёт управление коду из параметра −123 конфигурации сети. Новую версию этого кода утверждают валидаторы, по документации нужно больше 75% их суммарного веса, и она достаётся всем кошелькам Telegram сразу. Владелец кошелька не может закрепить версию или отказаться от обновления. Код W5 лежит в каждом аккаунте свой и не меняется.

Расширений нет. W5 поддерживает расширения: отдельные контракты, которым кошелёк разрешает действовать от своего имени, на них строятся подписки и дополнительные проверки. Двухфакторное подтверждение в самом My Wallet ищется именно как расширение кошелька W5. В WalletTg расширений нет, по замыслу авторов двухфакторка и подписки появятся в нём встроенными функциями.

Формат запросов свой. Подпись в WalletTg стоит перед телом запроса, у отправки одного сообщения отдельный короткий формат, а для оплаты комиссии за пользователя запрос передаётся через посредника, который прикладывает Gram на исполнение. Приложению, которое хочет не только показывать баланс кошелька Telegram, но и отправлять с него, приходится реализовать этот формат отдельно от W5.

Главное, ради чего WalletTg устроен иначе, — смена ключа без смены адреса. В описании контракта сказано прямо: Telegram создаёт кошелёк, но позволяет перейти на самостоятельное хранение. Импорт в сторонние приложения и есть вторая половина этого перехода.

Что рекомендовано остальным

Приложениям с входом через кошелёк TON Core советует обновить TON Connect и проверять подпись ton_proof так, как требует спецификация: ключ кошелька Telegram берётся из сети, а не из начального состояния. Эта часть разобрана в материале про TON Connect 5.0.

Обозревателям блокчейна предложено распознавать кошельки Telegram, показывать смену ключа и помечать переводы, комиссию за которые заплатил не владелец. API Toncenter научился различать контракт кошелька Telegram и отдаёт смену ключа отдельным действием с новым ключом и зашифрованным старым, а tonscan.org на этих данных уже подписывает кошельки Telegram и переводы с пометкой «Fee paid by Telegram».