Footguns

Common footguns in Anchor that can lead to issues.

Zeroed Discriminators

Anchor supports overriding discriminators with custom values (for accounts, events and instructions).

All-zero discriminators are only supported in events and instructions. In the case of accounts, e.g:

#[account(discriminator = [0, 0, 0])]
#[account(discriminator = 0)]
#[account(discriminator = ZERO_CONSTANT)]

they are explicitly not supported, as all-zero discriminators are indistinguishable from newly allocated accounts and can expose your code to security and usability issues.

Generic Account Types

Different instantiations of the same generic #[account] type share a default discriminator: generic arguments are not part of its derivation.

#[account]
pub struct State<T: AnchorSerialize + AnchorDeserialize + Clone> {
    pub value: T,
}
 
// State<A> and State<B> share a discriminator despite being distinct Rust types.
// State<u64>
// State<u128>

An account serialized as State<u128> can deserialize as State<u64>: the discriminator matches, and deserialization reads the first eight bytes of the value, leaving the remaining bytes unchecked. The reverse requires enough account data for a u128; a minimally sized State<u64> account is too short.

Generic arguments do not distinguish accounts

If the serialized data for one instantiation can be deserialized as another, an account can pass the discriminator check and be interpreted as the wrong type. Do not rely on generic arguments to prevent this type confusion. Use distinct account structs with distinct discriminators, or explicitly validate a stored type tag before using the account data.

Borrowed Data in Account Types

Account types used with Context should own their values rather than hold references borrowed from other accounts or handler-local data. Do not replace an AccountInfo's lamports or data backing reference with a reference to a field in ctx.accounts. Mutate the account contents instead of their backing references.

Avoid borrowed account data

For example, avoid account types such as struct Borrowed<'info> { value: Cell<Option<&'info u64>> }. Anchor 1.0 uses simplified Context lifetimes, which can make a reference retained through Cell, RefCell, or similar interior mutability outlive the value it points to.

On this page

GitHubEdit on GitHub