Skip to content

Commit

Permalink
added liquidate and redeem (with protocol fee) (#75)
Browse files Browse the repository at this point in the history
* added liquidated and redeem (with protocol fee)

* pr comments

* working tests

* protocol liquidation fee configurable

* adding protocol liquidation fee to cli

* justin feedback

* setting fee to 0 for initial deploy
  • Loading branch information
nope-finance authored Mar 15, 2022
1 parent 73260f0 commit f960664
Show file tree
Hide file tree
Showing 7 changed files with 494 additions and 22 deletions.
16 changes: 16 additions & 0 deletions token-lending/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ struct PartialReserveConfig {
pub borrow_limit: Option<u64>,
/// Liquidity fee receiver
pub fee_receiver: Option<Pubkey>,
/// Cut of the liquidation bonus that the protocol receives, as a percentage
pub protocol_liquidation_fee: Option<u8>,
}

/// Reserve Fees with optional fields
Expand Down Expand Up @@ -646,6 +648,8 @@ fn main() {
let flash_loan_fee_wad = (flash_loan_fee * WAD as f64) as u64;

let liquidity_fee_receiver_keypair = Keypair::new();
let protocol_liquidation_fee =
value_of(arg_matches, "protocol_liquidation_fee").unwrap();

let source_liquidity_account = config
.rpc_client
Expand Down Expand Up @@ -683,6 +687,7 @@ fn main() {
deposit_limit,
borrow_limit,
fee_receiver: liquidity_fee_receiver_keypair.pubkey(),
protocol_liquidation_fee,
},
source_liquidity_pubkey,
source_liquidity_owner_keypair,
Expand Down Expand Up @@ -713,6 +718,7 @@ fn main() {
let deposit_limit = value_of(arg_matches, "deposit_limit");
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 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 @@ -738,6 +744,7 @@ fn main() {
deposit_limit,
borrow_limit,
fee_receiver,
protocol_liquidation_fee,
},
pyth_product_pubkey,
pyth_price_pubkey,
Expand Down Expand Up @@ -1164,6 +1171,15 @@ fn command_update_reserve(
reserve.config.fee_receiver = reserve_config.fee_receiver.unwrap();
}

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

let mut new_pyth_product_pubkey = spl_token_lending::NULL_PUBKEY;
if pyth_price_pubkey.is_some() {
println!(
Expand Down
122 changes: 107 additions & 15 deletions token-lending/program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,35 @@ pub enum LendingInstruction {
/// Reserve config to update to
config: ReserveConfig,
},

// 17
/// Repay borrowed liquidity to a reserve to receive collateral at a discount from an unhealthy
/// obligation. Requires a refreshed obligation and reserves.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` Source liquidity token account.
/// Minted by repay reserve liquidity mint.
/// $authority can transfer $liquidity_amount.
/// 1. `[writable]` Destination collateral token account.
/// Minted by withdraw reserve collateral mint.
/// 2. `[writable]` Destination liquidity token account.
/// 3. `[writable]` Repay reserve account - refreshed.
/// 4. `[writable]` Repay reserve liquidity supply SPL Token account.
/// 5. `[writable]` Withdraw reserve account - refreshed.
/// 6. `[writable]` Withdraw reserve collateral SPL Token mint.
/// 7. `[writable]` Withdraw reserve collateral supply SPL Token account.
/// 8. `[writable]` Withdraw reserve liquidity supply SPL Token account.
/// 9. `[writable]` Withdraw reserve liquidity fee receiver account.
/// 10 `[writable]` Obligation account - refreshed.
/// 11 `[]` Lending market account.
/// 12 `[]` Derived lending market authority.
/// 13 `[signer]` User transfer authority ($authority).
/// 14 `[]` Token program id.
LiquidateObligationAndRedeemReserveCollateral {
/// Amount of liquidity to repay - u64::MAX for up to 100% of borrowed amount
liquidity_amount: u64,
},
}

impl LendingInstruction {
Expand Down Expand Up @@ -414,7 +443,8 @@ impl LendingInstruction {
let (host_fee_percentage, rest) = Self::unpack_u8(rest)?;
let (deposit_limit, rest) = Self::unpack_u64(rest)?;
let (borrow_limit, rest) = Self::unpack_u64(rest)?;
let (fee_receiver, _) = Self::unpack_pubkey(rest)?;
let (fee_receiver, rest) = Self::unpack_pubkey(rest)?;
let (protocol_liquidation_fee, _rest) = Self::unpack_u8(rest)?;
Self::InitReserve {
liquidity_amount,
config: ReserveConfig {
Expand All @@ -433,6 +463,7 @@ impl LendingInstruction {
deposit_limit,
borrow_limit,
fee_receiver,
protocol_liquidation_fee,
},
}
}
Expand Down Expand Up @@ -480,20 +511,20 @@ impl LendingInstruction {
Self::WithdrawObligationCollateralAndRedeemReserveCollateral { collateral_amount }
}
16 => {
let (optimal_utilization_rate, _rest) = Self::unpack_u8(rest)?;
let (loan_to_value_ratio, _rest) = Self::unpack_u8(_rest)?;
let (liquidation_bonus, _rest) = Self::unpack_u8(_rest)?;
let (liquidation_threshold, _rest) = Self::unpack_u8(_rest)?;
let (min_borrow_rate, _rest) = Self::unpack_u8(_rest)?;
let (optimal_borrow_rate, _rest) = Self::unpack_u8(_rest)?;
let (max_borrow_rate, _rest) = Self::unpack_u8(_rest)?;
let (borrow_fee_wad, _rest) = Self::unpack_u64(_rest)?;
let (flash_loan_fee_wad, _rest) = Self::unpack_u64(_rest)?;
let (host_fee_percentage, _rest) = Self::unpack_u8(_rest)?;
let (deposit_limit, _rest) = Self::unpack_u64(_rest)?;
let (borrow_limit, _rest) = Self::unpack_u64(_rest)?;
let (fee_receiver, _) = Self::unpack_pubkey(_rest)?;

let (optimal_utilization_rate, rest) = Self::unpack_u8(rest)?;
let (loan_to_value_ratio, rest) = Self::unpack_u8(rest)?;
let (liquidation_bonus, rest) = Self::unpack_u8(rest)?;
let (liquidation_threshold, rest) = Self::unpack_u8(rest)?;
let (min_borrow_rate, rest) = Self::unpack_u8(rest)?;
let (optimal_borrow_rate, rest) = Self::unpack_u8(rest)?;
let (max_borrow_rate, rest) = Self::unpack_u8(rest)?;
let (borrow_fee_wad, rest) = Self::unpack_u64(rest)?;
let (flash_loan_fee_wad, rest) = Self::unpack_u64(rest)?;
let (host_fee_percentage, rest) = Self::unpack_u8(rest)?;
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)?;
Self::UpdateReserveConfig {
config: ReserveConfig {
optimal_utilization_rate,
Expand All @@ -511,9 +542,14 @@ impl LendingInstruction {
deposit_limit,
borrow_limit,
fee_receiver,
protocol_liquidation_fee,
},
}
}
17 => {
let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
Self::LiquidateObligationAndRedeemReserveCollateral { liquidity_amount }
}
_ => {
msg!("Instruction cannot be unpacked");
return Err(LendingError::InstructionUnpackError.into());
Expand Down Expand Up @@ -609,6 +645,7 @@ impl LendingInstruction {
deposit_limit,
borrow_limit,
fee_receiver,
protocol_liquidation_fee,
},
} => {
buf.push(2);
Expand All @@ -626,6 +663,7 @@ impl LendingInstruction {
buf.extend_from_slice(&deposit_limit.to_le_bytes());
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());
}
Self::RefreshReserve => {
buf.push(3);
Expand Down Expand Up @@ -691,6 +729,11 @@ impl LendingInstruction {
buf.extend_from_slice(&config.deposit_limit.to_le_bytes());
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());
}
Self::LiquidateObligationAndRedeemReserveCollateral { liquidity_amount } => {
buf.push(17);
buf.extend_from_slice(&liquidity_amount.to_le_bytes());
}
}
buf
Expand Down Expand Up @@ -1216,3 +1259,52 @@ pub fn update_reserve_config(
data: LendingInstruction::UpdateReserveConfig { config }.pack(),
}
}

/// Creates a `LiquidateObligationAndRedeemReserveCollateral` instruction
#[allow(clippy::too_many_arguments)]
pub fn liquidate_obligation_and_redeem_reserve_collateral(
program_id: Pubkey,
liquidity_amount: u64,
source_liquidity_pubkey: Pubkey,
destination_collateral_pubkey: Pubkey,
destination_liquidity_pubkey: Pubkey,
repay_reserve_pubkey: Pubkey,
repay_reserve_liquidity_supply_pubkey: Pubkey,
withdraw_reserve_pubkey: Pubkey,
withdraw_reserve_collateral_mint_pubkey: Pubkey,
withdraw_reserve_collateral_supply_pubkey: Pubkey,
withdraw_reserve_liquidity_supply_pubkey: Pubkey,
withdraw_reserve_liquidity_fee_receiver_pubkey: Pubkey,
obligation_pubkey: Pubkey,
lending_market_pubkey: Pubkey,
user_transfer_authority_pubkey: Pubkey,
) -> Instruction {
let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
&[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
&program_id,
);
Instruction {
program_id,
accounts: vec![
AccountMeta::new(source_liquidity_pubkey, false),
AccountMeta::new(destination_collateral_pubkey, false),
AccountMeta::new(destination_liquidity_pubkey, false),
AccountMeta::new(repay_reserve_pubkey, false),
AccountMeta::new(repay_reserve_liquidity_supply_pubkey, false),
AccountMeta::new(withdraw_reserve_pubkey, false),
AccountMeta::new(withdraw_reserve_collateral_mint_pubkey, false),
AccountMeta::new(withdraw_reserve_collateral_supply_pubkey, false),
AccountMeta::new(withdraw_reserve_liquidity_supply_pubkey, false),
AccountMeta::new(withdraw_reserve_liquidity_fee_receiver_pubkey, false),
AccountMeta::new(obligation_pubkey, false),
AccountMeta::new_readonly(lending_market_pubkey, false),
AccountMeta::new_readonly(lending_market_authority_pubkey, false),
AccountMeta::new_readonly(user_transfer_authority_pubkey, true),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: LendingInstruction::LiquidateObligationAndRedeemReserveCollateral {
liquidity_amount,
}
.pack(),
}
}
Loading

0 comments on commit f960664

Please sign in to comment.