Commit Graph

6 Commits

Author SHA1 Message Date
subochev dd4f16a930 feat(relayer): API v2 (canBet/30% от баланса) + п.8 — ничья играет заново
Тестовый TS-шлюз (relayer/), обслуживающий smart-updown.binom.pw и
testfront.binom.pw. Контракт и prod-Kotlin сервер не тронуты.

1+2. GET /state: canBet (boolean) + canBetReasonCode/canBetReason (русский
     текст) — можно ли ставить с фиксированным кошельком. Учитывает:
     захардкоженный адрес (GW_FIXED_BETTOR), наличие открытой ставки,
     хватает ли фантиков на ставку, лимиты [min,max], paused, наличие цены.
3-6. POST /bet: тело {"side":"up"|"down"} — сумма с фронта НЕ приходит,
     размер = GW_BET_PERCENT (30) % от баланса фантиков, клампится в
     [minAmount, maxAmount]. Ответ: {accepted, reasonCode, reason,
     retryAfterSeconds, betId, txSig, ...}.
7.   GET /state: openBet / lastBet (взаимоисключающие: открытая имеет
     приоритет) + поле status ("open"/"closed"), result, retryAfterSeconds.
8.   Ничья (refunded, exit==entry) игроку НЕ показывается: планировщик молча
     пересоздаёт ставку тем же размером и направлением (хук replayBet) и
     шлёт WS-событие bet_replay вместо bet_closed. Лимит GW_MAX_REPLAYS
     (дефолт 10) обрывает цепочку при «замороженной» цене.
9.   relayer/API.md — документация всех endpoint-ов и WS-событий.

Новые файлы: src/betRules.ts (правила ставки: canBetDecision,
computeBetAmountUnits), API.md.
Новые env: GW_FIXED_BETTOR, GW_BET_PERCENT (30), GW_MAX_REPLAYS (10).

Проверено: npm run typecheck + npm run test — 127 проверок, 0 провалов;
живая приёмка tools/gateway-api-v2-check.cjs — 26/26 на
https://smart-updown.binom.pw/api (включая реальную ставку → payout_done).
2026-09-17 00:11:31 +03:00
Caffeine a73e44de9a feat(relayer): авто-закрытие ставок, WS-события, одна открытая ставка на адрес
- settle.ts: серверный планировщик (startScheduler, тик SMART_SETTLE_INTERVAL_MS)
  находит просроченные открытые ставки, берёт цену из истории на expire_time,
  сам зовёт close_bet; рассылает bet_closed
- gateway.ts: ручка POST /close удалена (404); проверка 'одна открытая ставка
  на адрес' под withBetLock -> 409 с openBetId; bet_open по WS при создании;
  lastState прокидывается в settleOnce (без него лог спамил каждый тик)
- settle.ts: пакетный getProgramAccounts вместо поштучного перебора по id
  (замер: 3 мс против 28 мс, 9.3x, 1 запрос вместо 15); фильтр не-Bet аккаунтов
  по BET_ACCOUNT_SIZE (Global исключён из unmapped)
- check.ts: +38 проверок (decodeBet, openBetOf, dueForSettle, события, границы)
2026-09-13 18:49:26 +03:00
Caffeine 0b136c6502 feat(relayer): цена закрытия из истории на момент экспирации — окно выбора времени закрыто
Проблема: handleClose брал getPrice() (текущую цену), поэтому игрок мог
подождать выгодного момента и закрыть ставку тогда, когда ему надо
(эксплуатировалось вживую: ставка висела open >2 мин после экспирации 15с).

Решение (только шлюз, контракт не тронут):
- src/priceHistory.ts (новый) — локальная история цены на встроенном node:sqlite
  (Node 22, без новых зависимостей). Запись только при изменении цены
  (~300 строк/сутки вместо 92k тиков), ретеншен 24ч, prune одним DELETE.
- natsPrice: в PriceTick добавлено eventTime (время сделки у Binance) — источник
  истины по времени; ts не тронут.
- config: SMART_PRICE_HISTORY_PATH (/app/data/price-history.db),
  SMART_PRICE_RETENTION_HOURS (24).
- gateway: каждый тик пишется в историю; handleClose берёт
  history.priceAtOrBefore(expireTime*1000) — последняя цена НЕ ПОЗЖЕ экспирации.
  Отказ 400 'bet not expired yet' до экспирации, 400 на уже закрытую,
  503 если истории нет (не выдумываем цену).
- helm: env SMART_PRICE_HISTORY_PATH / SMART_PRICE_RETENTION_HOURS.
- check.ts: 10 проверок истории, включая ключевую «НЕ отдаёт цену позже момента».

Верификация: typecheck 0 ошибок; npm test 34/34; мутационная приёмка — подмена
<= на >=/ASC роняет 4 проверки, снятие дедупа и смена ретеншена роняют свои.
Живой стенд: exitPrice = цена на момент экспирации (9977000000), тогда как
текущая цена в момент клика была 9976000000 — взята история, не 'сейчас'.
2026-09-13 14:46:47 +03:00
Caffeine 5ab6c9def5 feat(relayer): вынести начисление из /bet в отдельную тестовую ручку + гейт test/release
- /bet больше НЕ наливает SOL и фантики; address обязателен (400 с подсказкой)
- пред-проверка балансов перед ставкой (SOL >= 0.01, фантиков >= amount), 400
- новый POST /wallet — создать custodial-кошелёк, ничего не наливает
- src/faucetGate.ts: parseMode/faucetDenyReason, fail-closed
- SMART_MODE (test|release), дефолт release; /faucet: 403 если не test
- genesis-проверка: mainnet/devnet/testnet/genesis недоступен -> 403
- helm: SMART_MODE из .Values.smart.mode
- check.ts: 6 проверок гейта (мутационная приёмка пройдена)
2026-09-13 13:38:38 +03:00
Caffeine a3178cfa6e fix(helm): права на keystore — fsGroup + chmod 0644 в init
Шлюз в кубе падал с EACCES при записи .gateway-bettors.json: том от
local-path принадлежит root, контейнер бежит под node (uid 1000), а
seed-файл копировался в режиме 0444. Добавлены securityContext.fsGroup=1000
и chmod 0644 в init-контейнере. Проверено: ставка и закрытие проходят
через живой домен (entryPrice/exitPrice — реальные цены из NATS).
2026-09-13 06:05:24 +03:00
Caffeine 92cf2f61b4 feat(relayer): цена SOL из NATS + WS-раздача + деплой-артефакты
deploy-contract / deploy-contract (release) Failing after 3m34s
priceSource больше не рандомит: цена берётся из NATS (market.price.solusdt),
конверсия строка->целое x1e8 без float. Шлюз раздаёт цену по WS (/ws,
нормализация /api/... -> /...) — фронт просто выводит её. Добавлены
Dockerfile и helm-чарт для деплоя в k3s.
2026-09-13 05:53:48 +03:00