Rückzahlung
Die repayAnweisung ist die zweite Hälfte unseres Flash-Loan-Systems. Dank des Designs der loanAnweisung ist die Logik für repay recht einfach, da sie nur Folgendes tun muss:
Überprüfen, dass alle Guthaben korrekt zurückgezahlt wurden, indem das
loanKonto verwendet wird.Schließen des
loanKontos, da es nicht mehr benötigt wird.
Erforderliche Konten
borrower: Der Benutzer, der den Flash-Loan angefordert hat. Er stellt die Lamports für die Erstellung desloanKontos bereit. Muss veränderbar sein.loan: Das temporäre Konto, das verwendet wird, um dieprotocol_token_accountund die erforderliche finalebalancezu speichern. Muss veränderbar sein, da es am Ende der Anweisung geschlossen wird.
Hier ist die Implementierung:
pub struct RepayAccounts<'a> {
pub borrower: &'a AccountInfo,
pub loan: &'a AccountInfo,
pub token_accounts: &'a [AccountInfo],
}
impl<'a> TryFrom<&'a [AccountInfo]> for RepayAccounts<'a> {
type Error = ProgramError;
fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
let [borrower, loan, token_accounts @ ..] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
Ok(Self {
borrower,
loan,
token_accounts,
})
}
}Das Feld token_accounts ist ein dynamisches Array von Konten, die die Protokoll-Token-Konten darstellen, die mit dem Darlehen des Kreditnehmers verbunden sind.
Anweisungsdaten
Es werden keine Anweisungsdaten benötigt, da wir das Feld balance im loanKonto verwenden, um zu überprüfen, ob das Darlehen zurückgezahlt wurde.
Anweisungslogik
Wir beginnen damit, die Konten in die Struktur RepayAccounts zu parsen.
pub struct Repay<'a> {
pub accounts: RepayAccounts<'a>,
}
impl<'a> TryFrom<&'a [AccountInfo]> for Repay<'a> {
type Error = ProgramError;
fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
let accounts = RepayAccounts::try_from(accounts)?;
Ok(Self { accounts })
}
}
Als Nächstes überprüfen wir, ob alle Darlehen zurückgezahlt wurden. Wir tun dies, indem wir die Anzahl der Darlehen aus dem loanKonto abrufen und durch sie iterieren. Für jedes Darlehen überprüfen wir, ob die protocol_token_account korrekt ist und ob ihr Guthaben größer oder gleich dem geliehenen Betrag ist.
impl<'a> Repay<'a> {
pub const DISCRIMINATOR: &'a u8 = &1;
pub fn process(&mut self) -> ProgramResult {
let loan_data = self.accounts.loan.try_borrow_data()?;
let loan_num = loan_data.len() / size_of::<LoanData>();
if loan_num.ne(&self.accounts.token_accounts.len()) {
return Err(ProgramError::InvalidAccountData);
}
// Process each pair of token accounts (protocol, borrower) with corresponding amounts
for i in 0..loan_num {
// Validate that protocol_ata is the same as the one in the loan account
let protocol_token_account = &self.accounts.token_accounts[i];
if unsafe { *(loan_data.as_ptr().add(i * size_of::<LoanData>()) as *const [u8; 32]) } != *protocol_token_account.key() {
return Err(ProgramError::InvalidAccountData);
}
// Check if the loan is already repaid
let balance = get_token_amount(&protocol_token_account.try_borrow_data()?);
let loan_balance = unsafe { *(loan_data.as_ptr().add(i * size_of::<LoanData>() + size_of::<[u8; 32]>()) as *const u64) };
if balance < loan_balance {
return Err(ProgramError::InvalidAccountData);
}
}
//..
}
}Wir können dann fortfahren, das loanKonto zu schließen und die Miete zurückzufordern, da es nicht mehr benötigt wird:
drop(loan_data);
// Close the loan account and give back the lamports to the borrower
unsafe {
*self.accounts.borrower.borrow_mut_lamports_unchecked() += *self.accounts.loan.borrow_lamports_unchecked();
// There is no need to manually zero out lamports in the loan account because it is done in the close_unchecked function
self.accounts.loan.close_unchecked();
}