Skip to main content
Sigvex

Anchor Constraint Ordering Remediation

How to fix incorrect an account-validation framework constraint ordering.

Anchor Constraint Ordering Remediation

Overview

Related Detector: Anchor Constraint Ordering

Incorrect constraint ordering enables type confusion and privilege escalation. The fix is to rely on the account-validation framework’s derive macro for automatic ordering, and when implementing manual checks, follow: discriminator, owner, signer, writable, custom.

Before (Vulnerable)

// Manual checks in wrong order
require!(user.is_signer, Unauthorized);
require!(user.owner == &program_id, InvalidOwner);

After (Fixed)

// Let Anchor handle ordering via derive macro
#[derive(Accounts)]
pub struct MyInstruction<'info> {
    #[account(owner = my_program::ID)]  // Owner checked first
    pub data: Account<'info, MyData>,    // Discriminator auto-checked
    pub user: Signer<'info>,             // Signer checked by type
}

Alternative Mitigations

For native programs, enforce the correct manual order explicitly:

// 1. Discriminator
require!(account.data.borrow()[..8] == MyData::DISCRIMINATOR, InvalidDiscriminator);
// 2. Owner
require!(account.owner == program_id, InvalidOwner);
// 3. Signer
require!(account.is_signer, MissingSignature);
// 4. Writable
require!(account.is_writable, AccountNotWritable);
// 5. Custom constraints
require!(data.authority == authority.key(), Unauthorized);

Common Mistakes

Mistake: Relying on Anchor but Adding Manual Checks in Wrong Order

// WRONG: Anchor checks discriminator and owner, but manual check order is wrong
pub fn handler(ctx: Context<MyCtx>) -> Result<()> {
    require!(ctx.accounts.data.amount > 0, ZeroAmount);  // Custom before signer
    require!(ctx.accounts.user.is_signer, Unauthorized);  // Redundant and wrong order

Trust the account-validation framework’s constraint system and avoid redundant manual checks.

References