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:
@@ -17,5 +17,8 @@ pub const SIDE_DOWN: u64 = 1;
|
||||
pub const STATUS_OPEN: u64 = 0;
|
||||
pub const STATUS_PAYOUT_DONE: u64 = 1;
|
||||
pub const STATUS_HOUSE_WON: u64 = 2;
|
||||
/// Возврат залога при ничьей (`exit_price == entry_price`).
|
||||
/// Добавлен позже остальных — 0/1/2 уже зашиты в шлюз и в живые аккаунты цепи.
|
||||
pub const STATUS_REFUNDED: u64 = 3;
|
||||
|
||||
pub const BPS_DIVISOR: u64 = 10_000;
|
||||
|
||||
@@ -4,7 +4,7 @@ use anchor_spl::token::{Token, TokenAccount};
|
||||
use crate::{
|
||||
constants::{
|
||||
BPS_DIVISOR, BET_SEED, GLOBAL_SEED, SIDE_DOWN, SIDE_UP, STATUS_HOUSE_WON,
|
||||
STATUS_PAYOUT_DONE, STATUS_OPEN, VAULT_SEED,
|
||||
STATUS_PAYOUT_DONE, STATUS_OPEN, STATUS_REFUNDED, VAULT_SEED,
|
||||
},
|
||||
error::SmartUpdownError,
|
||||
events::BetClosed,
|
||||
@@ -64,6 +64,40 @@ pub fn handler(ctx: &mut Context<CloseBet>, id: u64, exit_price: u64) -> Result<
|
||||
let amount = ctx.accounts.bet.amount;
|
||||
|
||||
if exit_price == entry {
|
||||
require!(
|
||||
ctx.accounts.vault_token.amount >= amount,
|
||||
SmartUpdownError::InsufficientVault
|
||||
);
|
||||
{
|
||||
// Возврат залога: переводим amount (БЕЗ множителя) из ваулта беттору.
|
||||
// Подпись — сидами ваулт-PDA (как в ветке выигрыша ниже).
|
||||
let cpi_accounts = anchor_spl::token::Transfer {
|
||||
from: ctx.accounts.vault_token.to_account_info(),
|
||||
to: ctx.accounts.bettor_token.to_account_info(),
|
||||
authority: ctx.accounts.vault_authority.to_account_info(),
|
||||
};
|
||||
let signer_seeds: &[&[u8]] = &[
|
||||
GLOBAL_SEED,
|
||||
VAULT_SEED,
|
||||
&[ctx.bumps.vault_authority],
|
||||
];
|
||||
let vault_signer_seeds = [signer_seeds];
|
||||
let cpi_ctx = CpiContext::new_with_signer(
|
||||
ctx.accounts.token_program.to_account_info(),
|
||||
cpi_accounts,
|
||||
&vault_signer_seeds,
|
||||
);
|
||||
anchor_spl::token::transfer(cpi_ctx, amount)?;
|
||||
}
|
||||
ctx.accounts.bet.exit_price = exit_price;
|
||||
ctx.accounts.bet.status = STATUS_REFUNDED;
|
||||
emit!(BetClosed {
|
||||
bet: ctx.accounts.bet.key(),
|
||||
exit_price,
|
||||
user_won: false,
|
||||
payout: amount,
|
||||
status: STATUS_REFUNDED,
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user