# Инструменты проверки игрового контура Скрипты для **живой** приёмки механик ставок (не заменяют `npm test` в репозитории релейера, а проверяют боевой стенд). ## `exploit-decisive.cjs` — решающая проверка окна закрытия Проверяет главное: **цена закрытия берётся из истории на момент экспирации, а не в момент клика.** Как запускать (внутри пода шлюза — там есть `node_modules` и доступ к RPC): ```bash P=$(kubectl -n game get pod -l app.kubernetes.io/instance=testfront-gateway -o jsonpath='{.items[0].metadata.name}') kubectl -n game cp tools/exploit-decisive.cjs "$P:/app/data/probe/exploit-decisive.cjs" -c gateway kubectl -n game exec "$P" -c gateway -- sh -c "cd /app && node /app/data/probe/exploit-decisive.cjs 300" ``` ### Питфоллы (каждый пойман вживую) - **Нельзя просто сравнивать `exitPrice` с текущей ценой.** Цена часто стоит на месте, и сравнение вырождается в «совпало» — ложный успех. Скрипт ждёт, пока цена на экспирации и текущая **разойдутся**, и только тогда закрывает. Первый прогон без этого дал ложное «совпало» дважды. - **Копировать только в `/app/data/`**, не в `/app` (`Permission denied`, под не root). - **Запускать с `cd /app`**, иначе `Cannot find module '@solana/web3.js'` (из `/tmp`/`/app/data` без cwd `node_modules` не виден). - Ставка при `exit == entry`: с 2026-09-14 (`SEC-9`) **возвращает залог** статусом `3` и финализируется — залипаний больше нет. *Открытый вопрос:* хочется вместо немедленного возврата **подождать ещё 15 с** и взять цену заново (см. `TASKS.md`, «ждать, если цена не изменилась»). Читает `bet_time` / `expire_time` прямо из аккаунта Bet (сырые байты) — off-chain данными не верим. Раскладка аккаунта (Anchor, после 8-байтового дискриминатора): `amount u64 @8`, `bet_time i64 @16`, `expire_time i64 @24`, `entry_price u64 @32`, `exit_price u64 @40`, `side u64 @48`, `status u64 @56`, `bettor Pubkey @64`. ## `price-history-probe.cjs` — разбор истории цены Смотрит, что реально накопилось в `binance-market` (Postgres `192.168.76.182:5434`, БД `binance_market`) вокруг конкретной ставки: сделки в окне, цена на входе, цена на экспирации, и что было бы при «ждать выгодной цены». ```bash node tools/price-history-probe.cjs ``` ⚠️ **Символ в БД — в нижнем регистре** (`solusdt`, не `SOLUSDT`). Запрос с верхним регистром вернёт пусто, и это легко принять за «данных нет». ## `whose-fee.cjs` — с ЧЬЕГО кошелька уходит SOL за раунд (доказательство `SEC-16`) Мерит SOL **игрока** в трёх точках каждого раунда (до ставки → после ставки → после расчёта) и SOL **шлюза-админа**. Замер: игрок `5 000 000 000 → 4 998 440 960` (−**1 559 040** lamports), шлюз не меняется — значит ренту PDA-аккаунта ставки платит игрок. ```bash node tools/whose-fee.cjs ``` ## `bet-pda-rent.cjs` — сколько ренты заперто в аккаунтах ставок Обходит `getProgramAccounts` (фильтр `dataSize:96`) и показывает, что у каждой ставки PDA держит ровно **1 559 040** lamports. Аккаунт не закрывается никогда (`close_bet` не делает `close = bettor`) → рента теряется безвозвратно. Второе доказательство `SEC-16`. ```bash node tools/bet-pda-rent.cjs ``` ## `faucet-latency.cjs` / `sol-balance-cycle.cjs` — почему кажется, что «SOL не обновился» `GET /faucet/{addr}` отвечает **мгновенно**, но `getBalance` **на цепи** показывает `0` ещё **8–13 с** (замеры: 13309 / 13345 / 13345 мс). Игра читает баланс не через `/api/balance`, а через свой nginx-прокси `/solana-rpc/getBalance` → прямо с цепи → попадает в окно лага. Симптом «налил, а SOL не обновился» — это лаг зачисления, а не отставание шлюза. ```bash node tools/faucet-latency.cjs # лаг крана: цепь против отчёта шлюза node tools/sol-balance-cycle.cjs # баланс после крана: сразу 0, через ~20 с 5 SOL ``` ## `settle-math-test.cjs` — математика расчёта (правильный способ замера) ⚠️ Шлюз **кастодиальный**, поэтому дельту надо считать **от момента после lock** (фантики ушли в vault при ставке), а НЕ от старта раунда — иначе в дельту попадают и ставка, и выплата, и «математика не сходится» ложно. Замерено: выигрыш `+130 000 000` (1.3×), проигрыш `0`, ничья `+100 000 000` (возврат). Считать «от старта» = получить ложный провал (проверено живьём). ```bash node tools/settle-math-test.cjs ``` ## `backend-full-test.cjs` — контракт ручек и коды ошибок шлюза Приёмка API живьём: `/state`, `/price`, `/wallet`, `/faucet`, `/balance`, `/bet`, идемпотентность крана, `400` на кривые данные, `409` на вторую открытую ставку, `404` на снятые ручки (`/close`, `/bet/{id}`), WS-события `bet_open`/`bet_closed`. Полный прогон с расчётом на цепи. ```bash node tools/backend-full-test.cjs ``` ⚠️ `/bet` требует **число** в `amountUnits` (строка → `400`) и `address` в теле. ⚠️ `getTokenAccountsByOwner` принимает **2–3** параметра: commitment кладётся **внутрь третьего** (`cfg`), а не четвёртым — иначе `-32602 Expected from 2 to 3 parameters`. ## `k8s/smart-updown-api-ingress.yaml` — пути `/api`,`/ws` вне helm ⚠️ Чарт `fun-game-front2` рендерит **только** path `/`, поэтому любой `helm upgrade` игры **сносит** `/api` и `/ws` с боевого домена (домен начинает отдавать HTML игры на `/api/*`). Лечение — отдельный ingress `smart-updown-api` **вне helm**; front2-ingress остаётся `paths: [/]`. ```bash ssh root@192.168.76.120 "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml; \ kubectl apply -f /root/smart-updown-api-ingress.yaml" # проверка после каждого релиза игры: kubectl -n game get ingress -o custom-columns='NAME:.metadata.name,HOST:.spec.rules[0].host,PATHS:.spec.rules[0].http.paths[*].path' ``` ⚠️ НЕ править paths через `kubectl patch --type=json` с `remove` по индексам: при нескольких путях индексы съезжают, остаётся висячий `/ws`, а последний path удалить нельзя (`paths: Required value`). Надёжно — собрать нужный список целиком и `kubectl replace --force`.