Rust
AMM avec Pinocchio

AMM avec Pinocchio

13 Graduates

Deposit

L'instruction deposit effectue trois tâches principales :

  • Dépose les jetons mint_x et mint_y en fonction du montant de LP que l'utilisateur souhaite mint.

  • Calcule le montant à déposer et vérifie qu'il n'est pas supérieur aux valeurs max_x et max_y définies par l'utilisateur.

  • Mint la quantité appropriée de mint_lp dans l'ATA de l'utilisateur.

Comme mentionné dans la section relative à l'instruction initialize, nous allons initialiser tous les Associated Token Accounts en dehors de notre instruction à des fins d'optimisation.

Comptes Nécessaires

Voici les comptes nécessaires pour ce contexte :

  • user: L'utilisateur qui dépose des jetons dans la liquidité de l'Amm. Doit être signer.

  • mint_lp: Le compte de Mint qui représentera la liquidité de la pool. Doit être mutable.

  • vault_x: Le compte de jetons qui détient tous les jetons X déposés dans la pool. Doit être mutable.

  • vault_y: Le compte de jetons qui détient tous les jetons Y déposés dans la pool. Doit être mutable.

  • user_x_ata: Le compte de jeton associé de l'utilisateur pour le jeton X. Il s'agit du compte source à partir duquel le jeton X de l'utilisateur sera transféré dans la pool. Doit être mutable.

  • user_y_ata: Le compte de jeton associé de l'utilisateur pour le jeton Y. Il s'agit du compte source à partir duquel le jeton Y de l'utilisateur sera transféré dans la pool. Doit être mutable.

  • user_lp_ata: Le compte de jetons associé de l'utilisateur pour les jetons LP. Il s'agit du compte de destination dans lequel les jetons LP seront créés. Doit être mutable.

  • config: Le compte de configuration de la pool de l'AMM. Stocke tous les paramètres pertinents et l'état de la pool.

  • token program: Le compte du program SPL-Token. Cela est nécessaire pour effectuer des opérations liées aux jetons telles que les transferts et la création. Doit être executable.

Ici encore, je vous laisse le soin de l'implémenter :

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> {
    //.. 
  }
}

Données d'Instruction

Voici les données d'instruction que nous devons transmettre :

  • amount: Le montant de jetons LP que l'utilisateur souhaite recevoir. Doit être un [u64]

  • max_x: Le montant maximal de jetons X que l'utilisateur est prêt à déposer. Doit être un [u64]

  • max_y: Le montant maximal de jetons Y que l'utilisateur est prêt à déposer. Doit être un [u64]

  • expiration: L'expiration de cet ordre. Il est important de s'assurer que la transaction doit être effectuée dans un certain délai. Doit être un [i64]

Nous allons gérer l'implémentation de DepositInstructionData de la même manière que l'initialisation. Je vous laisse donc le soin de l'implémenter :

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> {
        //..
    }
}

Assurez-vous que tous les montants, telles que amount, max_y et max_x, sont supérieurs à zéro et que l'ordre n'a pas encore expiré à l'aide de la sysvar Clock.

Logique d'Instruction

Nous commençons par désérialiser les instruction_data et les accounts.

Nous devons ensuite :

  • Chargez le compte Config pour récupérer toutes les données qu'il contient. Nous pouvons le faire à l'aide de Config::load().

  • Vérifiez que AmmState est valide (donc si c'est égal à AmmState::Initialized)

  • Vérifiez que la dérivation de vault_x et vault_y correspond bien à des Comptes de Jetons Associés, comme ceci :

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);
}
  • Désérialisez tous les comptes de jetons impliqués et utilisez les données qu'ils contiennent pour calculer le montant des dépôts à l'aide du crate constant-product-curve et vérifiez le slippage comme ceci :

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);
}

S'il s'agit du premier dépôt, nous pouvons ignorer le calcul des jetons LP et des dépôts et simplement utiliser la valeur suggérée par l'utilisateur

  • Transférer les montants des comptes de jetons de l'utilisateur vers les vaults et mintez le montant approprié de jetons LP sur le compte de jetons de l'utilisateur.

Vous devriez être suffisamment compétent pour le faire vous-même, je vous laisse donc le soin de l'implémenter :

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 PageRetirer
OU PASSER AU CHALLENGE
Prêt à relever le challenge ?
Blueshift © 2025Commit: e573eab