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
)
The bump seed (nonce) used to derive the address
PDA without seeds (seeds are null)
static ProgramDerivedAddress createPDA(
List<byte[]> seeds,
PublicKey publicKey,
int nonce
)
List of seed byte arrays used to derive the address
Methods
The seeds used to derive this PDA (may be null)
The bump seed (nonce) value (0-255)
Finding PDAs
Use PublicKey.findProgramAddress() to find a PDA:
static ProgramDerivedAddress findProgramAddress(
List<byte[]> seeds,
PublicKey programId
)
List of seed byte arrays (max 16 seeds, each max 32 bytes)
Program ID that will own the derived address
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
)
List of seed byte arrays including the nonce as the last seed
Derived public key if off-curve, null if on-curve
Example Usage
Find PDA (Recommended)
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
- Concatenate all seeds + program ID + nonce
- Hash with SHA-256
- Check if result is off Ed25519 curve
- If on curve, decrement nonce and retry
- 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