Rust
AMM Pinocchio

AMM Pinocchio

77 Graduates

Swap

Instruksi swap melakukan dua tugas utama:

  • Menghitung jumlah mint_x yang akan kita terima dengan mengirim sejumlah mint_y tertentu ke amm (dan sebaliknya), termasuk fee.

  • Mentransfer token from ke vault dan token to ke akun token pengguna.

Seperti yang disebutkan dalam bagian instruksi initialize; kita akan menginisialisasi semua Associated Token Accounts di luar instruksi kita untuk tujuan optimasi.

Akun yang Diperlukan

Berikut akun-akun yang diperlukan untuk konteks ini:

  • user: Pengguna yang melakukan swap ke dalam AMM. Harus berupa signer.

  • user_x_ata: Associated token account milik pengguna untuk token X. Harus dikirim sebagai mutable.

  • user_y_ata: Associated token account milik pengguna untuk token Y. Harus dikirim sebagai mutable.

  • vault_x: Token account yang menyimpan token X milik pool. Harus dikirim sebagai mutable.

  • vault_y: Token account yang menyimpan token Y milik pool. Harus dikirim sebagai mutable.

  • config: Account konfigurasi AMM. Menyimpan state dan parameter pool.

  • token_program: Account token program. Ini diperlukan untuk operasi token seperti transfer dan minting. Harus executable.

Di sini, sekali lagi, saya akan menyerahkan implementasinya kepada Anda:

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> {
        //..
    }
}
Expand
[2 more lines]

Data Instruksi

Berikut adalah data instruksi yang perlu kita berikan:

  • is_x: Jika swap ini dilakukan dari token X ke token Y atau sebaliknya; diperlukan untuk menyusun account dengan benar. Harus berupa [u8]

  • amount: Jumlah token yang bersedia dikirim pengguna sebagai imbalan atas token lain di dalam pair. Harus berupa [u64]

  • min: Jumlah minimum token yang bersedia diterima pengguna sebagai pertukaran untuk amount. Harus berupa [u64]

  • expiration: Kedaluwarsa order ini. Penting untuk memastikan bahwa transaksi harus dijalankan dalam jangka waktu tertentu. Harus berupa [i64]

Kita akan menangani implementasi SwapInstructionData sama seperti inisialisasi. Jadi saya akan menyerahkan implementasinya kepada Anda:

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

Pastikan jumlah seperti amount dan min lebih besar dari nol dan bahwa order belum kedaluwarsa dengan menggunakan sysvar Clock.

Logika Instruksi

Kita mulai dengan mendeserialkan baik instruction_data maupun accounts.

Kemudian kita perlu:

  • Memuat akun Config untuk mengambil semua data di dalamnya. Kita dapat melakukannya menggunakan helper Config::load().

  • Memverifikasi bahwa AmmState valid (jika sama dengan AmmState::Initialized).

  • Memeriksa derivasi vault_x dan vault_y sebagai Associated Token Accounts.

  • Mendeserialkan semua akun token yang terlibat dan menggunakan data di dalamnya untuk menghitung jumlah yang akan di-swap menggunakan crate constant-product-curve serta memeriksa slippage seperti ini:

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);
}
Expand
[12 more lines]
  • Batch transfer token ke dalam satu CPI. Anda sudah melihat pattern batch di deposit, jadi di sini kita melakukan hal yang sama dengan dua instruksi Transfer dan hanya memanggil token program sekali. Karena transfer dari vault tetap memerlukan tanda tangan PDA config, batch harus diakhiri dengan batch.invoke_signed(&[signer])?;.

rust
const MAX_ACCOUNTS_LEN: usize = Transfer::MAX_ACCOUNTS_LEN * 2;
const MAX_DATA_LEN: usize = Batch::header_data_len(2) + Transfer::DATA_LEN * 2;

// ...

let mut batch_state = BatchState::new(MAX_ACCOUNTS_LEN, MAX_DATA_LEN);
let mut batch = batch_state.as_batch()?;

if self.instruction_data.is_x {
    Transfer::new(
        // ...
    )
    .into_batch(&mut batch)?;

    Transfer::new(
        // ...
    )
    .into_batch(&mut batch)?;
} else {
    Transfer::new(
        // ...
    )
    .into_batch(&mut batch)?;

    Transfer::new(
        // ...
    )
    .into_batch(&mut batch)?;
}

batch.invoke_signed(&[signer])?;
Expand
[16 more lines]

Pastikan pinocchio-token setidaknya versi 0.6.0 untuk mendukung operasi batch p-token.

Anda seharusnya cukup mahir untuk melakukan ini sendiri, jadi saya akan menyerahkan implementasinya kepada Anda:

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(())
    }
}
Expand
[14 more lines]
Daftar Isi
Lihat Sumber
Blueshift © 2026Commit: 3c44267