Swap
Instruksi swap melakukan dua tugas utama:
Menghitung jumlah
mint_xyang akan kita terima dengan mengirimkan sejumlahmint_yke dalam amm (dan sebaliknya) termasuk biaya.Mentransfer token
fromke vault dan tokentoke akun token pengguna
Required Accounts
Berikut adalah akun-akun yang diperlukan untuk konteks ini:
user: Pengguna yang menukarkan token ke dalam likuiditas Amm. Harus berupasigner.user_x_ata: Akun token terkait pengguna untuk token X. Ini adalah akun yang akan menerima atau mengirim token X ke dalam pool. Harus dilewatkan sebagaimutable.user_y_ata: Akun token terkait pengguna untuk token Y. Ini adalah akun yang akan menerima atau mengirim token Y ke dalam pool. Harus dilewatkan sebagaimutable.vault_x: Akun token yang menyimpan semua token X yang disimpan ke dalam pool. Harus dilewatkan sebagaimutable.vault_y: Akun token yang menyimpan semua token Y yang disimpan ke dalam pool. Harus dilewatkan sebagaimutable.config: Akun konfigurasi untuk pool AMM. Menyimpan semua parameter dan status pool yang relevan.token program: Akun program SPL Token. Ini diperlukan untuk melakukan operasi token seperti transfer dan minting. Harus berupaexecutable.
Di sini, sekali lagi, saya akan menyerahkan implementasinya kepada Anda:
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> {
//..
}
}Instruction Data
Berikut adalah data instruksi yang perlu kita masukkan:
is_x: Jika swap ini dilakukan dari token X ke token Y atau sebaliknya; diperlukan untuk menyelaraskan akun dengan benar. Harus berupa[u8]amount: Jumlah token yang pengguna bersedia kirim sebagai pertukaran untuk token lain pada pasangan tersebut. Harus berupa[u64]min: Jumlah minimum token yang pengguna bersedia terima sebagai pertukaran dariamount. Harus berupa[u64]expiration: Waktu kedaluwarsa dari pesanan ini. Penting untuk memastikan bahwa transaksi harus dijalankan dalam jangka waktu tertentu. Harus berupa[i64]
Kita akan menangani implementasi untuk SwapInstructionData sama seperti inisialisasi. Jadi saya akan menyerahkan implementasinya kepada Anda:
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> {
//..
}
}Instruction Logic
Kita mulai dengan mendeserialkan baik instruction_data maupun accounts.
Kemudian kita perlu:
Memuat akun
Configuntuk mengambil semua data di dalamnya. Kita dapat melakukannya menggunakan helperConfig::load().Memverifikasi bahwa
AmmStatevalid (jadi jika sama denganAmmState::Initialized).Memeriksa derivasi dari
vault_xdanvault_ysebagai Associated Token Accounts.Mendeserialkan semua akun token yang terlibat dan menggunakan data di dalamnya untuk menghitung jumlah yang akan diswap menggunakan crate
constant-product-curvedan memeriksa slippage seperti ini:
// 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);
}Buat logika transfer dengan memeriksa nilai
is_xdan transfer jumlahfromke vault dan jumlahtoke akun token pengguna seperti ini:
if self.instruction_data.is_x {
Transfer {
//...
}
.invoke()?;
Transfer {
//...
}
.invoke_signed(&signer_seeds)?;
} else {
Transfer {
//...
}
.invoke()?;
Transfer {
//...
}
.invoke_signed(&signer_seeds)?;
}Anda seharusnya cukup mahir untuk melakukan ini sendiri, jadi saya akan menyerahkan implementasinya kepada Anda:
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(())
}
}