The name is pretty self-explanatoryβa PDA is an address derived from a program.
The address points to a data account, which can hold state.
3/ A quick note about terminologyβin this thread, I'll use "PDA" to mean "program derived address", and "PDA account" to mean "an account whose address is a PDA."
There's a difference between an account and an address. Addresses point to accounts, and accounts store data.
4/ How does a PDA get derived?
There's a function called findProgramAddress that spits out a PDA based on two inputs: a program ID and a list of seeds.
The program ID is the address of some Solana program. The list of seeds is used to differentiate that program's PDAs.
5/ There are two main use cases of PDAs:
- Storing a program's state
- Signing cross-program invocations (CPIs)
Let's start with the first one.
6/ Say you want to write a Solana program that lets anyone increment a counter. The only thing you want to store is the current count.
This is pretty straightforward. You just need an "increment" instruction that takes a single account and increments its "count" property.
7/ There's just one small problem... how do you know what account to pass to the "increment" instruction?
Another way to think about thisβlet's say we come across this program, and want to write a frontend that displays the count. How do we know which account to fetch?
8/ Here's one way to fix itβjust add a comment to the code!
Now, everyone knows what account to fetch in order to read the program's state.
Great, problem solved.
9/ This technically works, but there's a better solutionβPDAs!
Instead of storing the count in some arbitrary account, we can store it in a PDA account.
Then, in code, we can define the PDA's seeds, making it so that anyone can derive the PDA.
10/ Anchor gives us a simple way to do this.
The code below just says "I'm expecting an account whose address is a PDA derived from the program ID and a seed of 'counter.'"
Then, any client or frontend can use findProgramAddress to derive the PDA.
11/ In this simple example, it may not seem necessary to use a PDA.
But what if we want to keep track of how many times each user has incremented the count?
PDAs are a great way to store this kind of mappingβyou just need a new PDA account for each user.
12/ Alright, we've covered the first use case!
To summarize, when you're writing a Solana program, it can be useful to store the program's state in PDA accounts.
This makes it easy for anyone reading or interacting with the code to derive the relevant account addresses.
13/ Let's move onto the next use case of PDAsβcross-program invocations.
This use case is a little trickier. However, if you can understand this, you'll have gotten over the main hurdles of learning Solana dev!
14/ First, let's clear up some terminology.
A CPI is just when a program calls another program.
Put another wayβtypically, a user sends instructions to a program (e.g. hey program, swap tokenA for tokenB). A CPI is when a program sends an instruction to another program.
15/ In order to explain PDAs and CPIs, we're going to talk about the SPL Token Swap program.
Simply put, this program lets you swap some tokenA for some tokenB.
17/ Here's a general outline of how the swap program works.
First, you send an instruction to the swap program, telling it to swap tokenA for tokenB.
18/ Next, the swap program calls the token program (a CPI) and tells it to transfer tokenA from your tokenA account to the program's tokenA account
19/ Finally, the swap program calls the token program again (a CPI) and tells it to transfer tokenB from the program's tokenB account to your tokenB account.
That's it! You've swapped some amount of tokenA for some amount of tokenB.
20/ Let's examine that last step a little more closely...
In order to transfer tokens from one account to another, you must be the owner of the sending account.
This prevents me from transferring tokens from your token account to mine (it prevents me from stealing your tokens).
21/ That raises the questionβwho owns the program's tokenB account?
The answer: a PDA!
Specifically, a PDA derived from the swap program's address.
22/ Why is the owner a PDA?
In short, it gives the swap program, and ONLY the swap program, the authority to transfer tokens from the tokenB account.
23/ How does this work?
When the swap program tells the token program to transfer tokens from the tokenB account, the owner of the tokenB account must sign the instruction.
Otherwise, the instruction will fail.
24/ Usually, in order to sign for an account, you must use the account's private key.
However, by definition, a PDA doesn't have an associated private key.
Only the program that created the PDA can sign for it by including the PDA's seeds when invoking an instruction.
25/ Here's what this looks like in code (this is from the swap program).
This looks complicated, but the main thing to note is that the PDA's seeds (authority_signature_seeds in code) are passed to invoke_signed.
In effect, this signs the instruction for the PDA.
26/ Why can't another program also do this?
Well, because the code makes sure the following invariant is true. If another program tries, the PDA will be different (because the program ID is different) and the invariant will fail.
27/ Let's put it all together.
The owner of the program's tokenB account is a PDA derived from the swap program's address.
When the swap program calls the token program to transfer tokens from its tokenB account, it signs for the PDA. This authorizes the transfer.
28/ Whew, that was quite complicated π΅βπ«. Here's a high level recap:
- A CPI is when one program calls another program
- A program can sign an instruction using a PDA's seeds
- The SPL token swap program is a good example of this
29/ Before we wrap up, let's quickly cover two more topics:
- How are PDA accounts created?
- Does the program that a PDA is derived from always own the PDA?
30/ Creating a normal account (an account associated with a keypair) can be done entirely with client-side code (using @solana/web3.js).
You just create an instruction with SystemProgram.createAccount, add it to a transaction, and send the transaction.
31/ Creating a PDA account can ONLY be done by a program. You cannot create a PDA account with just client-side code!
Specifically, if a PDA is derived from a certain program's ID, only that program can create the PDA account.
Here's what the Rust code looks like π
32/ Note that the client-side code we saw before doesn't create a PDA account, it just finds an address that is a valid PDA.
An account may or may not exist at that address.
33/ Why can only Solana programs create PDA accounts?
Because otherwise it would be possible for bad actors to create PDA accounts for any program, and fill them with arbitrary data.
34/ One last thing! It's important to know that the program a PDA is derived from is not necessarily the PDA account's owner.
The program a PDA is derived from can sign for the PDA account.
The program that owns a PDA account can modify its state.
We're starting with a mushroom-inspired NFT collection (b/c mushrooms are awesome), but our larger goal is to help onboard more artists and creators into web3.
Right now, getting into web3 as an artist is hard π΅βπ«.
Which blockchain should you choose? Which marketplace should you list on? How do you make an NFT collection? How do you airdrop NFTs to people? How do you market yourself?
We're building a community that cares about helping artists. With that community, we'll create tools and resources that make it easy for any artist or creator to get started with web3.
Data accounts store data. Program accounts store executable programs.
Each account has an address (usually a public key) and an owner (address of a program account). There are a few more fields every account stores, see π
2/ There are a few important ownership rules:
- Only a data account's owner can modify its data and subtract lamports.
- Anyone is allowed to give lamports to a data account.
- The owner of an account may assign a new owner if the account's data is zeroed out.
2/ Anchor (by @ProjectSerum) is a framework that will make your life much easier.
There are three parts to Anchor:
- A TypeScript library that's similar to web3.js
- Rust crates that make writing Solana programs easier
- A CLI for building/testing, similar to @HardhatHQ