Coda
Packages

@macalinao/codama-instruction-accounts-dedupe-visitor

Codama visitor for deduplicating and flattening instruction accounts from Anchor IDL

This package provides a Codama visitor that flattens nested account structures from Anchor IDLs while preserving relationships through naming conventions.

Installation

bun add -D @macalinao/codama-instruction-accounts-dedupe-visitor

Or with npm:

npm install -D @macalinao/codama-instruction-accounts-dedupe-visitor

Why Use This Package?

Anchor IDL supports nested account structures where accounts can be grouped together. When converting these to Codama nodes, the nested structure needs to be flattened while preserving the relationship. This visitor:

  1. Flattens Nested Accounts: Converts nested account groups into flat structures
  2. Preserves Relationships: Uses underscore-separated naming to maintain parent-child relationships
  3. Updates PDA Seeds: Properly adjusts PDA seed references when accounts are flattened
  4. Maintains Type Safety: Ensures all account references remain valid after flattening

This addresses an active issue in Codama regarding nested account handling.

Usage

Basic Usage

import { instructionAccountsDedupeVisitor } from "@macalinao/codama-instruction-accounts-dedupe-visitor";
import { rootNodeFromAnchor } from "@codama/nodes-from-anchor";
import { visit } from "codama";

// Your Anchor IDL
const idl = /* your anchor IDL */;

// Create the root node from Anchor IDL
const root = rootNodeFromAnchor(idl);

// Apply the dedupe visitor
const visitor = instructionAccountsDedupeVisitor(idl);
const deduplicatedRoot = visit(root, visitor);

With Coda Configuration

Use it in your coda.config.mjs:

import { defineConfig } from "@macalinao/coda";
import { instructionAccountsDedupeVisitor } from "@macalinao/codama-instruction-accounts-dedupe-visitor";
import fs from "fs";

export default defineConfig({
  idlPath: "./idls/my_program.json",
  outputDir: "./src/generated",
  visitors: (idl) => [
    // Pass the IDL to the visitor
    instructionAccountsDedupeVisitor(idl)
  ]
});

How It Works

The visitor transforms nested account structures through a multi-step process:

  1. Traversing Account Groups: Recursively processes nested account groups
  2. Prefixing Names: Joins parent and child names with underscores
  3. Adjusting PDA Seeds: Updates seed paths to match the flattened structure
  4. Preserving All Metadata: Maintains all account properties and constraints

Example Transformation

Before (Nested Structure)

{
  name: "mintAccounts",
  accounts: [
    { 
      name: "mint",
      isMut: false,
      isSigner: false
    },
    { 
      name: "metadata",
      isMut: true,
      isSigner: false
    }
  ]
}

After (Flattened Structure)

[
  { 
    name: "mintAccounts_mint",
    isMut: false,
    isSigner: false
  },
  { 
    name: "mintAccounts_metadata",
    isMut: true,
    isSigner: false
  }
]

PDA Seed Updates

When flattening accounts, PDA seeds are also updated to reflect the new structure:

Before

{
  seeds: [
    { 
      kind: "account",
      path: "mintAccounts.mint"
    }
  ]
}

After

{
  seeds: [
    { 
      kind: "account",
      path: "mintAccounts_mint"
    }
  ]
}

API Reference

instructionAccountsDedupeVisitor

Creates a visitor that flattens nested instruction accounts:

function instructionAccountsDedupeVisitor(idl: AnchorIdl): Visitor;

Parameters:

  • idl: The Anchor IDL containing the instruction definitions with potentially nested accounts

Returns:

  • A Codama visitor that can be applied to transform the AST

Use Cases

Complex Nested Structures

Particularly useful for programs with deeply nested account structures:

// Handles multi-level nesting
{
  name: "tokenAccounts",
  accounts: [
    {
      name: "source",
      accounts: [
        { name: "mint" },
        { name: "authority" }
      ]
    },
    {
      name: "destination",
      accounts: [
        { name: "mint" },
        { name: "authority" }
      ]
    }
  ]
}

// Becomes:
[
  { name: "tokenAccounts_source_mint" },
  { name: "tokenAccounts_source_authority" },
  { name: "tokenAccounts_destination_mint" },
  { name: "tokenAccounts_destination_authority" }
]

Maintaining Compatibility

The flattened structure maintains compatibility with the original Anchor program while providing a cleaner interface for client generation.

Best Practices

  1. Apply Early: Use this visitor early in your visitor chain to ensure other visitors work with the flattened structure
  2. Consistent Naming: The underscore separator is consistent and predictable
  3. PDA References: Always verify PDA seed references are correctly updated after flattening

Troubleshooting

Accounts Not Flattening

Ensure you're passing the correct IDL and that it contains nested account structures:

// Check if your IDL has nested structures
const hasNested = idl.instructions.some(ix => 
  ix.accounts.some(acc => acc.accounts && acc.accounts.length > 0)
);

PDA Seeds Not Updating

The visitor automatically updates PDA seeds. If seeds aren't updating correctly, ensure they reference accounts using the dot notation (e.g., parent.child).