Coda
Packages

@macalinao/codama-rename-visitor

Codama visitor for renaming accounts, instructions, and defined types within Solana programs

This package provides flexible visitors for renaming accounts, instructions, and defined types in your Codama AST. It's particularly useful when working with multiple programs that may have naming conflicts.

Installation

bun add -D @macalinao/codama-rename-visitor

Or with npm:

npm install -D @macalinao/codama-rename-visitor

Why Use This Package?

When working with multiple Solana programs, you might need to:

  • Avoid naming conflicts between multiple programs
  • Follow consistent naming conventions across your codebase
  • Add context to generic instruction names
  • Customize generated code without modifying the original IDL

This visitor allows you to rename elements at the AST level, ensuring all references are updated correctly throughout the generated code.

Usage

The most powerful way to use this visitor is to specify renames for specific programs:

import { renameVisitor } from "@macalinao/codama-rename-visitor";
import { visit } from "codama";

// Rename elements in specific programs
const visitor = renameVisitor({
  quarryMine: {
    instructions: {
      claimRewards: "claimRewardsMine",
      stake: "stakeInQuarry"
    },
    accounts: {
      miner: "quarryMiner"
    },
    definedTypes: {
      StakeEvent: "QuarryStakeEvent"
    }
  },
  quarryMergeMine: {
    instructions: {
      claimRewards: "claimRewardsMergeMine",
      stake: "stakeInMergePool"
    },
    accounts: {
      miner: "mergeMiner"
    }
  }
});

const updatedRoot = visit(root, visitor);

With Coda Configuration

Use it in your coda.config.mjs:

import { defineConfig } from "@macalinao/coda";
import { renameVisitor } from "@macalinao/codama-rename-visitor";

export default defineConfig({
  idlPath: "./idls/*.json",
  outputDir: "./src/generated",
  visitors: [
    renameVisitor({
      quarryMine: {
        instructions: {
          // Avoid conflicts with quarryMergeMine
          claimRewards: "claimRewardsMine",
          claimRewardsV2: "claimRewardsMineV2"
        }
      },
      quarryMergeMine: {
        instructions: {
          // Give these distinct names
          claimRewards: "claimRewardsMergeMine"
        }
      }
    })
  ]
});

Renaming Specific Element Types

For simpler use cases, you can rename specific types of elements across all programs:

Rename Instructions Only

import { renameInstructionsVisitor } from "@macalinao/codama-rename-visitor";

const visitor = renameInstructionsVisitor({
  transfer: "transferTokens",
  mint: "mintNft",
  burn: "burnToken"
});

const updatedRoot = visit(root, visitor);

Rename Accounts Only

import { renameAccountsVisitor } from "@macalinao/codama-rename-visitor";

const visitor = renameAccountsVisitor({
  userAccount: "user",
  configAccount: "config",
  metadataAccount: "metadata"
});

const updatedRoot = visit(root, visitor);

Rename Defined Types Only

import { renameDefinedTypesVisitor } from "@macalinao/codama-rename-visitor";

const visitor = renameDefinedTypesVisitor({
  TokenMetadata: "NftMetadata",
  TransferEvent: "TokenTransferEvent"
});

const updatedRoot = visit(root, visitor);

API Reference

renameVisitor

The main visitor for comprehensive renaming:

function renameVisitor(
  renamesByProgram: Record<string, ProgramRenameOptions>
): Visitor;

interface ProgramRenameOptions {
  accounts?: Record<string, string>;
  instructions?: Record<string, string>;
  definedTypes?: Record<string, string>;
}

Parameters:

  • renamesByProgram: Object mapping program names to their rename configurations

renameInstructionsVisitor

Visitor for renaming only instructions:

function renameInstructionsVisitor(
  mapping: Record<string, string>
): Visitor;

Parameters:

  • mapping: Object mapping old instruction names to new names

renameAccountsVisitor

Visitor for renaming only accounts:

function renameAccountsVisitor(
  mapping: Record<string, string>
): Visitor;

Parameters:

  • mapping: Object mapping old account names to new names

renameDefinedTypesVisitor

Visitor for renaming only defined types:

function renameDefinedTypesVisitor(
  mapping: Record<string, string>
): Visitor;

Parameters:

  • mapping: Object mapping old type names to new names

Transform Functions

The package also exports transform functions that can be used directly:

// Transform individual nodes
renameInstructionTransform(node, mapping);
renameAccountTransform(node, mapping);
renameDefinedTypeTransform(node, mapping);

These are useful when building custom visitors or transforming nodes directly.

Use Cases

Avoiding Naming Conflicts

When working with multiple related programs like Quarry:

renameVisitor({
  // Quarry Mine program
  quarryMine: {
    instructions: {
      createMiner: "createQuarryMiner",
      stake: "stakeInQuarry",
      claimRewards: "claimQuarryRewards"
    },
    accounts: {
      miner: "quarryMiner",
      quarry: "quarryAccount"
    }
  },
  // Quarry Merge Mine program
  quarryMergeMine: {
    instructions: {
      createMiner: "createMergeMiner",
      stake: "stakeInMergePool",
      claimRewards: "claimMergeRewards"
    },
    accounts: {
      miner: "mergeMiner",
      pool: "mergePool"
    }
  }
})

Adding Context

Make generic names more specific:

renameVisitor({
  token: {
    instructions: {
      transfer: "transferToken",
      burn: "burnToken",
      mint: "mintToken"
    },
    accounts: {
      account: "tokenAccount",
      mint: "tokenMint"
    }
  },
  nft: {
    instructions: {
      transfer: "transferNft",
      burn: "burnNft",
      mint: "mintNft"
    },
    accounts: {
      metadata: "nftMetadata",
      edition: "nftEdition"
    }
  }
})

Consistent Naming Conventions

Enforce naming patterns across your codebase:

renameVisitor({
  myProgram: {
    instructions: {
      // Convert snake_case to camelCase
      create_account: "createAccount",
      update_metadata: "updateMetadata",
      delete_record: "deleteRecord"
    },
    definedTypes: {
      // Add consistent suffixes
      AccountCreated: "AccountCreatedEvent",
      MetadataUpdated: "MetadataUpdatedEvent",
      RecordDeleted: "RecordDeletedEvent"
    }
  }
})

Real-World Example

The Quarry client uses this visitor to handle conflicts between multiple programs:

// From clients/quarry/coda.config.mjs
renameVisitor({
  quarryMine: {
    instructions: {
      claimRewards: "claimRewardsMine",
      claimRewardsV2: "claimRewardsMineV2",
      extractFees: "extractFeesMine",
      pause: "pauseMine",
      rescueTokens: "rescueTokensMine",
      unpause: "unpauseMine",
      // ... more renames
    }
  },
  quarryMergeMine: {
    instructions: {
      claimRewards: "claimRewardsMergeMine",
      initMergeMiner: "initMergeMinerV2",
      initMiner: "initMinerMergeMine",
      withdrawTokens: "withdrawTokensMergeMine"
    }
  }
})

Best Practices

  1. Use Program-Specific Renaming: Always prefer the program-specific format to avoid unintended renames
  2. Document Renames: Comment why certain renames are necessary
  3. Be Consistent: Apply similar naming patterns across related programs
  4. Test Generated Code: Verify that renamed elements work correctly in your client code
  5. Order Matters: Apply rename visitors early in your visitor chain

Troubleshooting

Renames Not Applied

Ensure the visitor is added to your configuration and that names match exactly:

// Program names must match your IDL
console.log(idl.metadata?.name || idl.name); // Should match keys in renameVisitor config

Type Errors After Renaming

Make sure all references are updated. The visitor handles most cases, but verify:

  • Instruction builders use new names
  • Account types use new names
  • Defined types are correctly referenced

Case Sensitivity

All names are converted to camelCase automatically. The visitor handles this conversion for you.