Забрати
Інструкція take завершує обмін:
Закриває запис ескроу, повертаючи його лампорти оренди творцю.
Переміщує Токен A зі сховища до отримувача, потім закриває сховище.
Переміщує узгоджену кількість Токену B від отримувача до творця.
Необхідні облікові записи
Нижче наведені облікові записи, які потрібні для контексту:
taker: особа, яка хоче прийняти угоду. Має бути підписантом і змінюваною.
maker: творець ескроу. Має бути змінюваним.
escrow: обліковий запис ескроу, який ми ініціалізуємо. Має бути змінюваним.
mint_a: токен, який ми депонуємо в ескроу
mint_b: токен, який ми хочемо отримати
vault: пов'язаний обліковий запис токена, що належить ескроу. Має бути змінюваним
taker_ata_a: пов'язаний обліковий запис токена, що належить отримувачу для mint_a. Має бути змінюваним
taker_ata_b: пов'язаний обліковий запис токена, що належить отримувачу для mint_b. Має бути змінюваним
maker_ata_b: пов'язаний обліковий запис токена, що належить творцю для mint_b. Має бути змінюваним
system_program: системна програма. Має бути виконуваною
token_program: програма токенів. Має бути виконуваною
І ми виконуємо такі перевірки:
pub struct TakeAccounts<'a> {
pub taker: &'a AccountInfo,
pub maker: &'a AccountInfo,
pub escrow: &'a AccountInfo,
pub mint_a: &'a AccountInfo,
pub mint_b: &'a AccountInfo,
pub vault: &'a AccountInfo,
pub taker_ata_a: &'a AccountInfo,
pub taker_ata_b: &'a AccountInfo,
pub maker_ata_b: &'a AccountInfo,
pub system_program: &'a AccountInfo,
pub token_program: &'a AccountInfo,
}
impl<'a> TryFrom<&'a [AccountInfo]> for TakeAccounts<'a> {
type Error = ProgramError;
fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
let [taker, maker, escrow, mint_a, mint_b, vault, taker_ata_a, taker_ata_b, maker_ata_b, system_program, token_program, _] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
// Basic Accounts Checks
SignerAccount::check(taker)?;
ProgramAccount::check(escrow)?;
MintInterface::check(mint_a)?;
MintInterface::check(mint_b)?;
AssociatedTokenAccount::check(taker_ata_b, taker, mint_b, token_program)?;
AssociatedTokenAccount::check(vault, escrow, mint_a, token_program)?;
// Return the accounts
Ok(Self {
taker,
maker,
escrow,
mint_a,
mint_b,
taker_ata_a,
taker_ata_b,
maker_ata_b,
vault,
system_program,
token_program,
})
}
}Дані інструкції
Усі дані, які нам потрібні для виконання логіки, вже містяться в обліковому записі Escrow або в облікових записах, які ми десеріалізуємо. З цієї причини нам не потрібні жодні instruction_data для цієї інструкції.
Логіка інструкції
Ми починаємо з ініціалізації необхідних облікових записів у реалізації TryFrom, після того як ми десеріалізували облікові записи.
Для цього кроку ми переконуємося, що обидва рахунки — рахунок Токену А покупця та рахунок Токену B продавця — ініціалізовані за допомогою AssociatedTokenAccount::init_if_needed, оскільки ми не впевнені, що вони вже існують
pub struct Take<'a> {
pub accounts: TakeAccounts<'a>,
}
impl<'a> TryFrom<&'a [AccountInfo]> for Take<'a> {
type Error = ProgramError;
fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
let accounts = TakeAccounts::try_from(accounts)?;
// Initialize necessary accounts
AssociatedTokenAccount::init_if_needed(
accounts.taker_ata_a,
accounts.mint_a,
accounts.taker,
accounts.taker,
accounts.system_program,
accounts.token_program,
)?;
AssociatedTokenAccount::init_if_needed(
accounts.maker_ata_b,
accounts.mint_b,
accounts.taker,
accounts.maker,
accounts.system_program,
accounts.token_program,
)?;
Ok(Self {
accounts,
})
}
}Тепер ми можемо зосередитися на самій логіці, яка буде:
Переказувати токени з
taker_ata_bдоmaker_ata_b.Переказувати токени з
vaultдоtaker_ata_a.Закривати тепер порожній
vaultі виводити орендну плату з рахунку.
impl<'a> Take<'a> {
pub const DISCRIMINATOR: &'a u8 = &1;
pub fn process(&mut self) -> ProgramResult {
let data = self.accounts.escrow.try_borrow_data()?;
let escrow = Escrow::load(&data)?;
// Check if the escrow is valid
let escrow_key = create_program_address(&[b"escrow", self.accounts.maker.key(), &escrow.seed.to_le_bytes(), &escrow.bump], &crate::ID)?;
if &escrow_key != self.accounts.escrow.key() {
return Err(ProgramError::InvalidAccountOwner);
}
let seed_binding = escrow.seed.to_le_bytes();
let bump_binding = escrow.bump;
let escrow_seeds = [
Seed::from(b"escrow"),
Seed::from(self.accounts.maker.key().as_ref()),
Seed::from(&seed_binding),
Seed::from(&bump_binding),
];
let signer = Signer::from(&escrow_seeds);
let amount = TokenAccount::get_amount(self.accounts.vault);
// Transfer from the Vault to the Taker
Transfer {
from: self.accounts.vault,
to: self.accounts.taker_ata_a,
authority: self.accounts.escrow,
amount,
}.invoke_signed(&[signer.clone()])?;
// Close the Vault
CloseAccount {
account: self.accounts.vault,
destination: self.accounts.maker,
authority: self.accounts.escrow,
}.invoke_signed(&[signer.clone()])?;
// Transfer from the Taker to the Maker
Transfer {
from: self.accounts.taker_ata_b,
to: self.accounts.maker_ata_b,
authority: self.accounts.taker,
amount: escrow.receive,
}.invoke()?;
// Close the Escrow
drop(data);
ProgramAccount::close(self.accounts.escrow, self.accounts.taker)?;
Ok(())
}
}