Repay
L'instruction repay est la seconde partie de notre système de prêt flash. Grâce à la conception de l'instruction loan, la logique de repay est assez simple puisqu'elle ne nécessite que :
Vérifier que tous les soldes ont été correctement remboursés à l'aide du compte
loan.Fermer le compte
loancar il n'est plus nécessaire.
Comptes Nécessaires
borrower: L'utilisateur qui a demandé le prêt flash. Il fournit les lamports nécessaires à la création du compteloan. Doit être mutable.loan: Le compte temporaire utilisé pour stocker leprotocol_token_accountet labalancefinale. Doit être mutable puisqu'il sera fermé à la fin de l'instruction.
Voici l'implémentation:
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,
})
}
}Le champ token_accounts est un tableau dynamique de comptes représentant les comptes de jetons du protocole associés au prêt de l'emprunteur.
Données d'Instruction
Aucune donnée d'instruction n'est nécessaire puisque nous utilisons le champ balance du compte loan pour vérifier si le prêt a été remboursé.
Logique d'Instruction
Nous commençons par parser les comptes dans la structure RepayAccounts.
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 })
}
}
Ensuite, nous vérifions si tous les prêts ont été remboursés. Pour ce faire, nous récupérons le nombre de prêts à partir du compte loan et les parcourons un par un. Pour chaque prêt, nous vérifions que protocol_token_account est correct et que son solde est supérieur ou égal au montant prêté.
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);
}
}
//..
}
}Nous pouvons alors procéder à la clôture du compte loan et récupérer la rente puisqu'il n'est plus nécessaire :
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();
}