rustsolanasolana-web3jsanchor-solanasolana-program-library

Solana PDA passing from javascript client


I'm writing a Solana program which uses PDAs to store auction positions and I have a question about passing the accounts from client when calling instructions. My positions use a numeric counter as a seed (because all other properties are not unique) and when creating a new position account, this counter is taken from the another (state) account as shown below:

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
}

What surprises me is that I must pass the position account (with its seeds) from the javascript client code that calls the start_auction instruction. It's not clear why - because the program itself already knows where to get the seed (from state) to init the account. But this is not only about code cleanliness - on client side I don't know the current counter at the time of calling. Of course I can fetch state and retrieve the counter, but what if in between of fetching and calling another user calls the same instruction and the counter becomes incremented in the meantime? Is there a way to avoid passing the accounts from client code and if no, how Solana developers would deal with the above described counter problem?

P.S. I can think of another problem caused by the need to pass the accounts from client. Imagine I'm creating a program that stores some list of addresses to which it should send tokens at certain conditions. And the addresses (accounts) to which tokens need to be sent are determined by the program's logic and cannot be known on the client side. How to implement such functionality in Solana?


Solution

  • In order to parallelize transactions, the Solana runtime requires that all accounts in a transaction be declared explicitly at the start, so there is no "dynamic loading" of accounts. This includes PDA accounts.

    In your design, it's definitely possible to run into a race condition of multiple users trying to create the same account, so you may want to consider a different design for your PDA seeds.

    I imagine that you aim to keep track of all the accounts for a given auction. In that case, each Position can be keyed off of a user, or even a random u64, and they can all store some ID number for each Position in the same auction. Whenever you want to iterate through all accounts for an auction, you can do a getProgramAccounts() for all of those in order to resolve them.

    Until there's some other Solana runtime that allow for dynamic loading of accounts or some MEV client that allows you to be certain of when your transaction will land, this will be your best bet!