Deposit
L'instruction deposit effectue trois tâches principales :
Dépose les jetons
mint_xetmint_yen fonction du montant de LP que l'utilisateur souhaitemint.Calcule le montant à déposer et vérifie qu'il n'est pas supérieur aux valeurs
max_xetmax_ydéfinies par l'utilisateur.Mint la quantité appropriée de
mint_lpdans l'ATA de l'utilisateur.
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 êtresigner.mint_lp: Le compte de Mint qui représentera la liquidité de la pool. Doit êtremutable.vault_x: Le compte de jetons qui détient tous les jetons X déposés dans la pool. Doit êtremutable.vault_y: Le compte de jetons qui détient tous les jetons Y déposés dans la pool. Doit êtremutable.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 êtremutable.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 êtremutable.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 êtremutable.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. Il est nécessaire pour effectuer des opérations sur les jetons comme les transfers et le mint. Doit êtreexecutable.
Ici encore, je vous laisse le soin de l'implémenter :
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 :
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> {
//..
}
}Logique d'Instruction
Nous commençons par désérialiser les instruction_data et les accounts.
Nous devons ensuite :
Chargez le compte
Configpour récupérer toutes les données qu'il contient. Nous pouvons le faire à l'aide deConfig::load().Vérifiez que
AmmStateest valide (donc si c'est égal àAmmState::Initialized)Vérifiez que la dérivation de
vault_xetvault_ycorrespond bien à des Comptes de Jetons Associés, comme ceci :
// 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-curveet vérifiez le slippage comme ceci :
// 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);
}Transférer les montants des comptes de jetons de l'utilisateur vers les vaults et minter le montant approprié de jetons LP sur le compte de jetons de l'utilisateur.
Grâce à la mise à niveau p-token, nous pouvons batcher les transferts de jetons et le mint en un seul CPI, au lieu d'effectuer trois appels CPI séparés.
use pinocchio_token::instructions::{Batch, BatchState, MintTo, Transfer};
/// Le nombre de comptes pour l'instruction batch.
const MAX_ACCOUNTS_LEN: usize =
Transfer::MAX_ACCOUNTS_LEN * 2
+ MintTo::MAX_ACCOUNTS_LEN;
/// La longueur des données d'instruction pour l'instruction batch.
const MAX_DATA_LEN: usize = Batch::header_data_len(3)
+ Transfer::DATA_LEN * 2
+ MintTo::DATA_LEN;
// ...
let mut batch_state = BatchState::new(MAX_ACCOUNTS_LEN, MAX_DATA_LEN);
let mut batch = batch_state.as_batch()?;
Transfer::new(
self.accounts.user_x_ata,
self.accounts.vault_x,
self.accounts.user,
x,
)
.into_batch(&mut batch)?;
Transfer::new(
self.accounts.user_y_ata,
self.accounts.vault_y,
self.accounts.user,
y,
)
.into_batch(&mut batch)?;
MintTo::new(
self.accounts.mint_lp,
self.accounts.user_lp_ata,
self.accounts.config,
self.instruction_data.amount,
)
.into_batch(&mut batch)?;
batch.invoke_signed(&[signer])?;Vous devriez être suffisamment compétent pour faire le reste vous-même, je vous laisse donc le soin de l'implémenter :
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(())
}
}