存款
deposit 指令执行以下三个主要任务:
根据用户希望
mint的 LP 数量,存入mint_x和mint_y代币。计算存款金额,并检查金额是否超过用户指定的
max_x和max_y。在用户的 ata 中铸造正确数量的
mint_lp。
所需账户
以下是此上下文所需的账户:
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。
这里,我将再次把实现留给你:
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 的实现。所以我将实现留给你:
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> {
//..
}
}Instruction Logic
我们首先反序列化 instruction_data 和 accounts。
然后我们需要:
加载
Config账户以获取其中的所有数据。我们可以使用Config::load()辅助工具来完成。验证
AmmState是否有效(例如它是否等于AmmState::Initialized)。检查
vault_x和vault_y的派生是否为关联代币账户(Associated Token Accounts),如下所示:
// 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-curvecrate 计算存款金额,并检查滑点,如下所示:
// 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 代币
你应该已经足够熟练可以独立完成这部分内容,所以我将实现部分留给你:
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(())
}
}