> ## Documentation Index
> Fetch the complete documentation index at: https://whalescorp.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Получение и отправка транзакций

# Отправка TON-транзакций

<Info>
  Обратите внимание: отправка транзакции является необратимым процессом. После подтверждения отменить отправку невозможно.
</Info>

1. Подготовка транзакции:

* Приложение получает текущее значение `seqno` кошелька через RPC, функция `fetchSeqno`.

<Info>
  `Seqno`, то есть `sequence number`, — это порядковый номер транзакции кошелька в TON. Это счетчик, показывающий, сколько транзакций этот кошелек уже отправил.
</Info>

* Получает последний блок блокчейна для проверки актуальности транзакции.
* Формирует внутреннее сообщение с параметрами:
  * адрес получателя
  * сумма в nanotons
  * payload, то есть комментарий или данные
  * `stateInit`, если нужна активация кошелька получателя

2. Создание внешнего сообщения:

* Создается внешнее сообщение для смарт-контракта кошелька.
* Если `seqno === 0`, то есть это первая транзакция, добавляется `init` для деплоя контракта.
* Сообщение подписывается локально приватным ключом через `WalletContract.createTransfer`.

3. Подписание:

* Приватный ключ извлекается из secure storage после аутентификации через PIN или биометрию.
* Подписание выполняется локально на устройстве, приватный ключ никогда не покидает устройство.

4. Отправка в сеть:

* Подписанное сообщение сериализуется в `BOC`, то есть `Bag of Cells`.
* Отправляется в TON RPC node через `client.sendMessage(msg.toBoc())`.
* Приложение регистрирует транзакцию как `pending` для отслеживания статуса.

5. Ожидание подтверждения:

* Приложение отслеживает статус через `BlocksWatcher`, то есть SSE-подключение к блокчейну.
* Когда транзакция появляется в блоке, статус меняется с `pending` на `confirmed`.

# Отправка TON-токенов

Отправка token — это отправка внутреннего сообщения на адрес token-wallet отправителя.

* Отправитель в Tonhub подписывает и отправляет внешнюю TON-транзакцию в свой token-wallet с инструкцией перевода token.
* Эта внешняя транзакция уходит в основной wallet contract отправителя.
* Token-wallet отправляет internal message в token-wallet получателя, либо создает его при необходимости.

Структура TON-транзакции:

* В payload сообщения в token-wallet передается:
  * `op_code = 0xf8a7ea5`, это token transfer
  * сумма token
  * адрес получателя
  * адрес для возврата excess, обычно адрес отправителя
  * `forward_ton_amount`, комиссия за доставку token получателю
  * `forward_payload`, комментарий при наличии

# Отправка Solana-транзакций

1. Получение последнего blockhash:

* Выполняется запрос `getLatestBlockhash()` к Solana RPC node.
* `Blockhash` нужен для валидности транзакции и обычно действует около 60 секунд.

2. Сборка транзакции:

* Создается инструкция `SystemProgram.transfer`.

3. Добавление комментария, memo:

* Если есть комментарий, добавляется инструкция с `programId = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"`.

4. Подписание транзакции:

* После аутентификации извлекается приватный ключ.
* Транзакция подписывается локально через `transaction.sign(keyPair)`.
* Подписание выполняется на устройстве.

5. Сериализация и отправка:

* Транзакция сериализуется в base64 через `transaction.serialize().toString('base64')`.
* Отправляется в Solana RPC через `client.sendEncodedTransaction(encodedTx)`.
* Возвращается signature в base58, это и есть transaction ID.

6. Резервный механизм:

* Если основной RPC недоступен, используется публичный резервный client.

# Отправка SPL-токенов (Solana)

Структура похожа на отправку SOL, но есть свои особенности:

1. Tonhub поддерживает отображение отдельного token на SOL.
2. Token Account, то есть ATA:

* Каждый SPL token хранится в отдельном token account, то есть `Associated Token Account`.
* Адрес ATA вычисляется детерминированно на основе owner address и token mint address.

3. Автоматическое создание ATA:

* При отправке token приложение проверяет, существует ли ATA у получателя.
* Если ATA отсутствует, автоматически добавляется инструкция `createAssociatedTokenAccount`.
* Это требует дополнительной комиссии в SOL, примерно 0.002–0.003 SOL.

4. Структура транзакции:

* Instruction 1, при необходимости, это `createAssociatedTokenAccount` для получателя.
* Instruction 2 — `createTransferInstruction` для перевода token из ATA отправителя в ATA получателя.

# Мониторинг транзакций

## TON

1. BlocksWatcher (SSE):

* Приложение устанавливает SSE-соединение с Tonhub API, `mainnet-v4.tonhubapi.com` или `testnet-v4.tonhubapi.com`.
* Когда появляется новый блок, сервер отправляет список изменившихся адресов.
* Если адрес пользователя есть в этом списке, приложение запрашивает новые транзакции.

2. Запросы транзакций:

* Используется `fetchTransactionsPage` с cursor pagination.
* Транзакции загружаются пачками, обычно по 20–50.

3. Обработка:

* Выполняется разбор тел сообщений, чтобы определить тип операции, например обычный перевод, перевод token и так далее.
* Обновляются балансы и история транзакций в интерфейсе.

## Solana

1. Polling через RPC:

* Выполняются периодические запросы в Solana RPC, чтобы получить транзакции по адресу. Точного fixed interval нет, запрос выполняется каждый раз при открытии или повторной активации экрана.
* Используются `getSignaturesForAddress` и `getTransaction`.

2. Мониторинг token account:

* Для каждого известного SPL token проверяется баланс соответствующего ATA.
* Изменения баланса интерпретируются как входящие или исходящие транзакции.

3. Обработка:

* Разбор transaction instructions для определения типа операции.
* Обновление балансов SOL и SPL token.

# Лимиты суммы транзакций

**TON, SOL** — минимального ограничения на сумму отправки нет. Пользователь может отправить даже 0, но система покажет предупреждение о нулевой транзакции. Входящие транзакции меньше 0.1 TON, это настраиваемое пользователем значение, автоматически классифицируются как SPAM, и уведомления по ним не отправляются. SOL-транзакции как spam не маркируются. Максимальная сумма ограничена балансом за вычетом fees.\
**Tokens** — минимальная сумма перевода token должна быть больше нуля. Точное минимальное значение определяется характеристиками конкретного token, обычно это одна минимальная единица, например 0.000001 для USDT. Комиссия в TON оплачивается отправителем, кроме случая gasless transactions.

# Статусы транзакций

`Pending`, ожидание подтверждения:

* Транзакция отправлена, но еще не включена в блок.
* Для TON это означает ожидание включения блока через BlocksWatcher.
* Для Solana это означает ожидание подтверждения через RPC polling.

`Confirmed`:

* Транзакция включена в блок и подтверждена сетью.
* Для TON обычно достаточно одного подтверждения.
* Для Solana обычно требуется несколько подтверждений, чтобы достичь finality.

`Failed`:

* Транзакция отклонена сетью, например из-за недостатка средств, неверного адреса и так далее.
* Приложение показывает пользователю ошибку.

# Почему пользователь может «не видеть» входящую транзакцию

* **Неверная сеть.** В TON в приложении может быть выбрана `testnet`, а перевод пришел в `mainnet`, или наоборот. Адреса в mainnet и testnet отличаются, это разные формат и флаг. Нужно проверить переключатель сети и убедиться, что отправитель использовал адрес и сеть, соответствующие выбранным в Tonhub. В Solana может быть выбрана `mainnet`, а перевод сделан в `devnet`, или наоборот. История и баланс запрашиваются отдельно для mainnet и devnet. Нужно сверить сеть у отправителя и в Tonhub, если там есть переключатель.
* **Неверный актив или неверный экран.** В TON входящий token показывается в разделе токенов, а не в "основной" истории TON. Если пользователь смотрит только список переводов нативного TON, он не увидит token-депозиты. В Solana входящий SPL token виден в балансе и истории конкретного токена, то есть по ATA. Если token не добавлен в список или пользователь смотрит только историю SOL, входящий перевод токена легко пропустить.
* **Задержка RPC и обновления.** В TON данные приходят через Tonhub API и BlocksWatcher. При задержках или разрывах SSE обновление списка транзакций может приходить с опозданием. В этом случае помогает ручное обновление, `pull-to-refresh`, или повторное открытие экрана. В Solana список транзакций не опрашивается по таймеру, а обновляется при открытии экрана и возврате в приложение. Если RPC или indexer отстают, новые входящие транзакции появятся только при следующем таком обновлении. Рекомендация: обновить экран вручную или подождать и открыть его заново.
* **Адрес еще не активирован, TON.** Перевод пришел на адрес, который никогда не отправлял транзакций, то есть контракт еще не был задеплоен. Такие входящие переводы могут не отображаться в истории до первой активации кошелька. Пользователю нужно выполнить первую исходящую транзакцию, то есть активацию, после чего баланс и история должны обновиться.
* **Ошибка отправителя или rollback.** Транзакция не попала в блок, например была отклонена сетью, у отправителя не хватило fee, в Solana истек `blockhash` и так далее. Фактически получатель ничего не получил, поэтому в его истории нет транзакции. Нужно проверить статус транзакции и ее hash в explorer вместе с отправителем, для TON или Solana.

# Пограничные случаи и повторная отправка

### Транзакция отправлена, но RPC не вернул ответ

**TON и Solana:** при timeout на RPC выполняется повтор с backoff, в TON до 15 попыток, в Solana до 5. Если все попытки проваливаются, пользователю показывается ошибка, а `pending` не создается.\
**Риск:** RPC мог принять транзакцию, но ответ не дошел. При повторной отправке создается вторая транзакция, а значит возможен двойной расход средств.\
**Рекомендация:** при долгом ожидании и ошибке сначала проверить историю и баланс, и только потом повторять отправку.

### Транзакция зависла в pending-статусе

Timeout для TON и Solana составляет 60 секунд. Приложение опрашивает статус примерно каждые 6 секунд. При подтверждении `pending` переходит в `sent`. Если подтверждение не пришло за 60 секунд, статус меняется на `timed-out`, без повторного списания средств.

### Solana: истечение blockhash

В Solana транзакции имеют ограниченный срок жизни через `lastValidBlockHeight`. Tonhub отдельно не проверяет истечение `blockhash`, поэтому транзакция остается в `pending` до подтверждения или 60-секундного timeout.

### Политика повторной отправки

**TON:** кнопка `Send again` появляется только после перехода в статус `timed-out`.\
**Solana:** кнопки retry нет. После timeout пользователь создает новый transfer вручную.

* Пока транзакция находится в статусе `pending`, то есть первые 60 секунд, приложение только ждет подтверждения.

# Безопасность при отправке

* **Контакты.** В приложении реализован раздел контактов, который позволяет сохранять знакомые адреса и назначать им имена. При отправке или получении транзакций имя контакта автоматически показывается пользователю, что повышает уверенность в правильности выбора получателя.
* **Заблокированные адреса, deny list и spam.** Некоторые адреса кошелек автоматически помечает как spam, что помогает избежать случайной отправки средств на них. Список таких адресов хранится на backend и может дополняться вручную. Полной блокировки отправки на такие адреса нет — реализовано только визуальное предупреждение.
* **Restricted addresses.** Для адресов, отмеченных как restricted, например отдельных staking pools, перед отправкой показывается диалог с предупреждением. Отправка возможна только после явного подтверждения пользователя, `Continue anyway`. Эта функция связана с ограничениями smart contract, а не с защитой от фишинга.
* **Домены `.ton`.** При вводе `.ton`-домена система выполняет резолв адреса и перед отправкой проверяет соответствие полученного адреса целевому, включая token transactions. Если обнаружено несоответствие, показывается ошибка `transfer.error.invalidDomain`. Это снижает риск подмены адреса через поддельные домены.
* **Предупреждение о несовпадении сети.** Если пользователь находится в `mainnet`, но ввел адрес формата `testnet`, или наоборот, система показывает предупреждение и просит подтверждение, `transfer.error.addressIsForTestnet`. Это помогает предотвратить случайную отправку средств в неправильную сеть.

# Gas при отправке

Комиссия за транзакцию всегда оплачивается отправителем, независимо от типа переводимых активов. Tonhub не дает пользователю возможности настраивать размер fee вручную. Сумма рассчитывается с запасом, чтобы гарантировать покрытие всех расходов на выполнение транзакции. Неиспользованная часть комиссии автоматически возвращается на баланс пользователя как отдельная входящая транзакция. Комиссия оплачивается только в нативной валюте соответствующего блокчейна: TON для TON network и SOL для Solana network. Из-за этого у пользователя могут быть tokens на балансе, но не быть возможности отправить их из-за нехватки нативной валюты на fee. Чтобы решить эту проблему, в TON ecosystem была разработана новая версия кошелька W5 с поддержкой gasless transactions.

# Gasless-транзакции

Gasless transactions не означают полного отсутствия комиссий для пользователя. Эта технология позволяет оплачивать fee при отправке некоторых tokens в валюте самих tokens. Tonhub поддерживает gasless transactions только для TON blockchain. Например, при использовании W5 wallet пользователь может оплатить fee за отправку USDT напрямую токенами USDT, даже если на балансе нет TON. Пользователь может выбрать способ оплаты: в token currency, это будет дороже из-за обменных издержек, либо в нативной валюте.

## Как работает gasless

Gasless sending по сути является надстройкой над обычными транзакциями. Сеть не может отправлять транзакции без оплаты fee в нативной валюте, потому что именно эти средства идут валидаторам. В gasless sending relayer оплачивает комиссию сам, удерживая часть отправляемого token себе. Relayer в этом контексте — это сервис с TON wallet, который:

* Принимает подписанную транзакцию от пользователя, где пользователь платит fee token-ом
* Самостоятельно отправляет эту транзакцию в блокчейн и оплачивает fee в TON со своего wallet
* Удерживает fee в token

У Tonhub нет собственного relayer, поэтому используется сторонний сервис.

## Ограничения gasless

* Доступно только для части tokens, список приходит с сервера из config, например это USDT.
* Поддерживается только на V5 wallets.
* Существует cooldown: оплачивать fee token-ом можно только раз в несколько минут. При необходимости пользователь может подождать или заплатить в TON.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.