Виведення коштів
Інструкція withdraw виконує три основні завдання:
Виведення токенів
mint_xтаmint_yна основі кількості LP, яку користувач хочеburn.Розрахунок суми для виведення та перевірка, що сума не менша за
mint_xтаmint_y, визначені користувачем.Спалення відповідної кількості
mint_lpз користувацького ata.
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.
Тут, знову ж таки, я залишу реалізацію вам:
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 так само, як і ініціалізацію. Тому я залишу реалізацію вам:
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_data, так і accounts.
Потім нам потрібно:
Завантажити акаунт
Config, щоб отримати всі дані всередині нього. Ми можемо зробити це за допомогою хелпераConfig::load().Перевірити, що
AmmStateє дійсним (тобто не дорівнюєAmmState::Disabled).Перевірити деривацію
vault_xтаvault_yяк Associated Token Accounts.Десеріалізувати всі задіяні токен-акаунти та використати дані всередині них для обчислення суми для виведення, використовуючи крейт
constant-product-curveта перевіряючи проковзування таким чином:
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 токенів з токен-рахунку користувача
Ви повинні бути достатньо кваліфікованими, щоб зробити це самостійно, тому я залишаю реалізацію вам:
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(())
}
}