Is this an account or is it an address? Why do I need it? What problem or use case does it solve? Why can't I just use a regular account instead?
6 Answers
Is this an account or is it an address?
A PDA is an address with special properties. They are 32-byte strings that look like public keys, but don’t have corresponding private keys.
Why do I need it?
There are two main reasons for PDAs;
- Store program state
- Sign for CPIs
What problem or use case does it solve?
PDA's allow you to create a "hashmap-like" interface for indexing accounts. The seeds used to create the PDAs, function as a way to lookup addresses for a particular piece of data. The seeds can be anything. A pubkey, a string, an array of numbers etc.
Why can't I just use a regular account instead?
You can, but it requires you to explicitly know all the addresses of where data is stored for your program.
Full explanation can be found here
Other resources for understanding PDAs:
https://solanacookbook.com/core-concepts/pdas.html#facts
https://twitter.com/pencilflip/status/1455948263853600768?s=20&t=J2JXCwv395D7MNkX7a9LGw
Program Derived Addresses (PDAs) are used to allow one program (ProgramB
)to assert authority over accounts owned by another (ProgramA
).
Since PDAs can't have private keys (both operationally untenable and algorithmically enforced), they have no way to cryptographically sign for an instruction that requires a signature to prove authority. Instead, ProgramB
calls ProgramA
's instruction via CPI with invoke_signed()
, one parameter of which takes "seed" byte strings, which are hashed with ProgramB
's Program ID to create a PDA which the runtime then promotes to a transaction signer for the duration of that instruction's execution.
An example use of PDAs is the stake pool program which wraps the native stake program to control delegations to the pool member validators, mint pool tokens in exchange for new delegations and burn pool tokens when a user withdraws.
I like the other explanations but I thought I'd provide a more theoretical one.
You could think of a Solana Public key as a 32 byte string. However, that wouldn't be the whole picture. In the ed25519 public-key cryptography system used by Solana, not every 32 byte string has a corresponding private key. We call strings that don't have a corresponding private key "off-curve".
All Solana accounts are indexed by a 32 byte string. If you can provide (via signing) a private key for that string, you can freely modify that account. If we can verify that a given 32 byte string is off-curve (which we can), then we can guarantee that nobody can ever modify the account it points to (not that useful yet).
Now I will identify the issue that PDAs were created to solve: anyone can read the contents of any Solana account (including programs, which are just accounts marked as executable). So any private keys held by a program could be read and used by anyone. That would mean programs couldn't control their own state accounts or sign anything.
To solve this problem, we introduce a new "fake" signing mechanism, where we just say that a program can sign for any address P if if 1) P is off-curve and 2) the program can provide a set of seeds such that those seeds hashed together with the program's own address produces P. We call P a Program Derived Address.
This way, programs don't have to hold private keys. And we can be certain that nobody's sitting on a private key for any PDA.
-
-
OK. That implies, anyone who knows program address and the seeds, can actually modify the accounts owned by the program? program addresses are publicly available data, right? How about the seeds? How secure are they? Commented Feb 5, 2023 at 22:09
-
The seeds are typically also public information. The security mechanism is that the calling program's own address, which can't be controlled, is included in the set of seeds. Commented Feb 6, 2023 at 23:05
A PDA is a Program Derived Address, it's derived from a collection of seeds
and a program id
.
generating
import {PublicKey} from '@solana/web3'
const [pda,pdaBump] = await PublicKey.findProgramAddress(
[wallet.publicKey.toBuffer(),Buffer.from(random.toString())], // seeds
program.programId // programId you're deriving from
);
In this case, I'm using the wallet
public key and random
string as seeds
.
you can generate an infinite number of PDAs for a program
. but using the same seeds
will result in the same PDA address.
usage
A PDA valid transaction signature can only be generated inside the program. this means if a PDA has 5 Sol, only the program can generate a
valid transaction
that sends the Sol (as if the program has theprivate key
, but it doesn't because PDA areoff curve
).A program can have
ownership
over any account. If that account is owned by a PDA, signing atransaction
and using the PDA as asigner
will allow you to control that account.
types
A PDA can be of any type, system account
to send/receive sol (or sign), token account
to send/receive spl tokens, or data account
to store data
for a program or metadata
for an NFT.
example usage (anchor)
pub struct ChangeUsername<'info> {
#[account(mut,seeds=[payer.key().as_ref()], bump)]
pub user: Account<'info, User>,
#[account(mut)]
pub payer: Signer<'info>,
}
I have a struct that contains a username
, and I want each user to be able to change only their username
.
We can have the desired effect by creating a PDA (data account in this case) for each user
with their public key
as seeds. Let's call this User
PDA
When a user wants to change their username
. We check if the passed PDA is valid (matches a PDA generated using the signer
publickey as seeds).
This means if a user2
passes user1
User PDA wishing to change their username
. the transaction will fail since the PDA passed doesn't match the PDA generated inside the program using the user2
(signer) public key as seeds
This constraint is checked implicitly in anchor here:
#[account(mut,seeds=[payer.key().as_ref()], bump)]
If the PDA is valid, the transaction goes through and the program will modify the username
.
You can send money from walletA to an account or walletA to walletB. In Solana every time you send money, someone needs to give permission. A wallet has a user who can give permission to transfer assets.
But what if you need to send money from an account to a wallet? Who is going to give permission? A PDA account is an account that is controlled by the program in order to send funds, not by private keys. Solana has some native programs and those are stored on the chain. They need a way to sign transactions but using a private key is not secure because we have to store this private key on the chain which is public, so everyone can see private keys
Quoting from here:
The secret sauce behind PDAs is that while they look like standard public keys, they don’t actually lie on the ed25519 elliptic curve at all. This means they have no associated private key.
PDAs are derived from a program id and a collection of seeds such as the string "vote_account". This combination of seeds and a program id is then run through a sha256 hash function to see whether or not they generate a public key that lies on the elliptic curve.
It may be helpful to consider that PDAs are not technically created, but rather found. In running our program id and seeds through a hash function, there is a ~50% chance that we actually end up with a valid public key that does lie on the elliptic curve. In this case, we simply add something to fudge our input a little bit and try again. The technical term for this fudge factor is a bump. In Solana, we start with bump = 255 and simply iterate down through bump = 254, bump = 253, etc. until we get an address that is not on the elliptic curve. This may seem rudimentary, but once found it gives us a deterministic way of deriving the same PDA over and over again. This also allows our program to now sign for an account using Solana’s program-signed-accounts
This is an old question, but I'd love to try and give an even simpler answer.
What is a Program Derived Address (PDA) exactly?
It's an address that isn't made from a public key, but instead made from seeds that a program specifies.
- To store your balance of USDC, the token program uses the seeds of your wallet address and the USDC mint
- To store metadata for your NFT, the Metaplex metadata program uses the string
metadata
, and your NFT's mint address - If you want to make your own Solana program, that has some global configuration, you can store that using the seed of just the string
config
- If you want to make your own Solana program, that stores some information that is specific to each user - say a movie review - you might want to use the seeds of the film name, release date and the reviewers wallet address. Inside that account you can store your review's text, and the star rating.
Why do I need it? What problem or use case does it solve? Why can't I just use a regular account instead?
The PDA system is a key/value store - the key is the address, the value is the data (stored as a 'struct') inside. It allows you to store arbitrary data on the blockchain and look it up quickly!
Is this an account or is it an address?
In the real world, people use the term 'PDA' for both. The key is from context:
- "I'll store that in a PDA" means I'll store that data inside an account with a PDA address
- "Let's calculate a PDA based on the seeds" means let's calculate an address based on some seeds