Rust
Pinocchio AMM

Pinocchio AMM

13 Graduates

存款

deposit 指令執行三個主要任務:

  • 根據用戶希望 mint 的 LP 數量,存入 mint_xmint_y 代幣。

  • 計算存款數量,並檢查該數量是否超過用戶指定的 max_xmax_y

  • 在用戶的 ata 中鑄造正確數量的 mint_lp

initialize 指令部分所述;為了優化,我們將在指令之外初始化所有 Associated Token Accounts

所需帳戶

以下是此情境所需的帳戶:

  • user:將代幣存入 AMM 流動性池的用戶。必須是 signer

  • mint_lp:代表池流動性的鑄幣帳戶。必須作為 mutable 傳遞。

  • vault_x:持有存入池中的所有 X 代幣的代幣帳戶。必須作為 mutable 傳遞。

  • vault_y:持有存入池中的所有 Y 代幣的代幣帳戶。必須作為 mutable 傳遞。

  • user_x_ata:用戶的 X 代幣關聯代幣帳戶。這是用戶的 X 代幣將從中轉移到池中的來源帳戶。必須作為 mutable 傳遞。

  • user_y_ata:用戶的 Y 代幣關聯代幣帳戶。這是用戶的 Y 代幣將從中轉移到池中的來源帳戶。必須作為 mutable 傳遞。

  • user_lp_ata:用戶的 LP 代幣關聯代幣帳戶。這是將鑄造 LP 代幣的目標帳戶。必須作為 mutable 傳遞。

  • config:AMM 池的配置帳戶。存儲所有相關的池參數和狀態。

  • token program:SPL 代幣程式帳戶。這是執行代幣操作(如轉移和鑄造)所需的。必須是 executable

在這裡,我會再次將實現部分留給你:

rust
pub struct DepositAccounts<'a> {
    pub user: &'a AccountInfo,
    pub mint_lp: &'a AccountInfo,
    pub vault_x: &'a AccountInfo,
    pub vault_y: &'a AccountInfo,
    pub user_x_ata: &'a AccountInfo,
    pub user_y_ata: &'a AccountInfo,
    pub user_lp_ata: &'a AccountInfo,
    pub config: &'a AccountInfo,
    pub token_program: &'a AccountInfo,
}

impl<'a> TryFrom<&'a [AccountInfo]> for DepositAccounts<'a> {
  type Error = ProgramError;

  fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
    //..
  }
}

Instruction Data

以下是我們需要傳遞的指令數據:

  • amount:用戶希望接收的 LP 代幣數量。必須是 [u64]

  • max_x:用戶願意存入的最大 Token X 數量。必須是 [u64]

  • max_y:用戶願意存入的最大 Token Y 數量。必須是 [u64]

  • expiration:此訂單的到期時間。確保交易必須在一定時間內完成非常重要。必須是 [i64]

我們將以與初始化相同的方式處理 DepositInstructionData 的實現。所以我會將實現部分留給你:

rust
pub struct DepositInstructionData {
    pub amount: u64,
    pub max_x: u64,
    pub max_y: u64,
    pub expiration: i64,
}

impl<'a> TryFrom<&'a [u8]> for DepositInstructionData {
    type Error = ProgramError;

    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
        //..
    }
}

確保任何數量,例如 amountmax_ymax_x 都大於零,並且使用 Clock sysvar 確保訂單尚未過期。

Instruction Logic

我們首先需要反序列化 instruction_dataaccounts

接下來我們需要:

  • 加載 Config 賬戶以獲取其中的所有數據。我們可以使用 Config::load() 幫助器來完成。

  • 驗證 AmmState 是否有效(例如它是否等於 AmmState::Initialized

  • 驗證 vault_xvault_y 的派生是否為關聯代幣賬戶,如下所示:

rust
// Check if the vault_x is valid
let (vault_x, _) = find_program_address(
    &[
        self.accounts.config.key(),
        self.accounts.token_program.key(),
        config.mint_x(),
    ],
    &pinocchio_associated_token_account::ID,
);

if vault_x.ne(self.accounts.vault_x.key()) {
    return Err(ProgramError::InvalidAccountData);
}
  • 反序列化所有涉及的代幣賬戶,並使用其中的數據通過 constant-product-curve crate 計算存款數量,並檢查滑點,如下所示:

rust
// Deserialize the token accounts
let mint_lp = unsafe { Mint::from_account_info_unchecked(self.accounts.mint_lp)? };
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)? };

// Grab the amounts to deposit
let (x, y) = match mint_lp.supply() == 0 && vault_x.amount() == 0 && vault_y.amount() == 0 {
    true => (self.instruction_data.max_x, self.instruction_data.max_y),
    false => {
        let amounts = ConstantProduct::xy_deposit_amounts_from_l(
            vault_x.amount(),
            vault_y.amount(),
            mint_lp.supply(),
            self.instruction_data.amount,
            6,
        )
        .map_err(|_| ProgramError::InvalidArgument)?;

        (amounts.x, amounts.y)
    }
};

// Check for slippage
if !(x <= self.instruction_data.max_x && y <= self.instruction_data.max_y) {
    return Err(ProgramError::InvalidArgument);
}

如果這是第一次存款,我們可以跳過 LP 代幣和存款的計算,直接使用用戶建議的數值。

  • 將用戶的代幣帳戶中的金額轉移到保管庫,並鑄造相應數量的 LP 代幣到用戶的代幣帳戶。

你應該已經足夠熟練可以自行完成這部分,所以我將實現的工作留給你:

rust
pub struct Deposit<'a> {
    pub accounts: DepositAccounts<'a>,
    pub instruction_data: DepositInstructionData,
}

impl<'a> TryFrom<(&'a [u8], &'a [AccountInfo])> for Deposit<'a> {
    type Error = ProgramError;

    fn try_from((data, accounts): (&'a [u8], &'a [AccountInfo])) -> Result<Self, Self::Error> {
        let accounts = DepositAccounts::try_from(accounts)?;
        let instruction_data = DepositInstructionData::try_from(data)?;

        // Return the initialized struct
        Ok(Self {
            accounts,
            instruction_data,
        })
    }
}

impl<'a> Deposit<'a> {
    pub const DISCRIMINATOR: &'a u8 = &1;

    pub fn process(&mut self) -> ProgramResult {
      //..

      Ok(())
    }
}
Next Page提款
或跳過到挑戰
準備好參加挑戰了嗎?
Blueshift © 2025Commit: e573eab