F-2026-0002·account-validation

Missing token account constraints leads to potential mint confusion and unauthorized token transfers

Fixedsolanavaulted25519
TL;DR

ProcessWithdrawal token accounts lack ownership and mint constraints, enabling confusion attacks where attackers manipulate inputs to bypass mint restrictions.

Severity
MEDIUM
Impact
MEDIUM
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
MEDIUM
Exploitability
MEDIUM
02Section · Description

Description

The ProcessWithdrawal instruction context fails to validate critical properties of the token accounts used in the withdrawal operation. Specifically, vault_token_account and recipient_token_account lack constraints to verify:

  1. Vault token account ownership: No validation that vault_token_account is owned by vault_authority.
  2. Mint matching: No validation that either token account matches the expected vault.token_mint.

Current implementation:

rust
#[account(mut)]
pub vault_token_account: Account<'info, TokenAccount>,
#[account(mut)]
pub recipient_token_account: Account<'info, TokenAccount>,

The only validation performed is a runtime check at line 151 that recipient_token_account.owner matches the authorized recipient, but this does not validate the mint or the vault account's ownership.

03Section · Impact

Impact

Attackers can potentially manipulate token account inputs to withdraw wrong tokens or bypass mint restrictions. While the SPL Token Program provides some protection by verifying authority signatures, the lack of mint and ownership validation creates opportunities for confusion attacks and could allow transfers of unintended token types. This is particularly critical given that vault.token_mint is stored but never enforced, making the entire mint tracking system ineffective.

04Section · Recommendation

Recommendation

Add explicit constraints to the vault_token_account and recipient_token_account in ProcessWithdrawal:

rust
// vault_token_account: Add ownership and mint validation
#[account(
mut,
constraint = vault_token_account.owner == vault_authority.key() @ ErrorCode::InvalidVaultAccount,
constraint = vault_token_account.mint == vault.token_mint @ ErrorCode::InvalidMint
)]
pub vault_token_account: Account<'info, TokenAccount>,
// recipient_token_account: Add mint validation
#[account(
mut,
constraint = recipient_token_account.mint == vault.token_mint @ ErrorCode::MintMismatch
)]
pub recipient_token_account: Account<'info, TokenAccount>,

Additionally, ensure vault.token_mint is properly validated during initialization:

rust
#[account(
constraint = token_mint.owner == &spl_token::ID @ ErrorCode::InvalidMint
)]
pub token_mint: Account<'info, Mint>,

Define the new error codes:

rust
#[msg("Token account not owned by vault authority")]
InvalidVaultAccount,
#[msg("Token mint does not match vault mint")]
InvalidMint,
#[msg("Recipient token mint does not match vault mint")]
MintMismatch,
05Section · Resolution

Resolution

Fair Casino: Fixed.

Zealynx: Verified.

Status
Fixed
F-2026-0002

oog
zealynx

Smart Contract Security Digest

Monthly exploit breakdowns, audit checklists, and DeFi security research — straight to your inbox

© 2026 Zealynx