Защита от reorg: финальность, которой можно верить
Блок может быть пере-собран и откатан, утянув с собой «зачисленный» перевод. Suward не зачисляет по одному блоку. Каждый платёж проходит Pending → Safe → Finalized и держится до заданного порога финальности — за ним стоят авто- и ручная перепроверка.
Что такое reorg
Представьте: два майнера нашли блок почти на одной высоте. На миг у цепочки два «кончика». Сеть оставляет более длинную ветку, отбрасывает другую — и любая транзакция, что жила только на отброшенной ветке, просто исчезает, будто её и не было. Это и есть reorg. Наивный листенер зачисляет мерчанту сразу, как увидел перевод, и не отыгрывает назад, когда блок пропал, — а мерчант остаётся без денег. Это реальный риск публичных сетей. И именно его Suward снимает с вас.
Pending → Safe → Finalized
Один перевод, три состояния. По-настоящему завершённым Suward считает платёж только на последнем.
- Pending
Pending. Перевод попал в блок, но блок молодой, и подтверждения ещё набираются. Reorg тут вполне возможен, поэтому ничего не зачисляется. Товар придержите.
- Safe
Safe. Подтверждений набралось столько, что откат по порогу этой сети маловероятен. Практически безопасно, но ещё не финально.
- Finalized
Finalized. Заданный порог финальности сети достигнут, и перевод стал частью устоявшейся истории. Отгружайте заказ.
За этим стоят две настройки, а не одна. Per-сеть required_confirmations считает, сколько блоков должно лечь сверху. Safe- и finalized-офсеты монитора решают, где проходят Safe и Finalized. Вместе они и проводят черту, по которой можно отгружать.
Авто-перепроверка
Монитор следит за головой каждой цепи. Когда блоки у кончика перестраиваются, он сам пересчитывает затронутые статусы — вплоть до Finalized, без оператора. В баланс платёж попадает только пройдя уровень финальности. Это зачисление ложится атомарно как Decimal128 и никогда не уходит ниже нуля. Работа идёт, смотрите вы или нет.
Ручной ReverifyBlock
На редкий краевой случай оператор может принудительно перепроверить блок. ReverifyBlock(blockchain_id, number, head_number) пересчитывает Pending, Safe и Finalized для одного блока; передайте head_number=0, чтобы оставить его как Pending. Это backstop, а не замена авто-логике. Под капотом outbox переносит статус-события из монитора в cryptopay, чтобы сбой сервиса их не уронил.
Подтверждения по сетям
Значение required_confirmations задаётся для каждой сети. Вот сколько ждёт каждая.
| Network | required_confirmations |
|---|---|
| 12 | |
| 12 | |
| 12 | |
| 12 | |
| 15 | |
| 128 | |
PlaPlasmaSoon | 12 |
Polygon ждёт 128 подтверждений намеренно: у него история частых reorg, поэтому доверие он набирает медленнее остальных. Plasma пока помечен Soon, пока чинится проблема с chainID. Детали по каждой сети — на её странице.
Отслеживание статуса через API
За платежом вы следите опросом GET /v1/payments/{paymentId}. В ответе — PaymentStatusEnum (pending, accepted, success, failed) и более тонкий PaymentSubStatusEnum (confirming, acceptedCompleted, acceptedOverpaid, acceptedUnderpaid). Этот poll — источник истины в любой момент, потому что вебхуков для мерчанта пока нет. Когда появятся, сначала будут помечены Soon.
Две вещи, о которых честно сказать заранее. ERC-20-детект читает только calldata transfer(), а значит, не подхватит transferFrom или мульти-сенд, поэтому правило простое: пусть плательщики шлют обычный transfer на уникальный адрес — и всё сходится. Вебхуков пока тоже нет. Статус берётся из poll выше. Это инженерные факты, а не маркетинговые.
Reorg — частые вопросы
Когда статус становится Finalized — то есть достигнут заданный порог финальности сети. До этого он на Pending или Safe, и деньги ещё не дошли до баланса.
Safe значит «откат маловероятен», а не «невозможен». Именно поэтому это не Finalized. Если reorg всё же случился, монитор сам пересчитывает статус до финальности, а баланс — от on-chain-реальности. Можете дождаться Finalized — дождитесь.
Считается не время, а число подтверждений. Ethereum ждёт 12, Polygon — 128, поэтому Polygon ощутимо дольше, и это намеренно. На странице каждой сети указано её число.