Skip to content

Commit 67ab029

Browse files
Validate Flashloan condition (#380)
* Validate Flashloan condition Co-Authored-By: Henry-E <12613144+Henry-E@users.noreply.github.com> * Add test Co-Authored-By: Henry-E <12613144+Henry-E@users.noreply.github.com> * Delete poc.patch Co-Authored-By: Henry-E <12613144+Henry-E@users.noreply.github.com> * Update flash_loan.rs Co-Authored-By: Henry-E <12613144+Henry-E@users.noreply.github.com> * Update flash_loan.rs Co-Authored-By: Henry-E <12613144+Henry-E@users.noreply.github.com> * restore original test * clean up a little --------- Co-authored-by: Henry-E <12613144+Henry-E@users.noreply.github.com>
1 parent 79f9d2e commit 67ab029

4 files changed

Lines changed: 92 additions & 9 deletions

File tree

‎programs/marginfi/src/instructions/marginfi_account/flashloan.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ pub fn lending_account_end_flashloan<'info>(
132132

133133
let mut marginfi_account = ctx.accounts.marginfi_account.load_mut()?;
134134

135+
check!(
136+
!marginfi_account.get_flag(ACCOUNT_DISABLED),
137+
MarginfiError::AccountDisabled
138+
);
139+
135140
marginfi_account.unset_flag(ACCOUNT_IN_FLASHLOAN);
136141

137142
let (risk_result, _engine) =

‎programs/marginfi/src/instructions/marginfi_account/transfer_account.rs‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use crate::{
2-
check_eq,
2+
check, check_eq,
33
constants::ACCOUNT_TRANSFER_FEE,
44
events::{AccountEventHeader, MarginfiAccountTransferToNewAccount},
55
prelude::*,
6-
state::marginfi_account::{LendingAccount, MarginfiAccount, ACCOUNT_DISABLED},
6+
state::marginfi_account::{
7+
LendingAccount, MarginfiAccount, ACCOUNT_DISABLED, ACCOUNT_IN_FLASHLOAN,
8+
},
79
};
810
use anchor_lang::prelude::*;
911
use bytemuck::Zeroable;
@@ -20,6 +22,11 @@ pub fn transfer_to_new_account(ctx: Context<TransferToNewAccount>) -> MarginfiRe
2022

2123
let mut old_account = ctx.accounts.old_marginfi_account.load_mut()?;
2224

25+
check!(
26+
!old_account.get_flag(ACCOUNT_IN_FLASHLOAN),
27+
MarginfiError::AccountInFlashloan
28+
);
29+
2330
// Prevent multiple migrations from the same account
2431
check_eq!(
2532
old_account.migrated_to,

‎programs/marginfi/tests/user_actions/flash_loan.rs‎

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use fixtures::{assert_custom_error, prelude::*};
44
use marginfi::prelude::*;
55
use pretty_assertions::assert_eq;
66
use solana_program_test::*;
7+
use solana_sdk::signature::Keypair;
8+
use solana_sdk::system_program;
79
use solana_sdk::{
810
compute_budget::ComputeBudgetInstruction, signer::Signer, transaction::Transaction,
911
};
@@ -18,6 +20,7 @@ use solana_sdk::{
1820
// 7. Flashloan fails because of invalid `end_flashloan` ix order
1921
// 8. Flashloan fails because `end_flashloan` ix is for another account
2022
// 9. Flashloan fails because account is already in a flashloan
23+
// 10. Flashloan fails because account transfer during flashloan
2124

2225
#[tokio::test]
2326
async fn flashloan_success_1op() -> anyhow::Result<()> {
@@ -41,7 +44,6 @@ async fn flashloan_success_1op() -> anyhow::Result<()> {
4144

4245
let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await;
4346
// Borrow SOL
44-
4547
let borrow_ix = borrower_mfi_account_f
4648
.make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000)
4749
.await;
@@ -56,7 +58,7 @@ async fn flashloan_success_1op() -> anyhow::Result<()> {
5658
.await;
5759

5860
let flash_loan_result = borrower_mfi_account_f
59-
.try_flashloan(vec![borrow_ix, repay_ix], vec![], vec![])
61+
.try_flashloan(vec![borrow_ix, repay_ix], vec![], vec![], None)
6062
.await;
6163

6264
assert!(flash_loan_result.is_ok());
@@ -108,7 +110,7 @@ async fn flashloan_success_3op() -> anyhow::Result<()> {
108110
ixs.push(ComputeBudgetInstruction::set_compute_unit_limit(1_400_000));
109111

110112
let flash_loan_result = borrower_mfi_account_f
111-
.try_flashloan(ixs, vec![], vec![])
113+
.try_flashloan(ixs, vec![], vec![], None)
112114
.await;
113115

114116
assert!(flash_loan_result.is_ok());
@@ -137,13 +139,12 @@ async fn flashloan_fail_account_health() -> anyhow::Result<()> {
137139

138140
let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await;
139141
// Borrow SOL
140-
141142
let borrow_ix = borrower_mfi_account_f
142143
.make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000)
143144
.await;
144145

145146
let flash_loan_result = borrower_mfi_account_f
146-
.try_flashloan(vec![borrow_ix], vec![], vec![sol_bank.key])
147+
.try_flashloan(vec![borrow_ix], vec![], vec![sol_bank.key], None)
147148
.await;
148149

149150
assert_custom_error!(
@@ -192,7 +193,7 @@ async fn flashloan_ok_missing_flag() -> anyhow::Result<()> {
192193
.await;
193194

194195
let flash_loan_result = borrower_mfi_account_f
195-
.try_flashloan(vec![borrow_ix, repay_ix], vec![], vec![])
196+
.try_flashloan(vec![borrow_ix, repay_ix], vec![], vec![], None)
196197
.await;
197198

198199
assert!(flash_loan_result.is_ok());
@@ -503,3 +504,66 @@ async fn flashloan_fail_already_in_flashloan() -> anyhow::Result<()> {
503504

504505
Ok(())
505506
}
507+
508+
#[tokio::test]
509+
async fn flashloan_fail_account_transfer_during_flashloan() -> anyhow::Result<()> {
510+
let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await;
511+
512+
let sol_bank = test_f.get_bank(&BankMint::Sol);
513+
514+
// Fund SOL lender
515+
let lender_mfi_account_f = test_f.create_marginfi_account().await;
516+
let lender_token_account_f_sol = test_f
517+
.sol_mint
518+
.create_token_account_and_mint_to(1_000)
519+
.await;
520+
lender_mfi_account_f
521+
.try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000, None)
522+
.await?;
523+
524+
// Fund SOL borrower
525+
let borrower_mfi_account_f = test_f.create_marginfi_account().await;
526+
527+
let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await;
528+
529+
// Borrow SOL
530+
let borrow_ix = borrower_mfi_account_f
531+
.make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000)
532+
.await;
533+
534+
let new_authority = Keypair::new();
535+
let new_account = Keypair::new();
536+
537+
let account = borrower_mfi_account_f.load().await;
538+
539+
let transfer_account_ix = Instruction {
540+
program_id: marginfi::id(),
541+
accounts: marginfi::accounts::TransferToNewAccount {
542+
old_marginfi_account: borrower_mfi_account_f.key,
543+
new_marginfi_account: new_account.pubkey(),
544+
group: account.group,
545+
authority: test_f.payer(),
546+
new_authority: new_authority.pubkey(),
547+
global_fee_wallet: test_f.marginfi_group.fee_wallet,
548+
system_program: system_program::ID,
549+
}
550+
.to_account_metas(None),
551+
data: marginfi::instruction::TransferToNewAccount {}.data(),
552+
};
553+
554+
let flash_loan_result = borrower_mfi_account_f
555+
.try_flashloan(
556+
vec![borrow_ix, transfer_account_ix],
557+
vec![],
558+
vec![sol_bank.key],
559+
Some(&new_account),
560+
)
561+
.await;
562+
563+
assert_custom_error!(
564+
flash_loan_result.unwrap_err(),
565+
MarginfiError::AccountInFlashloan
566+
);
567+
568+
Ok(())
569+
}

‎test-utils/src/marginfi_account.rs‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ impl MarginfiAccountFixture {
619619
ixs: Vec<Instruction>,
620620
exclude_banks: Vec<Pubkey>,
621621
include_banks: Vec<Pubkey>,
622+
signer: Option<&Keypair>,
622623
) -> std::result::Result<(), BanksClientError> {
623624
let mut ixs = ixs;
624625
let start_ix = self
@@ -633,10 +634,16 @@ impl MarginfiAccountFixture {
633634

634635
let ctx = self.ctx.borrow_mut();
635636

637+
let signers = if let Some(signer) = signer {
638+
vec![&ctx.payer, signer]
639+
} else {
640+
vec![&ctx.payer]
641+
};
642+
636643
let tx = Transaction::new_signed_with_payer(
637644
&ixs,
638645
Some(&ctx.payer.pubkey().clone()),
639-
&[&ctx.payer],
646+
&signers,
640647
ctx.last_blockhash,
641648
);
642649

0 commit comments

Comments
 (0)