Abheben
Die withdraw Anweisung führt drei Hauptaufgaben aus:
Abheben der
mint_xundmint_yToken basierend auf der LP-Menge, die der Benutzerburnmöchte.Berechnung des abzuhebenden Betrags und Überprüfung, dass der Betrag nicht geringer ist als die vom Benutzer festgelegten
mint_xundmint_y.Verbrennen der richtigen Menge an
mint_lpaus dem Benutzer-ATA.
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 abhebt. Muss einsignersein.mint_lp: Das Mint-Konto, das die Liquidität des Pools repräsentiert. Muss alsmutableübergeben werden.vault_x: Das Token-Konto, das alle in den Pool eingezahlten Token X enthält. Muss alsmutableübergeben werden.vault_y: Das Token-Konto, das alle in den Pool eingezahlten Token Y enthält. Muss alsmutableübergeben werden.user_x_ata: Das zugehörige Token-Konto des Benutzers für Token X. Dies ist das Zielkonto, auf das die Token X des Benutzers aus dem Pool übertragen werden. Muss alsmutableübergeben werden.user_y_ata: Das zugehörige Token-Konto des Benutzers für Token Y. Dies ist das Zielkonto, auf das die Token Y des Benutzers aus dem Pool übertragen werden. Muss alsmutableübergeben werden.user_lp_ata: Das zugehörige Token-Konto des Benutzers für LP-Token. Dies ist das Quellkonto, von dem LP-Token verbrannt werden. Muss alsmutableü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 wird für Token-Operationen wie Transfers und Minting benötigt. Mussexecutablesein.
Hier überlasse ich die Implementierung wieder dir:
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> {
//..
}
}Instruction Data
Hier sind die Instruktionsdaten, die wir übergeben müssen:
amount: Die Menge an LP-Token, die der Benutzer verbrennen möchte. Muss ein[u64]seinmin_x: Die Mindestmenge an Token X, die der Benutzer abheben möchte. Muss ein[u64]seinmin_y: Die Mindestmenge an Token Y, die der Benutzer abheben möchte. Muss ein[u64]seinexpiration: 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 WithdrawInstructionData genauso wie bei der Initialisierung handhaben. Daher überlasse ich dir die Implementierung:
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> {
//..
}
}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 dies mit demConfig::load()Helper tun.Überprüfen, ob der
AmmStategültig ist (also nicht gleichAmmState::Disabled).Die Ableitung von
vault_xundvault_yprüfen, um sicherzustellen, dass es sich um Associated Token Accounts handelt.Alle beteiligten Token-Konten deserialisieren und die darin enthaltenen Daten verwenden, um den abzuhebenden Betrag mit dem
constant-product-curveCrate zu berechnen und den Slippage wie folgt zu überprüfen:
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);
}Übertrage die Beträge aus den Tresoren auf die Token-Konten des Benutzers und verbrenne die entsprechende Menge an LP-Tokens vom Token-Konto des Benutzers. Genau wie in der
Deposit-Instruction verwendest du einen einzelnenBatch, damit beide Transfers und das Burn in einem CPI ausgeführt werden.
Du solltest kompetent genug sein, um dies selbständig zu tun, daher überlasse ich dir die Implementierung:
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(())
}
}