Make
L'instruction make a trois fonctions :
Initialise le compte Escrow et stocke toutes les conditions de l'échange.
Crée le vault (un ATA pour le
mint_aappartenant à l'escrow).Déplace le jeton A du créateur dans ce vault graàce à un CPI vers le programme SPL-Token.
Comptes Nécessaires
Voici les comptes dont le contexte a besoin :
maker: le créateur de l'escrow. Doit être un signataire et mutable
escrow: le compte d'escrow que nous initialisons. Doit être mutable
mint_a: le jeton que nous déposons dans l'escrow
mint_b: le jeton que nous souhaitons recevoir
maker_ata_a: le compte de jetons associé appartenant au créateur. Doit être mutable
vault: le compte de jetons associé appartenant à l'escrow. Doit être mutable
system_program: le programme système. Doit être executable
token_program: le programme de jeton. Doit être executable
Remarque : Nous allons utiliser les assistants que nous avons présentés dans l'Introduction à Pinocchio.
Cela se présente donc ainsi :
pub struct MakeAccounts<'a> {
pub maker: &'a AccountInfo,
pub escrow: &'a AccountInfo,
pub mint_a: &'a AccountInfo,
pub mint_b: &'a AccountInfo,
pub maker_ata_a: &'a AccountInfo,
pub vault: &'a AccountInfo,
pub system_program: &'a AccountInfo,
pub token_program: &'a AccountInfo,
}
impl<'a> TryFrom<&'a [AccountInfo]> for MakeAccounts<'a> {
type Error = ProgramError;
fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
let [maker, escrow, mint_a, mint_b, maker_ata_a, vault, system_program, token_program, _] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
// Basic Accounts Checks
SignerAccount::check(maker)?;
MintInterface::check(mint_a)?;
MintInterface::check(mint_b)?;
AssociatedTokenAccount::check(maker_ata_a, maker, mint_a, token_program)?;
// Return the accounts
Ok(Self {
maker,
escrow,
mint_a,
mint_b,
maker_ata_a,
vault,
system_program,
token_program,
})
}
}Données d'Instruction
Voici les données d'instruction que nous devons transmettre :
seed: le nombre aléatoire utilisé lors de la dérivation des seeds. Doit être un u64
receive: le montant que le créateur souhaite recevoir. Doit être un u64
amount: le montant que le créateur souhaite déposer. Doit être un u64
Nous vérifierons que l'amount n'est pas nul puisque cela n'aurait pas de sens pour un escrow.
Voilà ce que cela donne :
pub struct MakeInstructionData {
pub seed: u64,
pub receive: u64,
pub amount: u64,
}
impl<'a> TryFrom<&'a [u8]> for MakeInstructionData {
type Error = ProgramError;
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
if data.len() != size_of::<u64>() * 3 {
return Err(ProgramError::InvalidInstructionData);
}
let seed = u64::from_le_bytes(data[0..8].try_into().unwrap());
let receive = u64::from_le_bytes(data[8..16].try_into().unwrap());
let amount = u64::from_le_bytes(data[16..24].try_into().unwrap());
// Instruction Checks
if amount == 0 {
return Err(ProgramError::InvalidInstructionData);
}
Ok(Self {
seed,
receive,
amount,
})
}
}Logique d'Instruction
Tout d'abord, après avoir désérialisé les instruction_data et les accounts dans l'implémentation du TryFrom, nous initialisons les comptes nécessaires.
Pour cette étape, nous créons le compte Escrow en utilisant le trait ProgramAccount::init::<Escrow> des fonctions d'aide introduites dans l'Introduction à Pinocchio. De même, nous initialisons le compte du Vault :
pub struct Make<'a> {
pub accounts: MakeAccounts<'a>,
pub instruction_data: MakeInstructionData,
pub bump: u8,
}
impl<'a> TryFrom<(&'a [u8], &'a [AccountInfo])> for Make<'a> {
type Error = ProgramError;
fn try_from((data, accounts): (&'a [u8], &'a [AccountInfo])) -> Result<Self, Self::Error> {
let accounts = MakeAccounts::try_from(accounts)?;
let instruction_data = MakeInstructionData::try_from(data)?;
// Initialize the Accounts needed
let (_, bump) = find_program_address(&[b"escrow", accounts.maker.key(), &instruction_data.seed.to_le_bytes()], &crate::ID);
let seed_binding = instruction_data.seed.to_le_bytes();
let bump_binding = [bump];
let escrow_seeds = [
Seed::from(b"escrow"),
Seed::from(accounts.maker.key().as_ref()),
Seed::from(&seed_binding),
Seed::from(&bump_binding),
];
ProgramAccount::init::<Escrow>(
accounts.maker,
accounts.escrow,
&escrow_seeds,
Escrow::LEN
)?;
// Initialize the vault
AssociatedTokenAccount::init(
accounts.vault,
accounts.mint_a,
accounts.maker,
accounts.escrow,
accounts.system_program,
accounts.token_program,
)?;
Ok(Self {
accounts,
instruction_data,
bump,
})
}
}Nous pouvons maintenant nous concentrer sur la logique elle-même. Celle-ci consiste à remplir le compte d'escrow à transférer les jetons vers le vault.
impl<'a> Make<'a> {
pub const DISCRIMINATOR: &'a u8 = &0;
pub fn process(&mut self) -> ProgramResult {
// Populate the escrow account
let mut data = self.accounts.escrow.try_borrow_mut_data()?;
let escrow = Escrow::load_mut(data.as_mut())?;
escrow.set_inner(
self.instruction_data.seed,
*self.accounts.maker.key(),
*self.accounts.mint_a.key(),
*self.accounts.mint_b.key(),
self.instruction_data.receive,
[self.bump],
);
// Transfer tokens to vault
Transfer {
from: self.accounts.maker_ata_a,
to: self.accounts.vault,
authority: self.accounts.maker,
amount: self.instruction_data.amount
}.invoke()?;
Ok(())
}
}