Skip to content

Program Derived Addresses (Off-Curve Addresses)

On the last three pages you built up a chain of facts: an ed25519 keypair is a private scalar and a public point on a curve; an address is that public point, Base58-encoded; and a signature is a proof that whoever holds the matching private key authorized a message. Every one of those pages assumed a human — or their wallet — holds a key and signs.

This page breaks that assumption on purpose, and it is one of the most important moves in the whole system. We want a program — pure code, with no human behind it — to own accounts and authorize actions for them. But a program has no fingers to guard a private key. So Solana does something that looks like a trick and is really just geometry: it uses addresses for which no private key can possibly exist. Those are Program Derived Addresses (PDAs), and they are the enabler for every on-chain vault, escrow, and mint you have ever used.

The problem: who signs when there is no human?

Section titled “The problem: who signs when there is no human?”

Recall the account model from Part 1 (the account model). State does not live inside a program; it lives in separate accounts that the program owns. A program is the only thing allowed to write an account it owns. That is clean — until you ask a sharper question:

A user signs a transaction with their key to prove they authorized it. When a program needs to move funds out of an account, or update a record it controls, what proves the program authorized it?

You cannot give the program a keypair. A keypair is a secret number; the moment you write that secret into code or config, anyone who reads the source, the binary, or the deploy logs holds the program’s identity and can impersonate it forever. There is no safe place to hide a program’s private key, because a program is public by construction — its bytecode is on-chain for the whole world to read.

So the requirement is precise: we need an address that a program can provably control without any secret existing anywhere. The way you get “control without a secret” is to make sure the secret cannot exist. That is what “off the curve” buys you.

Recall the shape of a normal wallet address from the keypairs page:

private scalar ──(ed25519 scalar multiplication)──► public point on the curve ──► address
d A = d·B

A valid ed25519 public key is a point that lies on the ed25519 curve. Signature verification is defined only for such points — the math literally requires the public key to be a real curve point, because it re-derives one side of the signature equation from it. If a 32-byte value is not a point on the curve, it is not a valid public key, and no private scalar d produces it. There is nothing to solve for.

Here is the key fact: a random 32-byte value has only about a 50% chance of decoding to a valid curve point. Roughly half of all 32-byte strings are simply off the curve — they are perfectly good 32-byte addresses in shape, but no private key maps to them, and none ever can.

A PDA is exactly this: a 32-byte address deliberately chosen to NOT be a valid point on the ed25519 curve.

wallet address Program Derived Address (PDA)
────────────── ─────────────────────────────
ON the ed25519 curve OFF the ed25519 curve
a valid public key NOT a valid public key
HAS a matching private key ⟶ signs NO private key can EVER exist ⟶ program "signs"
the human/wallet is authority the owning program is the sole authority

Same 32-byte shape. Same Base58 look. Opposite guarantee. A wallet address advertises “there is a key behind me, and it can sign for me.” A PDA advertises “there is no key behind me, so the only thing that can act for me is the runtime, on behalf of the program that derived me.”

It is tempting to think the “off the curve” detail is a quirky implementation choice. It is the entire security property. Walk the two cases:

  • If a PDA were on-curve: it would be a valid public key, which means some private key produces it. Whoever found or held that key could sign as the PDA and impersonate the program — draining its vault, minting from its mint, releasing its escrow. The program would not be the sole authority; it would be one of possibly several authorities, and the others are human-held secrets you cannot audit.
  • Because a PDA is off-curve: no private key exists, so no one — not the developer, not the deployer, not an attacker who reads the source — can sign as it. The only mechanism that can authorize an action for a PDA is the runtime itself, and the runtime grants that authority only to the program that owns the PDA’s seeds. Off-curve turns “the program is the sole authority” from a hope into a mathematical guarantee.

That is the payoff in one line: because there is no private key, only the owning program can authorize actions for a PDA. We will see the exact seed-and-bump mechanism that lets a program prove ownership on the next page, Deriving a PDA — Seeds & the Bump. For now, hold the geometric fact: no key exists, so no human can act; the runtime lets the program act instead.

Connecting to the account model: a PDA is just an account

Section titled “Connecting to the account model: a PDA is just an account”

Nothing about a PDA changes what an account is. Go back to Part 1’s definition — an account is lamports + data + owner:

pub struct Account {
pub lamports: u64, // native balance
pub data: Vec<u8>, // opaque bytes the owning program interprets
pub owner: Pubkey, // the program id allowed to mutate this account
}

A PDA is an account like any other. The only thing special is its address: instead of being a random on-curve key some human generated, it is an off-curve address derived deterministically from the owning program’s id and a handful of seeds. That determinism is what makes PDAs the natural home for per-user or per-item program state:

program id + seed "vault" + user's wallet key ──derive──► a specific PDA
───────────────────────────────────────────────── ───────────
"the vault account that belongs to THIS user, under THIS program"
Alice's wallet ──► PDA("vault", Alice) ← Alice's vault, keyless, program-owned
Bob's wallet ──► PDA("vault", Bob) ← Bob's vault, a different keyless address

Because the address is a pure function of the seeds, a program can compute the address of a piece of state rather than store a lookup table of “user → account.” Want Alice’s vault? Hash ["vault", Alice] with the program id. There is no directory to keep in sync, no key to lose, and no way for two users’ state to collide. This is how programs give every user and every item its own deterministic, keyless account.

Under the hood — how the runtime lets a program “sign”

Section titled “Under the hood — how the runtime lets a program “sign””

A PDA has no key, so how does a transaction that spends from a PDA ever get authorized? The answer is a special runtime instruction (in real Solana, invoke_signed) used during a cross-program invocation (CPI) — one program calling another.

When program P wants to act as its PDA, it does not produce a signature. Instead it hands the runtime the seeds it used to derive the PDA. The runtime re-derives the address from those seeds plus the calling program’s own id, checks that the result equals the PDA in question, and — if it matches — marks that account as a signer for the duration of the call.

program P ──invoke_signed(inner_ix, seeds=["vault", Alice, bump])──► runtime
re-derive PDA from seeds ++ P's program id ─────────┤
matches the target account? ── yes ──► mark it a signer, proceed
── no ──► reject the CPI

The seeds include the program’s own id, so only program P can produce a valid invoke_signed for a PDA derived under P. No other program can forge it — its id would be different, and the re-derivation would land on a different address. This is “program-derived authority”: the program proves control not with a secret, but by demonstrating it knows the seeds that produce the address under its own id. The seed-and-bump details are the next page; the point here is that “signing” for a PDA is re-derivation, not cryptography.

The mental model: a program that can hold funds and act

Section titled “The mental model: a program that can hold funds and act”

Put the pieces together and a PDA promotes a program from “code that mutates accounts” to a first-class entity on the ledger:

  • It can hold funds — a PDA account has lamports, and only the owning program can move them.
  • It can hold state — a PDA account’s data is the natural place for per-user or per-item records.
  • It can authorize CPIs — via invoke_signed, the program acts as the PDA to call other programs.

And it does all of this with no human-held key anywhere. That is precisely what a vault, an escrow, or a token mint needs: an owner that is a rule, not a person. An escrow PDA holds Alice’s deposit and releases it only when the program’s logic says so — there is no admin key that can override it, because there is no key at all. A mint’s authority can be a PDA, so new tokens can be created only through the program’s minting instruction, never by a leaked admin secret. Keyless ownership is what makes “the code is the only authority” true rather than aspirational.

This is the direct answer to the book’s throughline — how do you build a single global state machine that runs at hardware speed without falling apart? PDAs let autonomous programs be full participants in that state machine: they can own value and enforce rules without a trusted human in the loop, and the “without falling apart” part is guaranteed by geometry, not by hoping a key stays secret.

  • Why does it exist? Because a program has no fingers to guard a private key, yet we need programs to own accounts and authorize actions for them. A PDA is an address with no private key, so a program can control it without any secret existing to leak.
  • What problem does it solve? “Who signs when there is no human?” It gives autonomous code a way to own funds and state and to invoke_signed for CPIs, so vaults, escrows, and mints can be governed by rules instead of by a human-held admin key.
  • What are the trade-offs? You gain keyless, deterministic, program-controlled accounts — but authority now rests entirely on the program’s own seed validation. Forget to re-derive and check an account against its seeds and you reopen the door (see the Wormhole incident). The safety moves from “guard a secret” to “always validate the account.”
  • When should I avoid it? When a human should be the authority. If the intended owner is a person or a wallet — something that should sign for itself — use a normal on-curve keypair. A PDA has no key precisely so that no human can act for it; that is wrong when a human should.
  • What breaks if I remove it? Programs lose the ability to own value or authorize actions autonomously. Every construct that depends on “the code is the only owner” — on-chain vaults, escrows, program-controlled mints, per-user state accounts — collapses back to needing a human-held key, which is unauditable and impossible to secure for public code.
  1. In one sentence, what makes an address a PDA rather than a wallet address, in terms of the ed25519 curve?
  2. Why is “off the curve” a security property and not just an implementation detail? Walk through what would go wrong if a PDA were on-curve.
  3. A random 32-byte value has about a 50% chance of being a valid public key. How does that fact relate to how a keyless PDA gets found?
  4. A PDA has no private key, so how does a program ever authorize an action — like moving funds — for one of its PDAs?
  5. Give a concrete use case (vault, escrow, or mint) and explain what specifically about a PDA makes it the right tool, versus using a normal keypair.
Show answers
  1. A wallet address is a valid point on the ed25519 curve, so a matching private key exists and can sign for it. A PDA is a 32-byte address deliberately chosen to lie off the curve, so it is not a valid public key and no private key can ever exist for it.
  2. If a PDA were on-curve it would be a valid public key, meaning some private key produces it — and whoever held that key could sign as the PDA and impersonate the program (draining its vault, minting from its mint). Because it is off-curve, no such key exists, so the only thing that can authorize an action for the PDA is the runtime acting on behalf of the owning program. Off-curve turns “the program is the sole authority” into a guarantee.
  3. Deriving a PDA hashes the seeds and program id into 32 bytes, which land on the curve ~50% of the time. An on-curve result would be a valid key (the thing we must avoid), so the derivation appends a bump byte and re-hashes until it lands off the curve. Because each try has ~50% odds, it usually succeeds in one or two attempts, and the process is deterministic so everyone re-derives the same address.
  4. The program does not sign — it uses invoke_signed during a CPI, handing the runtime the seeds it used to derive the PDA. The runtime re-derives the address from those seeds plus the calling program’s own id; if it matches the target account, the runtime marks that account as a signer for the call. Only the program whose id is in the seeds can produce a valid derivation, so only it can act as its PDA.
  5. Example — an escrow: a PDA holds Alice’s deposit and the program releases it only when its logic permits. Because the PDA has no key, there is no admin secret that could override the rules or be leaked to drain the funds; the code is the sole authority. A normal keypair would reintroduce a human-held secret that could sign around the program’s logic, which is exactly what you do not want for funds governed by a rule. (Vault and mint answers are analogous: keyless ownership makes “the program is the only authority” true.)