feat: миграция smart-updown на anchor 0.31.1 + solana 2.x (SBPFv3)

- anchor-lang 0.31.1 вместо 2.0-rc/anchor-next (git)
- Address -> Pubkey (state/events/instructions)
- убран #[discrim], добавлен #[derive(InitSpace)] + space=8+INIT_SPACE
- тесты переписаны на solana-program-test 2.x (10 passed)
- сборка SBPFv3 через cargo build-sbf --tools-version v1.57
- deploy успешен в нашу сеть (agave 4.2.2, SBPFv3)
This commit is contained in:
2026-09-03 05:27:34 +03:00
parent 2133f65e21
commit 9f100ee122
13 changed files with 5784 additions and 1395 deletions
+3 -3
View File
@@ -2,8 +2,8 @@ use anchor_lang::prelude::*;
#[event]
pub struct BetCreated {
pub bet: Address,
pub bettor: Address,
pub bet: Pubkey,
pub bettor: Pubkey,
pub side: u64,
pub amount: u64,
pub bet_time: i64,
@@ -13,7 +13,7 @@ pub struct BetCreated {
#[event]
pub struct BetClosed {
pub bet: Address,
pub bet: Pubkey,
pub exit_price: u64,
pub user_won: bool,
pub payout: u64,
@@ -1,4 +1,4 @@
use anchor_lang::{prelude::*, Lamports};
use anchor_lang::prelude::*;
use crate::{
constants::{
@@ -12,33 +12,33 @@ use crate::{
#[derive(Accounts)]
#[instruction(id: u64, exit_price: u64)]
pub struct CloseBet {
pub relayer: Signer,
pub struct CloseBet<'info> {
pub relayer: Signer<'info>,
#[account(
mut,
seeds = [BET_SEED, id.to_le_bytes()],
seeds = [BET_SEED, &id.to_le_bytes()],
bump
)]
pub bet: Account<Bet>,
pub bet: Account<'info, Bet>,
#[account(
mut,
seeds = [GLOBAL_SEED],
bump
)]
pub global: Account<Global>,
pub global: Account<'info, Global>,
/// CHECK: recipient must be the bet's bettor and is validated in the handler.
#[account(mut)]
pub bettor: SystemAccount,
pub clock: Sysvar<Clock>,
pub bettor: SystemAccount<'info>,
pub clock: Sysvar<'info, Clock>,
}
pub fn handler(ctx: &mut Context<CloseBet>, id: u64, exit_price: u64) -> Result<()> {
require!(
anchor_lang::address_eq(ctx.accounts.relayer.address(), &ctx.accounts.global.relayer),
ctx.accounts.relayer.key() == ctx.accounts.global.relayer,
SmartUpdownError::NotRelayer
);
require!(
anchor_lang::address_eq(ctx.accounts.bettor.address(), &ctx.accounts.bet.bettor),
ctx.accounts.bettor.key() == ctx.accounts.bet.bettor,
SmartUpdownError::NotRelayer
);
require!(
@@ -59,24 +59,24 @@ pub fn handler(ctx: &mut Context<CloseBet>, id: u64, exit_price: u64) -> Result<
let user_won = (side == SIDE_UP && exit_price > entry)
|| (side == SIDE_DOWN && exit_price < entry);
let payout;
if user_won {
let multiplier = ctx.accounts.global.multiplier_bps;
let payout_amount =
(amount as u128)
.checked_mul(multiplier as u128)
.unwrap()
.checked_div(BPS_DIVISOR as u128)
.unwrap() as u64;
require!(
ctx.accounts.global.view().lamports() >= payout_amount,
SmartUpdownError::InsufficientVault
);
payout = payout_amount;
ctx.accounts.global.sub_lamports(payout)?;
ctx.accounts.bettor.add_lamports(payout)?;
ctx.accounts.bet.status = STATUS_PAYOUT_DONE;
} else {
let payout;
if user_won {
let multiplier = ctx.accounts.global.multiplier_bps;
let payout_amount =
(amount as u128)
.checked_mul(multiplier as u128)
.unwrap()
.checked_div(BPS_DIVISOR as u128)
.unwrap() as u64;
require!(
ctx.accounts.global.get_lamports() >= payout_amount,
SmartUpdownError::InsufficientVault
);
payout = payout_amount;
ctx.accounts.global.sub_lamports(payout)?;
ctx.accounts.bettor.add_lamports(payout)?;
ctx.accounts.bet.status = STATUS_PAYOUT_DONE;
} else {
payout = 0;
ctx.accounts.bet.status = STATUS_HOUSE_WON;
}
@@ -84,7 +84,7 @@ pub fn handler(ctx: &mut Context<CloseBet>, id: u64, exit_price: u64) -> Result<
ctx.accounts.bet.exit_price = exit_price;
emit!(BetClosed {
bet: *ctx.accounts.bet.address(),
bet: ctx.accounts.bet.key(),
exit_price,
user_won,
payout,
@@ -9,24 +9,25 @@ use crate::{
#[derive(Accounts)]
#[instruction(id: u64, side: u64, amount: u64, entry_price: u64)]
pub struct CreateBet {
pub struct CreateBet<'info> {
#[account(mut)]
pub bettor: Signer,
pub bettor: Signer<'info>,
#[account(
mut,
seeds = [GLOBAL_SEED],
bump
)]
pub global: Account<Global>,
pub global: Account<'info, Global>,
#[account(
init,
payer = bettor,
seeds = [BET_SEED, id.to_le_bytes()],
space = 8 + Bet::INIT_SPACE,
seeds = [BET_SEED, &id.to_le_bytes()],
bump
)]
pub bet: Account<Bet>,
pub clock: Sysvar<Clock>,
pub system_program: Program<System>,
pub bet: Account<'info, Bet>,
pub clock: Sysvar<'info, Clock>,
pub system_program: Program<'info, System>,
}
pub fn handler(
@@ -52,8 +53,8 @@ pub fn handler(
);
let now = ctx.accounts.clock.unix_timestamp;
let bettor_address = *ctx.accounts.bettor.address();
let bet_address = *ctx.accounts.bet.address();
let bettor_address = ctx.accounts.bettor.key();
let bet_address = ctx.accounts.bet.key();
let expiry_seconds = ctx.accounts.global.expiry_seconds;
{
@@ -71,12 +72,15 @@ pub fn handler(
ctx.accounts.global.counter = ctx.accounts.global.counter.checked_add(1).unwrap();
{
let cpi_accounts = system_program::Transfer {
from: ctx.accounts.bettor.cpi_handle_mut(),
to: ctx.accounts.global.cpi_handle_mut(),
let cpi_accounts = anchor_lang::system_program::Transfer {
from: ctx.accounts.bettor.to_account_info(),
to: ctx.accounts.global.to_account_info(),
};
let cpi_ctx = CpiContext::new(ctx.accounts.system_program.address(), cpi_accounts);
system_program::transfer(cpi_ctx, amount)?;
let cpi_ctx = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
cpi_accounts,
);
anchor_lang::system_program::transfer(cpi_ctx, amount)?;
}
emit!(BetCreated {
@@ -3,17 +3,18 @@ use anchor_lang::prelude::*;
use crate::{constants::GLOBAL_SEED, state::Global};
#[derive(Accounts)]
pub struct Initialize {
pub struct Initialize<'info> {
#[account(mut)]
pub admin: Signer,
pub admin: Signer<'info>,
#[account(
init,
payer = admin,
space = 8 + Global::INIT_SPACE,
seeds = [GLOBAL_SEED],
bump
)]
pub global: Account<Global>,
pub system_program: Program<System>,
pub global: Account<'info, Global>,
pub system_program: Program<'info, System>,
}
pub fn handler(
@@ -24,8 +25,8 @@ pub fn handler(
expiry_seconds: i64,
) -> Result<()> {
let global = &mut ctx.accounts.global;
global.admin = *ctx.accounts.admin.address();
global.relayer = *ctx.accounts.admin.address();
global.admin = ctx.accounts.admin.key();
global.relayer = ctx.accounts.admin.key();
global.counter = 0;
global.min_amount = min_amount;
global.max_amount = max_amount;
@@ -7,19 +7,19 @@ use crate::{
};
#[derive(Accounts)]
pub struct Update {
pub admin: Signer,
pub struct Update<'info> {
pub admin: Signer<'info>,
#[account(
mut,
seeds = [GLOBAL_SEED],
bump
)]
pub global: Account<Global>,
pub global: Account<'info, Global>,
}
pub fn handler(
ctx: &mut Context<Update>,
new_relayer: Address,
new_relayer: Pubkey,
min_amount: u64,
max_amount: u64,
multiplier_bps: u64,
@@ -27,7 +27,7 @@ pub fn handler(
expiry_seconds: i64,
) -> Result<()> {
require!(
anchor_lang::address_eq(ctx.accounts.admin.address(), &ctx.accounts.global.admin),
ctx.accounts.admin.key() == ctx.accounts.global.admin,
SmartUpdownError::NotAdmin
);
let global = &mut ctx.accounts.global;
+9 -13
View File
@@ -14,41 +14,37 @@ declare_id!("9ALsnxXNzDBv3mokngCHbruRTWUZiWR7pf7vswS1fCpf");
pub mod smart_updown {
use super::*;
#[discrim = 0]
pub fn initialize(
ctx: &mut Context<Initialize>,
mut ctx: Context<Initialize>,
min_amount: u64,
max_amount: u64,
multiplier_bps: u64,
expiry_seconds: i64,
) -> Result<()> {
initialize::handler(ctx, min_amount, max_amount, multiplier_bps, expiry_seconds)
initialize::handler(&mut ctx, min_amount, max_amount, multiplier_bps, expiry_seconds)
}
#[discrim = 1]
pub fn create_bet(
ctx: &mut Context<CreateBet>,
mut ctx: Context<CreateBet>,
id: u64,
side: u64,
amount: u64,
entry_price: u64,
) -> Result<()> {
create_bet::handler(ctx, id, side, amount, entry_price)
create_bet::handler(&mut ctx, id, side, amount, entry_price)
}
#[discrim = 2]
pub fn close_bet(
ctx: &mut Context<CloseBet>,
mut ctx: Context<CloseBet>,
id: u64,
exit_price: u64,
) -> Result<()> {
close_bet::handler(ctx, id, exit_price)
close_bet::handler(&mut ctx, id, exit_price)
}
#[discrim = 3]
pub fn update(
ctx: &mut Context<Update>,
new_relayer: Address,
mut ctx: Context<Update>,
new_relayer: Pubkey,
min_amount: u64,
max_amount: u64,
multiplier_bps: u64,
@@ -56,7 +52,7 @@ pub mod smart_updown {
expiry_seconds: i64,
) -> Result<()> {
update::handler(
ctx,
&mut ctx,
new_relayer,
min_amount,
max_amount,
+5 -3
View File
@@ -1,6 +1,7 @@
use anchor_lang::prelude::*;
#[account]
#[derive(InitSpace)]
pub struct Global {
pub counter: u64,
pub min_amount: u64,
@@ -8,11 +9,12 @@ pub struct Global {
pub multiplier_bps: u64,
pub expiry_seconds: i64,
pub paused: u64,
pub admin: Address,
pub relayer: Address,
pub admin: Pubkey,
pub relayer: Pubkey,
}
#[account]
#[derive(InitSpace)]
pub struct Bet {
pub amount: u64,
pub bet_time: i64,
@@ -21,5 +23,5 @@ pub struct Bet {
pub exit_price: u64,
pub side: u64,
pub status: u64,
pub bettor: Address,
pub bettor: Pubkey,
}