5
async function debug(){

  var basePubKey = new solanaWeb3.PublicKey("6gHtRckgbXmRtXkkCx5vhAh9jBUC3AuXTgmhi9gMHMVM");
  var programID = new solanaWeb3.PublicKey("HnFghw7hfphghDi87X1uVdCvGYAF5JtqqNnrt74K6ke8");
  var pda = await solanaWeb3.PublicKey.createWithSeed(basePubKey, "bank", programID);
  console.log(pda.toBase58()); // this prints 6C5xTpDj47nGrAn58mwkJGytd52cWqfF35Jj8FqEnZFL

  var seedBytes = new Uint8Array([98, 97, 110, 107]);

  var try1 = await solanaWeb3.PublicKey.findProgramAddress([seedBytes], programID);
  console.log(try1[0].toBase58()); //this prints CPPAAFkKFW91G4SzVFs2p4aqFZHR7Ba8xjAfTzbnxD5p

  var try2 = await solanaWeb3.PublicKey.findProgramAddress([basePubKey.toBuffer(), seedBytes, programID.toBuffer()], programID);
  console.log(try2[0].toBase58()); // this prints 2v9FKEF564tMixjncDpe8Ebsv8fuyK2QmsRpMyxMskBk

  var try3 = await solanaWeb3.PublicKey.findProgramAddress([basePubKey.toBuffer(), seedBytes], programID);
  console.log(try3[0].toBase58()); // this prints 3PYjPRXcrUzW37fbWSUw62SifqrFpgDKDVttSf7UTTy5
}

I made a PDA with createWithSeed(), and when I call findProgramAddress() with the same seed and programID, I get back a different address for the PDA. Why? I've tried different combinations of seeds. I've already checked that seedBytes is the "bank" in ASCII. I need to call findProgramAddress to get the bump of the pda, so that I can sign transaction with that pda in a program.

3 Answers 3

14

There's a big difference between using createWithSeed() and findProgramAddress().

createWithSeed()

This function hashes together basePubKey, "bank", and programID to create a new public key, doesn't check whether its on or off the ed25519 curve, and gives that back to you.

The seed for the attempt will look like this:

basePubKey "bank" programID

findProgramAddress()

This function will do its very best to find an address off the curve. It will take the seeds you give it and also concatenate a nonce (the bump) to it, and the bytestring "ProgramDerivedAddress", counting down from 255 to 0. Throwing but catching the error and trying again, but if it decrements down to 0 without finding an address off the curve then it will throw for the final time and tell you to get different seeds.

The seed for each attempt will look like this:

basePubKey "bank" currentNonce programID "ProgramDerivedAddress"

Conclusions

So its actually practically impossible for you to get the same addresses because under the hood the functions are using vastly different seeds.

Lastly, findProgramAddress() returned the bump to you but you are only accessing the first item in returned array inside of your try variables.

To get the bump seed, you'd have to unpack the returned promise into two variables like so:

let [pda_address, pda_bump] = await PublicKey.findProgramAddress(...args);

or just use console.log(try1[1]);

Here's the source code for PublicKey, a wonderful reference.

https://github.com/solana-labs/solana-web3.js/blob/e1567abf41c5746f00ccd18c52021b9d6c566bb9/src/publickey.ts

1
  • Thanks, I see the difference. Sorry I didn't make this clear, I'm aware that findProgramAddress() gives me the bump, but I just wanted to show that I can't find the bump of the address I made with createWithSeed() because findProgramAddress() gives a different address. How would I allocate space, rent exempt, and store data on an address returned by findProgramAddress()? SystemProgram.createAccountWithSeed() requires that the address of the new pda passed in must be made with PublicKey.createWithSeed().
    – Qpqpqpqp
    Commented Jul 16, 2022 at 18:32
7

createWithSeed and findProgramAddress are wholly incompatible. They're totally different address derivation schemes, from different times with different goals. PDAs should be preferred in nearly all cases today.

Correct usage is to find the bump seed for the PDA first with findProgramAddress which is more expensive due to the need of iteratively grinding a hash that does not map to a byte-equivalent ed25519 public key. Once the bump seed is found, createProgramAddress is used to recover the address in the future.

use solana_sdk::pubkey::Pubkey;

fn main() {
    const DOMAIN: &[u8] = b"domain_specifier";
    const CLASS: &[u8] = b"pda_class";
    let program_id = Pubkey::new_unique();
    let user_identifier = Pubkey::new_unique();
    let mut seeds = vec![DOMAIN, CLASS, user_identifier.as_ref()];
    let (found_pda, bump_val) = Pubkey::find_program_address(seeds.as_slice(), &program_id);
    let bump_seed = [bump_val];
    seeds.push(&bump_seed);
    let recovered_pda = Pubkey::create_program_address(seeds.as_slice(), &program_id).unwrap();
    assert_eq!(found_pda, recovered_pda);
}
2

I would recommend not using createWithSeed at all for creating PDAs, since createWithSeed does not guarantee the resulting Pubkey is off the curve. So (in theory) it could be someone's wallet and they could sign for it. Even though the risk might be minimal, why not use the proper way using findProgramAddress. You can just use createAccount instead of createWithSeed. The best way to calculate program addresses and bumps, create accounts, find them again etc. is using the Anchor framework.

E.g.

#[derive(Accounts)]
#[instruction(mint_bump: u8, amount: u64)]
pub struct Airdrop<'info> {
    #[account(init_if_needed, payer = payer, seeds = [b"faucet-mint".as_ref()], bump, mint::decimals = 6, mint::authority = mint)]
    pub mint: Account<'info, Mint>,
    #[account(init_if_needed, payer = payer, associated_token::mint = mint, associated_token::authority = payer)]
    pub destination: Account<'info, TokenAccount>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub rent: Sysvar<'info, Rent>,
}

Here, init_if_needed will create the account if it doesn't exist. And it will find the program using the given seed, and give you the bump. All without further coding. See the Anchor Book.

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.