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

Take rate #83

Closed
wants to merge 9 commits into from
20 changes: 20 additions & 0 deletions token-lending/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,15 @@ fn main() {
.default_value("30")
.help("Amount of liquidation bonus going to fee reciever: [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 reciever: [0, 100]"),
)
.arg(
Arg::with_name("deposit_limit")
.long("deposit-limit")
Expand Down Expand Up @@ -529,6 +538,15 @@ fn main() {
.required(false)
.help("Amount of liquidation bonus going to fee reciever: [0, 100]"),
)
.arg(
Arg::with_name("protocol_take_rate")
nope-finance marked this conversation as resolved.
Show resolved Hide resolved
.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 reciever: [0, 100]"),
)
.arg(
Arg::with_name("deposit_limit")
.long("deposit-limit")
Expand Down Expand Up @@ -669,6 +687,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 +726,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
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(),
}
}
73 changes: 73 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,71 @@ fn process_update_reserve_config(
Ok(())
}

#[inline(never)] // avoid stack frame limit
fn process_redeem_fees(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
Copy link

@0xCactus 0xCactus May 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we restrict who can execute this function for security as its protocol specific? E.g only allowed by the market owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not needed because the function sends the fees to reserve.config.fee_receiver only, so it doesn't really matter who triggers this instruction.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we specifically dont want to limit it because want to set up a crank to call it once a day from a non essential address

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)?;
nope-finance marked this conversation as resolved.
Show resolved Hide resolved

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());
}

nope-finance marked this conversation as resolved.
Show resolved Hide resolved
let lending_market = LendingMarket::unpack(&lending_market_info.data.borrow())?;
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 fee_reciever_claimable_amount = reserve
nope-finance marked this conversation as resolved.
Show resolved Hide resolved
.liquidity
.accumulated_protocol_fees_wads
.try_floor_u64()?;

reserve.liquidity.accumulated_protocol_fees_wads = reserve
.liquidity
.accumulated_protocol_fees_wads
.try_sub(Decimal::from(fee_reciever_claimable_amount))?;

nope-finance marked this conversation as resolved.
Show resolved Hide resolved
spl_token_transfer(TokenTransferParams {
source: reserve_supply_liquidity_info.clone(),
destination: reserve_liquidity_fee_receiver_info.clone(),
amount: fee_reciever_claimable_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 +2674,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