Архитектура большинства гарант-сервисов неразрывно связана с рисками: ошибками, возможностью мошенничества, сложными и непрозрачными схемами. Ее можно заменить аналогом с принципиально другим уровнем безопасности — системой по модели эскроу. В ней нет необходимости доверять человеку, только математике.

Показываем, как реализовать технические гарантии безопасности сделок с помощью MultiSig-кошельков, Node.js, smart-контрактов, TronWeb v5. Разбираем код и логику проектирования P2P-платформы, которая не может украсть деньги пользователя (даже если захочет) 👇

👋 Привет, Хабр. На связи Кирилл, основатель KeyShield. В статье показываю, как может работать система для финансовых сделок между физлицами, в архитектуре которой доступ к деньгам не предусмотрен даже у админов.

Проблема — риски денежных переводов на доверии

Независимо от того, по какой причине открывается P2P-сделка (покупаете аккаунт, сайт, курс или платите фрилансеру), есть общая проблема: непонятно, кому ходить первым. Продавец не отдает товар без предоплаты, покупатель не отправляет деньги без гарантий. Участники не доверяют друг другу, поэтому используют гарант-сервисы. 

Гарант — посредник (третья сторона) между заказчиком и исполнителем или между продавцом и покупателем. Этот человек или организация следит за безопасностью сделки, принимает оплату от одного участника и передает ее второму участнику только после того, как прописанные условия сделки выполнены.

P2P обмен с гарантом кажется рабочей схемой:

  1. Покупатель открывает сделку (условия, дедлайны, суммы, порядок оплаты).

  2. Покупатель на время отправляет деньги гаранту. 

  3. Продавец выполняет работу или продает товар.

  4. Покупатель принимает, и посредник отправляет деньги продавцу. 

Гарант-сервисы часто используются в таких сферах, как IT, финансы, недвижимость, маркетинг. Например, они нужны, если вы хотите выплатить гонорар подрядчику или купить цифровые товары. Юз-кейсов P2P обмена много.

Чем гарант-сервисы отличаются от эскроу

Сама по себе гарант-схема содержит уязвимости и риски. В этой модели два участника сделки вынуждены доверять третьему. При этом у посредника есть техническая возможность забрать деньги и исчезнуть.

Репутация и обещания — это не архитектура безопасности.

Если отправить крипто перевод на личный кошелек гаранта, фактически приходится слепо верить отзывам и маркетинговым заявлениям. Невозможно рассчитывать на 100% прозрачность и честность, потому что контроль над деньгами остается у третьей стороны.

Один из вариантов решения этой проблемы — эскроу. Из-за того, что он пока не получил широкого распространения на рынке СНГ, есть искажение восприятия: многие считают, что эскроу = гарант. Это не так. 

Три основных отличия:

  1. Гарант вручную контролирует сделку с криптовалютами, эскроу-сервис — автоматизирует P2P сделки за счет smart-контрактов и multisig-кошельков.

  2. Гарант хранит деньги на личном кошельке и решает, кому их отдать. В эскроу деньги находятся в защищенной криптографией среде. 

  3. С гарантами все держится на доверии к человеку или бренду. С эскроу-счетами пользователь доверяет только математике и блокчейну.

Мы рассматриваем эскроу в узком смысле, как специальный финтех-сервис на основе блокчейна, где эскроу-счета = адреса кошельков в блокчейне, и выполнение условий договора контролируется автоматически. Это своеобразный условный счет, через который выполняется передача средств (только при выполнении условий). 

Требования к архитектуре

Эскроу-модель задает направление, но не отвечает на вопрос реализации. Нужно выбрать конкретный стек: технологию управления средствами, блокчейн и интерфейс.

1. Технология без доверия

Trust-minimized технология уже есть — это multisig-кошельки (2-из-3). Мультиподписные криптокошельки для авторизации перевода требуют несколько закрытых ключей, в данном случае, как минимум двух из трех. Они помогают уйти от единой точки контроля, диверсифицировать управление средствами между несколькими сторонами. Если P2P сделки проходят через multisig-кошелек, ни одна сторона не имеет возможности забрать деньги без согласия других участников.

2. Блокчейн TRON для нативной поддержки USDT

Выбрали TRON, так как это крупнейшая сеть по объему USDT: более 50% всех стейблкоинов в мире — на TRC-20. Также там комиссии в доли цента, а не $5–20 как в Ethereum, и блоки с подтверждением за ~3 секунды. В протоколе TRON инфраструктура и система прав доступа реализованы удобно для разработчика. Он поддерживает мультиподпись на уровне аккаунта (не нужен отдельный smart-контракт).

3. Telegram как интерфейс и TWA

Много P2P-обменов уже происходит в Telegram: там заказчики и исполнители договариваются о работе. Также в TG встроено мини-приложение Wallet для крипто переводов, и платежи через мессенджер уже привычны. Telegram Web Apps позволяет рисовать нормальный фронтенд на JS/HTML. Создавать сервис с нуля казалось лишним, поэтому остановились на формате крипто бота Телеграмм.

Технологический стек и интеграции

Коротко о том, какие инструменты нужны для реализации системы эскроу-сделок с мультисигом на Троне через ТГ:

Технология

Назначение

Node.js

Серверная логика, асинхронная обработка

Telegraf v4

Telegram Bot API фреймворк

MongoDB + Mongoose

Хранение сделок, пользователей, логов

TronWeb v5

Взаимодействие с блокчейном TRON

TronGrid API

Нода для чтения данных блокчейна

Express.js

REST API для админ-панели и партнерского API

FeeSaver

Аренда энергии в TRON для снижения комиссий

Остро стоит вопрос об интеграциях с P2P-площадками и сообществами. Мы уже сейчас используем бесплатный и стабильный API-провайдер TronGrid (правда, пришлось писать rate-limiter) и сервис аренды энергии FeeSaver. Планируем также добавить:

  • Кошельки: TrustWallet, TronLink, SafePal;

  • Биржи: Binance, Bybit, OKX (как источник средств);

  • P2P-площадки: интеграция как безопасный эскроу;

  • Фриланс-платформы: плагин для безопасных оплат.

Стек понятен. Теперь разберем, как все это работает изнутри — начиная с самого главного: как создается кошелек, которым не может управлять ни одна сторона.

Как устроены multisig-кошельки 2-��з-3

Каждая сделка генерирует уникальный escrow-адрес в блокчейне TRON с тремя подписантами:

// blockchain.js — создание мультисиг-кошелька

async createMultisigWallet(buyerKey, sellerKey, arbiterKey) {
  const newAccount = await tronWeb.createAccount();
  const address = newAccount.address.base58;

  const permissionConfig = {
    owner_address: tronWeb.address.toHex(address),
    actives: [{
      type: 2,
      permission_name: 'escrow_active',
      threshold: 2,  // ← ПОРОГ: нужны 2 подписи из 3
      keys: [
        { address: buyerAddress,   weight: 1 },
        { address: sellerAddress,  weight: 1 },
        { address: arbiterAddress, weight: 1 }
      ]
    }]
  };
  return { address, threshold: 2, permissionsJson: permissionConfig };
}

На этот адрес отправляются деньги. Они хранятся там от момента депозита покупателя до вывода продавцу. Для запуска инициализации перевода нужны 2 из 3 подписей. Поэтому сервис-посредник технически не сможет забрать деньги без согласия одной из сторон, даже если захочет.

Ограничение доверия через управление private keys

У участников крипто сделки есть 3 приватных ключа:

🔑 Ключ покупателя

🔑 Ключ продавца 

🔑 Ключ арбитра (сервиса)

Генерация ключей для участников проходит так:

// blockchain.js — генерация ключевой пары

async generateKeyPair() {
  const account = await tronWeb.createAccount();
  return {
    privateKey: account.privateKey,   // Показывается 1 раз, не сохраняется
    publicKey: account.publicKey,
    address: account.address.base58
  };
}

Приватные ключи пользователей генерируются автоматически, показываются один раз не более чем на 60 секунд, не хранятся в базе данных и сразу стираются из памяти и логов. Да, требуется минимальное доверие в момент генерации, но доступ админов после нее исключен полностью. Так как сообщение с ключом мгновенно удаляется после первого назначения, ключи хранятся с флагом select: false.

Никто не может украсть деньги, потому что реализуются следующие сценарии:

Ситуация

Почему скам невозможен

Арбитр хочет забрать деньги

Нужен ключ покупателя ИЛИ продавца

Покупатель хочет забрать деньги

Нужен ключ продавца ИЛИ арбитра

Продавец хочет забрать деньги

Нужен ключ покупателя ИЛИ арбитра

Модель мультисиг-кошелька в БД 

При механизме multisig 2-из-3 для запуска перевода крипты нужны две любые подписи. Это обеспечивается на уровне TRON.

// blockchain.js — мультиподпись (2-of-3)

async multiSignTransaction(transaction, privateKeys) {
  let signedTx = transaction;
  for (const key of privateKeys) {
    signedTx = await tronWeb.trx.multiSign(signedTx, key);
  }
  return signedTx;
  // Транзакция пройдёт только если набран порог (2 подписи)
}

Внутри базы данных можно реализовать модель multisig-кошельков следующим образом:

// MultisigWallet.js — структура хранения

const multisigWalletSchema = {
  dealId:           ObjectId,   // К какой сделке привязан
  address:          String,     // Адрес кошелька в TRON
  buyerPublicKey:   String,
  sellerPublicKey:  String,
  arbiterPublicKey: String,
  privateKey:       String,     // select: false
  threshold:        2,          // Всегда 2-of-3
  permissionsJson:  Object,
  balances: { TRX: Number, USDT: Number }
};

Сделка от начала до конца: конечный State Machine

Каждой сделке мы присвоили 6 основных состояний и несколько второстепенных (для случаев отхождения от стандартного пути, например, при сорванных дедлайнах). Жизненный цикл — конечный автомат. Сделка не может перескочить из статуса created сразу в completed, она должна получить все статусы последовательно.

Диаграмма основных состояний

pending_counterparty (ожидание контрагента)
       ↓
waiting_for_wallet (ожидание кошелька)
       ↓
waiting_for_deposit (ожидание депозита)
       ↓
    locked (средства заблокированы)
       ↓
  in_progress (работа в процессе)
       ↓
  ┌────┴────┐
  ↓         ↓
completed  dispute → resolved

* При дедлайне + 12 часов → expired (автовозврат)

Это важно, потому что нельзя полагаться на последовательность HTTP-запросов от клиента: они могут прийти в любом порядке и задублироваться.

Состояние locked — центр архитектуры. До блокировки средств сделка представляет собой запись в базе данных MongoDB. А после перехода в locked она превращается в смарт-контракт на блокчейне. С этого момента невозможно изменить условия. Запускается серверный таймер автовозврата на случай, если продавец пропадет.

Как это выглядит для пользователя

Процесс сделки мы разделили на 4 основных этапа, каждый из которых состоит из нескольких шагов. Первый этап — создание — самый трудоемкий для пользователя. Все последующие процессы проходят быстро и прозрачно.

Этап 1: Создание (8–10 шагов)

  1. Выбор роли → Покупатель или Продавец

  2. Выбор метода → Username или Invite-ссылка

  3. Указание контрагента

  4. Название товара/услуги

  5. Описание (от 20 символов)

  6. Выбор актива → USDT (TRC-20)

  7. Сумма (от 50 USDT)

  8. Кто платит комиссию → Покупатель / Продавец / 50/50

  9. Срок выполнения → 24ч / 48ч / 3 дня / 7 дней / 14 дней

  10. Указание кошелька → Из сохраненных или ввод нового

Пример готовой сделки от лица покупателя
Пример готовой сделки от лица покупателя

Этап 2: Подключение контрагента

  1. Контрагент получает уведомление или переходит по invite-ссылке

  2. Указывает свой кошелек

  3. Получает приватный ключ (показывается один раз)

Пример уведомления о новой сделке у продавца
Пример уведомления о новой сделке у продавца

Этап 3: Депозит

  1. Покупатель переводит USDT на мультисиг-адрес

  2. Система автоматически обнаруживает перевод

  3. После подтверждения: средства заблокированы, статус → locked

  4. Оба участника получают уведомление

Сообщение об условиях сделки перед депозитом
Сообщение об условиях сделки перед депозитом

Этап 4: Выполнение работы и выплата

  1. Продавец выполняет работу и нажимает «Работа выполнена»

  2. Покупатель проверяет и нажимает «Принять работу»

  3. Победитель вводит свой приватный ключ

  4. Система создает транзакцию, подписывает и о��правляет в блокчейн

  5. Средства поступают на кошелек продавца

  6. Комиссия автоматически отправляется сервису

  7. Обе стороны получают уведомление со ссылкой на TronScan

Изнутри создание и отправка транзакции спроектированы так:

// Полный цикл выплаты

const releaseTx = await blockchainService.createReleaseTransaction(
  deal.multisigAddress, deal.sellerAddress, releaseAmount, 'USDT'
);
const signedTx = await blockchainService.signTransaction(releaseTx, wallet.privateKey);
const result = await blockchainService.broadcastTransaction(signedTx);


if (result.success) {
  console.log(`Выплата отправлена: ${result.txHash}`);
}

💡 TRON — публичная сеть. Через сервис TronScan (TRON BlockChain Explorer) любой пользователь может увидеть, что деньги находятся на адресе, созданном специально для его сделки. Бот работает напрямую с блокчейном. Поэтому у пользователя нет ограничений, касающихся того, с какого кошелька или биржи он может отправить USDT.

Полная модель статусов сделки

Чтобы сделка никогда не оказывалась в неопределенном состоянии, мы разбили процесс на статусы. Это позволяет Telegram-боту показывать только тот экран, который соответствует текущему шагу.

status: {
  enum: [
    'created',                    // Создана
    'pending_counterparty',       // Ожидание контрагента
    'waiting_for_seller_wallet',  // Ожидание кошелька продавца
    'waiting_for_buyer_wallet',   // Ожидание кошелька покупателя
    'waiting_for_deposit',        // Ожидание депозита
    'locked',                     // Средства заблокированы
    'in_progress',                // Работа в процессе
    'completed',                  // Завершена успешно
    'dispute',                    // Открыт спор
    'resolved',                   // Спор решён
    'cancelled',                  // Отменена
    'expired'                     // Автовозврат
  ]
}

Реальные алгоритмы работы

Депозиты покупателя обнаруживаются автоматически, без ручного подтверждения. Алгоритм обнаружения депозита обновляется каждые 30 секунд. Он должен:

  • Получить список сделок в статусе «ожидание депозита»;

  • Для каждой сделки запросить у TronGrid входящие USDT-переводы (Rate-limit: не более 8 запросов/сек к TronGrid);

  • Проверить сумму, и если она достаточна, записать транзакцию, активировать кошелек и уведомить стороны.

// depositMonitor.js

async checkDealDeposit(deal) {
  let expectedAmount = deal.amount;
  if (deal.commissionType === 'buyer') {
    expectedAmount = deal.amount + deal.commission;
  }

  const deposit = await blockchainService.checkDeposit(
    deal.multisigAddress, deal.asset, 0
  );

  if (deposit) {
    // Атомарное обновление: защита от race condition
    const updated = await Deal.findOneAndUpdate(
      { _id: deal._id, status: 'waiting_for_deposit' },
      { $set: { status: 'locked', depositTxHash: deposit.txHash } },
      { new: true }
    );
    if (!updated) return;
    await this.queueActivation(deal._id, deal.multisigAddress);
    await notifyBothParties(deal, deposit);
  }
}

💡 Атомарная операция findOneAndUpdate гарантирует, что депозит будет обработан ровно один раз. 

Алгоритм автовозврата запускается каждые 5 минут, чтобы:

  1. Получить сделки с истекшим дедлайном.

  2. Если дедлайн истек — уведомить обе стороны (12 часов на действия).

  3. Если дедлайн + 12 часов: 

    1. работа сдана → автовыплата продавцу; 

    2. работа НЕ сдана → автовозврат покупателю.

Алгоритм разрешения спора запускается, если одна из сторон не согласна доводить сделку до завершения. При таком сценарии:

  1. Одна из сторон открывает спор;

  2. Обе стороны могут оставлять комментарии;

  3. Арбитр изучает доказательства и принимает решение;

  4. Победитель вводит приватный ключ → получает средства;

  5. Проигравший получает +1 к счетчику поражений.

Если пользователь проигрывает 3 спора, он улетает в бан. Это можно реализовать, если нужно, чтобы в системе не было проблемных исполнителей и заказчиков, продавцов и покупателей.

Резюме о реализации безопасности

Чтобы была сформирована архитектура безопасной сделки, нужны технические гарантии:

  • TRON и TronScan дают прозрачность и проверяемость.

  • Мультисиг 2-из-3 гарантирует, что ни одна сторона не может забрать средства.

  • Личные подписи никогда не окажутся у арбитра или другой стороны сделки.

  • Депозиты покупателя обнаруживаются без подтверждения. 

  • Если продавец не выполнил обязательства, деньги вернутся покупателю.

UX в TWA?

Чтобы не перегружать интерфейс десятками уведомлений, иконок, окон, кнопок и ссылок, в чате крипто бота Телеграмм мы используем всего 2 сообщения для всех процессов сделки.

  1. Команда /start от пользователя.

  2. Единственное сообщение бота, которое перерисовывается на каждом этапе.

Как это реализовано:

  • Стек навигации — как в мобильном приложении. Каждый экран сохраняется в историю.

  • Кнопка «Назад» — возвращает к предыдущему экрану с сохранением введенных данных.

  • Автоудаление ввода — когда пользователь вводит текст, сообщение удаляется сразу после обработки. Чат остается чистым.

  • Новое уведомление заменяет текущий экран — в чате всегда отображается только одно активное сообщение. Предыдущий экран сохраняется и восстанавливается по кнопке «Назад».

Пример UX-потока:

[Главное меню] → «Создать сделку»
  → [Выбор роли] → «Покупатель»
    → [Выбор метода] → «Ввести username»
      → [Ввод username] → пользователь пишет @seller
        (сообщение удалено, экран обновлён)
      → [Название товара] → ...

От идеи до рабочего прототипа — 6 недель

Менее чем 2 месяца ушло на архитектуру мультиподписей: permission-систему TRON, генерацию ключей, мониторинг депозитов и логику арбитража. Еще 2–3 недели — на доработку до публичной версии: UX, обработку edge-кейсов (неполный депозит, потерянный ключ), систему уведомлений.

Реализована полная цепочка: создание сделки → депозит → выполнение → выплата / спор / автовозврат. Недавно мы выкатили большое обновление интерфейса и технички и расширили лимит до 3 одновременных сделок на пользователя.

Когда безопасность — свойство архитектуры

Сейчас продукт на стадии early launch. У нас 50 зарегистрированных пользователей — знакомые, для которых вопрос безопасных оплат в крипте актуален в повседневной работе. В планах — расширение фич, поддержка дополнительных сетей и улучшение UX на основе отзывов.

Мы не питаем иллюзий, потому что полностью убрать доверие при ограничениях UX в Telegram сложно, но можно к этому приблизиться. Если подобные нашему подходы начнут применяться шире, возможно, со временем появится и стандарт сделок, где безопасность — свойство архитектуры.