Среди пользователей криптовалют сложилось устойчивое убеждение: если ты указал правильный адрес, выбрал нужную сеть и держишь достаточно средств на комиссию в нативном токене этой сети - транзакция пройдёт. Это убеждение справедливо в подавляющем большинстве случаев. Но у каждого блокчейна есть свои скрытые правила, которые проявляются только в специфических ситуациях - и тогда транзакция завершается ошибкой, хотя ты, кажется, сделал всё правильно.
Именно в такую ситуацию мы попали, когда клиент нашего обменника Rabbit.io менял LTC на SOL, и сумма обмена составляла менее 0,0003 SOL (по текущему курсу это менее трёх центов). Во многих моих статьях вы могли прочитать, что у нас нет верхних лимитов для обмена, - но да, и такие мизерные объёмы мы тоже часто можем обменять.
При отправке требуемой суммы мы столкнулись с проблемой. Адрес верный, сеть - Solana Mainnet, баланс у нас достаточный, комиссия учтена. Но транзакция раз за разом завершалась ошибкой.
Разгадка оказалась неожиданной даже для нас. И когда мы рассказали о ней клиенту, он тоже не поверил. Этот случай натолкнул меня на вопрос: сколько ещё подобных “подводных камней” прячется в разных блокчейнах? Я изучил этот вопрос и собрали наиболее интересные примеры - те, что могли бы застать врасплох даже меня, хоть я и провожу множество обменов самых разных криптовалют ежедневно.
Многие слышали, что в Solana для получения токенов стандарта SPL (USDT, USDC и других) на адрес, на котором ранее этих токенов никогда не было, чтобы на адресе получателя была небольшая сумма SOL. Строго говоря, это не совсем так, но часть правды в этом есть. Каждый токен хранится в отдельном Associated Token Account (ATA), и его создание стоит около 0,002 SOL. Но и без SOL на балансе токены поступят, потому что за создание ATA платит отправитель, а не получатель.
В Solana каждый аккаунт - это не просто строка адреса, а запись в файле текущего состояния блокчейна. Аккаунт занимает место на дисках валидаторов, и за это место нужно платить. Механизм называется rent exemption: чтобы аккаунт существовал, его баланс должен превышать определённый минимум - стоимость двух лет аренды.
Это значение не фиксировано. Для базового системного аккаунта (обычного кошелька) на данный момент оно составляет минимум составляет 890 880 лампортов, или 0,00089088 SOL. Если адрес ещё не существует в блокчейне (баланс = 0), то первая транзакция, пытающаяся создать аккаунт с суммой ниже этого порога, отклоняется.
Именно это и происходило с нашими попытками: мы отправляли чуть меньше 0,0003 SOL на новый адрес, а это в три раза ниже порога. Наш клиент отказывался верить в существование этой проблемы. А официальных подтверждений, опубликованных разработчиками Solana, мы никак не могли найти. Пришлось довольствоваться косвенным упоминанием: описание RPC-метод getMinimumBalanceForRentExemption в документации Solana подтверждает это правило, хотя в явном виде применительно к нативному SOL о нём, похоже, нигде не написано.
Важная особенность: ранее мы неоднократно отправляли клиентам такие маленькие суммы, и все транзакции проходили успешно. Мы точно знали - на собственном опыте - что в сети Solana можно отправлять сколь угодно маленькие суммы. Но оказалось, что суммы меньше данного порога можно отправлять только если это будет вторая, третья, сотая, но не первая транзакция для адреса-получателя. В этих случаях адрес действительно может получать любые суммы - хоть 1 лампорт. Но при первичном создании аккаунта проводится проверка rent exemption.
Проблема, с которой мы столкнулись в сети Solana - это частный случай более широкого паттерна. В нескольких сетях адрес как криптографический объект и аккаунт как запись в распределённом реестре (блокчейне) - разные вещи. Без минимального начального баланса аккаунт не существует.
В XRP Ledger каждый новый аккаунт должен получить минимальный резерв - 1 XRP. Раньше было было гораздо больше: 200 XRP при запуске сети, затем 50 XRP, 20 XRP, 10 XRP и, наконец, с ростом курса XRP это требование было снижено до сегодняшнего 1 XRP.
Коварство ситуации в том, что этот резерв постоянно заморожен на счёте. Его нельзя потратить, нельзя использовать как комиссию. Некоторые эксплореры XRP Ledger даже специально разделяют полный баланс и замороженный баланс адреса, чтобы владельцы кошельков могли чётко видеть, сколько XRP на их адресах доступны для трат.
В сети Stellar адрес тоже не существует в реестре до тех пор, пока не получит минимальный баланс. Аккаунт требует не менее 1 XLM для существования.
Помните, я как-то рассказывал об ошибке, превратившей 1000 USDC в 1 XLM? В ситуации, описанной в той статье, речь идёт как раз о случае, когда транзакция с токенами не может быть зачислена на баланс получателя, даже если она корректно подписана отправителем. Особенностью XRP Ledger и Stellar является то, что ни один аккаунт не может получить токены, не дав предварительно явное согласие на взаимодействие с конкретным активом. Это сознательное решение разработчиков, и в нём есть плюсы: в других сетях адреса китов и знаменитостей страдают от того, что на них постоянно присылают свежесозданные мусорные токены, чтобы возникла иллюзия того, будто владелец адреса купил их. В XRP Legder и в Stellar такой спам невозможен.
Lightning Network - это сеть платёжных каналов для отправки биткоинов без записи перевода в блокчейн. В отличие от обычных блокчейн-транзакций, платёж здесь не проверяется всеми узлами сети и не закрепляется майнерами, включающими его в блок. Транзакция проходит лишь через цепочку промежуточных узлов. И именно здесь возникает целый класс нетривиальных ошибок.
Чтобы платёж дошёл от Алисы к Дэвиду через промежуточные узлы Боба и Кэрол, на каждом участке маршрута должна быть достаточная ликвидность в нужном направлении. Сеть знает общую ёмкость каждого канала, но не знает, как баланс распределён между двумя сторонами. И может сложиться такой сценарий:

Перевод от Алисы к Дэвиду застревает с ошибкой NO_ROUTE, хотя у Алисы есть необходимая сумма, у Дэвида есть действующий канал для её принятия, и формально маршрут существует.
Особо коварный нюанс: в LND (одной из распространённых реализаций узла Lightning) лимит комиссии маршрутизации по умолчанию равен 0 сатоши. Это означает, что система ищет только бесплатные маршруты, а их практически не существует в реальной сети. Те пользователи, которые не изменили настройки по умолчанию, будут видеть ошибку “маршрут не найден”, хотя перевод вполне возможен при небольшой комиссии.
Но это не недоработка. Наоборот, разработчики LND осознанно решили не решать за пользователей, какую максимальную комиссию они готовы заплатить. Если бы в настройках изначально был выставлен положительный лимит, пользователи могли бы столкнуться с тем, что при переводе с них была списана комиссия, которую они не ожидали.
Другие причины ошибки «маршрут не найден в сети Lightning»:
Со всеми этими ошибками мы сталкиваемся при обмене биткоинов в сети Lightning Network. Они выглядят очень необычно для криптовалют, но каждая такая проблема может быть решена. Роэтому на rabbit.io вы можете и отправлять, и получать биткоины через сеть Lightning без опасений.
Стейблкоины USDT и USDC - централизованно управляемые активы. Их смарт-контракты содержат функции заморозки адресов: эмитенты (Tether и Circle) могут в любой момент добавить адрес в чёрный список, после чего операции с токенами на этом адресе станут невозможными.
Это известный факт. Но мало кто знает, что архитектура ограничений у USDT и USDC принципиально различается, и это имеет важные практические последствия.
В контракте USDC проверка чёрного списка применяется и к отправителю, и к получателю. Если адрес получателя заблокирован, транзакция будет отклонена смарт-контрактом. Отправитель просто потеряет немного газа, а USDC останутся у него. Это безопасная для отправителя модель: его деньги не уйдут в никуда.
В исходном коде контракта USDT (TetherToken) функция transfer содержит проверку: require(!isBlackListed[msg.sender]). Это означает, что проверка касается только отправителя, а адрес получателя при переводе не проверяется.
К чему это приводит на практике:
Ни получатель, ни кто-либо ещё не сможет с ними ничего сделать - исходящие операции с заблокированного адреса запрещены. Разве что сам эмитент - Tether - cможет уничтожить токены на заблокированном адресе, обнулив его баланс. Для этого предназначена функция destroyBlackFunds.
Похоже, что в этом примере уж лучше пусть будет так, что правильно составленная транзакция не пройдёт. Этот подход, реализованный в смарт-контрактах USDC, кажется гораздо более честным, чем то, как это организовано в USDT, где транзакция пройдёт, но потом выяснится, что это было зря.
Мои советы:
Но и при отправке USDT транзакция тоже в некоторых случаях может не пройти, даже если все данные будут в ней на первый взгляд указаны абсолютно корректно. О таких случаях, а также о малоизвестных ограничениях на транзакции в сетях Биткоин, Эфириум и Трон, читайте во второй части этой статьи.
Она выйдет здесь же ровно через неделю.