Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[solana] Fix bugs in transfer_vesting and claim_vesting #220

Merged
merged 11 commits into from
Jan 10, 2025
8 changes: 6 additions & 2 deletions solana/programs/staking/src/contexts/cancel_vesting.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anchor_lang::prelude::*;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

use crate::context::{VESTING_BALANCE_SEED, VESTING_CONFIG_SEED, VEST_SEED};
Expand All @@ -11,8 +12,9 @@ pub struct CancelVesting<'info> {
admin: Signer<'info>,
mint: InterfaceAccount<'info, Mint>,
#[account(
token::mint = mint,
token::token_program = token_program
associated_token::mint = mint,
associated_token::authority = vester_ta.owner,
associated_token::token_program = token_program
)]
vester_ta: InterfaceAccount<'info, TokenAccount>,
#[account(
Expand All @@ -38,6 +40,8 @@ pub struct CancelVesting<'info> {
bump = vesting_balance.bump
)]
vesting_balance: Account<'info, VestingBalance>,

associated_token_program: Program<'info, AssociatedToken>,
token_program: Interface<'info, TokenInterface>,
system_program: Program<'info, System>,
}
Expand Down
39 changes: 29 additions & 10 deletions solana/programs/staking/src/contexts/claim_vesting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ pub struct ClaimVesting<'info> {
vault: InterfaceAccount<'info, TokenAccount>,
#[account(
mut,
token::mint = mint
associated_token::mint = mint,
associated_token::authority = vester,
associated_token::token_program = token_program
)]
vester_ta: InterfaceAccount<'info, TokenAccount>,
#[account(
Expand Down Expand Up @@ -57,7 +59,9 @@ pub struct ClaimVesting<'info> {
/// CheckpointData and StakeAccountMetadata accounts are optional because
/// in order to be able to claim vests that have not been delegated
#[account(mut)]
pub stake_account_checkpoints: Option<AccountLoader<'info, CheckpointData>>,
pub delegate_stake_account_checkpoints: Option<AccountLoader<'info, CheckpointData>>,
#[account(mut)]
pub delegate_stake_account_metadata: Option<Box<Account<'info, StakeAccountMetadata>>>,
#[account(mut)]
pub stake_account_metadata: Option<Box<Account<'info, StakeAccountMetadata>>>,
#[account(
Expand All @@ -76,19 +80,24 @@ impl<'info> ClaimVesting<'info> {
// If vesting_balance.stake_account_metadata is not set it means that vester has not
// delegated his vests
if self.vesting_balance.stake_account_metadata != Pubkey::default() {
if let (Some(stake_account_metadata), Some(stake_account_checkpoints)) = (
if let (
Some(stake_account_metadata),
Some(delegate_stake_account_metadata),
Some(delegate_stake_account_checkpoints),
) = (
&mut self.stake_account_metadata,
&mut self.stake_account_checkpoints,
&mut self.delegate_stake_account_metadata,
&mut self.delegate_stake_account_checkpoints,
) {
// Check if stake account checkpoints is out of bounds
let loaded_checkpoints = stake_account_checkpoints.load()?;
let loaded_checkpoints = delegate_stake_account_checkpoints.load()?;
require!(
loaded_checkpoints.next_index
< self.global_config.max_checkpoints_account_limit.into(),
ErrorCode::TooManyCheckpoints,
);

// Verify that the actual address matches the expected one
// Verify that the actual delegate_stake_account_checkpoints address matches the expected one
require!(
stake_account_metadata.delegate.key() == loaded_checkpoints.owner,
VestingError::InvalidStakeAccountCheckpoints
Expand All @@ -101,6 +110,12 @@ impl<'info> ClaimVesting<'info> {
VestingError::InvalidStakeAccountOwner
);

// Verify that the actual delegate_stake_account_metadata address matches the expected one
require!(
stake_account_metadata.delegate == delegate_stake_account_metadata.owner,
VestingError::InvalidStakeAccountOwner
);

let new_recorded_vesting_balance = stake_account_metadata
.recorded_vesting_balance
.checked_sub(self.vest.amount)
Expand All @@ -112,12 +127,12 @@ impl<'info> ClaimVesting<'info> {

// Update checkpoints
let current_delegate_checkpoints_account_info =
stake_account_checkpoints.to_account_info();
delegate_stake_account_checkpoints.to_account_info();

let current_timestamp: u64 = Clock::get()?.unix_timestamp.try_into()?;

push_checkpoint(
stake_account_checkpoints,
delegate_stake_account_checkpoints,
&current_delegate_checkpoints_account_info,
self.vest.amount,
Operation::Subtract,
Expand All @@ -126,11 +141,15 @@ impl<'info> ClaimVesting<'info> {
&self.system_program.to_account_info(),
)?;

let loaded_checkpoints = stake_account_checkpoints.load()?;
let loaded_checkpoints = delegate_stake_account_checkpoints.load()?;
if loaded_checkpoints.next_index
>= self.global_config.max_checkpoints_account_limit.into()
{
stake_account_metadata.stake_account_checkpoints_last_index += 1;
if delegate_stake_account_metadata.key() == stake_account_metadata.key() {
stake_account_metadata.stake_account_checkpoints_last_index += 1;
} else {
delegate_stake_account_metadata.stake_account_checkpoints_last_index += 1;
}
}
} else {
return err!(VestingError::ErrorOfStakeAccountParsing);
Expand Down
8 changes: 6 additions & 2 deletions solana/programs/staking/src/contexts/create_vesting.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anchor_lang::prelude::*;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

use crate::context::{VESTING_BALANCE_SEED, VESTING_CONFIG_SEED, VEST_SEED};
Expand All @@ -12,8 +13,9 @@ pub struct CreateVesting<'info> {
admin: Signer<'info>,
mint: InterfaceAccount<'info, Mint>,
#[account(
token::mint = mint,
token::token_program = token_program
associated_token::mint = mint,
associated_token::authority = vester_ta.owner,
associated_token::token_program = token_program
)]
vester_ta: InterfaceAccount<'info, TokenAccount>,
#[account(
Expand All @@ -39,6 +41,8 @@ pub struct CreateVesting<'info> {
bump = vesting_balance.bump
)]
vesting_balance: Account<'info, VestingBalance>,

associated_token_program: Program<'info, AssociatedToken>,
token_program: Interface<'info, TokenInterface>,
system_program: Program<'info, System>,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::context::{VESTING_BALANCE_SEED, VESTING_CONFIG_SEED};
use crate::state::{VestingBalance, VestingConfig};
use anchor_lang::prelude::*;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

#[derive(Accounts)]
Expand All @@ -24,10 +25,13 @@ pub struct CreateVestingBalance<'info> {
)]
vesting_balance: Account<'info, VestingBalance>,
#[account(
token::mint = mint,
token::token_program = token_program
associated_token::mint = mint,
associated_token::authority = vester_ta.owner,
associated_token::token_program = token_program
)]
vester_ta: InterfaceAccount<'info, TokenAccount>,

associated_token_program: Program<'info, AssociatedToken>,
token_program: Interface<'info, TokenInterface>,
system_program: Program<'info, System>,
}
Expand Down
2 changes: 1 addition & 1 deletion solana/programs/staking/src/contexts/finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub struct Finalize<'info> {
impl<'info> Finalize<'info> {
pub fn finalize(&mut self) -> Result<()> {
require!(
self.vault.amount == self.config.vested,
self.vault.amount >= self.config.vested,
VestingError::VestedBalanceMismatch
);

Expand Down
Loading