4

Is it possible to have an account with a counter, and an instruction that can be called by any user at any time and will init a new pda with the counter number as seed and add 1 to the counter?

I find difficult to do it because, although I can fetch the counter value from the counter account to pass it in the instruction, as any user can make this transaction at any time, if more than one user transacts at the same time, only one will succeed since the pda with that seed will already be created.

Is it a way to make pdas like this, with a counter on chain? Is it any other way to do it?

Thanks

4
  • What would you like to happen if more than one user makes the transaction at a time? It's a bit hard to understand how you want this to work, to be able to suggest a solution
    – Callum M
    Commented Sep 7, 2022 at 12:56
  • I dont want the transaction to fail due to already existing pda. If 2 users send the transaction at the same time, and I fetch the counter from chain to find the pda's address, the counter will be the same, the first user will get a succesfull result but the 2nd will fails cause the pda with that seed is already created Commented Sep 7, 2022 at 14:41
  • So what would you want to happen if there's an already existing PDA? Should the 2nd transaction be a no-op? Should it increment the counter created by the 1st transaction? Something else?
    – Callum M
    Commented Sep 7, 2022 at 14:42
  • Supposing I already have an account created with a counter: u64 on chain, and I have an instruction that inits a pda with seeds equal to the account counter and adds 1 to the counter, then I want users on the client side to call the instruction and never fail due to already existing account. I want to know if there is any way to do this, I cant figure it out, Im thinking that it is not possible, but maybe someone did something similar. I need to keep track of different pdas and want to use this counter as an id. so the pda's seeds will be: ["seed"].as_ref(), id.to_le_bytes.as_ref() Commented Sep 7, 2022 at 15:12

2 Answers 2

1

Several ways to handle it:

  1. Use init_if_needed , hence the first one creates and the rest update.

  2. If your PDA is global, you can initialize it on your own and then all users will only update.

5
  • The global pda is already created, with a counter on it. Users dont just update, they create a new pda. This pda has the counter as seeds. So if user1 call the instruction, a new pda with seed "1" will be created and counter will add 1, then user2 calls it and creates pda with seed "2".. and as users creates pdas, I want the seeds to be 3,4,5.. etc. The problem is when 2 users wants to create the pda at the same time. They will both fetch the counter on the client and get the same number. Then one user will fail the transaction because the other one has already created the pda with that seed. Commented Sep 7, 2022 at 20:50
  • What part of the global counter is used for the user specific counter? Also what you're trying to do? Maybe can be done in other ways.
    – Ohad Dahan
    Commented Sep 8, 2022 at 16:00
  • I want to store picture urls in various pdas (1 on each) to fetch them in the front end and show the image. So Im trying to set the seed of those pdas with an id that is a on-chain counter. The idea is to fetch the counter first and get the total amount if pictures, and then loop to get the pictures url and show the images. Commented Sep 12, 2022 at 1:26
  • Why this requires the seeds to depend on the counter?
    – Ohad Dahan
    Commented Sep 12, 2022 at 19:55
  • How could I get the urls otherwise? I need to have the seed of the pdas and I think that an id would be a good way because with an id I will get all the pdas. Commented Sep 12, 2022 at 21:17
0

In a previous project I used the same architecture, but it is very dangerous. This is because anyone could deny access to a specific user by fetching their main account. But in case you want to execute that type of architecture, here is an example.

#[derive(Accounts)]
pub struct Update<'info> {
    #[account(mut, seeds = [b"SOLotery", main.authority.key().as_ref()], bump = main.bump_original)]
    pub main: Account<'info, Main>,
    #[account(init, seeds = [main.counter.to_le_bytes().as_ref()], bump = main.bump_winner, space = 8, payer=user)]
    pub unsafe_pda: Account<'info, UnsafePDA>,
    #[account(mut)]
    pub user: Signer<'info>,
}
#[account]
pub struct Main {
    pub authority: Pubkey, 
    pub counter: u8,
    pub bump_winner: u8, 
}
 #[account]
    pub struct UnsafePDA {
    pub authority: Pubkey, 
    pub counter: u8,
    pub bump_winner: u8, 
}
1
  • So when I call the instruction from the client, I need to pass the unsafe_pda address and to get it, I need the counter for the seed. The only way to get it is by fetching the counter and placing it as seed. But if 2 users run the ix at the same time, they will fetch the same counter and only the first tx will succeed. So I think is not possible to make pdas with consecutive ids from a counter on-chain and that many users can call the ix at the same time. Commented Sep 12, 2022 at 1:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.