-
PDAs can be thought of as a way of mimicking Web2's databases by writing schemas. For instance, a dApp that needs to update its own data without having a 'client' authorizing those changes.
-
PDAs are addresses with special properties. They are not public keys (so they don't have an associated public key).
-
PDAs provide a mechanism to build hashmap-like structures on-chain, allowing programs to sign instructions.
findProgramAddress()
will deterministically derive a PDA from aprogram_id
and seeds (collection of bytes).- A bump (one byte) is used to push a potential PDA off the ed25519 elliptic curve.
- Programs can sign for their PDAs by providing the seeds and bump to
invoke_signed
.
-
PDAs simplify the programming model and make programs more secure.
When you use seeds to derive a public key, there is a chance that the seed you use and the public derived from them have an associated private key (depicted by the ed2559 elliptic curve).
So if the seeds derive a private key that exists in the curve, Solana will add an additional integer (a bump) to the seed list to make sure it bumps off the curve and cannot have a private key.
-
PDAs are created by hashing a number of seeds the user can choose with the
program_id
. -
Seeds can be anything: pubkey, strings, an array of numbers, etc.
-
There is a 50% chance that this hash can result in a public key. This is how a bump can be searched:
fn find_pda(seeds, program_id) {
for bump in 0..256 {
let potential_pda = hash(seeds, bump, program_id);
if is_pubkey(potential_pda) {
continue;
}
return (potential_pda, bump);
}
panic!("Could not find pda after 256 tries.");
}
- The first bump that results in a PDA is called a "canonical bump," and they are the recommended one for usage.
-
When a PDA is generated,
findProgramAddress()
returns both the address and the bump used to kick the address off the elliptic curve. -
With a bump, a program can sign any instruction that requires its PDA, by passing the instruction, the list of accounts, and the seeds and bumps used to derive the PDA to
invoke_signed
.
-
PDAs are hashed from a bump, a
program_id
, and several seeds. These seeds can be used to build hashmap-like structures on-chain. -
With PDAs, you can create structs that encode the information about a relationship between the user and some data account, so that PDAs serve as the address:
pub struct UserStats {
level: u16,
name: String,
bump: u8
}
-
In some cases, it's possible to reduce the number of accounts needed by making a PDA storing state also sign a CPI instead of defining a separate PDA for that.
-
This means that programs can be given control over assets, which they then manage according to the rules defined in the code.
- A program with a global state:
const [pda, bump] = await findProgramAddress(Buffer.from("GLOBAL_STATE"), program_id)
- A program with user-specific data:
const [pda, bump] = await web3.PublicKey.findProgramAddress(
[
publicKey.toBuffer()
],
program_id
)
- A program with multiple data items per user:
const [pda, bump] = await web3.PublicKey.findProgramAddress(
[
publicKey.toBuffer(),
Buffer.from("Shopping list")
],
program_id,
);
💡 Tip: If
find_program_address
has to take a long time to find a valid address (i.e., it as a high bump) the compute unit usage might be high. Finding the PDAs after initialization can be optimized by saving the bump into an account.
- Learn how PDA works on Anchor through the backend's demo 3.
- Learn how PDA and CPI work on Anchor through the backend's demo 4.
- Build a Twitter PDA scheme through the backend's demo 5.
- Build a frontend dApp that leverages PDA through the frontend's demo 5 and the frontend's demo 6.