2

I'm wondering what are the best practices when using timestamps as a seed to calculate a PDA (or if using a timestamp is recommended at all). I should generate the timestamp from within my program:

use solana_program::clock::Clock;

```rust
#[derive(Accounts)]
#[account(
    init,
    seeds = [b"DATA", signer.key().as_ref(), Clock::get().unix_timestamp],
    bump
)]
pub data: Account<'info, DataAccount>

Or from outside my program, passing it as a parameter:

#[derive(Accounts)]
#[instruction(timestamp: u64)]
#[account(
    init,
    seeds = [b"DATA", signer.key().as_ref(), timestamp],
    bump
)]
pub data: Account<'info, DataAccount>

My worry is that I need a way to guarantee that a user can't call the instruction to initialize this account with the same timestamp.

2 Answers 2

3

Question is why you wanna do this. If you just want a unique PDA you could just save a counter in an account and increase it with every new PDA like here:

https://github.com/brimigs/solana-payroll/blob/main/anchor/programs/payroll/src/lib.rs


 pub fn add_employee(
        ctx: Context<AddEmployee>,
        salary: u64,
        name: String,
    ) -> Result<()> {
        let payroll = &mut ctx.accounts.solana_payroll;
        let employee = &mut ctx.accounts.employee;

        // Ensure that the admin is removing the employee
        if payroll.admin != ctx.accounts.admin.key() {
            return Err(ProgramError::IllegalOwner.into());
        }

        payroll.id_counter += 1;
        let employee_id = payroll.id_counter;

        employee.employee_id = employee_id;
        employee.salary = salary;
        employee.name = name;

        // payroll.employees.push(pubkey);

        Ok(())
    }

#[derive(Accounts)]
#[instruction(salary: u64, name: String,)]
pub struct AddEmployee<'info> {
    #[account(
        init,
        seeds = [name.as_bytes(), &solana_payroll.id_counter.to_le_bytes()],
        bump,
        space = 8 + 1000,
        payer = admin
      )]
    pub employee: Account<'info, Employee>,
    #[account(mut)]
    pub solana_payroll: Account<'info, PayrollState>,
    #[account(mut)]
    pub admin: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct Employee {
    employee_id: u32,
    salary: u64,
    name: String,
    pubkey: Pubkey,
}

If you really want to use the timestamp you need to get the timestamp in the client to find the address which is difficult since the time in the validator would not be the same in the client. If you pass it as instruction data the users could just send any timestamp, which may not what you want. So you could maybe send it in the instruction data and then check in the program again if its in the time range you would want to allow.

1
2

I assume the usage of timestamp is "not recommended" but it may depend on your use case.

I believe (while feel free to test) that the definition

#[account(
    init,
    seeds = [b"DATA", signer.key().as_ref(), Clock::get().unix_timestamp],
    bump
)]

will be difficult to get working. The macro #[account()] does verification of the provided addresses on-chain. The macro does not (somehow) "creates/generates" an account. The process is that the client generates the PDA based on the business requirements (here you say the PDA should be generated with constant "DATA", then from pubkey provided as signer parameter in transaction and then from some third parameter that's by chance is a timestamp. The address generated by client is then passed on-chain to Solana validator.

Then on-chain Anchor program generates a PDA based on seeds definition for verification purposes. The Anchor program finds out what is the current timestamp for Validator runtime that's used for the block. It's hard to expect the client and validator timestamp will match. See Solana transaction timestamp

When you use the #[instruction(timestamp: u64)] then it will work to get the account created (as the seeds argument is directly provided by client call and it is just used). Then it's your code to handle further.

Just be aware that Clock block timestamp is not fully accurate to "real-time" timestamp https://docs.solanalabs.com/implemented-proposals/bank-timestamp-correction#timestamp-correction while it should be guaranteed it never goes back.

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.