✳ SOLANA ACADEMYCourse overviewGlossary

Intermediate · LESSON 07 OF 12

7. Program-derived addresses

By the end, you can design deterministic state addresses without inventing private keys.

Four ordered, bounded badge seeds combine with the program ID and canonical bump to derive an address; derivation does not initialize or authorize it.
One level-and-revision badge, one deterministic address. Four ordered, bounded badge seeds combine with the program ID and canonical bump to derive an address; derivation does not initialize or authorize it.
Without field boundaries, the seed tuples ab plus cd and abcd become the same byte string before hashing. Fixed-width or length-prefixed fields preserve the tuple. Multiple bumps may create valid off-curve addresses for the same application seeds, so on-chain logic should validate the canonical bump and expected account relation.
Avoid PDA seed collisions and validate the canonical bump. Without field boundaries, the seed tuples ab plus cd and abcd become the same byte string before hashing. Fixed-width or length-prefixed fields preserve the tuple. Multiple bumps may create valid off-curve addresses for the same application seeds, so on-chain logic should validate the canonical bump and expected account relation.
Four fixed-width badge seeds are ordered before the canonical bump and program identifier determine the derived address.
The badge PDA's exact byte schema. Four fixed-width badge seeds are ordered before the canonical bump and program identifier determine the derived address.
A derived address crosses into a signed instruction, where program checks and issuer eligibility precede funded account creation and one-time state commit.
From a derived PDA to an authorized account. A derived address crosses into a signed instruction, where program checks and issuer eligibility precede funded account creation and one-time state commit.

Read the workflow

The application seed sequence is the fixed UTF-8 domain badge/v1, a 32-byte learner public key, a one-byte level enum, and a 32-byte issuer-published curriculum digest. The derivation routine appends the canonical bump as a fifth slice, and the program ID participates in the address. Every field has a fixed width, so the tuple boundaries are unambiguous. The resulting address is public, has no private key, and does not create an account or prove that an exam was passed.

  1. BADGE/V1 · FIXED DOMAIN
  2. LEARNER PUBLIC KEY · 32 BYTES
  3. LEVEL ENUM · ONE BYTE
  4. REVISION DIGEST · 32 BYTES

Read a text version: The badge PDA's exact byte schema

The first seed is the fixed eight-byte UTF-8 domain badge/v1. The next seed is the learner public key in its 32-byte form. A one-byte enum identifies Basic, Intermediate, or Expert, and a 32-byte SHA-256 digest identifies the issuer-published curriculum revision. These four ordered application seeds plus the appended canonical bump use five seed slices total. The program ID is also part of derivation. The address is public and proves neither account initialization nor course completion.

  1. badge/v1 · fixed domain · 8 bytes
  2. Learner public key · 32 bytes
  3. Level enum · 1 byte · values 1–3
  4. Revision SHA-256 digest · 32 bytes

Read a text version: From a derived PDA to an authorized account

A client derives the public address locally, which creates no account and needs no wallet approval. The learner signs an instruction; that proves control of the wallet, not exam completion. The program checks the canonical seeds and bump, the expected account type and ownership, the learner binding, the approved level and revision, and one-time eligibility. Anchor init creates the PDA through a System Program call using the program's PDA signer. A designated mutable signer pays the account's minimum storage balance, and the program owns the account data; the payer can differ from the learner. Storage and network fees are separate. A failed instruction leaves no new claim account.

  1. Client derives the address
  2. Learner signs an instruction
  3. Program validates canonical seeds
  4. Verify issuer eligibility and revision
  5. Initialize with payer and space
  6. Commit the claim once

FIELD NOTE 01

What a PDA proves—and what it does not

A program-derived address (PDA) is a deterministic 32-byte address derived from an ordered seed list, a program ID, and a bump. Solana rejects an on-curve result, so no Ed25519 private key exists for the accepted PDA. The program whose ID participates in derivation may ask the runtime to sign for it during a valid cross-program invocation (CPI). A PDA is not secret, does not automatically exist as an account, and does not prove that an assessment passed.

FIELD NOTE 02

Freeze an application seed schema

Seeds are public byte strings in a fixed order. Treat their layout like a versioned program interface: changing the domain, order, encoding, or program ID changes the resulting address. Solana permits at most 16 seed slices per derivation and 32 bytes per slice; the bump is appended as a seed and counts toward the limit. Keep a written byte schema beside the program and client so both derive the same address.

FIELD NOTE 03

Worked badge schema: four ordered fields

This lesson and its downloadable workbook use one illustrative schema: seed 1 is the fixed UTF-8 domain `badge/v1` (8 bytes); seed 2 is the learner public key (32 bytes); seed 3 is a level enum (one byte: 1 Basic, 2 Intermediate, 3 Expert); seed 4 is the curriculum release digest (32 bytes from SHA-256). The derivation routine appends the canonical bump as seed 5, so this schema uses five of the sixteen available slices. The issuer, not the learner, must publish the digest and connect it to the exact curriculum revision and passed assessment. This is a teaching schema, not a deployed Academy issuer contract.

FIELD NOTE 04

Make field boundaries unambiguous

PDA derivation concatenates seed bytes; it does not add field lengths for you. The variable tuples ["ab", "cd"] and ["abcd"] therefore supply the same combined bytes. The offline workbook now derives both tuples: without framing they produce the same PDA, while one-byte length prefixes make the encoded tuples differ. Use fixed-width fields, explicit length prefixes, or a reviewed injective encoding whenever fields can vary in size. The badge schema above has fixed-width wallet, level, and digest fields plus a fixed domain, so its field boundaries are determined by the schema. Do not silently truncate an oversized value or hash arbitrary text and assume that makes its original boundaries safe.

FIELD NOTE 05

Use the canonical bump consistently

The SDK searches bump values and returns the first one that yields an off-curve address; that is the canonical bump. Other bump values can produce other valid off-curve addresses for the same application fields. If a program accepts them indiscriminately, one logical claim may have multiple addresses. Re-derive and enforce the canonical address on chain. In Anchor, `seeds = [...]` with a bare `bump` performs the canonical check; a bump remains public data, not a secret or authorization.

FIELD NOTE 06

Separate address calculation from account creation

A client derives the address locally; no account exists yet and no wallet approval or SOL is needed for that calculation. The wallet signs an instruction that supplies the learner identity and candidate PDA. Anchor's `#[account(init, payer = ..., space = ...)]` creates a new account through a System Program CPI; with `seeds` and canonical `bump`, the program authorizes its PDA address during that CPI. The payer must be a mutable signer that funds the account's minimum storage balance. The account's data owner is the program, while the record can store the learner as its separate authority. A payer can differ from the learner. Account storage and the transaction network fee are distinct costs. A failed transaction does not leave the new account behind, though a submitted failure can still cost its network fee.

FIELD NOTE 07

Prove claim eligibility before one-time initialization

A derived address, learner signature, existing account owner, or `has_one` field alone does not prove an exam was passed. An issuer must connect the wallet, level, and approved curriculum revision to an explicit eligibility proof and enforce its single-use rule before the state transition. For an off-chain exam, that proof needs an authenticated issuer mechanism or other trusted program state; a client-supplied `passed = true` flag is not evidence. `init` naturally rejects an already-created PDA, but `init_if_needed` has both create and existing-account paths and must repeat authorization and preserve invariants on each path. Keep email, answers, and scores off chain. The sample constraints are teaching material, not a deployed Academy issuer.

FIELD NOTE 08

Public addresses do not protect learner privacy

Anyone who knows the program ID and seed values can derive the PDA, and public account data can be enumerated. Hashing or deriving an address does not conceal a learner, course, or revision when those inputs are public or guessable. The wallet and credential level are public when a learner mints; email, legal name, exam score, and answer choices must stay off chain. Do not put sensitive identifiers in seeds or present an opaque-looking address as privacy.

FIELD NOTE 09

Test identity, boundaries, and failure paths

Before trusting a schema, test the same inputs twice; vary learner, level, revision, and program ID one at a time; reject malformed addresses and digests; and prove an unapproved revision cannot be issued. The workbook's boundary demo is an offline cryptographic derivation fixture, not a validator: it proves that two ambiguous tuples feed the same bytes to derivation, then checks that explicit framing separates them. At program level, test wrong seeds, a noncanonical bump, the wrong stored learner, wrong account owner or type, duplicate initialization, a failed assessment, unauthorized changes, and every existing-account reinitialization path. The workbook cannot prove that a deployed program enforces these rules.

MAKE THE CONNECTION

A detail worth keeping

Every seed scheme has implications for uniqueness, privacy, and migration. Public keys and clear course labels in seeds can reveal relationships through account enumeration. Prefer deterministic but intentionally designed seed inputs, and do not mistake hashing public inputs for confidentiality.

Work through an example

A unique claim address derived from the badge/v1 domain, learner, one-byte level, and revision digest distinguishes level and curriculum records. Define retake and renewal policy before changing the digest: a new revision namespace must not accidentally grant another credential when the learner did not meet that revision's assessment requirements.

Illustrative Anchor account checks for a wallet-, level-, and revision-scoped badge. The issuer must separately authorize the revision and passed assessment; the omitted handler does not create an account or implement issuance.
#[derive(Accounts)]
#[instruction(level: u8, revision: [u8; 32])]
pub struct ReadBadge<'info> {
    pub learner: Signer<'info>,
    #[account(
        seeds = [b"badge/v1", learner.key().as_ref(), &[level], revision.as_ref()],
        bump,
        has_one = learner,
        constraint = level >= 1 && level <= 3 @ AcademyError::UnsupportedLevel,
        constraint = badge.level == level,
        constraint = badge.revision == revision @ AcademyError::RevisionMismatch
    )]
    pub badge: Account<'info, BadgeRecord>,
}

// Also require `revision` to match the issuer's published release and confirm
// a passed assessment before any one-time credential initialization.

Write the byte schema for the illustrative badge PDA: badge/v1, a 32-byte learner key, one-byte Basic/Intermediate/Expert enum, a 32-byte issuer-published curriculum digest, then the canonical bump. Run `npm run compare-seeds` in the offline workbook. Explain why fictional fields ["ab", "cd"] and ["abcd"] collide when concatenated and how explicit length prefixes separate them. Count every slice against the 16-seed maximum and enforce each slice's 32-byte limit. Then map client derivation, wallet approval, issuer eligibility, canonical PDA validation, payer, account initialization, and stored data ownership. State which facts can be checked locally and which require program enforcement. Use no real learner data.

GUIDED PRACTICE · INTERMEDIATE

Do the work, one step at a time

Use fictional addresses and disposable test data. Do not enter a recovery phrase or private key. Unless a lesson explicitly says “read-only Devnet,” use paper or a local test and do not connect to a public network.

  1. Download and unzip PDA workbook 01; compare its four ordered badge seeds with the byte-schema diagram.
  2. Run npm ci with Node.js 20 or later to install the pinned Solana Kit dependency.
  3. Run npm run compare-seeds and inspect the identical unframed bytes and addresses beside the different length-prefixed seed encodings.
  4. Run npm test; explain how the tests check the collision, deterministic derivation, and learner, level, revision, and program separation.
  5. Derive a sample PDA with public addresses, one of the three levels, and a 64-character SHA-256 revision digest. Use only a published course digest in a real system.
  6. Try an unsupported level, malformed address, and malformed revision digest; explain why a derived address still needs program-side account, eligibility, initialization, and assessment checks.

Before you move on, check your reasoning

  • Specify exact seed order and byte encoding.
  • Check the program-derived address for two fictional learners.
  • Explain why knowing a claim address does not authorize changing it.

OPEN SELF-CHECK · UNGRADED

Pause and test your reasoning

Practice 1 of 3

Several bumps produce viable off-curve addresses for the same seeds. Which address should a canonical PDA design use?

  1. The canonical bump returned by the standard derivation for those seeds and program ID.
  2. Any bump supplied by an untrusted caller, because every off-curve result is interchangeable.
  3. A random on-curve public key stored beside the PDA.
Reveal the answer and explanation

One best answer: The canonical bump returned by the standard derivation for those seeds and program ID.

A PDA is derived from seeds, a program ID, and a bump that yields an off-curve address. Use the canonical derivation consistently and validate it on-chain; accepting arbitrary alternatives can create multiple addresses for one logical record.

Sources: Program Derived Addresses (PDAs) ↗ · Program Derived Address with Anchor ↗

Practice 2 of 3

A learner computes a PDA from public seeds. What security property should they not assume?

  1. A PDA is secret because it has no public keypair, so only its program can reveal the address.
  2. A PDA is public and has no private key.
  3. Anyone who derives a PDA can sign as the program without invoking that program.
Reveal the answer and explanation

One best answer: A PDA is public and has no private key.

PDA derivation provides deterministic addressing, not secrecy or blanket permission. The owning program can sign for its PDA during a CPI only after validating the intended instruction and seeds.

Sources: Program Derived Addresses (PDAs) ↗ · PDA Derivation ↗

Practice 3 of 3

After a browser derives the canonical badge PDA, what has happened on chain?

  1. The program account itself is initialized with the badge fields.
  2. The account exists under the program ID, but it needs another transaction before its data is visible to RPC.
  3. No account is created by derivation alone; a checked instruction must initialize it.
Reveal the answer and explanation

One best answer: No account is created by derivation alone; a checked instruction must initialize it.

PDA derivation is local address calculation. Anchor `init` creates a new account during an on-chain System Program call; the program validates seeds and eligibility, while a mutable payer funds storage. The program owns account data even when the record stores a learner authority. The learner signature alone does not prove that an exam was passed.

Sources: Program Derived Addresses (PDAs) ↗ · Program Derived Address with Anchor ↗ · Anchor Account Constraints ↗

This practice is not scored, saved, or part of a certificate. Account-based lesson checkpoints and level exams remain separate.

Scored checkpoints and level exams are available online through a free account. They are not included in this pack.

Optional lab files

These files are included with the pack. Follow the safety and toolchain notes in the lesson before running a lab; a Devnet read requires an internet connection.

READ THE SOURCE

Go deeper in the official docs

Reviewed on 2026-10-03. Protocol documentation can change; check the linked page’s current version when you reconnect.