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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 20 additions & 2 deletions token-lending/LIQUIDITY_MINING.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,29 @@ 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 from the admin's token account.

### `cancel_pool_reward`
### `edit_pool_reward`

This ix sets the end time of the pool reward to "now" and returns any unallocated rewards to the admin.
Both extending and shortening calculate the difference between total rewards linearly.
Users will still be able to claim rewards they accrued until this point.

#### Cancel

Cancelling a pool reward can be done by setting the end time to 0.
Note that only rewards longer than [solend_sdk::MIN_REWARD_PERIOD_SECS] can be cancelled.
In this case we transfer tokens from the reward vault to the lending market reward token account.

#### Shorten

If the new endtime is in the future, larger than start time and smaller than previous end time
then we shorten the reward period, refunding the unallocated rewards to the lending market
reward token account.

#### Extend

If the new endtime is in the future, larger than start time and larger than previous end time
then we extend the reward period, taking more tokens from the lending market reward token
account.

### `claim_pool_reward`

Permission-less way to claim allocated user liquidity mining rewards.
Expand Down
8 changes: 5 additions & 3 deletions token-lending/program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,17 +225,19 @@ pub fn process_instruction(
accounts,
)
}
LendingInstruction::CancelPoolReward {
LendingInstruction::EditPoolReward {
reward_authority_bump,
position_kind,
pool_reward_index,
new_end_time_secs,
} => {
msg!("Instruction: Cancel Pool Reward");
liquidity_mining::cancel_pool_reward::process(
msg!("Instruction: Edit Pool Reward");
liquidity_mining::edit_pool_reward::process(
program_id,
reward_authority_bump,
position_kind,
pool_reward_index as _,
new_end_time_secs,
accounts,
)
}
Expand Down
4 changes: 2 additions & 2 deletions token-lending/program/src/processor/liquidity_mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//!
//! There are three admin-only ixs:
//! - [add_pool_reward]
//! - [cancel_pool_reward]
//! - [edit_pool_reward]
//! - [close_pool_reward]
//!
//! There is an ix related to migration:
Expand All @@ -20,9 +20,9 @@
//! [suilend-lm]: https://github.com/solendprotocol/suilend/blob/dc53150416f352053ac3acbb320ee143409c4a5d/contracts/suilend/sources/liquidity_mining.move#L2

pub(crate) mod add_pool_reward;
pub(crate) mod cancel_pool_reward;
pub(crate) mod claim_user_reward;
pub(crate) mod close_pool_reward;
pub(crate) mod edit_pool_reward;
pub(crate) mod upgrade_reserve;

use solana_program::program_pack::Pack;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
//! Cancel a pool reward.
//! Edits a pool reward.
//!
//! This ix sets the end time of the pool reward to now and returns any
//! unallocated rewards to the admin.
//! Users will still be able to claim rewards.
//! # Cancel
//! Cancelling a pool reward can be done by setting the end time to 0.
//! Note that only rewards longer than [solend_sdk::MIN_REWARD_PERIOD_SECS] can be cancelled.
//! In this case we transfer tokens from the reward vault to the lending market reward token account.
//!
//! # Shorten
//! If the new endtime is in the future, larger than start time and smaller than previous end time
//! then we shorten the reward period, refunding the unallocated rewards to the lending market
//! reward token account.
//!
//! # Extend
//! If the new endtime is in the future, larger than start time and larger than previous end time
//! then we extend the reward period, taking more tokens from the lending market reward token
//! account.
//!
//! ---
//!
//! Both extending and shortening calculate the difference between total rewards linearly.

use crate::processor::liquidity_mining::{
check_and_unpack_pool_reward_accounts_for_admin_ixs, unpack_token_account,
Expand All @@ -23,7 +38,7 @@ use solend_sdk::{error::LendingError, state::PositionKind};
use super::{Bumps, CheckAndUnpackPoolRewardAccounts, ReserveBorrow};

/// Use [Self::from_unchecked_iter] to validate the accounts.
struct CancelPoolRewardAccounts<'a, 'info> {
struct EditPoolRewardAccounts<'a, 'info> {
/// ✅ belongs to this program
/// ✅ unpacks
/// ✅ belongs to `lending_market_info`
Expand All @@ -34,7 +49,7 @@ struct CancelPoolRewardAccounts<'a, 'info> {
/// ✅ belongs to the token program
/// ✅ matches `reward_mint_info`
/// ✅ is writable
reward_token_destination_info: &'a AccountInfo<'info>,
lending_market_reward_token_account_info: &'a AccountInfo<'info>,
/// ✅ seed of `lending_market_info`, `reserve_info`, `reward_mint_info`
reward_authority_info: &'a AccountInfo<'info>,
/// ❓ we don't know whether it matches the reward vault pubkey stored in [Reserve]
Expand All @@ -45,7 +60,7 @@ struct CancelPoolRewardAccounts<'a, 'info> {
lending_market_info: &'a AccountInfo<'info>,
/// ✅ is a signer
/// ✅ matches `lending_market_info`
_lending_market_owner_info: &'a AccountInfo<'info>,
lending_market_owner_info: &'a AccountInfo<'info>,
/// ✅ matches `lending_market_info`
token_program_info: &'a AccountInfo<'info>,

Expand All @@ -54,16 +69,18 @@ struct CancelPoolRewardAccounts<'a, 'info> {

/// # Effects
///
/// 1. Cancels any further reward emission, effectively setting end time to now.
/// 2. Transfers any unallocated rewards to the `reward_token_destination` account.
/// 1. Sets the new time
/// 2. Either refunds the admin or takes more tokens from the admin, based on the new end time
/// relation to the old end time
pub(crate) fn process(
program_id: &Pubkey,
reward_authority_bump: u8,
position_kind: PositionKind,
pool_reward_index: usize,
new_end_time_secs: u64,
accounts: &[AccountInfo],
) -> ProgramResult {
let mut accounts = CancelPoolRewardAccounts::from_unchecked_iter(
let mut accounts = EditPoolRewardAccounts::from_unchecked_iter(
program_id,
Bumps {
reward_authority: reward_authority_bump,
Expand All @@ -73,10 +90,10 @@ pub(crate) fn process(

// 1.

let (expected_vault, unallocated_rewards) = accounts
let (expected_vault, change_to_vault_amount) = accounts
.reserve
.pool_reward_manager_mut(position_kind)
.cancel_pool_reward(pool_reward_index, &Clock::get()?)?;
.edit_pool_reward(pool_reward_index, new_end_time_secs, &Clock::get()?)?;

if expected_vault != *accounts.reward_token_vault_info.key {
msg!("Reward vault provided does not match the reward vault pubkey stored in [Reserve]");
Expand All @@ -85,33 +102,46 @@ pub(crate) fn process(

// 2.

spl_token_transfer(TokenTransferParams {
source: accounts.reward_token_vault_info.clone(),
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,
)
.as_slice(),
&[&[reward_authority_bump]],
]
.concat(),
token_program: accounts.token_program_info.clone(),
})?;

Ok(())
msg!("Change to vault amount: {}", change_to_vault_amount);

match change_to_vault_amount {
0 => Ok(()),
// transfer more tokens to the vault
1.. => spl_token_transfer(TokenTransferParams {
source: accounts.lending_market_reward_token_account_info.clone(),
destination: accounts.reward_token_vault_info.clone(),
amount: change_to_vault_amount.unsigned_abs(),
authority: accounts.lending_market_owner_info.clone(),
authority_signer_seeds: &[],
token_program: accounts.token_program_info.clone(),
}),
// refund to lending market reward token account
..=-1 => spl_token_transfer(TokenTransferParams {
source: accounts.reward_token_vault_info.clone(),
destination: accounts.lending_market_reward_token_account_info.clone(),
amount: change_to_vault_amount.unsigned_abs(),
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,
)
.as_slice(),
&[&[reward_authority_bump]],
]
.concat(),
token_program: accounts.token_program_info.clone(),
}),
}
}

impl<'a, 'info> CancelPoolRewardAccounts<'a, 'info> {
impl<'a, 'info> EditPoolRewardAccounts<'a, 'info> {
fn from_unchecked_iter(
program_id: &Pubkey,
bump: Bumps,
iter: &mut impl Iterator<Item = &'a AccountInfo<'info>>,
) -> Result<CancelPoolRewardAccounts<'a, 'info>, ProgramError> {
) -> Result<EditPoolRewardAccounts<'a, 'info>, ProgramError> {
let reserve_info = next_account_info(iter)?;
let reward_mint_info = next_account_info(iter)?;
let reward_token_destination_info = next_account_info(iter)?;
Expand Down Expand Up @@ -163,11 +193,11 @@ impl<'a, 'info> CancelPoolRewardAccounts<'a, 'info> {
Ok(Self {
_reserve_info: reserve_info,
reward_mint_info,
reward_token_destination_info,
lending_market_reward_token_account_info: reward_token_destination_info,
reward_authority_info,
reward_token_vault_info,
lending_market_info,
_lending_market_owner_info: lending_market_owner_info,
lending_market_owner_info,
token_program_info,

reserve,
Expand Down
4 changes: 2 additions & 2 deletions token-lending/program/tests/add_pool_reward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use solend_program::{
math::Decimal,
state::{PoolRewardId, PoolRewardManager, PositionKind, Reserve, UserRewardManager},
};
use solend_sdk::state::{PoolReward, PoolRewardSlot, UserReward};
use solend_sdk::state::{PoolReward, PoolRewardEntry, UserReward};

#[tokio::test]
async fn test_add_pool_reward_for_deposit() {
Expand Down Expand Up @@ -64,7 +64,7 @@ async fn test_(position_kind: PositionKind) {
pool_rewards: {
let mut og = PoolRewardManager::default().pool_rewards;

og[0] = PoolRewardSlot::Occupied(Box::new(PoolReward {
og[0] = PoolRewardEntry::Occupied(Box::new(PoolReward {
id: PoolRewardId(1),
vault: reward_vault.pubkey(),
start_time_secs: current_time,
Expand Down
Loading
Loading