Kод смарт-контракта на Rust + Anchor:

pub fn initialize(ctx: Context<Initialize>, reward_rate: u64) -> Result<()> {
    let staking_account = &mut ctx.accounts.staking_account;
    staking_account.reward_rate = reward_rate;
    staking_account.total_staked = 0;
    Ok(())
}

pub fn stake(ctx: Context<Stake>, amount: u64) -> Result<()> {
    let staking_account = &mut ctx.accounts.staking_account;
    let user_staking = &mut ctx.accounts.user_staking;

    user_staking.amount += amount;
    staking_account.total_staked += amount;
    user_staking.start_time = Clock::get()?.unix_timestamp;

    token::transfer(ctx.accounts.into_transfer_context(), amount)?;
    Ok(())
}

pub fn unstake(ctx: Context<Unstake>) -> Result<()> {
    let staking_account = &mut ctx.accounts.staking_account;
    let user_staking = &mut ctx.accounts.user_staking;
    
    let current_time = Clock::get()?.unix_timestamp;
    let duration = current_time - user_staking.start_time;
    
    let base_reward = user_staking.amount * staking_account.reward_rate * duration as u64;
    let scaled_reward = base_reward / (1_000_000 + duration as u64); // Smooth scaling to prevent excessive rewards
    
    token::transfer(ctx.accounts.into_transfer_back_context(), user_staking.amount + scaled_reward)?;
    
    staking_account.total_staked -= user_staking.amount;
    user_staking.amount = 0;
    Ok(())
}

Tесты для проверки функционала стейкинга и анстейкинга, включая расчёт наград.

#[test]
fn test_stake_unstake() {
    let mut staking_account = StakingAccount { reward_rate: 10, total_staked: 0 };
    let mut user_staking = UserStaking { amount: 0, start_time: 0 };
    
    let amount = 100;
    let timestamp = 1_000_000;
    
    user_staking.amount += amount;
    staking_account.total_staked += amount;
    user_staking.start_time = timestamp;
    
    assert_eq!(user_staking.amount, 100);
    assert_eq!(staking_account.total_staked, 100);
    
    let duration = 1_000_000;
    let base_reward = user_staking.amount * staking_account.reward_rate * duration as u64;
    let scaled_reward = base_reward / (1_000_000 + duration as u64);
    
    staking_account.total_staked -= user_staking.amount;
    user_staking.amount = 0;
    
    assert_eq!(user_staking.amount, 0);
    assert_eq!(staking_account.total_staked, 0);
    assert_eq!(scaled_reward, 999); // Adjusted for smooth scaling
}

Last updated