Rust
Pinocchio AMM

Pinocchio AMM

13 Graduates

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 bằng cách gửi một số lượng nhất định mint_y vào AMM (và ngược lại) bao gồm phí.

  • Chuyển token from đến vault và 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 chúng ta vì mục đích tối ưu hóa.

Các tài khoản cần thiết

Dưới đây là các tài khoản cần thiết cho context này:

  • user: Người dùng đang hoán đổi token trong thanh khoản của AMM. Phải là signer.

  • vault_x: Tài khoản token chứa tất cả token X được gửi vào pool. Phải được truyền vào như mutable.

  • vault_y: Tài khoản token chứa tất cả token Y được gửi vào pool. Phải được truyền vào như mutable.

  • user_x_ata: Tài khoản token liên kết của người dùng cho token X. Đây là tài khoản sẽ nhận hoặc gửi token X vào pool. Phải được truyền vào như mutable.

  • user_y_ata: Tài khoản token liên kết của người dùng cho token Y. Đây là tài khoản sẽ nhận hoặc gửi token Y vào pool. Phải được truyền vào như mutable.

  • config: Tài khoản cấu hình cho AMM pool. Lưu trữ tất cả các tham số và trạng thái pool liên quan.

  • token program: Tài khoản chương trình SPL Token. Điều này cần thiết để thực hiện các hoạt động token như chuyển khoản và minting. Phải là executable.

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

rust
pub struct SwapAccounts<'a> {
    pub user: &'a AccountInfo,
    pub user_x: &'a AccountInfo,
    pub user_y: &'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> {
        //.. 
    }
}

Dữ liệu cho Instruction

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

  • is_x: Liệu swap này có được thực hiện từ token X sang token Y hay ngược lại; cần thiết để sắp xếp các tài khoản một cách chính xác. Phải là [u8]

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

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

  • expiration: Thời gian hết hạn của lệnh này. Quan trọng để đảm bảo rằng 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ý việc triển khai cho SwapInstructionData như khởi tạo. Vì vậy tôi sẽ để việc 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> {
        //..
    }
}

Đảm bảo rằng bất kỳ số lượng nào, như amountmint đều lớn hơn zero và lệnh chưa hết hạn bằng cách sử dụng sysvar Clock.

Instruction Logic

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

Sau đó chúng ta cần:

  • Tải tài khoản Config để lấy tất cả dữ liệu bên trong nó. Chúng ta có thể làm điều này bằng cách sử dụng helper Config::load().

  • Xác minh rằng AmmState hợp lệ (vì vậy nếu nó bằng AmmState::Initialized).

  • Kiểm tra việc tạo vault_xvault_y để trở thành Associated Token Accounts.

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

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))?;

// Kiểm tra các giá trị chính xác
if swap_result.deposit == 0 || swap_result.withdraw == 0 {
    return Err(ProgramError::InvalidArgument);
}
  • Tạo logic chuyển khoản kiểm tra giá trị is_x và chuyển số lượng from đến các vault và số lượng to đến tài khoản token của người dùng như thế này:

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

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

    }
    .invoke()?;

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

Bạn đủ khả năng triển khai logic này, vì vậy tôi sẽ để việc triển khai 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)?;

        // Trả về struct đã khởi tạo
        Ok(Self {
            accounts,
            instruction_data,
        })
    }
}
impl<'a> Swap<'a> {
    pub const DISCRIMINATOR: &'a u8 = &3;

    pub fn process(&mut self) -> ProgramResult {
        //..
        
        Ok(())
    }
}
Next PageKết luận
HOẶC BỎ QUA ĐỂ LÀM THỬ THÁCH
Sẵn sàng làm thử thách?
Nội dung
Xem mã nguồn
Blueshift © 2025Commit: e573eab