Anchor
Anchor Flash Loan

Anchor Flash Loan

27 Graduates

Repay

The repay instruction completes our flash loan cycle by ensuring the borrowed funds are returned with appropriate fees. This instruction performs two essential steps:

  1. Extract loan amount: Use instruction introspection to retrieve the original amount_borrowed from the borrow instruction's data

  2. Transfer funds back: Calculate the fee and transfer the borrowed amount plus fee back to the protocol

Instruction Introspection

First, we need to examine the first instruction in the transaction to extract the original loan amount:

rust
let ixs = ctx.accounts.instructions.to_account_info();

let mut amount_borrowed: u64;

if let Ok(borrow_ix) = load_instruction_at_checked(0, &ixs) {
    // Check the amount borrowed:
    let mut borrowed_data: [u8;8] = [0u8;8];
    borrowed_data.copy_from_slice(&borrow_ix.data[8..16]);
    amount_borrowed = u64::from_le_bytes(borrowed_data)

} else {
    return Err(ProtocolError::MissingBorrowIx.into());
}

We're not checking that this is the actual borrow_ix using the program ID and discriminator because it doesn't matter if they actually construct a "fake" instruction; it's safe for the protocol since it's just getting paid. At the same time, if we loaned the money we know that it's going to be the first instruction and that the amount_borrowed will be there.

Transfer Funds

Next, we calculate the protocol fee and transfer the total amount back:

rust
// Add the fee to the amount borrowed (In our case we hardcoded it to 500 basis point)
let fee = (amount_borrowed as u128).checked_mul(500).unwrap().checked_div(10_000).ok_or(ProtocolError::Overflow)? as u64;
amount_borrowed = amount_borrowed.checked_add(fee).ok_or(ProtocolError::Overflow)?;

// Transfer the funds from the protocol to the borrower
transfer(
    CpiContext::new(ctx.accounts.token_program.to_account_info(), Transfer {
        from: ctx.accounts.borrower_ata.to_account_info(),
        to: ctx.accounts.protocol_ata.to_account_info(),
        authority: ctx.accounts.borrower.to_account_info(),
    }), 
    amount_borrowed
)?;

Our fee is hard coded to 500 basis point, and we perform "checked" math to make sure that the amount doesn't overflow which could be exploited with very large numbers. Additionally, we convert the amount to u128 for the multiplication to prevent intermediate overflow, then we safely cast back to u64

Ready to take the challenge?
Contents
View Source
Blueshift © 2025Commit: e573eab