Solana accounts need validation before use. Let's implement the patterns you'll use every day!
A discriminator is a unique byte (or bytes) that identifies account type:
const TOKEN_ACCOUNT_DISCRIMINATOR: u8 = 1;
const METADATA_ACCOUNT_DISCRIMINATOR: u8 = 2;
#[repr(C)]
struct TokenAccount {
discriminator: u8, // What type of account is this?
is_initialized: bool, // Has it been set up?
owner: [u8; 32], // Who owns it?
balance: u64, // The data
}Anchor uses 8-byte discriminators derived from the account name hash. Native programs often use 1-byte discriminators.
impl TokenAccount {
fn validate(&self) -> Result<(), &'static str> {
if self.discriminator != TOKEN_ACCOUNT_DISCRIMINATOR {
return Err("Invalid discriminator");
}
if !self.is_initialized {
return Err("Account not initialized");
}
Ok(())
}
fn is_owner(&self, pubkey: &[u8; 32]) -> bool {
self.owner == *pubkey
}
}impl TokenAccount {
fn new(owner: [u8; 32], balance: u64) -> Self {
Self {
discriminator: TOKEN_ACCOUNT_DISCRIMINATOR,
is_initialized: true,
owner,
balance,
}
}
}| Check | Purpose |
|---|---|
| Discriminator | Correct account type |
| is_initialized | Account has been set up |
| Owner match | Caller has authority |
| Signer | Transaction was signed by key |
Implement for TokenAccount:
new(owner, balance) - creates initialized account with discriminatorvalidate() - checks discriminator and is_initializedis_owner(pubkey) - checks if pubkey matches ownerdiscriminator = 1 and is_initialized = truevalidate() returns Ok(()) for valid accountsis_owner() compares byte arraysfn new(owner: [u8; 32], balance: u64) -> Self {
Self {
discriminator: ACCOUNT_DISCRIMINATOR,
is_initialized: true,
owner,
balance,
}
}
fn validate(&self) -> Result<(), &'static str> {
if self.discriminator != ACCOUNT_DISCRIMINATOR {
return Err("Invalid discriminator");
}
if !self.is_initialized {
return Err("Not initialized");
}
Ok(())
}
fn is_owner(&self, pubkey: &[u8; 32]) -> bool {
self.owner == *pubkey
}