feat: migrate smart-updown contract to SPL-token (фантики)
- create_bet: escrow фантиков с bettor на vault (PDA[global,vault]) через anchor_spl::token - close_bet: выплата фантиков из vault на bettor (CPI с signer_seeds vault) - state/constants: comments про единицы (token units decimals=6), + VAULT_SEED - IDL регенерирован (idl/ + relayer/idl/) с аккаунтами bettor_token/vault_token/token_program - тесты обновлены под токен (10/10 + doctest)
This commit is contained in:
@@ -2,26 +2,43 @@ use {
|
||||
anchor_lang::{
|
||||
solana_program::{
|
||||
instruction::Instruction,
|
||||
program_option::COption,
|
||||
program_pack::Pack,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
system_instruction,
|
||||
},
|
||||
AccountDeserialize, InstructionData, ToAccountMetas,
|
||||
},
|
||||
smart_updown::state::{Bet, Global},
|
||||
smart_updown::{constants::VAULT_SEED, state::{Bet, Global}},
|
||||
solana_account::{Account as SolanaAccount},
|
||||
solana_clock::Clock,
|
||||
solana_keypair::Keypair,
|
||||
solana_program_test::{BanksClient, ProgramTest, ProgramTestContext},
|
||||
solana_signer::Signer,
|
||||
solana_transaction::Transaction,
|
||||
spl_associated_token_account,
|
||||
spl_token,
|
||||
spl_token::state::{Account as TokenData, AccountState, Mint},
|
||||
};
|
||||
|
||||
const CLOCK_ADDR: &str = "SysvarC1ock11111111111111111111111111111111";
|
||||
const SYSTEM_ADDR: &str = "11111111111111111111111111111111";
|
||||
const MIN_AMOUNT: u64 = 1_000_000_000; // 1 SOL
|
||||
const MIN_AMOUNT: u64 = 1_000_000_000; // minimum bet, token units
|
||||
const MULTIPLIER_BPS: u64 = 13_000; // 1.3x
|
||||
const EXPIRY_SECONDS: i64 = 15;
|
||||
const START_TIME: i64 = 1_700_000_000;
|
||||
|
||||
// SPL token (фантики) environment
|
||||
const TOKEN_DECIMALS: u8 = 6;
|
||||
const MINT_SIZE: u64 = 82; // classic SPL token mint account size
|
||||
const TOKEN_ACCOUNT_SIZE: u64 = 165; // classic SPL token account size
|
||||
const VAULT_TOKEN_SUPPLY: u64 = 100 * MIN_AMOUNT; // vault seed, covers many payouts
|
||||
const BETTOR_TOKEN_SUPPLY: u64 = 10 * MIN_AMOUNT; // per-bettor token supply
|
||||
/// Fixed фантики mint: mint + vault token accounts are pre-loaded into
|
||||
/// genesis because the vault is a PDA and cannot sign its own create_account.
|
||||
const MINT: Pubkey = Pubkey::new_from_array([3u8; 32]);
|
||||
|
||||
fn pid() -> Pubkey {
|
||||
smart_updown::id()
|
||||
}
|
||||
@@ -30,16 +47,73 @@ fn global_pda() -> Pubkey {
|
||||
Pubkey::find_program_address(&[b"global"], &pid()).0
|
||||
}
|
||||
|
||||
fn vault_pda() -> Pubkey {
|
||||
Pubkey::find_program_address(&[b"global", VAULT_SEED], &pid()).0
|
||||
}
|
||||
|
||||
fn bet_pda(id: u64) -> Pubkey {
|
||||
Pubkey::find_program_address(&[b"bet", &id.to_le_bytes()], &pid()).0
|
||||
}
|
||||
|
||||
/// Boot the program-test context with the фантики mint and the program's
|
||||
/// token vault (PDA([global, vault])) pre-loaded into genesis. The vault is a
|
||||
/// PDA owned by itself, so it can never sign a top-level create_account; the
|
||||
/// account data below matches what the token program would have created.
|
||||
async fn setup() -> (ProgramTestContext, Keypair) {
|
||||
let admin = Keypair::new();
|
||||
let mint_data = {
|
||||
let m = Mint {
|
||||
mint_authority: COption::Some(admin.pubkey()),
|
||||
supply: 0,
|
||||
decimals: TOKEN_DECIMALS,
|
||||
is_initialized: true,
|
||||
freeze_authority: COption::None,
|
||||
};
|
||||
let mut buf = [0u8; MINT_SIZE as usize];
|
||||
Pack::pack(m, &mut buf).expect("pack mint");
|
||||
buf
|
||||
};
|
||||
let vault_data = {
|
||||
let a = TokenData {
|
||||
mint: MINT,
|
||||
owner: vault_pda(),
|
||||
amount: VAULT_TOKEN_SUPPLY,
|
||||
delegate: COption::None,
|
||||
state: AccountState::Initialized,
|
||||
is_native: COption::None,
|
||||
delegated_amount: 0,
|
||||
close_authority: COption::None,
|
||||
};
|
||||
let mut buf = [0u8; TOKEN_ACCOUNT_SIZE as usize];
|
||||
Pack::pack(a, &mut buf).expect("pack vault token account");
|
||||
buf
|
||||
};
|
||||
|
||||
let mut pt = ProgramTest::default();
|
||||
pt.add_program("smart_updown", pid(), None);
|
||||
let rent = Rent::default();
|
||||
pt.add_account(
|
||||
MINT,
|
||||
SolanaAccount {
|
||||
lamports: rent.minimum_balance(MINT_SIZE as usize),
|
||||
data: mint_data.to_vec(),
|
||||
owner: spl_token::ID,
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
},
|
||||
);
|
||||
pt.add_account(
|
||||
vault_pda(),
|
||||
SolanaAccount {
|
||||
lamports: rent.minimum_balance(TOKEN_ACCOUNT_SIZE as usize),
|
||||
data: vault_data.to_vec(),
|
||||
owner: spl_token::ID,
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
},
|
||||
);
|
||||
// Fund the deploy keypair separately in the test; the validator in CI funds it.
|
||||
let context = pt.start_with_context().await;
|
||||
let admin = Keypair::new();
|
||||
context.banks_client.process_transaction(Transaction::new_signed_with_payer(
|
||||
&[system_instruction::transfer(
|
||||
&context.payer.pubkey(),
|
||||
@@ -93,7 +167,14 @@ fn make_initialize_ix(admin: Pubkey) -> Instruction {
|
||||
)
|
||||
}
|
||||
|
||||
fn make_create_ix(id: u64, side: u64, amount: u64, entry_price: u64, bettor: Pubkey) -> Instruction {
|
||||
fn make_create_ix(
|
||||
id: u64,
|
||||
side: u64,
|
||||
amount: u64,
|
||||
entry_price: u64,
|
||||
bettor: Pubkey,
|
||||
bettor_ata: &Pubkey,
|
||||
) -> Instruction {
|
||||
Instruction::new_with_bytes(
|
||||
pid(),
|
||||
&smart_updown::instruction::CreateBet {
|
||||
@@ -109,12 +190,21 @@ fn make_create_ix(id: u64, side: u64, amount: u64, entry_price: u64, bettor: Pub
|
||||
bet: bet_pda(id),
|
||||
clock: CLOCK_ADDR.parse().unwrap(),
|
||||
system_program: SYSTEM_ADDR.parse().unwrap(),
|
||||
bettor_token: *bettor_ata,
|
||||
vault_token: vault_pda(),
|
||||
token_program: spl_token::ID,
|
||||
}
|
||||
.to_account_metas(None),
|
||||
)
|
||||
}
|
||||
|
||||
fn make_close_ix(id: u64, exit_price: u64, relayer: Pubkey, bettor: Pubkey) -> Instruction {
|
||||
fn make_close_ix(
|
||||
id: u64,
|
||||
exit_price: u64,
|
||||
relayer: Pubkey,
|
||||
bettor: Pubkey,
|
||||
bettor_ata: &Pubkey,
|
||||
) -> Instruction {
|
||||
Instruction::new_with_bytes(
|
||||
pid(),
|
||||
&smart_updown::instruction::CloseBet { id, exit_price }.data(),
|
||||
@@ -124,6 +214,9 @@ fn make_close_ix(id: u64, exit_price: u64, relayer: Pubkey, bettor: Pubkey) -> I
|
||||
global: global_pda(),
|
||||
bettor,
|
||||
clock: CLOCK_ADDR.parse().unwrap(),
|
||||
vault_token: vault_pda(),
|
||||
bettor_token: *bettor_ata,
|
||||
token_program: spl_token::ID,
|
||||
}
|
||||
.to_account_metas(None),
|
||||
)
|
||||
@@ -167,22 +260,90 @@ async fn airdrop_to(ctx: &mut ProgramTestContext, to: &Pubkey, lamports: u64) {
|
||||
ctx.banks_client.process_transaction(tx).await.unwrap();
|
||||
}
|
||||
|
||||
async fn fund_vault(ctx: &mut ProgramTestContext, lamports: u64) {
|
||||
airdrop_to(ctx, &global_pda(), lamports).await;
|
||||
/// Create the token mint, the program's token vault (PDA([global, vault]),
|
||||
/// owned by the global PDA), and seed the vault with tokens.
|
||||
// The фантики env (mint + seeded vault) is pre-loaded into genesis by setup();
|
||||
// a PDA vault can never sign its own top-level create_account, so this only
|
||||
// returns the fixed mint the genesis accounts were built around.
|
||||
async fn create_token_env(_ctx: &mut ProgramTestContext, _admin: &Keypair) -> Pubkey {
|
||||
MINT
|
||||
}
|
||||
|
||||
/// Mint tokens to a destination account (admin is the mint authority).
|
||||
async fn mint_to(
|
||||
ctx: &mut ProgramTestContext,
|
||||
admin: &Keypair,
|
||||
mint: &Pubkey,
|
||||
dest: &Pubkey,
|
||||
amount: u64,
|
||||
) {
|
||||
let ix = spl_token::instruction::mint_to(&spl_token::ID, mint, dest, &admin.pubkey(), &[], amount)
|
||||
.expect("mint_to");
|
||||
run_ix(ctx, admin, ix).await.expect("mint_to");
|
||||
}
|
||||
|
||||
/// New bettor: lamports account + associated token account + token supply.
|
||||
async fn create_bettor(
|
||||
ctx: &mut ProgramTestContext,
|
||||
admin: &Keypair,
|
||||
mint: &Pubkey,
|
||||
) -> (Keypair, Pubkey) {
|
||||
let bettor = Keypair::new();
|
||||
airdrop_to(ctx, &bettor.pubkey(), 100_000_000_000).await;
|
||||
let ata = spl_associated_token_account::get_associated_token_address(&bettor.pubkey(), mint);
|
||||
let ix = spl_associated_token_account::instruction::create_associated_token_account(
|
||||
&admin.pubkey(),
|
||||
&bettor.pubkey(),
|
||||
mint,
|
||||
&spl_token::ID,
|
||||
);
|
||||
run_ix(ctx, admin, ix).await.expect("create ata");
|
||||
mint_to(ctx, admin, mint, &ata, BETTOR_TOKEN_SUPPLY).await;
|
||||
(bettor, ata)
|
||||
}
|
||||
|
||||
/// Read the `amount` field of a classic SPL token account (offset 64).
|
||||
async fn read_token_balance(ctx: &mut ProgramTestContext, addr: &Pubkey) -> u64 {
|
||||
let data = read_account_data(&ctx.banks_client, addr).await;
|
||||
u64::from_le_bytes(data[64..72].try_into().expect("amount offset"))
|
||||
}
|
||||
|
||||
/// close_bet pays out by moving tokens out of the vault; the vault PDA signs
|
||||
/// that transfer CPI inside the program, so the transaction needs only the
|
||||
/// relayer's signature.
|
||||
async fn run_close_tx(
|
||||
ctx: &mut ProgramTestContext,
|
||||
signer: &Keypair,
|
||||
ix: &Instruction,
|
||||
) -> Result<(), String> {
|
||||
// The vault PDA signs the payout CPI inside the program (via signer_seeds);
|
||||
// the transaction itself needs only the relayer's signature.
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
std::slice::from_ref(ix),
|
||||
Some(&signer.pubkey()),
|
||||
&[signer],
|
||||
ctx.last_blockhash,
|
||||
);
|
||||
ctx.banks_client
|
||||
.process_transaction(tx)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
}
|
||||
|
||||
async fn create_up_bet(
|
||||
ctx: &mut ProgramTestContext,
|
||||
side: u64,
|
||||
) -> (Keypair, u64, u64) {
|
||||
let bettor = Keypair::new();
|
||||
airdrop_to(ctx, &bettor.pubkey(), 100_000_000_000).await;
|
||||
mint: &Pubkey,
|
||||
admin: &Keypair,
|
||||
) -> (Keypair, Pubkey, u64, u64) {
|
||||
let (bettor, ata) = create_bettor(ctx, admin, mint).await;
|
||||
set_clock(ctx, START_TIME).await;
|
||||
let amount = MIN_AMOUNT;
|
||||
let entry_price = 100_000_000u64;
|
||||
let create_ix = make_create_ix(0, side, amount, entry_price, bettor.pubkey());
|
||||
let create_ix = make_create_ix(0, side, amount, entry_price, bettor.pubkey(), &ata);
|
||||
run_ix(ctx, &bettor, create_ix).await.expect("create_bet");
|
||||
(bettor, amount, entry_price)
|
||||
(bettor, ata, amount, entry_price)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -200,13 +361,20 @@ async fn initialize_creates_global() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_bet_escrows_sol() {
|
||||
async fn create_bet_escrows_tokens() {
|
||||
let (mut ctx, admin) = setup().await;
|
||||
initialize(&mut ctx, &admin).await;
|
||||
let (bettor, amount, entry_price) = create_up_bet(&mut ctx, 0).await;
|
||||
let mint = create_token_env(&mut ctx, &admin).await;
|
||||
let (bettor, ata, amount, entry_price) = create_up_bet(&mut ctx, 0, &mint, &admin).await;
|
||||
|
||||
let vault_balance = ctx.banks_client.get_balance(global_pda()).await.unwrap();
|
||||
assert!(vault_balance >= amount);
|
||||
assert_eq!(
|
||||
read_token_balance(&mut ctx, &vault_pda()).await,
|
||||
VAULT_TOKEN_SUPPLY + amount
|
||||
);
|
||||
assert_eq!(
|
||||
read_token_balance(&mut ctx, &ata).await,
|
||||
BETTOR_TOKEN_SUPPLY - amount
|
||||
);
|
||||
let b = read_bet_typed(&read_account_data(&ctx.banks_client, &bet_pda(0)).await);
|
||||
assert_eq!(b.bettor, bettor.pubkey());
|
||||
assert_eq!(b.side, 0);
|
||||
@@ -222,18 +390,27 @@ async fn create_bet_escrows_sol() {
|
||||
async fn close_bet_user_wins_up() {
|
||||
let (mut ctx, admin) = setup().await;
|
||||
initialize(&mut ctx, &admin).await;
|
||||
let (bettor, amount, entry_price) = create_up_bet(&mut ctx, 0).await;
|
||||
let mint = create_token_env(&mut ctx, &admin).await;
|
||||
let (bettor, ata, amount, entry_price) = create_up_bet(&mut ctx, 0, &mint, &admin).await;
|
||||
|
||||
set_clock(&mut ctx, START_TIME + EXPIRY_SECONDS + 1).await;
|
||||
let exit_price = entry_price + 1_000;
|
||||
let before = ctx.banks_client.get_balance(bettor.pubkey()).await.unwrap();
|
||||
let before = read_token_balance(&mut ctx, &ata).await;
|
||||
let payout = amount * MULTIPLIER_BPS / 10_000;
|
||||
fund_vault(&mut ctx, 10 * amount).await;
|
||||
|
||||
let close_ix = make_close_ix(0, exit_price, admin.pubkey(), bettor.pubkey());
|
||||
run_ix(&mut ctx, &admin, close_ix).await.expect("close_bet");
|
||||
run_close_tx(
|
||||
&mut ctx,
|
||||
&admin,
|
||||
&make_close_ix(0, exit_price, admin.pubkey(), bettor.pubkey(), &ata),
|
||||
)
|
||||
.await
|
||||
.expect("close_bet");
|
||||
|
||||
assert_eq!(ctx.banks_client.get_balance(bettor.pubkey()).await.unwrap(), before + payout);
|
||||
assert_eq!(read_token_balance(&mut ctx, &ata).await, before + payout);
|
||||
assert_eq!(
|
||||
read_token_balance(&mut ctx, &vault_pda()).await,
|
||||
VAULT_TOKEN_SUPPLY + amount - payout
|
||||
);
|
||||
let b = read_bet_typed(&read_account_data(&ctx.banks_client, &bet_pda(0)).await);
|
||||
assert_eq!(b.status, 1);
|
||||
assert_eq!(b.exit_price, exit_price);
|
||||
@@ -243,16 +420,22 @@ async fn close_bet_user_wins_up() {
|
||||
async fn close_bet_house_wins_down() {
|
||||
let (mut ctx, admin) = setup().await;
|
||||
initialize(&mut ctx, &admin).await;
|
||||
let (bettor, _amount, entry_price) = create_up_bet(&mut ctx, 1).await;
|
||||
let mint = create_token_env(&mut ctx, &admin).await;
|
||||
let (bettor, ata, _amount, entry_price) = create_up_bet(&mut ctx, 1, &mint, &admin).await;
|
||||
|
||||
set_clock(&mut ctx, START_TIME + EXPIRY_SECONDS + 1).await;
|
||||
let exit_price = entry_price + 1_000; // side=DOWN but price rose -> lose
|
||||
let before = ctx.banks_client.get_balance(bettor.pubkey()).await.unwrap();
|
||||
let before = read_token_balance(&mut ctx, &ata).await;
|
||||
|
||||
let close_ix = make_close_ix(0, exit_price, admin.pubkey(), bettor.pubkey());
|
||||
run_ix(&mut ctx, &admin, close_ix).await.expect("close_bet");
|
||||
run_close_tx(
|
||||
&mut ctx,
|
||||
&admin,
|
||||
&make_close_ix(0, exit_price, admin.pubkey(), bettor.pubkey(), &ata),
|
||||
)
|
||||
.await
|
||||
.expect("close_bet");
|
||||
|
||||
assert_eq!(ctx.banks_client.get_balance(bettor.pubkey()).await.unwrap(), before);
|
||||
assert_eq!(read_token_balance(&mut ctx, &ata).await, before);
|
||||
assert_eq!(read_bet_typed(&read_account_data(&ctx.banks_client, &bet_pda(0)).await).status, 2);
|
||||
}
|
||||
|
||||
@@ -260,23 +443,23 @@ async fn close_bet_house_wins_down() {
|
||||
async fn close_bet_equal_price_stays_open() {
|
||||
let (mut ctx, admin) = setup().await;
|
||||
initialize(&mut ctx, &admin).await;
|
||||
let (bettor, amount, entry_price) = create_up_bet(&mut ctx, 0).await;
|
||||
let mint = create_token_env(&mut ctx, &admin).await;
|
||||
let (bettor, ata, amount, entry_price) = create_up_bet(&mut ctx, 0, &mint, &admin).await;
|
||||
|
||||
set_clock(&mut ctx, START_TIME + EXPIRY_SECONDS + 1).await;
|
||||
let before = ctx.banks_client.get_balance(bettor.pubkey()).await.unwrap();
|
||||
let before = read_token_balance(&mut ctx, &ata).await;
|
||||
let payout = amount * MULTIPLIER_BPS / 10_000;
|
||||
fund_vault(&mut ctx, 10 * amount).await;
|
||||
|
||||
let equal_ix = make_close_ix(0, entry_price, admin.pubkey(), bettor.pubkey());
|
||||
run_ix(&mut ctx, &admin, equal_ix).await.expect("equal close");
|
||||
let equal_ix = make_close_ix(0, entry_price, admin.pubkey(), bettor.pubkey(), &ata);
|
||||
run_close_tx(&mut ctx, &admin, &equal_ix).await.expect("equal close");
|
||||
assert_eq!(read_bet_typed(&read_account_data(&ctx.banks_client, &bet_pda(0)).await).status, 0);
|
||||
assert_eq!(ctx.banks_client.get_balance(bettor.pubkey()).await.unwrap(), before);
|
||||
assert_eq!(read_token_balance(&mut ctx, &ata).await, before);
|
||||
|
||||
let exit_price = entry_price + 1_000;
|
||||
let second_ix = make_close_ix(0, exit_price, admin.pubkey(), bettor.pubkey());
|
||||
run_ix(&mut ctx, &admin, second_ix).await.expect("second close");
|
||||
let second_ix = make_close_ix(0, exit_price, admin.pubkey(), bettor.pubkey(), &ata);
|
||||
run_close_tx(&mut ctx, &admin, &second_ix).await.expect("second close");
|
||||
assert_eq!(
|
||||
ctx.banks_client.get_balance(bettor.pubkey()).await.unwrap(),
|
||||
read_token_balance(&mut ctx, &ata).await,
|
||||
before + payout
|
||||
);
|
||||
assert_eq!(read_bet_typed(&read_account_data(&ctx.banks_client, &bet_pda(0)).await).status, 1);
|
||||
@@ -286,37 +469,40 @@ async fn close_bet_equal_price_stays_open() {
|
||||
async fn close_bet_already_finalized() {
|
||||
let (mut ctx, admin) = setup().await;
|
||||
initialize(&mut ctx, &admin).await;
|
||||
let (bettor, amount, entry_price) = create_up_bet(&mut ctx, 0).await;
|
||||
let mint = create_token_env(&mut ctx, &admin).await;
|
||||
let (bettor, ata, _amount, entry_price) = create_up_bet(&mut ctx, 0, &mint, &admin).await;
|
||||
|
||||
set_clock(&mut ctx, START_TIME + EXPIRY_SECONDS + 1).await;
|
||||
let win_price = entry_price + 500;
|
||||
fund_vault(&mut ctx, 10 * amount).await;
|
||||
run_ix(&mut ctx, &admin, make_close_ix(0, win_price, admin.pubkey(), bettor.pubkey()))
|
||||
.await
|
||||
.expect("first close");
|
||||
|
||||
let res = run_ix(
|
||||
run_close_tx(
|
||||
&mut ctx,
|
||||
&admin,
|
||||
make_close_ix(0, win_price + 1, admin.pubkey(), bettor.pubkey()),
|
||||
&make_close_ix(0, win_price, admin.pubkey(), bettor.pubkey(), &ata),
|
||||
)
|
||||
.await
|
||||
.expect("first close");
|
||||
|
||||
let res = run_close_tx(
|
||||
&mut ctx,
|
||||
&admin,
|
||||
&make_close_ix(0, win_price + 1, admin.pubkey(), bettor.pubkey(), &ata),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_err(), "expected close after finalization to be rejected");
|
||||
let b = read_bet_typed(&read_account_data(&ctx.banks_client, &bet_pda(0)).await);
|
||||
assert_eq!(b.status, 1);
|
||||
assert_eq!(b.exit_price, win_price);
|
||||
assert_eq!(b.status, 1);
|
||||
assert_eq!(b.exit_price, win_price);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn close_bet_before_expiry_rejected() {
|
||||
let (mut ctx, admin) = setup().await;
|
||||
initialize(&mut ctx, &admin).await;
|
||||
let (bettor, _amount, entry_price) = create_up_bet(&mut ctx, 0).await;
|
||||
let mint = create_token_env(&mut ctx, &admin).await;
|
||||
let (bettor, ata, _amount, entry_price) = create_up_bet(&mut ctx, 0, &mint, &admin).await;
|
||||
|
||||
let close_ix = make_close_ix(0, entry_price + 500, admin.pubkey(), bettor.pubkey());
|
||||
let res = run_ix(&mut ctx, &admin, close_ix).await;
|
||||
let close_ix = make_close_ix(0, entry_price + 500, admin.pubkey(), bettor.pubkey(), &ata);
|
||||
let res = run_close_tx(&mut ctx, &admin, &close_ix).await;
|
||||
assert!(res.is_err(), "expected premature close to be rejected");
|
||||
assert_eq!(read_bet_typed(&read_account_data(&ctx.banks_client, &bet_pda(0)).await).status, 0);
|
||||
}
|
||||
@@ -325,13 +511,14 @@ async fn close_bet_before_expiry_rejected() {
|
||||
async fn close_bet_wrong_relayer_rejected() {
|
||||
let (mut ctx, admin) = setup().await;
|
||||
initialize(&mut ctx, &admin).await;
|
||||
let (bettor, _amount, entry_price) = create_up_bet(&mut ctx, 0).await;
|
||||
let mint = create_token_env(&mut ctx, &admin).await;
|
||||
let (bettor, ata, _amount, entry_price) = create_up_bet(&mut ctx, 0, &mint, &admin).await;
|
||||
let attacker = Keypair::new();
|
||||
airdrop_to(&mut ctx, &attacker.pubkey(), 100_000_000_000).await;
|
||||
|
||||
set_clock(&mut ctx, START_TIME + EXPIRY_SECONDS + 1).await;
|
||||
let close_ix = make_close_ix(0, entry_price + 500, attacker.pubkey(), bettor.pubkey());
|
||||
let res = run_ix(&mut ctx, &attacker, close_ix).await;
|
||||
let close_ix = make_close_ix(0, entry_price + 500, attacker.pubkey(), bettor.pubkey(), &ata);
|
||||
let res = run_close_tx(&mut ctx, &attacker, &close_ix).await;
|
||||
assert!(res.is_err(), "expected wrong relayer to be rejected");
|
||||
assert_eq!(read_bet_typed(&read_account_data(&ctx.banks_client, &bet_pda(0)).await).status, 0);
|
||||
}
|
||||
@@ -340,32 +527,34 @@ async fn close_bet_wrong_relayer_rejected() {
|
||||
async fn close_bet_replay_rejected() {
|
||||
let (mut ctx, admin) = setup().await;
|
||||
initialize(&mut ctx, &admin).await;
|
||||
let (bettor, _amount, entry_price) = create_up_bet(&mut ctx, 0).await;
|
||||
let mint = create_token_env(&mut ctx, &admin).await;
|
||||
let (bettor, ata, _amount, entry_price) = create_up_bet(&mut ctx, 0, &mint, &admin).await;
|
||||
|
||||
set_clock(&mut ctx, START_TIME + EXPIRY_SECONDS + 1).await;
|
||||
fund_vault(&mut ctx, 10 * MIN_AMOUNT).await;
|
||||
let close_ix = make_close_ix(0, entry_price + 500, admin.pubkey(), bettor.pubkey());
|
||||
run_ix(&mut ctx, &admin, close_ix).await.expect("first close");
|
||||
let before = ctx.banks_client.get_balance(bettor.pubkey()).await.unwrap();
|
||||
let res = run_ix(
|
||||
let close_ix = make_close_ix(0, entry_price + 500, admin.pubkey(), bettor.pubkey(), &ata);
|
||||
run_close_tx(&mut ctx, &admin, &close_ix).await.expect("first close");
|
||||
let before = read_token_balance(&mut ctx, &ata).await;
|
||||
let res = run_close_tx(
|
||||
&mut ctx,
|
||||
&admin,
|
||||
make_close_ix(0, entry_price + 501, admin.pubkey(), bettor.pubkey()),
|
||||
&make_close_ix(0, entry_price + 501, admin.pubkey(), bettor.pubkey(), &ata),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_err(), "expected replay close to be rejected");
|
||||
assert_eq!(ctx.banks_client.get_balance(bettor.pubkey()).await.unwrap(), before);
|
||||
assert_eq!(read_token_balance(&mut ctx, &ata).await, before);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_bet_rejects_out_of_range() {
|
||||
let (mut ctx, admin) = setup().await;
|
||||
initialize(&mut ctx, &admin).await;
|
||||
let bettor = Keypair::new();
|
||||
airdrop_to(&mut ctx, &bettor.pubkey(), 100_000_000_000).await;
|
||||
let mint = create_token_env(&mut ctx, &admin).await;
|
||||
let (bettor, ata) = create_bettor(&mut ctx, &admin, &mint).await;
|
||||
set_clock(&mut ctx, START_TIME).await;
|
||||
|
||||
let ix = make_create_ix(0, 0, 1, 100_000_000u64, bettor.pubkey());
|
||||
let vault_before = read_token_balance(&mut ctx, &vault_pda()).await;
|
||||
let ix = make_create_ix(0, 0, 1, 100_000_000u64, bettor.pubkey(), &ata);
|
||||
let res = run_ix(&mut ctx, &bettor, ix).await;
|
||||
assert!(res.is_err(), "expected below-min bet to be rejected");
|
||||
assert_eq!(read_token_balance(&mut ctx, &vault_pda()).await, vault_before);
|
||||
}
|
||||
Reference in New Issue
Block a user