Как принимать USDT: что нужно со стороны инфраструктуры
USDT — самый популярный стейблкоин, но у него нет единственной сети. Один и тот же тикер выпущен сразу в нескольких блокчейнах, и с точки зрения инфраструктуры это разные токены с разными правилами проверки. Разберём, что нужно сервису, чтобы принимать USDT корректно, а не полагаться на удачу.
USDT — это несколько токенов с одним тикером
Самые распространённые варианты — USDT в сети TRON (стандарт TRC-20), в Ethereum (ERC-20) и в BNB Smart Chain (BEP-20). Встречаются и другие сети. Внешне для пользователя это один и тот же доллар, но технически — разные контракты на разных нодах, с разной адресацией и разными правилами газа. Адрес, выпущенный в одной сети, не имеет смысла в другой: одинаковый формат адреса ещё не значит совместимость.
У различий есть и практическое следствие для отправителя: чтобы вывести USDT из сети, нужна нативная монета этой же сети на оплату комиссии — TRX для TRC-20, ETH для ERC-20, BNB для BEP-20. Если клиент по ошибке отправил перевод не в ту сеть, у него часто просто не окажется монеты для оплаты комиссии на вывод, и без ручного вмешательства перевод зависает. Отсюда правило: сервис должен явно указывать сеть рядом с каждым адресом и не полагаться на то, что клиент сам разберётся.
Почему сеть нельзя определить по одному адресу
Проблема в том, что визуально адреса разных сетей иногда похожи, а иногда заметно различаются, но сам факт получения перевода не говорит вам, в какой сети он произошёл — это нужно знать заранее, до того как клиент отправит платёж. Поэтому сервисы обычно выдают отдельный адрес и отдельную инструкцию под каждую поддерживаемую сеть, и явно предупреждают: перевод не в ту сеть может быть невозможно принять.
Как технически проверить входящий перевод
Для сетей с виртуальной машиной, совместимой с Ethereum, — самой Ethereum
и BNB Smart Chain — перевод токена виден
как событие Transfer в логах контракта; его можно получить
методом eth_getLogs или подпиской на новые блоки.
TRON устроен иначе: вместо JSON-RPC там HTTP REST API с именованными путями.
Баланс адреса по TRC-20 контракту обычно проверяют вызовом
wallet/triggerconstantcontract, а не чтением логов. Пример
такого запроса — на странице TRON API и
на посвящённой именно USDT-TRC20 странице USDT TRC-20 API.
# tron — имя ноды для примера, точное название — в личном кабинете
curl -X POST https://nodes-api.cryptosafe.pro/proxy/tron/wallet/triggerconstantcontract \
-H "Authorization: <ваш-ключ>" \
-H "Content-Type: application/json" \
-d '{"owner_address":"<адрес-владельца>","contract_address":"<адрес-контракта-USDT>","function_selector":"balanceOf(address)","parameter":"<encoded-адрес>"}'
Важная деталь: HTTP-прокси не присылает уведомления сам — он только отвечает на запрос, который отправили вы. Значит, отслеживание входящих переводов строится на регулярном опросе ноды: сервис периодически проверяет баланс адреса или последние блоки и сравнивает результат с тем, что видел в прошлый раз. Частоту опроса каждый сервис подбирает сам, исходя из того, насколько быстро нужно замечать новые переводы.
Подтверждения перед тем, как считать перевод окончательным
Перевод, который только что появился в мемпуле или попал в последний блок, ещё может быть отменён при реорганизации цепочки. Поэтому перед тем, как зачислить сумму клиенту, сервис обычно ждёт несколько подтверждений — сколько именно, зависит от конкретной сети и её скорости финализации. Подробно про логику ожидания и реорганизации — в статье «Мониторинг транзакций».
Что показывать клиенту, пока перевод не подтверждён
Между «нода увидела перевод» и «перевод можно засчитать» проходит время, и это стоит явно отражать в интерфейсе, а не оставлять клиента в неведении. Обычно выделяют три состояния: перевод не найден (клиент ещё не отправил или сеть его не увидела), перевод найден, но подтверждений пока недостаточно, и перевод подтверждён окончательно. Явный статус снижает число обращений в поддержку с вопросом «а где мои деньги» — люди видят, что перевод уже замечен и просто ждёт финализации.
Что для этого нужно от доступа к ноде
С точки зрения интеграции всё сводится к обычному HTTP-запросу: нода
определяется по имени в адресе, а ключ передаётся в заголовке
Authorization без изменения тела запроса и без парсинга
прокси. Ключ можно привязать к IP-адресам ваших серверов — тогда даже
при утечке ключа запрос с чужого адреса не пройдёт. Если сервис работает
сразу в нескольких сетях с USDT, для каждой из них используется свой
путь — например, /proxy/tron или адрес, указанный на странице
Ethereum RPC для ERC-20-варианта.
Если для вашего сервиса ещё не решён вопрос, поднимать ли собственную инфраструктуру или пользоваться готовым доступом, — этот выбор подробно разобран в статье о приватных и публичных RPC-нодах.