Take
Instruksi take menyelesaikan pertukaran:
Menutup catatan escrow, mengirimkan lamport sewanya kembali ke pembuat.
Memindahkan Token A dari vault ke pengambil, kemudian menutup vault.
Memindahkan jumlah Token B yang disepakati dari pengambil ke pembuat.
Required Accounts
Berikut adalah akun yang dibutuhkan oleh konteks:
taker: orang yang ingin mengambil penawaran. Harus menjadi penandatangan dan dapat diubah.
maker: pembuat escrow. Harus dapat diubah.
escrow: akun escrow yang kita inisialisasi. Harus dapat diubah.
mint_a: token yang kita setorkan ke dalam escrow
mint_b: token yang ingin kita terima
vault: akun token terkait yang dimiliki oleh escrow. Harus dapat diubah
taker_ata_a: akun token terkait yang dimiliki oleh pengambil untuk mint_a. Harus dapat diubah
taker_ata_b: akun token terkait yang dimiliki oleh pengambil untuk mint_b. Harus dapat diubah
maker_ata_b: akun token terkait yang dimiliki oleh pembuat untuk mint_b. Harus dapat diubah
system_program: program sistem. Harus dapat dieksekusi
token_program: program token. Harus dapat dieksekusi
Dan kita melakukan pemeriksaan berikut padanya:
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,
})
}
}Instruction Data
Semua data yang kita butuhkan untuk melakukan logika sudah ada di akun Escrow atau pada akun yang kita deserialisasi. Karena alasan ini kita tidak memerlukan instruction_data untuk instruksi ini.
Instruction Logic
Kita mulai dengan menginisialisasi akun yang diperlukan dalam implementasi TryFrom, setelah kita mendeserialisasi akun.
Untuk langkah ini, kita memastikan bahwa akun Token A milik taker dan akun Token B milik maker sudah diinisialisasi menggunakan AssociatedTokenAccount::init_if_needed karena kita tidak yakin apakah akun-akun tersebut sudah ada
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,
})
}
}Sekarang kita bisa fokus pada logika itu sendiri yang akan:
Mentransfer token dari
taker_ata_bkemaker_ata_b.Mentransfer token dari
vaultketaker_ata_a.Menutup
vaultyang sekarang kosong dan menarik biaya sewa dari akun tersebut.
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(())
}
}