Skip to main content

ProgramDerivedAddress Interface

software.sava.core.accounts.ProgramDerivedAddress Represents a Program Derived Address (PDA) - a public key derived from a program ID and seeds, guaranteed to be off the Ed25519 curve.

What is a PDA?

Program Derived Addresses are deterministic account addresses that:
  • Are derived from a program ID and a set of seeds
  • Are guaranteed to be off the Ed25519 curve (no private key exists)
  • Can only be signed by the program that derives them
  • Use a nonce (bump seed) to find a valid off-curve address

Factory Methods

Create PDA

static ProgramDerivedAddress createPDA(
    PublicKey publicKey,
    int nonce
)
publicKey
PublicKey
required
The derived public key
nonce
int
required
The bump seed (nonce) used to derive the address
return
ProgramDerivedAddress
PDA without seeds (seeds are null)
static ProgramDerivedAddress createPDA(
    List<byte[]> seeds,
    PublicKey publicKey,
    int nonce
)
seeds
List<byte[]>
required
List of seed byte arrays used to derive the address
publicKey
PublicKey
required
The derived public key
nonce
int
required
The bump seed (0-255)

Methods

List<byte[]> seeds()
return
List<byte[]>
The seeds used to derive this PDA (may be null)
PublicKey publicKey()
return
PublicKey
The derived public key
int nonce()
return
int
The bump seed (nonce) value (0-255)

Finding PDAs

Use PublicKey.findProgramAddress() to find a PDA:
static ProgramDerivedAddress findProgramAddress(
    List<byte[]> seeds,
    PublicKey programId
)
seeds
List<byte[]>
required
List of seed byte arrays (max 16 seeds, each max 32 bytes)
programId
PublicKey
required
Program ID that will own the derived address
return
ProgramDerivedAddress
PDA with public key and nonce. The nonce starts at 255 and decrements until a valid off-curve address is found.

Creating Program Addresses

Use PublicKey.createProgramAddress() to create an address with a specific nonce:
static PublicKey createProgramAddress(
    List<byte[]> seeds,
    PublicKey programId
)
seeds
List<byte[]>
required
List of seed byte arrays including the nonce as the last seed
programId
PublicKey
required
Program ID
return
PublicKey
Derived public key if off-curve, null if on-curve

Example Usage

import software.sava.core.accounts.PublicKey;
import software.sava.core.accounts.ProgramDerivedAddress;
import java.util.List;

// Define program ID
var programId = PublicKey.fromBase58Encoded(
    "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
);

// Define seeds
var seeds = List.of(
    "metadata".getBytes(),
    programId.toByteArray(),
    mintAddress.toByteArray()
);

// Find PDA (automatically finds valid nonce)
var pda = PublicKey.findProgramAddress(seeds, programId);

// Access derived address
var metadataAddress = pda.publicKey();
var nonce = pda.nonce();

System.out.println("Metadata PDA: " + metadataAddress.toBase58());
System.out.println("Nonce: " + nonce);

Create PDA with Known Nonce

import software.sava.core.accounts.PublicKey;
import java.util.List;

// Create seeds with known nonce
var seeds = List.of(
    "vault".getBytes(),
    authority.toByteArray(),
    new byte[]{(byte) 254}  // nonce = 254
);

// Create program address
var vaultAddress = PublicKey.createProgramAddress(seeds, programId);

if (vaultAddress != null) {
    System.out.println("Vault: " + vaultAddress.toBase58());
} else {
    System.out.println("Seeds resulted in on-curve address");
}

Common PDA Patterns

Associated Token Account
var seeds = List.of(
    wallet.toByteArray(),
    tokenProgramId.toByteArray(),
    mintAddress.toByteArray()
);
var pda = PublicKey.findProgramAddress(seeds, associatedTokenProgramId);
var ataAddress = pda.publicKey();
Token Metadata
var seeds = List.of(
    "metadata".getBytes(),
    metadataProgramId.toByteArray(),
    mint.toByteArray()
);
var pda = PublicKey.findProgramAddress(seeds, metadataProgramId);
var metadataAddress = pda.publicKey();
Master Edition
var seeds = List.of(
    "metadata".getBytes(),
    metadataProgramId.toByteArray(),
    mint.toByteArray(),
    "edition".getBytes()
);
var pda = PublicKey.findProgramAddress(seeds, metadataProgramId);
var editionAddress = pda.publicKey();
Custom Program PDA
var seeds = List.of(
    "state".getBytes(),
    user.toByteArray(),
    ByteBuffer.allocate(8).putLong(id).array()
);
var pda = PublicKey.findProgramAddress(seeds, myProgramId);
var stateAddress = pda.publicKey();

Seed Constraints

  • Maximum 16 seeds per PDA
  • Each seed maximum 32 bytes
  • Seeds are concatenated with program ID and nonce for derivation
  • Common seed types:
    • String literals as bytes
    • Public key bytes
    • Integer/long as bytes (little-endian)
    • Custom identifiers

PDA Derivation Process

  1. Concatenate all seeds + program ID + nonce
  2. Hash with SHA-256
  3. Check if result is off Ed25519 curve
  4. If on curve, decrement nonce and retry
  5. Return first valid off-curve address

Best Practices

  • Use findProgramAddress() instead of manually testing nonces
  • Cache nonces for frequently-used PDAs
  • Use descriptive string literals as seeds for readability
  • Keep seeds consistent across program invocations
  • Document PDA derivation logic for your programs

Build docs developers (and LLMs) love