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

trying take rate other way #89

Merged
merged 4 commits into from
Jun 13, 2022
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
37 changes: 35 additions & 2 deletions token-lending/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ struct PartialReserveConfig {
pub fee_receiver: Option<Pubkey>,
/// Cut of the liquidation bonus that the protocol receives, as a percentage
pub protocol_liquidation_fee: Option<u8>,
/// Protocol take rate is the amount borrowed interest protocol recieves, as a percentage
pub protocol_take_rate: Option<u8>,
}

/// Reserve Fees with optional fields
Expand Down Expand Up @@ -376,7 +378,16 @@ fn main() {
.takes_value(true)
.required(false)
.default_value("30")
.help("Amount of liquidation bonus going to fee reciever: [0, 100]"),
.help("Amount of liquidation bonus going to fee receiver: [0, 100]"),
)
.arg(
Arg::with_name("protocol_take_rate")
.long("protocol-take-rate")
.validator(is_parsable::<u8>)
.value_name("INTEGER_PERCENT")
.takes_value(true)
.required(false)
.help("Amount of interest spread going to fee receiver: [0, 100]"),
)
.arg(
Arg::with_name("deposit_limit")
Expand Down Expand Up @@ -527,7 +538,16 @@ fn main() {
.value_name("INTEGER_PERCENT")
.takes_value(true)
.required(false)
.help("Amount of liquidation bonus going to fee reciever: [0, 100]"),
.help("Amount of liquidation bonus going to fee receiver: [0, 100]"),
)
.arg(
Arg::with_name("protocol_take_rate")
.long("protocol-take-rate")
.validator(is_parsable::<u8>)
.value_name("INTEGER_PERCENT")
.takes_value(true)
.required(false)
.help("Amount of interest spread going to fee receiver: [0, 100]"),
)
.arg(
Arg::with_name("deposit_limit")
Expand Down Expand Up @@ -669,6 +689,7 @@ fn main() {
let liquidity_fee_receiver_keypair = Keypair::new();
let protocol_liquidation_fee =
value_of(arg_matches, "protocol_liquidation_fee").unwrap();
let protocol_take_rate = value_of(arg_matches, "protocol_take_rate").unwrap();

let source_liquidity_account = config
.rpc_client
Expand Down Expand Up @@ -707,6 +728,7 @@ fn main() {
borrow_limit,
fee_receiver: liquidity_fee_receiver_keypair.pubkey(),
protocol_liquidation_fee,
protocol_take_rate,
},
source_liquidity_pubkey,
source_liquidity_owner_keypair,
Expand Down Expand Up @@ -738,6 +760,7 @@ fn main() {
let borrow_limit = value_of(arg_matches, "borrow_limit");
let fee_receiver = pubkey_of(arg_matches, "fee_receiver");
let protocol_liquidation_fee = value_of(arg_matches, "protocol_liquidation_fee");
let protocol_take_rate = value_of(arg_matches, "protocol_take_rate");
let pyth_product_pubkey = pubkey_of(arg_matches, "pyth_product");
let pyth_price_pubkey = pubkey_of(arg_matches, "pyth_price");
let switchboard_feed_pubkey = pubkey_of(arg_matches, "switchboard_feed");
Expand All @@ -764,6 +787,7 @@ fn main() {
borrow_limit,
fee_receiver,
protocol_liquidation_fee,
protocol_take_rate,
},
pyth_product_pubkey,
pyth_price_pubkey,
Expand Down Expand Up @@ -1199,6 +1223,15 @@ fn command_update_reserve(
reserve.config.protocol_liquidation_fee = reserve_config.protocol_liquidation_fee.unwrap();
}

if reserve_config.protocol_take_rate.is_some() {
println!(
"Updating protocol_take_rate from {} to {}",
reserve.config.protocol_take_rate,
reserve_config.protocol_take_rate.unwrap(),
);
reserve.config.protocol_take_rate = reserve_config.protocol_take_rate.unwrap();
}

let mut new_pyth_product_pubkey = solend_program::NULL_PUBKEY;
if pyth_price_pubkey.is_some() {
println!(
Expand Down
4 changes: 4 additions & 0 deletions token-lending/program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,14 @@ pub enum LendingError {
/// Not enough liquidity after flash loan
#[error("Not enough liquidity after flash loan")]
NotEnoughLiquidityAfterFlashLoan,

// 45
/// Null oracle config
#[error("Null oracle config")]
NullOracleConfig,
/// Insufficent protocol fees to redeem or no liquidity availible to process redeem
#[error("Insufficent protocol fees to claim or no liquidity availible")]
InsufficientProtocolFeesToRedeem,
}

impl From<LendingError> for ProgramError {
Expand Down
52 changes: 50 additions & 2 deletions token-lending/program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,16 @@ pub enum LendingInstruction {
/// Amount of liquidity to repay - u64::MAX for up to 100% of borrowed amount
liquidity_amount: u64,
},

// 18
/// 0. `[writable]` Reserve account.
/// 1. `[writable]` Borrow reserve liquidity fee receiver account.
/// Must be the fee account specified at InitReserve.
/// 2. `[writable]` Reserve liquidity supply SPL Token account.
/// 3. `[]` Lending market account.
/// 4. `[]` Derived lending market authority.
/// 5. `[]` Token program id.
RedeemFees,
}

impl LendingInstruction {
Expand Down Expand Up @@ -444,7 +454,8 @@ impl LendingInstruction {
let (deposit_limit, rest) = Self::unpack_u64(rest)?;
let (borrow_limit, rest) = Self::unpack_u64(rest)?;
let (fee_receiver, rest) = Self::unpack_pubkey(rest)?;
let (protocol_liquidation_fee, _rest) = Self::unpack_u8(rest)?;
let (protocol_liquidation_fee, rest) = Self::unpack_u8(rest)?;
let (protocol_take_rate, _rest) = Self::unpack_u8(rest)?;
Self::InitReserve {
liquidity_amount,
config: ReserveConfig {
Expand All @@ -464,6 +475,7 @@ impl LendingInstruction {
borrow_limit,
fee_receiver,
protocol_liquidation_fee,
protocol_take_rate,
},
}
}
Expand Down Expand Up @@ -524,7 +536,8 @@ impl LendingInstruction {
let (deposit_limit, rest) = Self::unpack_u64(rest)?;
let (borrow_limit, rest) = Self::unpack_u64(rest)?;
let (fee_receiver, rest) = Self::unpack_pubkey(rest)?;
let (protocol_liquidation_fee, _rest) = Self::unpack_u8(rest)?;
let (protocol_liquidation_fee, rest) = Self::unpack_u8(rest)?;
let (protocol_take_rate, _rest) = Self::unpack_u8(rest)?;
Self::UpdateReserveConfig {
config: ReserveConfig {
optimal_utilization_rate,
Expand All @@ -543,13 +556,15 @@ impl LendingInstruction {
borrow_limit,
fee_receiver,
protocol_liquidation_fee,
protocol_take_rate,
},
}
}
17 => {
let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
Self::LiquidateObligationAndRedeemReserveCollateral { liquidity_amount }
}
18 => Self::RedeemFees,
_ => {
msg!("Instruction cannot be unpacked");
return Err(LendingError::InstructionUnpackError.into());
Expand Down Expand Up @@ -646,6 +661,7 @@ impl LendingInstruction {
borrow_limit,
fee_receiver,
protocol_liquidation_fee,
protocol_take_rate,
},
} => {
buf.push(2);
Expand All @@ -664,6 +680,7 @@ impl LendingInstruction {
buf.extend_from_slice(&borrow_limit.to_le_bytes());
buf.extend_from_slice(&fee_receiver.to_bytes());
buf.extend_from_slice(&protocol_liquidation_fee.to_le_bytes());
buf.extend_from_slice(&protocol_take_rate.to_le_bytes());
}
Self::RefreshReserve => {
buf.push(3);
Expand Down Expand Up @@ -730,11 +747,15 @@ impl LendingInstruction {
buf.extend_from_slice(&config.borrow_limit.to_le_bytes());
buf.extend_from_slice(&config.fee_receiver.to_bytes());
buf.extend_from_slice(&config.protocol_liquidation_fee.to_le_bytes());
buf.extend_from_slice(&config.protocol_take_rate.to_le_bytes());
}
Self::LiquidateObligationAndRedeemReserveCollateral { liquidity_amount } => {
buf.push(17);
buf.extend_from_slice(&liquidity_amount.to_le_bytes());
}
Self::RedeemFees {} => {
buf.push(18);
}
}
buf
}
Expand Down Expand Up @@ -1352,3 +1373,30 @@ pub fn liquidate_obligation_and_redeem_reserve_collateral(
.pack(),
}
}

/// Creates a `RedeemFees` instruction
pub fn redeem_fees(
program_id: Pubkey,
reserve_pubkey: Pubkey,
reserve_liquidity_fee_receiver_pubkey: Pubkey,
reserve_supply_liquidity_pubkey: Pubkey,
lending_market_pubkey: Pubkey,
) -> Instruction {
let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
&[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
&program_id,
);
let accounts = vec![
AccountMeta::new(reserve_pubkey, false),
AccountMeta::new(reserve_liquidity_fee_receiver_pubkey, false),
AccountMeta::new(reserve_supply_liquidity_pubkey, false),
AccountMeta::new_readonly(lending_market_pubkey, false),
AccountMeta::new_readonly(lending_market_authority_pubkey, false),
AccountMeta::new_readonly(spl_token::id(), false),
];
Instruction {
program_id,
accounts,
data: LendingInstruction::RedeemFees.pack(),
}
}
89 changes: 89 additions & 0 deletions token-lending/program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ pub fn process_instruction(
accounts,
)
}
LendingInstruction::RedeemFees => {
msg!("Instruction: RedeemFees");
process_redeem_fees(program_id, accounts)
}
}
}

Expand Down Expand Up @@ -2197,6 +2201,87 @@ fn process_update_reserve_config(
Ok(())
}

#[inline(never)] // avoid stack frame limit
fn process_redeem_fees(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
let account_info_iter = &mut accounts.iter().peekable();
let reserve_info = next_account_info(account_info_iter)?;
let reserve_liquidity_fee_receiver_info = next_account_info(account_info_iter)?;
let reserve_supply_liquidity_info = next_account_info(account_info_iter)?;
let lending_market_info = next_account_info(account_info_iter)?;
let lending_market_authority_info = next_account_info(account_info_iter)?;
let token_program_id = next_account_info(account_info_iter)?;
let clock = &Clock::get()?;

let mut reserve = Reserve::unpack(&reserve_info.data.borrow())?;
if reserve_info.owner != program_id {
msg!(
"Reserve provided is not owned by the lending program {} != {}",
&reserve_info.owner.to_string(),
&program_id.to_string(),
);
return Err(LendingError::InvalidAccountOwner.into());
}

if &reserve.config.fee_receiver != reserve_liquidity_fee_receiver_info.key {
msg!("Reserve liquidity fee receiver does not match the reserve liquidity fee receiver provided");
return Err(LendingError::InvalidAccountInput.into());
}
if &reserve.liquidity.supply_pubkey != reserve_supply_liquidity_info.key {
msg!("Reserve liquidity supply must be used as the reserve supply liquidity provided");
return Err(LendingError::InvalidAccountInput.into());
}
if &reserve.lending_market != lending_market_info.key {
msg!("Reserve lending market does not match the lending market provided");
return Err(LendingError::InvalidAccountInput.into());
}
if reserve.last_update.is_stale(clock.slot)? {
msg!("reserve is stale and must be refreshed in the current slot");
return Err(LendingError::ReserveStale.into());
}

let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
if lending_market_info.owner != program_id {
msg!("Lending market provided is not owned by the lending program");
return Err(LendingError::InvalidAccountOwner.into());
}
if &lending_market.token_program_id != token_program_id.key {
msg!("Lending market token program does not match the token program provided");
return Err(LendingError::InvalidTokenProgram.into());
}
let authority_signer_seeds = &[
lending_market_info.key.as_ref(),
&[lending_market.bump_seed],
];
let lending_market_authority_pubkey =
Pubkey::create_program_address(authority_signer_seeds, program_id)?;
if &lending_market_authority_pubkey != lending_market_authority_info.key {
msg!(
"Derived lending market authority does not match the lending market authority provided"
);
return Err(LendingError::InvalidMarketAuthority.into());
}

let withdraw_amount = reserve.calculate_redeem_fees()?;
if withdraw_amount == 0 {
return Err(LendingError::InsufficientProtocolFeesToRedeem.into());
}

reserve.liquidity.redeem_fees(withdraw_amount)?;
reserve.last_update.mark_stale();
Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;

spl_token_transfer(TokenTransferParams {
source: reserve_supply_liquidity_info.clone(),
destination: reserve_liquidity_fee_receiver_info.clone(),
amount: withdraw_amount,
authority: lending_market_authority_info.clone(),
authority_signer_seeds,
token_program: token_program_id.clone(),
})?;

Ok(())
}

fn assert_rent_exempt(rent: &Rent, account_info: &AccountInfo) -> ProgramResult {
if !rent.is_exempt(account_info.lamports(), account_info.data_len()) {
msg!(
Expand Down Expand Up @@ -2605,6 +2690,10 @@ fn validate_reserve_config(config: ReserveConfig) -> ProgramResult {
msg!("Protocol liquidation fee must be in range [0, 100]");
return Err(LendingError::InvalidConfig.into());
}
if config.protocol_take_rate > 100 {
msg!("Protocol take rate must be in range [0, 100]");
return Err(LendingError::InvalidConfig.into());
}
Ok(())
}

Expand Down
Loading