b8a1f8dc39
Сводка: открыто 8 (было 9), закрыто 8. SEC-9 перенесён в закрытые с полной историей: причина (close_bet.rs:66), замер доли ничьих (33.6%), решение владельца (правка контракта), состав правок, собственная мутационная приёмка, деплой и живой эффект (залипшие #0/#1 закрылись сами, ваулт −1000). Хвост SEC-8 о «вечном залипании» помечен как устранённый. Инструменты приёмки: - tie-refund-accept.cjs — ставки циклом, проверка статуса на цепи (живой прогон: 6 возвратов из 25 ставок) - tie-distribution.cjs, policy-economics.cjs — выбор политики ничьей - vault-check.cjs — разбор Bet-аккаунтов сырыми байтами
85 lines
3.4 KiB
JavaScript
85 lines
3.4 KiB
JavaScript
// Приёмка возврата при ничьей на ЖИВОМ стенде.
|
||
// Цикл: новый кошелёк → фаусет → ставка → ожидание авто-закрытия → чтение статуса.
|
||
// Победа = увидеть status=3 (REFUNDED) с exit==entry на НОВОЙ ставке.
|
||
const GW = process.env.GW || 'https://testfront.binom.pw/api';
|
||
const RPC = process.env.RPC || 'http://192.168.76.181:8899';
|
||
const PROG = '9ALsnxXNzDBv3mokngCHbruRTWUZiWR7pf7vswS1fCpf';
|
||
const { Connection, PublicKey } = require('@solana/web3.js');
|
||
|
||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||
|
||
async function j(path, body) {
|
||
const r = await fetch(GW + path, {
|
||
method: body ? 'POST' : 'GET',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: body ? JSON.stringify(body) : undefined,
|
||
});
|
||
const t = await r.text();
|
||
let d = null; try { d = JSON.parse(t); } catch {}
|
||
if (!r.ok) throw new Error(`HTTP ${r.status} ${t.slice(0, 120)}`);
|
||
return d;
|
||
}
|
||
|
||
function betPda(id) {
|
||
const b = Buffer.alloc(8); b.writeBigUInt64LE(BigInt(id));
|
||
return PublicKey.findProgramAddressSync([Buffer.from('bet'), b], new PublicKey(PROG))[0];
|
||
}
|
||
|
||
async function readBet(conn, id) {
|
||
const a = await conn.getAccountInfo(betPda(id));
|
||
if (!a) return null;
|
||
const d = a.data;
|
||
return {
|
||
amount: d.readBigUInt64LE(8),
|
||
entry: d.readBigUInt64LE(32),
|
||
exit: d.readBigUInt64LE(40),
|
||
status: Number(d.readBigUInt64LE(56)),
|
||
bettor: new PublicKey(d.subarray(64, 96)).toBase58(),
|
||
};
|
||
}
|
||
|
||
(async () => {
|
||
const conn = new Connection(RPC, 'confirmed');
|
||
const MAX = Number(process.env.MAX || 8);
|
||
let ties = 0, normal = 0;
|
||
|
||
for (let i = 1; i <= MAX; i++) {
|
||
let id;
|
||
try {
|
||
const w = await j('/wallet', {});
|
||
await j('/faucet/' + w.address);
|
||
const bet = await j('/bet', { side: 'UP', amountUnits: '100000000', address: w.address });
|
||
id = bet.id;
|
||
process.stdout.write(`[${i}] ставка #${id} открыта entry=${bet.entryPrice} … `);
|
||
} catch (e) {
|
||
console.log(`[${i}] ОШИБКА ставки: ${e.message}`);
|
||
continue;
|
||
}
|
||
|
||
// ждём авто-закрытие (expiry 15s + тик планировщика 2s + запас)
|
||
let b = null;
|
||
for (let t = 0; t < 45; t++) {
|
||
await sleep(2000);
|
||
b = await readBet(conn, id).catch(() => null);
|
||
if (b && b.status !== 0) break;
|
||
}
|
||
|
||
if (!b) { console.log('аккаунт не читается'); continue; }
|
||
if (b.status === 0) { console.log(`осталась open (exit=${b.exit}) — не закрылась`); continue; }
|
||
|
||
const names = { 1: 'payout_done', 2: 'house_won', 3: 'REFUNDED' };
|
||
const name = names[b.status] || ('status=' + b.status);
|
||
if (b.status === 3) {
|
||
ties++;
|
||
const ok = b.exit === b.entry;
|
||
console.log(`✅ НИЧЬЯ→ВОЗВРАТ: exit=${b.exit} entry=${b.entry} ${ok ? '(равны!)' : '(НЕ равны — ОШИБКА!)'}`);
|
||
} else {
|
||
normal++;
|
||
console.log(`${name}: exit=${b.exit} entry=${b.entry}`);
|
||
}
|
||
}
|
||
|
||
console.log(`\nИТОГ: возвратов=${ties}, обычных исходов=${normal}`);
|
||
console.log(ties > 0 ? 'ВЕРДИКТ: возврат при ничьей РАБОТАЕТ на новой ставке' : 'ВЕРДИКТ: ничью поймать не удалось (вероятность ~33.6% за попытку)');
|
||
})();
|