我正在编写一个使用pda存储拍卖位置的索拉纳程序,我有一个关于在调用指令时从客户端传递帐户的问题。我的位置使用一个数字计数器作为种子(因为所有其他属性都不是唯一的),当创建一个新的位置帐户时,这个计数器从另一个(state
)帐户中取出,如下所示:
pub fn start_auction(ctx: Context<StartAuction>) -> Result<()> {
let state = &mut ctx.accounts.state;
// ...
state.positions_count = state.positions_count + 1;
Ok(())
}
#[derive(Accounts)]
pub struct StartAuction<'info> {
#[account(init, seeds=[&state.positions_count.to_ne_bytes()], bump, payer = authority, space = 5000)]
pub position: Account<'info, Position>,
#[account(mut, seeds=[b"state"], bump)]
pub state: Account<'info, State>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>
}
#[account]
#[derive(Default)]
pub struct State {
pub positions_count: u32
}
令我惊讶的是,我必须从调用start_auction
指令的javascript客户端代码传递position
帐户(及其种子)。不清楚为什么—因为程序本身已经知道从哪里获得种子(从state
)来初始化帐户。但这不仅仅是关于代码整洁——在客户端,我不知道调用时的当前计数器。当然,我可以获取state
并检索计数器,但如果在获取和调用另一个用户调用相同的指令之间,计数器在此期间增加了怎么办?是否有一种方法可以避免从客户端代码传递帐户,如果没有,Solana开发人员将如何处理上述计数器问题?
注:我能想到另一个问题,因为需要从客户那里传递账目。假设我正在创建一个程序,该程序存储一些地址列表,它应该在某些条件下向这些地址发送令牌。需要发送令牌的地址(帐户)由程序的逻辑决定,在客户端无法知道。如何在Solana中实现这些功能?
为了并行化事务,Solana运行时要求在开始时显式声明事务中的所有帐户,因此没有"动态加载";的账户。这包括PDA帐户。
在您的设计中,肯定有可能遇到多个用户试图创建相同帐户的竞争条件,因此您可能需要考虑为您的PDA种子设计不同的设计。
我想您的目标是跟踪给定拍卖的所有帐户。在这种情况下,每个Position
都可以被一个用户,甚至是一个随机的u64所控制,并且他们都可以在同一个拍卖中为每个Position
存储一些ID号。当你想迭代拍卖的所有帐户时,你可以对所有这些帐户执行getProgramAccounts()
以解决它们。
直到有一些其他的Solana运行时允许动态加载帐户或一些MEV客户端允许您确定何时您的交易将着陆,这将是您最好的选择!