Skip to content

IDL generation missing PDA seeds constraint for some accounts with init attribute #4057

@takshakmudgal

Description

@takshakmudgal

Desc

When an account uses both init and seeds constraints in Anchor, the generated IDL omits the pda field that should describe the seed derivation. This makes it impossible for client developers to correctly derive the PDA using only the IDL, defeating the purpose of IDL-based client generation.

Expected Behavior

When an account has a seeds constraint, the IDL should include a pda field describing the seed derivation, regardless of whether the account also has an init constraint. This allows client developers to derive the correct PDA address using only the IDL.

Actual Behavior

Accounts with both init and seeds constraints are missing the pda field in the generated IDL, showing only "writable": true.

Reproduction Steps

1. Create a program with PDA account using init + seeds

In this file: https://github.com/MeteoraAg/damm-v2/blob/main/programs/cp-amm/src/instructions/initialize_pool/ix_initialize_customizable_pool.rs
The pool account is defined with both init and seeds constraints:

#[account(
        init,
        seeds = [
            CUSTOMIZABLE_POOL_PREFIX.as_ref(),
            &max_key(&token_a_mint.key(), &token_b_mint.key()),
            &min_key(&token_a_mint.key(), &token_b_mint.key()),
        ],
        bump,
        payer = payer,
        space = 8 + Pool::INIT_SPACE
    )]
    pub pool: AccountLoader<'info, Pool>,

    #[account(
        init,
        seeds = [
            POSITION_PREFIX.as_ref(),
            position_nft_mint.key().as_ref()
        ],
        bump,
        payer = payer,
        space = 8 + Position::INIT_SPACE
    )]
    pub position: AccountLoader<'info, Position>,

Where: https://github.com/MeteoraAg/damm-v2/blob/main/programs/cp-amm/src/constants.rs#L152

pub const CUSTOMIZABLE_POOL_PREFIX: &[u8] = b"cpool";
pub const POSITION_PREFIX: &[u8] = b"position";

2. Build and generate IDL

anchor build

3. Check generated IDL

Expected IDL output for pool account:

{
  "name": "pool",
  "writable": true,
  "pda": {
    "seeds": [
      {
        "kind": "const",
        "value": [99, 112, 111, 111, 108]
      },
      {
        "kind": "account",
        "path": "token_a_mint | token_b_mint"
      },
      {
        "kind": "account", 
        "path": "token_a_mint | token_b_mint"
      }
    ]
  }
}

Actual IDL output for pool account:

{
  "name": "pool",
  "docs": [
    "Initialize an account to store the pool state"
  ],
  "writable": true
}

4. Observe inconsistency

In the same IDL, the position account (which also has init + seeds) correctly includes the pda field:

{
  "name": "position",
  "writable": true,
  "pda": {
    "seeds": [
      {
        "kind": "const",
        "value": [112, 111, 115, 105, 116, 105, 111, 110]
      },
      {
        "kind": "account",
        "path": "position_nft_mint"
      }
    ]
  }
}

This inconsistency is confusing - why does position get the pda field but pool doesn't, when both use the same pattern?

5. Attempt to use IDL from client

Client code attempting to derive PDA using IDL:

// This fails because IDL doesn't specify the pool is a PDA
const pool = PublicKey.findProgramAddressSync(
  [
    Buffer.from("cpool"),
    getMaxKey(tokenAMint, tokenBMint),
    getMinKey(tokenAMint, tokenBMint),
  ],
  programId
)[0];

// Passing the incorrectly derived (or not derived) pool address
await program.methods
  .initializeCustomizablePool(params)
  .accounts({
    pool: pool, // This will fail with ConstraintSeeds error
    // ... other accounts
  })
  .rpc();

Error received:

Error: AnchorError caused by account: pool. Error Code: ConstraintSeeds. Error Number: 2006. 
Error Message: A seeds constraint was violated.
Program log: Left:
Program log: F8DxVitDgiFvXX3tYxtzZPrBnSc1B69sKWmUpixHw8BF
Program log: Right:
Program log: 6zwjucTW2d32jZRC8m7FE6C2aQuhsiGaWBWoge3feWCx

The only way to discover the correct seeds is to read the Rust source code, which defeats the purpose of the IDL!.

This appears to affect Anchor programs using:

  • AccountLoader<'info, T> account types
  • Complex seed derivations with helper functions
  • The init constraint combined with seeds

However, it's inconsistent - some accounts in the same struct show PDA seeds correctly while others don't.

here's the full IDL, but would suggest to check and build on your own too
cp_amm.json

Metadata

Metadata

Assignees

No one assigned

    Labels

    idlrelated to the IDL, either program or client side

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions