feat(contract): возврат залога при ничьей (exit_price == entry_price)
Раньше при равенстве цен close_bet делал `return Ok(())` — ставка навсегда оставалась в статусе open, а залог запирался в ваулте (замер: 33.6% окон). Из-за правила «одна открытая ставка на адрес» игрок получал 409 пожизненно. Теперь ничья возвращает залог целиком (без множителя) и финализирует ставку: - constants.rs: STATUS_REFUNDED = 3 (0/1/2 не тронуты — зашиты в шлюз и в цепи) - close_bet.rs: CPI-перевод amount из ваулта беттору под сидами ваулт-PDA, запись exit_price/status, событие BetClosed(status=3, payout=amount) - settle.ts: STATUS_REFUNDED, statusName "refunded", статус 3 в условии финализации - check.ts: проверка формы события refunded - тест close_bet_equal_price_stays_open → close_bet_equal_price_refunds (+ повторное закрытие отклоняется) Проверено: cargo test-sbf 10/10, npm run typecheck EXIT=0, две мутации (снятие статуса, снятие перевода) валят именно новый тест. Задеплоено: программа 9ALs… (хэш из цепи совпал побайтово), шлюз rev 15.
This commit is contained in:
@@ -33,6 +33,7 @@ import {
|
||||
STATUS_OPEN,
|
||||
STATUS_PAYOUT_DONE,
|
||||
STATUS_HOUSE_WON,
|
||||
STATUS_REFUNDED,
|
||||
} from "./settle";
|
||||
|
||||
const ok = (msg: string) => console.log(`ok: ${msg}`);
|
||||
@@ -421,6 +422,15 @@ async function main(): Promise<void> {
|
||||
check(ev.statusName === "house_won", 'betClosedEvent house_won: statusName==="house_won"');
|
||||
}
|
||||
|
||||
// Тест: betClosedEvent refunded.
|
||||
{
|
||||
const bettor = Keypair.generate().publicKey;
|
||||
const bet = decodeBet(makeBetBuffer({ status: STATUS_REFUNDED, bettor }));
|
||||
const ev = betClosedEvent(bet, 21, 10_112_000_000, STATUS_REFUNDED) as Record<string, unknown>;
|
||||
check(ev.status === 3, "betClosedEvent refunded: status (число 3)");
|
||||
check(ev.statusName === "refunded", 'betClosedEvent refunded: statusName==="refunded"');
|
||||
}
|
||||
|
||||
// Санити-проверка: STATUS_OPEN == 0 для совместимости с on-chain контрактом.
|
||||
check(STATUS_OPEN === 0, "settle: STATUS_OPEN === 0 (on-chain OPEN)");
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ const SENTINEL_UNMAPPED_LOGGED = -1;
|
||||
export const STATUS_OPEN = 0;
|
||||
export const STATUS_PAYOUT_DONE = 1;
|
||||
export const STATUS_HOUSE_WON = 2;
|
||||
export const STATUS_REFUNDED = 3;
|
||||
|
||||
export const SIDE_UP = 0;
|
||||
export const SIDE_DOWN = 1;
|
||||
@@ -173,6 +174,7 @@ export function betOpenEvent(bet: DecodedBet, id: number): Record<string, unknow
|
||||
function statusName(status: number): string {
|
||||
if (status === STATUS_PAYOUT_DONE) return "payout_done";
|
||||
if (status === STATUS_HOUSE_WON) return "house_won";
|
||||
if (status === STATUS_REFUNDED) return "refunded";
|
||||
return "open";
|
||||
}
|
||||
|
||||
@@ -358,7 +360,11 @@ export async function settleOnce(ctx: SchedulerCtx): Promise<{
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (after.status === STATUS_PAYOUT_DONE || after.status === STATUS_HOUSE_WON) {
|
||||
if (
|
||||
after.status === STATUS_PAYOUT_DONE ||
|
||||
after.status === STATUS_HOUSE_WON ||
|
||||
after.status === STATUS_REFUNDED
|
||||
) {
|
||||
stats.settled++;
|
||||
if (lastState) lastState.delete(item.id);
|
||||
ctx.broadcast(betClosedEvent(after, item.id, exitPrice, after.status));
|
||||
|
||||
Reference in New Issue
Block a user