Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions token-lending/program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ pub fn process_instruction(
process_donate_to_reserve(program_id, liquidity_amount, accounts)
}
LendingInstruction::AddPoolReward {
reward_authority_bump,
position_kind,
start_time_secs,
end_time_secs,
Expand All @@ -216,6 +217,7 @@ pub fn process_instruction(
msg!("Instruction: Add Pool Reward");
liquidity_mining::add_pool_reward::process(
program_id,
reward_authority_bump,
position_kind,
start_time_secs,
end_time_secs,
Expand All @@ -224,32 +226,42 @@ pub fn process_instruction(
)
}
LendingInstruction::CancelPoolReward {
reward_authority_bump,
position_kind,
pool_reward_index,
} => {
msg!("Instruction: Cancel Pool Reward");
liquidity_mining::cancel_pool_reward::process(
program_id,
reward_authority_bump,
position_kind,
pool_reward_index,
pool_reward_index as _,
accounts,
)
}
LendingInstruction::ClosePoolReward {
reward_authority_bump,
position_kind,
pool_reward_index,
} => {
msg!("Instruction: Close Pool Reward");
liquidity_mining::close_pool_reward::process(
program_id,
reward_authority_bump,
position_kind,
pool_reward_index,
pool_reward_index as _,
accounts,
)
}
LendingInstruction::ClaimReward => {
LendingInstruction::ClaimReward {
reward_authority_bump,
} => {
msg!("Instruction: Claim Reward");
liquidity_mining::claim_user_reward::process(program_id, accounts)
liquidity_mining::claim_user_reward::process(
program_id,
reward_authority_bump,
accounts,
)
}

// temporary ix for upgrade
Expand Down
77 changes: 28 additions & 49 deletions token-lending/program/src/processor/liquidity_mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
//! implementation of the same feature.
//!
//! There are three admin-only ixs:
//! - [add_pool_reward] (TODO: add bpf tests)
//! - [cancel_pool_reward] (TODO: add bpf tests)
//! - [close_pool_reward] (TODO: add bpf tests)
//! - [add_pool_reward]
//! - [cancel_pool_reward]
//! - [close_pool_reward]
//!
//! There is an ix related to migration:
//! - [upgrade_reserve] (TODO: add bpf tests)
Expand All @@ -27,42 +27,27 @@ pub(crate) mod upgrade_reserve;

use solana_program::program_pack::Pack;
use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey};
use solend_sdk::instruction::create_reward_vault_authority;
use solend_sdk::{error::LendingError, state::LendingMarket};
use spl_token::state::Account as TokenAccount;

use super::ReserveBorrow;
struct Bumps {
reward_authority: u8,
}

/// Unpacks a spl_token [TokenAccount].
fn unpack_token_account(data: &[u8]) -> Result<TokenAccount, LendingError> {
TokenAccount::unpack(data).map_err(|_| LendingError::InvalidTokenAccount)
}

/// Derives the reward vault authority PDA address.
///
/// TODO: Accept a bump seed to avoid recalculating it.
fn reward_vault_authority(
program_id: &Pubkey,
lending_market_key: &Pubkey,
reserve_key: &Pubkey,
reward_mint_key: &Pubkey,
) -> (Pubkey, u8) {
Pubkey::find_program_address(
&reward_vault_authority_seeds(lending_market_key, reserve_key, reward_mint_key),
program_id,
)
}

fn reward_vault_authority_seeds<'keys>(
lending_market_key: &'keys Pubkey,
reserve_key: &'keys Pubkey,
reward_mint_key: &'keys Pubkey,
) -> [&'keys [u8]; 4] {
[
b"RewardVaultAuthority",
lending_market_key.as_ref(),
reserve_key.as_ref(),
reward_mint_key.as_ref(),
]
/// Named args for [check_and_unpack_pool_reward_accounts]
struct CheckAndUnpackPoolRewardAccounts<'a, 'info> {
reserve_info: &'a AccountInfo<'info>,
reward_mint_info: &'a AccountInfo<'info>,
reward_authority_info: &'a AccountInfo<'info>,
lending_market_info: &'a AccountInfo<'info>,
token_program_info: &'a AccountInfo<'info>,
}

/// Does all the checks of [check_and_unpack_pool_reward_accounts] and additionally:
Expand All @@ -71,21 +56,11 @@ fn reward_vault_authority_seeds<'keys>(
/// * ✅ `lending_market_owner_info` matches `lending_market_info`
fn check_and_unpack_pool_reward_accounts_for_admin_ixs<'a, 'info>(
program_id: &Pubkey,
reserve_info: &'a AccountInfo<'info>,
reward_mint_info: &AccountInfo<'info>,
reward_authority_info: &AccountInfo<'info>,
lending_market_info: &AccountInfo<'info>,
bumps: Bumps,
accs: CheckAndUnpackPoolRewardAccounts<'a, 'info>,
lending_market_owner_info: &AccountInfo<'info>,
token_program_info: &AccountInfo<'info>,
) -> Result<(LendingMarket, ReserveBorrow<'a, 'info>), ProgramError> {
let (lending_market, reserve) = check_and_unpack_pool_reward_accounts(
program_id,
reserve_info,
reward_mint_info,
reward_authority_info,
lending_market_info,
token_program_info,
)?;
let (lending_market, reserve) = check_and_unpack_pool_reward_accounts(program_id, bumps, accs)?;

if lending_market.owner != *lending_market_owner_info.key {
msg!("Lending market owner does not match the lending market owner provided");
Expand All @@ -111,11 +86,14 @@ fn check_and_unpack_pool_reward_accounts_for_admin_ixs<'a, 'info>(
/// * ✅ `reward_mint_info` belongs to the token program
fn check_and_unpack_pool_reward_accounts<'a, 'info>(
program_id: &Pubkey,
reserve_info: &'a AccountInfo<'info>,
reward_mint_info: &AccountInfo<'info>,
reward_authority_info: &AccountInfo<'info>,
lending_market_info: &AccountInfo<'info>,
token_program_info: &AccountInfo<'info>,
bumps: Bumps,
CheckAndUnpackPoolRewardAccounts {
reserve_info,
reward_mint_info,
reward_authority_info,
lending_market_info,
token_program_info,
}: CheckAndUnpackPoolRewardAccounts<'a, 'info>,
) -> Result<(LendingMarket, ReserveBorrow<'a, 'info>), ProgramError> {
let reserve = ReserveBorrow::new_mut(program_id, reserve_info)?;

Expand All @@ -140,12 +118,13 @@ fn check_and_unpack_pool_reward_accounts<'a, 'info>(
return Err(LendingError::InvalidTokenOwner.into());
}

let (expected_reward_vault_authority, _bump_seed) = reward_vault_authority(
let expected_reward_vault_authority = create_reward_vault_authority(
program_id,
lending_market_info.key,
reserve_info.key,
reward_mint_info.key,
);
bumps.reward_authority,
)?;
if expected_reward_vault_authority != *reward_authority_info.key {
msg!("Reward vault authority does not match the expected value");
return Err(LendingError::InvalidAccountInput.into());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
//! Adds a new pool reward to a reserve.
//!
//! Each pool reward has a unique vault that holds the reward tokens.
//! This vault account must be created for the token program before calling this
//! ix.
//! In this ix we initialize the account as token account and transfer the
//! reward tokens to it.

use crate::processor::{
assert_rent_exempt, spl_token_init_account, spl_token_transfer, TokenInitializeAccountParams,
Expand All @@ -19,7 +23,8 @@ use solana_program::{
use solend_sdk::{error::LendingError, state::PositionKind};

use super::{
check_and_unpack_pool_reward_accounts_for_admin_ixs, unpack_token_account, ReserveBorrow,
check_and_unpack_pool_reward_accounts_for_admin_ixs, unpack_token_account, Bumps,
CheckAndUnpackPoolRewardAccounts, ReserveBorrow,
};

/// Use [Self::from_unchecked_iter] to validate the accounts except for
Expand Down Expand Up @@ -71,6 +76,7 @@ struct AddPoolRewardAccounts<'a, 'info> {
/// 2. Finds an empty slot in the [Reserve]'s LM reward vector and adds it there.
pub(crate) fn process(
program_id: &Pubkey,
reward_authority_bump: u8,
position_kind: PositionKind,
start_time_secs: u64,
end_time_secs: u64,
Expand All @@ -81,8 +87,13 @@ pub(crate) fn process(

let clock = &Clock::get()?;

let mut accounts =
AddPoolRewardAccounts::from_unchecked_iter(program_id, &mut accounts.iter())?;
let mut accounts = AddPoolRewardAccounts::from_unchecked_iter(
program_id,
Bumps {
reward_authority: reward_authority_bump,
},
&mut accounts.iter(),
)?;

// 1.

Expand Down Expand Up @@ -124,6 +135,7 @@ pub(crate) fn process(
impl<'a, 'info> AddPoolRewardAccounts<'a, 'info> {
fn from_unchecked_iter(
program_id: &Pubkey,
bumps: Bumps,
iter: &mut impl Iterator<Item = &'a AccountInfo<'info>>,
) -> Result<AddPoolRewardAccounts<'a, 'info>, ProgramError> {
let reserve_info = next_account_info(iter)?;
Expand All @@ -138,12 +150,15 @@ impl<'a, 'info> AddPoolRewardAccounts<'a, 'info> {

let (_, reserve) = check_and_unpack_pool_reward_accounts_for_admin_ixs(
program_id,
reserve_info,
reward_mint_info,
reward_authority_info,
lending_market_info,
bumps,
CheckAndUnpackPoolRewardAccounts {
reserve_info,
reward_mint_info,
reward_authority_info,
lending_market_info,
token_program_info,
},
lending_market_owner_info,
token_program_info,
)?;

if reward_token_source_info.owner != token_program_info.key {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! Cancel a pool reward.
//!
//! This ix sets the end time of the pool reward to now are returns any
//! unallocated rewards to the admin.
//! Users will still be able to claim rewards.

use crate::processor::liquidity_mining::{
check_and_unpack_pool_reward_accounts_for_admin_ixs, unpack_token_account,
};
Expand All @@ -11,9 +17,10 @@ use solana_program::{
program_error::ProgramError,
pubkey::Pubkey,
};
use solend_sdk::instruction::reward_vault_authority_seeds;
use solend_sdk::{error::LendingError, state::PositionKind};

use super::{reward_vault_authority_seeds, ReserveBorrow};
use super::{Bumps, CheckAndUnpackPoolRewardAccounts, ReserveBorrow};

/// Use [Self::from_unchecked_iter] to validate the accounts.
struct CancelPoolRewardAccounts<'a, 'info> {
Expand Down Expand Up @@ -51,12 +58,18 @@ struct CancelPoolRewardAccounts<'a, 'info> {
/// 2. Transfers any unallocated rewards to the `reward_token_destination` account.
pub(crate) fn process(
program_id: &Pubkey,
reward_authority_bump: u8,
position_kind: PositionKind,
pool_reward_index: usize,
accounts: &[AccountInfo],
) -> ProgramResult {
let mut accounts =
CancelPoolRewardAccounts::from_unchecked_iter(program_id, &mut accounts.iter())?;
let mut accounts = CancelPoolRewardAccounts::from_unchecked_iter(
program_id,
Bumps {
reward_authority: reward_authority_bump,
},
&mut accounts.iter(),
)?;

// 1.

Expand All @@ -77,11 +90,16 @@ pub(crate) fn process(
destination: accounts.reward_token_destination_info.clone(),
amount: unallocated_rewards,
authority: accounts.reward_authority_info.clone(),
authority_signer_seeds: &reward_vault_authority_seeds(
accounts.lending_market_info.key,
&accounts.reserve.key(),
accounts.reward_mint_info.key,
),
authority_signer_seeds: &[
reward_vault_authority_seeds(
accounts.lending_market_info.key,
&accounts.reserve.key(),
accounts.reward_mint_info.key,
)
.as_slice(),
&[&[reward_authority_bump]],
]
.concat(),
token_program: accounts.token_program_info.clone(),
})?;

Expand All @@ -91,6 +109,7 @@ pub(crate) fn process(
impl<'a, 'info> CancelPoolRewardAccounts<'a, 'info> {
fn from_unchecked_iter(
program_id: &Pubkey,
bump: Bumps,
iter: &mut impl Iterator<Item = &'a AccountInfo<'info>>,
) -> Result<CancelPoolRewardAccounts<'a, 'info>, ProgramError> {
let reserve_info = next_account_info(iter)?;
Expand All @@ -104,12 +123,15 @@ impl<'a, 'info> CancelPoolRewardAccounts<'a, 'info> {

let (_, reserve) = check_and_unpack_pool_reward_accounts_for_admin_ixs(
program_id,
reserve_info,
reward_mint_info,
reward_authority_info,
lending_market_info,
bump,
CheckAndUnpackPoolRewardAccounts {
reserve_info,
reward_mint_info,
reward_authority_info,
lending_market_info,
token_program_info,
},
lending_market_owner_info,
token_program_info,
)?;

if reward_token_destination_info.owner != token_program_info.key {
Expand Down
Loading
Loading