Withdraw
L'instruction withdraw effectue trois tâches principales :
Retire les jetons
mint_xetmint_yen fonction du montant de LP que l'utilisateur souhaiteburn.Calcule le montant à retirer et vérifie qu'il n'est pas inférieur aux valeurs
mint_xetmint_ydéfinies par l'utilisateur.Burn la quantité appropriée de
mint_lpprovenant de l'ATA de l'utilisateur.
Comptes Nécessaires
Voici les comptes nécessaires pour ce contexte :
user: L'utilisateur qui retire les jetons de 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 source à partir duquel les jetons LP seront brûlé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. Cela est nécessaire pour effectuer des opérations liées aux jetons telles que les transferts et la création. Doit êtreexecutable.
Ici encore, je vous laisse le soin de l'implémenter :
pub struct WithdrawAccounts<'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 WithdrawAccounts<'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 brûler. Doit être un[u64]min_x: Le montant minimum de jetons X que l'utilisateur est prêt à retirer. Doit être un[u64]min_y: Le montant minimum de jetons Y que l'utilisateur est prêt à retirer. 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 WithdrawInstructionData de la même manière que l'initialisation. Je vous laisse donc le soin de l'implémenter :
pub struct WithdrawInstructionData {
pub amount: u64,
pub min_x: u64,
pub min_y: u64,
pub expiration: i64,
}
impl<'a> TryFrom<&'a [u8]> for WithdrawInstructionData {
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 ce n'est pas égal àAmmState::Disabled).Vérifiez que la dérivation de
vault_xetvault_ycorrespond bien à des Comptes de Jetons Associés.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 :
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)? };
let (x, y) = match mint_lp.supply() == self.instruction_data.amount {
true => (vault_x.amount(), vault_y.amount()),
false => {
let amounts = ConstantProduct::xy_withdraw_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.min_x && y >= self.instruction_data.min_y) {
return Err(ProgramError::InvalidArgument);
}Transférez les montants des vaults vers les comptes de jetons de l'utilisateur et brûler le montant approprié de jetons LP du 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 :
pub struct Withdraw<'a> {
pub accounts: WithdrawAccounts<'a>,
pub instruction_data: WithdrawInstructionData,
}
impl<'a> TryFrom<(&'a [u8], &'a [AccountInfo])> for Withdraw<'a> {
type Error = ProgramError;
fn try_from((data, accounts): (&'a [u8], &'a [AccountInfo])) -> Result<Self, Self::Error> {
let accounts = WithdrawAccounts::try_from(accounts)?;
let instruction_data = WithdrawInstructionData::try_from(data)?;
// Return the initialized struct
Ok(Self {
accounts,
instruction_data,
})
}
}
impl<'a> Withdraw<'a> {
pub const DISCRIMINATOR: &'a u8 = &2;
pub fn process(&mut self) -> ProgramResult {
//..
Ok(())
}
}