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 ist erforderlich, um Token-Operationen wie Überweisungen und Prägungen durchzuführen. 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
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(())
}
}