From c374bb429e5b87920fcd6b9e23d9a02064d5a9d2 Mon Sep 17 00:00:00 2001 From: 0xripleys <0xripleys@solend.fi> Date: Tue, 2 Jan 2024 22:18:26 -0500 Subject: [PATCH] max repay function --- ...eceiving_ctokens.rs => wrapper_program.rs} | 54 +++++++++++++ token-lending/wrapper/src/processor.rs | 81 ++++++++++++++++++- 2 files changed, 133 insertions(+), 2 deletions(-) rename token-lending/program/tests/{liquidate_obligation_without_receiving_ctokens.rs => wrapper_program.rs} (84%) diff --git a/token-lending/program/tests/liquidate_obligation_without_receiving_ctokens.rs b/token-lending/program/tests/wrapper_program.rs similarity index 84% rename from token-lending/program/tests/liquidate_obligation_without_receiving_ctokens.rs rename to token-lending/program/tests/wrapper_program.rs index 336c147ba13..7463c024e78 100644 --- a/token-lending/program/tests/liquidate_obligation_without_receiving_ctokens.rs +++ b/token-lending/program/tests/wrapper_program.rs @@ -4,6 +4,7 @@ use crate::solend_program_test::TokenBalanceChange; use solana_sdk::instruction::InstructionError; use solana_sdk::transaction::TransactionError; use std::collections::HashSet; +use wrapper::processor::max_repay; use crate::solend_program_test::custom_scenario; use crate::solend_program_test::find_reserve; @@ -286,3 +287,56 @@ async fn test_liquidate_fail() { let (balance_changes, _) = balance_checker.find_balance_changes(&mut test).await; assert!(balance_changes.is_empty()); } + +#[tokio::test] +async fn test_repay() { + let (mut test, lending_market, reserves, obligations, users, _lending_market_owner) = + custom_scenario( + &[ + ReserveArgs { + mint: usdc_mint::id(), + config: reserve_config_no_fees(), + liquidity_amount: 10 * FRACTIONAL_TO_USDC, + price: PriceArgs { + price: 10, + conf: 0, + expo: -1, + ema_price: 10, + ema_conf: 0, + }, + }, + ReserveArgs { + mint: wsol_mint::id(), + config: reserve_config_no_fees(), + liquidity_amount: 100 * LAMPORTS_PER_SOL, + price: PriceArgs { + price: 10, + conf: 0, + expo: 0, + ema_price: 10, + ema_conf: 0, + }, + }, + ], + &[ObligationArgs { + deposits: vec![(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC)], + borrows: vec![(wsol_mint::id(), LAMPORTS_PER_SOL)], + }], + ) + .await; + + let instruction = max_repay( + wrapper::id(), + solend_program::id(), + users[0].get_account(&wsol_mint::id()).unwrap(), + reserves[1].account.liquidity.supply_pubkey, + reserves[1].pubkey, + obligations[0].pubkey, + lending_market.pubkey, + users[0].keypair.pubkey(), + ); + + test.process_transaction(&[instruction], Some(&[&users[0].keypair])) + .await + .unwrap(); +} diff --git a/token-lending/wrapper/src/processor.rs b/token-lending/wrapper/src/processor.rs index 02b9e4382ef..b155ce1846b 100644 --- a/token-lending/wrapper/src/processor.rs +++ b/token-lending/wrapper/src/processor.rs @@ -14,7 +14,9 @@ use solana_program::{ program_pack::Pack, pubkey::Pubkey, }; -use solend_sdk::instruction::liquidate_obligation_and_redeem_reserve_collateral; +use solend_sdk::instruction::{ + liquidate_obligation_and_redeem_reserve_collateral, repay_obligation_liquidity, +}; use thiserror::Error; /// Instruction types @@ -27,6 +29,8 @@ pub enum WrapperInstruction { /// amount to liquidate liquidity_amount: u64, }, + /// Repay obligation liquidity with max amount in token account + RepayMax, } /// Processes an instruction @@ -111,9 +115,55 @@ pub fn process_instruction( msg!("We received ctokens, aborting"); return Err(WrapperError::ReceivedCTokens.into()); } + + Ok(()) + } + WrapperInstruction::RepayMax => { + msg!("Instruction: RepayMax"); + let account_info_iter = &mut accounts.iter(); + let solend_program_id = next_account_info(account_info_iter)?; + let source_liquidity_info = next_account_info(account_info_iter)?; + let destination_liquidity_info = next_account_info(account_info_iter)?; + let repay_reserve_info = next_account_info(account_info_iter)?; + let obligation_info = next_account_info(account_info_iter)?; + let lending_market_info = next_account_info(account_info_iter)?; + let user_transfer_authority_info = next_account_info(account_info_iter)?; + let token_program_id = next_account_info(account_info_iter)?; + + let source_liquidity_balance = spl_token::state::Account::unpack_from_slice( + &source_liquidity_info.try_borrow_data()?, + )? + .amount; + msg!("source_liquidity_balance: {}", source_liquidity_balance); + + let instruction = repay_obligation_liquidity( + *solend_program_id.key, + source_liquidity_balance, + *source_liquidity_info.key, + *destination_liquidity_info.key, + *repay_reserve_info.key, + *obligation_info.key, + *lending_market_info.key, + *user_transfer_authority_info.key, + ); + + invoke( + &instruction, + &[ + solend_program_id.clone(), + source_liquidity_info.clone(), + destination_liquidity_info.clone(), + repay_reserve_info.clone(), + obligation_info.clone(), + lending_market_info.clone(), + user_transfer_authority_info.clone(), + token_program_id.clone(), + ], + )?; + + Ok(()) } } - Ok(()) } /// Errors that may be returned by the TokenLending program. @@ -179,3 +229,30 @@ pub fn liquidate_without_receiving_ctokens( .unwrap(), } } + +/// max repay instruction +pub fn max_repay( + program_id: Pubkey, + solend_program_id: Pubkey, + source_liquidity_pubkey: Pubkey, + destination_liquidity_pubkey: Pubkey, + repay_reserve_pubkey: Pubkey, + obligation_pubkey: Pubkey, + lending_market_pubkey: Pubkey, + user_transfer_authority_pubkey: Pubkey, +) -> Instruction { + Instruction { + program_id, + accounts: vec![ + AccountMeta::new_readonly(solend_program_id, false), + AccountMeta::new(source_liquidity_pubkey, false), + AccountMeta::new(destination_liquidity_pubkey, false), + AccountMeta::new(repay_reserve_pubkey, false), + AccountMeta::new(obligation_pubkey, false), + AccountMeta::new(lending_market_pubkey, false), + AccountMeta::new_readonly(user_transfer_authority_pubkey, true), + AccountMeta::new_readonly(spl_token::id(), false), + ], + data: WrapperInstruction::RepayMax.try_to_vec().unwrap(), + } +}