Rust
Pinocchio AMM

Pinocchio AMM

13 Graduates

Виведення коштів

Інструкція withdraw виконує три основні завдання:

  • Виведення токенів mint_x та mint_y на основі кількості LP, яку користувач хоче burn.

  • Розрахунок суми для виведення та перевірка, що сума не менша за mint_x та mint_y, визначені користувачем.

  • Спалення відповідної кількості mint_lp з користувацького ata.

Як зазначено в розділі інструкції initialize; ми ініціалізуємо всі Associated Token Accounts поза нашою інструкцією з метою оптимізації.

Required Accounts

Нижче наведено рахунки, необхідні для цього контексту:

  • user: Користувач, який виводить токен у ліквідність Amm. Має бути signer.

  • mint_lp: Рахунок Mint, який представлятиме ліквідність пулу. Має бути переданий як mutable.

  • vault_x: Токен-рахунок, який містить усі токени X, внесені в пул. Має бути переданий як mutable.

  • vault_y: Токен-рахунок, який містить усі токени Y, внесені в пул. Має бути переданий як mutable.

  • user_x_ata: Асоційований токен-рахунок користувача для токена X. Це цільовий рахунок, на який буде переведено токен X користувача з пулу. Має бути переданий як mutable.

  • user_y_ata: Асоційований токен-рахунок користувача для токена Y. Це цільовий рахунок, на який буде переведено токен Y користувача з пулу. Має бути переданий як mutable.

  • user_lp_ata: Асоційований токен-рахунок користувача для LP токенів. Це вихідний рахунок, з якого будуть спалені LP токени. Має бути переданий як mutable.

  • config: Конфігураційний рахунок для пулу AMM. Зберігає всі відповідні параметри та стан пулу.

  • token program: Рахунок програми SPL Token. Це необхідно для виконання операцій з токенами, таких як перекази та створення. Має бути executable.

Тут, знову ж таки, я залишу реалізацію вам:

rust
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> {
        //..
    }
}

Дані інструкції

Ось дані інструкції, які нам потрібно передати:

  • amount: Кількість LP токенів, які користувач бажає спалити. Має бути [u64]

  • min_x: Мінімальна кількість токенів X, яку користувач готовий вивести. Має бути [u64]

  • min_y: Мінімальна кількість токенів Y, яку користувач готовий вивести. Має бути [u64]

  • expiration: Термін дії цього ордера. Важливо переконатися, що транзакція має бути виконана за певний проміжок часу. Має бути [i64]

Ми будемо обробляти реалізацію для WithdrawInstructionData так само, як і ініціалізацію. Тому я залишу реалізацію вам:

rust
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> {
        //..
    }
}

Переконайтеся, що будь-яка з сум, як-от amount, min_y та min_x більше нуля, і що термін дії ордера ще не минув, використовуючи sysvar Clock.

Логіка інструкції

Ми починаємо з десеріалізації як instruction_data, так і accounts.

Потім нам потрібно:

  • Завантажити акаунт Config, щоб отримати всі дані всередині нього. Ми можемо зробити це за допомогою хелпера Config::load().

  • Перевірити, що AmmState є дійсним (тобто не дорівнює AmmState::Disabled).

  • Перевірити деривацію vault_x та vault_y як Associated Token Accounts.

  • Десеріалізувати всі задіяні токен-акаунти та використати дані всередині них для обчислення суми для виведення, використовуючи крейт constant-product-curve та перевіряючи проковзування таким чином:

rust
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);
}
  • Перекажіть суми з сховищ на токен-рахунки користувача та спаліть відповідну кількість LP токенів з токен-рахунку користувача

authority для vault_x та vault_y є рахунок config

Ви повинні бути достатньо кваліфікованими, щоб зробити це самостійно, тому я залишаю реалізацію вам:

rust
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(())
    }
}
Next PageОбмін
АБО ПЕРЕЙТИ ДО ЗАВДАННЯ
Готові прийняти завдання?
Blueshift © 2025Commit: e573eab