Skip to content

Latest commit

 

History

History
395 lines (290 loc) · 19 KB

File metadata and controls

395 lines (290 loc) · 19 KB

🎯 Механика Telegram Gift Auctions: Наш анализ и реализация

Этот документ описывает наше понимание механики Telegram Gift Auctions, принятые допущения и архитектурные решения.


📋 Содержание


🔍 Исследование продукта

Источники информации

Для понимания механики мы изучили:

  1. Официальные анонсы Telegram о Gift Auctions
  2. Статьи и обзоры (membertel.com/blog/telegram-gift-auctions/)
  3. Паттерны поведения реальных аукционов
  4. Сравнение с классическими аукционами (eBay, Sotheby's и др.)

Ключевые выводы

Аспект Классический аукцион Telegram Gift Auction
Структура Один дедлайн Множество раундов
Победители Один победитель Частичные победители в каждом раунде
Проигравшие Ставки отменяются Ставки переносятся в следующий раунд
Anti-sniping Редко Встроенный механизм
Участие Можно выйти в любой момент Средства заблокированы до победы/окончания

🔄 Многораундовая система

Концепция

Telegram Gift Auction — это не классический аукцион "победитель забирает всё", а система поэтапного распределения лимитированных товаров.

┌─────────────────────────────────────────────────────────────────┐
│                     АУКЦИОН: 100 NFT                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐         │
│  │  Раунд 1    │    │  Раунд 2    │    │  Раунд 3    │         │
│  │  30 NFT     │───▶│  40 NFT     │───▶│  30 NFT     │         │
│  │  2 минуты   │    │  2 минуты   │    │  2 минуты   │         │
│  └─────────────┘    └─────────────┘    └─────────────┘         │
│        │                  │                  │                  │
│        ▼                  ▼                  ▼                  │
│   Топ-30 ставок      Топ-40 ставок     Топ-30 ставок          │
│   = победители       = победители      = победители           │
│                                                                 │
│   Остальные ─────▶ Остальные ─────▶ Возврат средств           │
│   переносятся      переносятся                                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Логика распределения

  1. Раунд начинается в запланированное время
  2. Участники делают ставки или повышают существующие
  3. Раунд завершается (с возможными anti-sniping продлениями)
  4. Топ-N участников получают товары (N = количество товаров в раунде)
  5. Средства победителей списываются
  6. Проигравшие автоматически переходят в следующий раунд с текущей ставкой
  7. В последнем раунде оставшиеся ставки возвращаются

Почему такая система?

Преимущество Описание
Поддержание интереса Участники остаются вовлечены на протяжении всего аукциона
Справедливость Больше шансов получить товар при продолжении участия
Ликвидность Средства остаются в системе до финала
Динамика Каждый раунд — это новый шанс

💰 Модель ставок

Одна ставка на пользователя

flowchart TD
    U[👤 Пользователь] --> A{Есть активная ставка?}
    A -->|Нет| B[Создать новую ставку]
    A -->|Да| C{Новая сумма > текущей?}
    C -->|Да| D[Повысить ставку]
    C -->|Нет| E[❌ Отклонить]
    
    B --> F[Заблокировать сумму]
    D --> G[Заблокировать разницу]
    
    F --> H[✅ Ставка активна]
    G --> H
Loading

Правила ставок

  1. Одна активная ставка на аукцион на пользователя
  2. Только повышение — нельзя понизить ставку
  3. Мгновенная блокировка — средства блокируются сразу
  4. Инкрементальная блокировка — при повышении блокируется только разница

Пример

Баланс пользователя: 1000 USDT

Действие 1: Ставка 100 USDT
├── Баланс: 1000 → 900
├── Заблокировано: 0 → 100
└── Активная ставка: 100

Действие 2: Повышение до 150 USDT
├── Баланс: 900 → 850
├── Заблокировано: 100 → 150
└── Активная ставка: 150

Действие 3: Попытка понизить до 120 USDT
└── ❌ ОТКЛОНЕНО: нельзя понижать ставку

Результат раунда: Проигрыш
├── Ставка переносится в следующий раунд
└── Средства остаются заблокированы (150)

Результат финала: Проигрыш
├── Заблокировано: 150 → 0
└── Баланс: 850 → 1000 (возврат)

⏰ Anti-Sniping механизм

Проблема sniping

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

Без anti-sniping:
┌────────────────────────────────────────────────┐
│ Раунд                              Конец      │
│ ═══════════════════════════════════════│      │
│                                    ↑          │
│                              Ставка бота      │
│                              (последняя мс)   │
│                                               │
│ Другие участники не успевают ответить! ❌     │
└────────────────────────────────────────────────┘

Наше решение: Soft Close

С anti-sniping:
┌────────────────────────────────────────────────────────────┐
│ Раунд                    │ Anti-sniping │    Продление    │
│ ═════════════════════════│══════════════│═════════════════│
│                          ↑              ↑                  │
│                    30 сек до        Ставка =              │
│                    конца            продление             │
│                                                           │
│ Другие участники получают шанс ответить! ✅               │
└────────────────────────────────────────────────────────────┘

Конфигурация

Параметр Описание По умолчанию
antiSnipingWindowSeconds Окно детекции (последние N секунд) 30
antiSnipingExtensionSeconds Продление при ставке в окне 30
maxAntiSnipingExtensions Максимум продлений 5

Логика

flowchart TD
    BID[Новая ставка] --> CHECK{До конца < windowSeconds?}
    CHECK -->|Нет| NORMAL[Обычная обработка]
    CHECK -->|Да| EXT_CHECK{extensions < maxExtensions?}
    EXT_CHECK -->|Нет| NORMAL
    EXT_CHECK -->|Да| EXTEND[Продлить на extensionSeconds]
    EXTEND --> INC[extensions++]
    INC --> NOTIFY[WebSocket: anti-sniping-extended]
    NOTIFY --> NORMAL
Loading

Почему это важно?

Без anti-sniping С anti-sniping
Боты доминируют Честная конкуренция
Участники разочарованы Вовлечённость растёт
Низкие финальные цены Рыночные цены

💳 Финансовая модель

Ledger-first подход

Все движения средств записываются в append-only журнал:

sequenceDiagram
    participant U as Пользователь
    participant L as Ledger
    participant DB as MongoDB
    
    Note over L: Каждая операция = новая запись
    
    U->>L: Депозит 1000 USDT
    L->>DB: INSERT { type: "deposit", amount: 1000 }
    
    U->>L: Ставка 100 USDT
    L->>DB: INSERT { type: "hold", amount: 100, ref: bidId }
    
    U->>L: Повышение до 150 USDT
    L->>DB: INSERT { type: "hold", amount: 50, ref: bidId }
    
    Note over L: При победе
    L->>DB: INSERT { type: "capture", amount: 150, ref: bidId }
    
    Note over L: При проигрыше
    L->>DB: INSERT { type: "release", amount: 150, ref: bidId }
Loading

Типы операций

Тип Описание Влияние на баланс
deposit Пополнение balance ↑
hold Блокировка под ставку balance ↓, frozen ↑
capture Списание при победе frozen ↓
release Возврат при проигрыше frozen ↓, balance ↑
withdrawal_request Запрос вывода balance ↓, frozen ↑
withdrawal_complete Вывод завершён frozen ↓

Инварианты

Для каждого пользователя в любой момент времени:

1. balance >= 0
2. frozen >= 0
3. sum(active_holds) == frozen
4. sum(deposits) - sum(captures) - sum(withdrawals) == balance + frozen

Идемпотентность

Каждая операция имеет idempotencyKey:

// Первый вызов
await ledger.hold({ userId, amount: 100, idempotencyKey: "bid-123-v1" });
// → Создаёт запись, возвращает holdId

// Повторный вызов с тем же ключом
await ledger.hold({ userId, amount: 100, idempotencyKey: "bid-123-v1" });
// → Возвращает тот же holdId, без создания новой записи

// Вызов с тем же ключом, но другой суммой
await ledger.hold({ userId, amount: 200, idempotencyKey: "bid-123-v1" });
// → 409 Conflict

🔧 Edge Cases

1. Одновременные ставки

Проблема: Два запроса на ставку от одного пользователя одновременно.

Решение: Распределённая блокировка в Redis.

Request 1: SETNX lock:user:auction → SUCCESS → обработка
Request 2: SETNX lock:user:auction → FAIL → 409 Conflict

2. Равные ставки

Проблема: Две ставки с одинаковой суммой.

Решение: Детерминированное ранжирование:

  1. Сумма (DESC)
  2. Время создания (ASC) — кто раньше, тот выше
  3. ID ставки (ASC) — для полной детерминированности

3. Недостаточно участников

Проблема: В раунде меньше ставок, чем товаров.

Решение: Все участники с валидными ставками побеждают.

4. Сбой во время финализации

Проблема: Сервер падает в процессе расчёта победителей.

Решение:

  1. Распределённая блокировка с TTL
  2. Идемпотентные операции
  3. Возможность перезапуска финализации

5. Ставка в момент закрытия раунда

Проблема: Ставка отправлена за 1ms до закрытия.

Решение:

  • Если успела до закрытия — принята
  • Если раунд уже closed — отклонена с 400

6. Переполнение anti-sniping

Проблема: Боты бесконечно продлевают раунд.

Решение: maxAntiSnipingExtensions ограничивает продления.


📝 Наши допущения

Подтверждённые (из анализа)

Допущение Уверенность Источник
Многораундовая система ✅ Высокая Официальные анонсы
Перенос ставок между раундами ✅ Высокая Статьи и обзоры
Anti-sniping существует ✅ Высокая Упоминания в документации

Интерпретированные (наш выбор)

Допущение Обоснование
Одна ставка на аукцион Упрощает UX, предотвращает самоперебивание
Мгновенная блокировка Гарантия платёжеспособности
Нельзя понижать ставку Предотвращает манипуляции
Детерминированный tiebreaker Прозрачность и проверяемость
Продление = фиксированное время Проще понять для пользователей

Неизвестные (требуют уточнения)

Аспект Наш выбор Альтернатива
Минимальный шаг ставки Нет ограничения Фиксированный %
Минимальная ставка На уровне аукциона Глобальная
Уведомления о перебитии Мгновенные Батчами

📊 Резюме

Ключевые особенности реализации

Особенность Описание
Криптографическая проверяемость Merkle-корни ставок, подписи раундов, replay-эндпоинты
Ledger-first финансы Append-only журнал операций, полный аудит
Микросервисная архитектура Независимое масштабирование, изоляция сбоев
Прокси-ставки Максимум в эскроу, авто-повышение
3 стратегии кошельков address_pool, memo_tag, address_per_user
KMS интеграция Опциональное хранение ключей

Принципы проектирования

  1. Детерминированность — одинаковые входные данные дают одинаковые результаты
  2. Идемпотентность — безопасные повторные запросы
  3. Проверяемость — независимая верификация результатов
  4. Устойчивость — восстановление из журнала при сбоях
  5. Масштабируемость — горизонтальное масштабирование сервисов

Система спроектирована как production-ready решение с полным циклом: от депозитов до выдачи выигрышей.