Rust
Pinocchio AMM

Pinocchio AMM

13 Graduates

Обмін

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

  • Обчислює кількість mint_x, яку ми отримаємо, відправивши певну кількість mint_y в amm (і навпаки), включаючи комісію.

  • Переказує токен from до сховища та токен to на рахунок користувача

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

Required Accounts

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

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

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

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

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

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

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

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

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

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

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

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

  • is_x: Чи виконується цей обмін з токена X на токен Y чи навпаки; потрібно для правильного вирівнювання рахунків. Має бути [u8]

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

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

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

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

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

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

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

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

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

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

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

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

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

rust
// 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);
}
  • Створіть логіку переказу, перевіряючи значення is_x та переказуючи суми from до сховищ і суми to на токен-рахунки користувача, як показано нижче:

rust
if self.instruction_data.is_x {
    Transfer {
        //...
    }
    .invoke()?;

    Transfer {
        //...
    }
    .invoke_signed(&signer_seeds)?;
} else {
    Transfer {
        //...

    }
    .invoke()?;

    Transfer {
        //...
    }
    .invoke_signed(&signer_seeds)?;
}

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

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