Однажды в службу поддержки rabbit.io обратился пользователь с жалобой на то, что в результате обмена он вместо 1000 USDC получил 1 люмен XLM. Всё выглядело так, как будто он понёс огромные убытки. Ведь 1 XLM стоит намного меньше доллара (сейчас курс люмена - около $0,22).
Мы, конечно же, стали разбираться, что случилось. Если бы выяснилось, что ошибка в сумме и криптовалюте получения возникла по нашей вине, мы бы, разумеется, немедленно приняли меры, чтобы отправить пользователю правильную сумму.
Но всё оказалось гораздо интереснее. Наша автоматика сработала как положено, без сбоев. На адрес, предоставленный клиентом, было отправлено 1000 USDC. Но в сети Stellar есть одна особенность, которая действительно может превратить 1000 USDC в 1 XLM, если забыть об одной важной детали.
Давайте я расскажу вам, в чём дело.
Клиент rabbit.io был опытным пользователем криптовалют. Именно поэтому он хранил все средства в собственных некастодиальных кошельках, а не на биржах. Но обычно он пользовался EVM-сетями. И он привык, что какие бы токены ни были отправлены на твой адрес, они все будут успешно зачислены в кошелёк. В худшем случае нужно будет вручную добавить отображение соответствующего токена в интерфейсе кошелька. В совсем крайнем случае (если приложение-кошелёк не поддерживает конкретный токен) нужно импортировать сид-фразу в другое приложение, которое позволяет этим токеном пользоваться.
А в Stellar всё по-другому. Архитектура этой сети принципиально отличается от того, к чему мы привыкли в Ethereum, Binance Smart Chain или Polygon. Даже сами кошельки (адреса) в сетях EVM бесплатные, а в Stellar за добавление нового адреса в реестр нужно платить 1 XLM (ровно ту сумму, которую увидел на своём балансе наш клиент в результате обмена).
А самое важное для понимания проблемы отличие Stellar от EVM состоит в том, что в кошельках Stellar от получателя требуется дать разрешение на приём нового токена. Например, чтобы впервые получить USDC, кошелёк должен явно создать линию доверия (Trustline) к эмитенту USDC и заморозить 0.5 XLM для обеспечения записи об этой линии доверия в реестре.
Изначально при отправке токенов на адрес без Trustline транзакция просто отменялась с ошибкой. Вся сумма оставалась у отправителя. Но начиная с версии Stellar Protocol 15 в сеть внедрён механизм Claimable Balance. Это было сделано для удобства: чтобы можно было отправить любые токены на “неподготовленный” кошелёк.
Когда мы отправили USDC на адрес нашего клиента, у которого не было линии доверия к USDC, протокол сработал следующим образом:
То есть эти средства висели в реестре и ждали, пока кто-то нажмёт кнопку “Забрать” (Claim). Кто именно может это сделать - прописано в условиях транзакции. По умолчанию это могут сделать и отправитель, и получатель.
А откуда на счёте получателя взялся 1 XLM? Ответ: от нас, но специально мы его туда не отправляли. Вот как обрабатываются подобные транзакции.
Шаг 1. Отправитель инициирует перевод USDC и подписывает транзакцию вида:
На этом этапе отправитель соглашается с уплатой необходимого количества XLM за транзакцию, но обычно не обращает внимания на конкретную сумму комиссии, ведь в сети Stellar они обычно очень маленькие.
Шаг 2. Stellar проверяет, может ли операция быть выполнена:
Всё это говорит о том, что платёж невозможен.
Шаг 3. Включается механизм claimable balance. Чтобы не отвергать транзакцию, Stellar:
Но чтобы получатель мог быть указан как потенциальный владелец зависших средств, аккаунт получателя должен существовать в сети. На момент транзакции аккаунт существовал только в приложении-кошельке получателя. А в сети он объявлен не был.
В таких обстоятельствах аккаунт создаётся (объявляется) в рамках обрабатываемой транзакции. Но выше я писал, что в сети Stellar добавление адреса - платная операция. Кто за неё платит?
1 XLM для создания аккаунта берётся с источника операции, то есть со счёта отправителя:
1 XLM - это часть стоимости выполнения операции. Технически он попадает на баланс получателя, но воспользоваться ими он не может. В частности, он не может взять половину этой суммы, чтобы заплатить за создание Trustline для токена USDC и забрать причитающуюся сумму.
Если бы аккаунт получателя уже был известен сети, но в нём не было бы линии доверия к токену USDC, то дополнительный 1 XLM от нас не потребовался бы для проведения транзакции. А получатель вообще не заметил бы, что в его кошельке что-то изменилось.
В первую очередь, получателю нужно было подписать операцию типа changeTrust, относящуюся к токену USDC. Для успеха этой операции на балансе кошелька должны быть свободные 0,5 XLM. При создании линии доверия они блокируются.
У нашего клиента не было свободных XLM. Точнее, был только тот 1 XLM, который появился на счёте в результате обмена, однако воспользоваться им невозможно. Поэтому мы предложили ему обменять небольшое количество любой имеющейся у него криптовалюты на люмены. Он обменял, и это позволило ему создать “линию доверия” в своём кошельке.
Когда линия доверия к токену USDC была открыта, у пользователя появилась возможность забрать свои 1000 USDC. В данном случае всё для всех закончилось хорошо.
Ситуация, в которую попал клиент rabbit.io, не уникальна. И ему действительно повезло, что он использовал некастодиальные кошельки, находившиеся под его полным контролем. Только благодаря этому всё удалось легко решить.
А я вспомнил об этой истории, потому что на днях прочитал об аналогичной проблеме, которая возникла у другого пользователя. Там речь идёт о точно такой же сумме - 1000 USDC, - но есть обстоятельства, существенно затрудняющие решение проблемы.
Три дня назад в LinkedIn под последним постом CEO биржи Uphold Саймона МакЛафлина появился комментарий, где пользователь биржи рассказал такую историю:
Поскольку симптомы совпадают, я предполагаю, что проблема в том же самом. Вероятно, биржа Uphold создаёт адреса для депозитов непосредственно по запросу, и адрес для XLM, на который пользователь отправил средства, ещё не был объявлен в сети. Именно поэтому в результате транзакции на нём появился 1 XLM. Но вот 1000 USDC на нём не появились, и для исправления ситуации требуется, чтобы Uphold вручную создал для этого адреса линию доверия к USDC.
Почему служба поддержки отказала пользователю в восстановлении доступа к средствам? Подозреваю, что дело в системе безопасности. Любой бизнес, который работает с криптовалютами, очень тщательно выстраивает систему безопасности своих кошельков. Видимо, биржа Uphold не внедрила в неё процесс создания линий доверия там, где они не предполагались по базовому сценарию. И добавление этой дополнительной функциональности с учётом всех требований к безопасности теперь может обойтись бирже в сумму, которая намного превосходит 1000 USDC, потерянные клиентом. Разумеется, никто не может требовать от биржи идти на такие расходы из-за пользовательской ошибки.
На сегодняшний день комментарий к посту CEO Uphold удалён. Я не знаю, кто его удалил: сам пользователь или команда биржи. Надеюсь, что проблему пользователя решили, и он сам стёр сообщение о ней. Но если комментарий удалила биржа, то это, конечно, тревожный звоночек. Такие проблемы не стоит замалчивать. Наоборот, к ним нужно привлекать внимание, чтобы другие пользователи не повторяли подобных ошибок.
Поэтому я и решил сегодня рассказать о нашей истории и об истории клиента Uphold.
Если хотя бы один из кошельков - кошелек отправителя или кошелёк получателя - находится под полным контролем владельца средств, то всё просто:
Но когда речь идёт о кастодиальных кошельках - например, о кошельках бирж, - всё становится гораздо сложнее. Их владельцы не обязаны из-за вашей ошибки вмешиваться вручную в работу сервиса, подвергая его дополнительным рискам. Поэтому вам могут отказать в восстановлении доступа к средствам. Однако попробовать всё равно стоит. Шанс вернуть средства есть, пока биржа владеет ключами.
Но лучше всего, конечно, быть максимально внимательным при отправке любой криптовалюты, чтобы такие ситуации не возникали. У самостоятельного хранения средств много преимуществ, но оно требует и большой ответственности.