Rust
Pinocchio AMM

Pinocchio AMM

13 Graduates

Einzahlung

Die deposit Anweisung führt drei Hauptaufgaben aus:

  • Einzahlung der mint_x und mint_y Token basierend auf der LP-Menge, die der Benutzer mint möchte.

  • Berechnung des einzuzahlenden Betrags und Überprüfung, dass der Betrag nicht größer ist als max_x und max_y, die vom Benutzer festgelegt wurden.

  • Prägung der richtigen Menge an mint_lp im Benutzer-ATA.

Wie im Abschnitt zur initialize Anweisung erwähnt, werden wir alle Associated Token Accounts außerhalb unserer Anweisung aus Optimierungsgründen initialisieren.

Erforderliche Konten

Nachfolgend sind die für diesen Kontext erforderlichen Konten aufgeführt:

  • user: Der Benutzer, der den Token in die Liquidität des Amm einzahlt. Muss ein signer sein.

  • mint_lp: Das Mint-Konto, das die Liquidität des Pools repräsentiert. Muss als mutable übergeben werden.

  • vault_x: Das Token-Konto, das alle in den Pool eingezahlten Token X enthält. Muss als mutable übergeben werden.

  • vault_y: Das Token-Konto, das alle in den Pool eingezahlten Token Y enthält. Muss als mutable übergeben werden.

  • user_x_ata: Das zugehörige Token-Konto des Benutzers für Token X. Dies ist das Quellkonto, von dem die Token X des Benutzers in den Pool übertragen werden. Muss als mutable übergeben werden.

  • user_y_ata: Das zugehörige Token-Konto des Benutzers für Token Y. Dies ist das Quellkonto, von dem die Token Y des Benutzers in den Pool übertragen werden. Muss als mutable übergeben werden.

  • user_lp_ata: Das zugehörige Token-Konto des Benutzers für LP-Token. Dies ist das Zielkonto, auf das LP-Token geprägt werden. Muss als mutable übergeben werden.

  • config: Das Konfigurationskonto für den AMM-Pool. Speichert alle relevanten Pool-Parameter und -zustände.

  • token program: Das SPL-Token-Programmkonto. Dies ist erforderlich, um Token-Operationen wie Überweisungen und Prägungen durchzuführen. Muss executable sein.

Hier überlasse ich dir wieder die Implementierung:

rust
pub struct DepositAccounts<'a> {
    pub user: &'a AccountInfo,
    pub mint_lp: &'a AccountInfo,
    pub vault_x: &'a AccountInfo,
    pub vault_y: &'a AccountInfo,
    pub user_x_ata: &'a AccountInfo,
    pub user_y_ata: &'a AccountInfo,
    pub user_lp_ata: &'a AccountInfo,
    pub config: &'a AccountInfo,
    pub token_program: &'a AccountInfo,
}

impl<'a> TryFrom<&'a [AccountInfo]> for DepositAccounts<'a> {
  type Error = ProgramError;

  fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
    //..
  }
}

Instruction Data

Hier sind die Instruktionsdaten, die wir übergeben müssen:

  • amount: Die Menge an LP-Token, die der Benutzer erhalten möchte. Muss ein [u64] sein

  • max_x: Die maximale Menge an Token X, die der Benutzer bereit ist einzuzahlen. Muss ein [u64] sein

  • max_y: Die maximale Menge an Token Y, die der Benutzer bereit ist einzuzahlen. Muss ein [u64] sein

  • expiration: Das Ablaufdatum dieser Order. Wichtig, um sicherzustellen, dass die Transaktion innerhalb einer bestimmten Zeit ausgeführt werden muss. Muss ein [i64] sein

Wir werden die Implementierung für das DepositInstructionData genauso wie bei der Initialisierung handhaben. Daher überlasse ich dir die Implementierung:

rust
pub struct DepositInstructionData {
    pub amount: u64,
    pub max_x: u64,
    pub max_y: u64,
    pub expiration: i64,
}

impl<'a> TryFrom<&'a [u8]> for DepositInstructionData {
    type Error = ProgramError;

    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
        //..
    }
}

Stelle sicher, dass alle Beträge wie amount, max_y und max_x größer als null sind und dass die Order noch nicht abgelaufen ist, indem du das Clock sysvar verwendest.

Instruction Logic

Wir beginnen mit der Deserialisierung sowohl des instruction_data als auch des accounts.

Dann müssen wir:

  • Das Config-Konto laden, um alle darin enthaltenen Daten zu erfassen. Wir können dafür den Config::load()-Helfer verwenden.

  • Überprüfen, ob der AmmState gültig ist (also ob er gleich AmmState::Initialized ist)

  • Die Ableitung von vault_x und vault_y überprüfen, um sicherzustellen, dass es sich um Associated Token Accounts handelt, wie hier:

rust
// Check if the vault_x is valid
let (vault_x, _) = find_program_address(
    &[
        self.accounts.config.key(),
        self.accounts.token_program.key(),
        config.mint_x(),
    ],
    &pinocchio_associated_token_account::ID,
);

if vault_x.ne(self.accounts.vault_x.key()) {
    return Err(ProgramError::InvalidAccountData);
}
  • Alle beteiligten Token-Konten deserialisieren und die darin enthaltenen Daten verwenden, um die einzuzahlenden Beträge mit dem constant-product-curve-Crate zu berechnen und den Slippage wie folgt zu überprüfen:

rust
// Deserialize the token accounts
let mint_lp = unsafe { Mint::from_account_info_unchecked(self.accounts.mint_lp)? };
let vault_x = unsafe { TokenAccount::from_account_info_unchecked(self.accounts.vault_x)? };
let vault_y = unsafe { TokenAccount::from_account_info_unchecked(self.accounts.vault_y)? };

// Grab the amounts to deposit
let (x, y) = match mint_lp.supply() == 0 && vault_x.amount() == 0 && vault_y.amount() == 0 {
    true => (self.instruction_data.max_x, self.instruction_data.max_y),
    false => {
        let amounts = ConstantProduct::xy_deposit_amounts_from_l(
            vault_x.amount(),
            vault_y.amount(),
            mint_lp.supply(),
            self.instruction_data.amount,
            6,
        )
        .map_err(|_| ProgramError::InvalidArgument)?;

        (amounts.x, amounts.y)
    }
};

// Check for slippage
if !(x <= self.instruction_data.max_x && y <= self.instruction_data.max_y) {
    return Err(ProgramError::InvalidArgument);
}

Wenn es sich um die erste Einzahlung handelt, können wir die Berechnung der LP-Token und Einzahlungen überspringen und einfach den vom Benutzer vorgeschlagenen Wert verwenden

  • Übertrage die Beträge von den Token-Konten des Benutzers zu den Tresoren und präge die entsprechende Menge an LP-Token auf das Token-Konto des Benutzers

Du solltest kompetent genug sein, um dies selbstständig zu tun, daher überlasse ich dir die Implementierung:

rust
pub struct Deposit<'a> {
    pub accounts: DepositAccounts<'a>,
    pub instruction_data: DepositInstructionData,
}

impl<'a> TryFrom<(&'a [u8], &'a [AccountInfo])> for Deposit<'a> {
    type Error = ProgramError;

    fn try_from((data, accounts): (&'a [u8], &'a [AccountInfo])) -> Result<Self, Self::Error> {
        let accounts = DepositAccounts::try_from(accounts)?;
        let instruction_data = DepositInstructionData::try_from(data)?;

        // Return the initialized struct
        Ok(Self {
            accounts,
            instruction_data,
        })
    }
}

impl<'a> Deposit<'a> {
    pub const DISCRIMINATOR: &'a u8 = &1;

    pub fn process(&mut self) -> ProgramResult {
      //..

      Ok(())
    }
}
Next PageAuszahlen
ODER DIREKT ZUR HERAUSFORDERUNG
Bereit für die Herausforderung?
Blueshift © 2025Commit: e573eab