Swap
L'instruction swap effectue deux tâches principales :
Calcule le montant de
mint_xque nous allons recevoir en envoyant un certain montant demint_ydans l'amm (et vice versa), frais comprisTransfert le jeton
fromau vault et le jetontoau compte de jetons de l'utilisateur
Comptes Nécessaires
Voici les comptes nécessaires pour ce contexte :
user: L'utilisateur qui échange des jetons dans la liquidité de l'Amm. Doit êtresigner.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.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.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 SwapAccounts<'a> {
pub user: &'a AccountInfo,
pub user_x_ata: &'a AccountInfo,
pub user_y_ata: &'a AccountInfo,
pub vault_x: &'a AccountInfo,
pub vault_y: &'a AccountInfo,
pub config: &'a AccountInfo,
pub token_program: &'a AccountInfo,
}
impl<'a> TryFrom<&'a [AccountInfo]> for SwapAccounts<'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 :
is_x: Si cet échange est effectué du jeton X vers le jeton Y ou vice versa. Il est nécessaire "d'aligner" correctement les comptes. Doit être un[u8]amount: Le montant de jetons que l'utilisateur est prêt à envoyer en échange de l'autre jeton de la paire. Doit être un[u64]min: Le montant minimum de jetons que l'utilisateur est prêt à recevoir en échange de L'amount. 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 SwapInstructionData de la même manière que l'initialisation. Je vous laisse donc le soin de l'implémenter :
pub struct SwapInstructionData {
pub is_x: bool,
pub amount: u64,
pub min: u64,
pub expiration: i64,
}
impl<'a> TryFrom<&'a [u8]> for SwapInstructionData {
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.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 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)? };
// Swap Calculations
let mut curve = ConstantProduct::init(
vault_x.amount(),
vault_y.amount(),
vault_x.amount(),
config.fee(),
None,
)
.map_err(|_| ProgramError::Custom(1))?;
let p = match self.instruction_data.is_x {
true => LiquidityPair::X,
false => LiquidityPair::Y,
};
let swap_result = curve
.swap(p, self.instruction_data.amount, self.instruction_data.min)
.map_err(|_| ProgramError::Custom(1))?;
// Check for correct values
if swap_result.deposit == 0 || swap_result.withdraw == 0 {
return Err(ProgramError::InvalidArgument);
}Créez la logique de transfert en vérifiant la valeur
is_xet transférez les montantsfromvers les vaults et les montantstovers les comptes de jetons de l'utilisateur comme ceci :
if self.instruction_data.is_x {
Transfer {
//...
}
.invoke()?;
Transfer {
//...
}
.invoke_signed(&signer_seeds)?;
} else {
Transfer {
//...
}
.invoke()?;
Transfer {
//...
}
.invoke_signed(&signer_seeds)?;
}Vous devriez être suffisamment compétent pour le faire vous-même, je vous laisse donc le soin de l'implémenter :
pub struct Swap<'a> {
pub accounts: SwapAccounts<'a>,
pub instruction_data: SwapInstructionData,
}
impl<'a> TryFrom<(&'a [u8], &'a [AccountInfo])> for Swap<'a> {
type Error = ProgramError;
fn try_from((data, accounts): (&'a [u8], &'a [AccountInfo])) -> Result<Self, Self::Error> {
let accounts = SwapAccounts::try_from(accounts)?;
let instruction_data = SwapInstructionData::try_from(data)?;
// Return the initialized struct
Ok(Self {
accounts,
instruction_data,
})
}
}
impl<'a> Swap<'a> {
pub const DISCRIMINATOR: &'a u8 = &3;
pub fn process(&mut self) -> ProgramResult {
//..
Ok(())
}
}