Rust
Pinocchio AMM

Pinocchio AMM

Swap

Instruction swap thực hiện hai nhiệm vụ chính:

  • Tính toán số lượng mint_x mà chúng ta sẽ nhận được khi gửi một lượng mint_y nhất định vào amm (và ngược lại), bao gồm cả phí.

  • Chuyển token from vào vault và chuyển token to đến tài khoản token của người dùng.

Như đã đề cập trong phần instruction initialize; chúng ta sẽ khởi tạo tất cả Associated Token Accounts bên ngoài instruction của mình vì mục đích tối ưu hóa.

Tài Khoản Cần Thiết

Các tài khoản cần cho ngữ cảnh này là:

  • user: người dùng đang swap vào AMM. Phải là signer.

  • user_x_ata: associated token account của người dùng cho token X. Phải được truyền dưới dạng mutable.

  • user_y_ata: associated token account của người dùng cho token Y. Phải được truyền dưới dạng mutable.

  • vault_x: token account giữ token X của pool. Phải được truyền dưới dạng mutable.

  • vault_y: token account giữ token Y của pool. Phải được truyền dưới dạng mutable.

  • config: tài khoản cấu hình của AMM. Lưu trạng thái và các tham số của pool.

  • token_program: tài khoản token program. Tài khoản này cần thiết cho các thao tác token như transfer và minting. Phải là executable.

Ở đây, một lần nữa, tôi sẽ để phần triển khai cho bạn:

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]

Dữ Liệu Instruction

Đây là dữ liệu instruction mà chúng ta cần truyền vào:

  • is_x: Swap này được thực hiện từ token X sang token Y hay ngược lại; cần thiết để sắp xếp đúng các account. Phải là [u8]

  • amount: Số lượng token mà người dùng sẵn sàng gửi để đổi lấy token còn lại trong cặp. Phải là [u64]

  • min: Số lượng token tối thiểu mà người dùng sẵn sàng nhận được khi đổi amount. Phải là [u64]

  • expiration: Thời điểm hết hạn của lệnh này. Điều này quan trọng để đảm bảo giao dịch phải được thực hiện trong một khoảng thời gian nhất định. Phải là [i64]

Chúng ta sẽ xử lý phần triển khai SwapInstructionData giống như phần khởi tạo. Vì vậy tôi sẽ để phần triển khai cho bạn:

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

Hãy đảm bảo mọi số lượng như amountmin đều lớn hơn 0, đồng thời lệnh chưa hết hạn bằng cách sử dụng sysvar Clock.

Logic Instruction

Chúng ta bắt đầu bằng cách deserialize cả instruction_dataaccounts.

Sau đó chúng ta cần:

  • Load account Config để lấy toàn bộ dữ liệu bên trong. Chúng ta có thể thực hiện việc đó bằng helper Config::load().

  • Xác minh AmmState là hợp lệ (tức là bằng AmmState::Initialized).

  • Kiểm tra derivation của vault_xvault_y là Associated Token Accounts.

  • Deserialize tất cả các token account liên quan và sử dụng dữ liệu bên trong để tính toán số lượng swap bằng crate constant-product-curve, đồng thời kiểm tra slippage như sau:

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 các lần transfer token vào một CPI duy nhất. Bạn đã thấy pattern batch ở deposit, nên ở đây chúng ta làm điều tương tự với hai instruction Transfer và chỉ gọi token program một lần. Vì transfer từ vault vẫn cần chữ ký của PDA config, batch nên kết thúc bằng 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]

Hãy đảm bảo pinocchio-token có phiên bản ít nhất là 0.6.0 để hỗ trợ các thao tác batch của p-token.

Bạn nên đã đủ thành thạo để tự hoàn thành phần này, vì vậy tôi sẽ để việc triển khai lại cho bạn:

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]
Hoặc bỏ qua để làm thử thách
Nội dung
Xem mã nguồn
Blueshift © 2026Commit: 3c44267