Rust
Pinocchio Escrow

Pinocchio Escrow

47 Graduates

Забрати

Інструкція 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: програма токенів. Має бути виконуваною

І ми виконуємо такі перевірки:

rust
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, оскільки ми не впевнені, що вони вже існують

rust
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 і виводити орендну плату з рахунку.

rust
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(())
  }
}
Next PageПовернути
АБО ПЕРЕЙТИ ДО ЗАВДАННЯ
Готові прийняти завдання?
Blueshift © 2025Commit: e573eab