Skip to main content
The declare_program!() macro enables dependency-free interaction with Anchor programs by generating Rust modules from a program’s IDL file. This eliminates the need to add the external program as a crate dependency.

Overview

Traditionally, to interact with an external program, you would add it as a dependency in Cargo.toml. The declare_program!() macro provides an alternative approach: Traditional Approach:
With declare_program!:
The macro generates all necessary modules for both on-chain (CPI) and off-chain (client) interactions.

Generated Modules

The declare_program!() macro generates the following modules:

Setup

1. Obtain the IDL File

You need the target program’s IDL file (JSON format). Place it in an /idls directory anywhere in your project structure:
The /idls directory can be at any level in your project.

2. Invoke the Macro

In your program, invoke the macro with the IDL filename (without extension):

On-Chain Usage (CPI)

Use the generated cpi module to make cross-program invocations.

Complete Example

Let’s say you want to call an external counter program: Target Program (external_program):
Your Program (caller):

CPI with Seeds (PDA Signing)

When your program needs to sign the CPI as a PDA:

Off-Chain Usage (Client)

Use the generated client module to build instructions from your off-chain client.

Rust Client Example

TypeScript Client

For TypeScript clients, use the generated IDL types:

Working with Events

If the external program emits events, use the generated events module:
Off-chain event listening (Rust):

Handling Errors

Access program-specific errors through the generated errors module:

Working with Custom Types

Use the generated types module to access program-defined structs and enums:

Multiple Programs

Declare multiple external programs in the same file:

Advantages

Zero Dependencies

No need to add external programs as crate dependencies, reducing compilation time and dependency conflicts.

Version Flexibility

Work with different versions of external programs by simply swapping IDL files.

Unified Interface

Single macro generates both on-chain (CPI) and off-chain (client) code.

Type Safety

Fully typed interfaces generated from IDL ensure compile-time safety.

Limitations

Account Validation: The declare_program!() macro generates type definitions but doesn’t include the constraint validation logic from the original program. Always validate account relationships in your program.
IDL Availability: Requires the external program to publish its IDL. Programs without IDLs cannot be used with this macro.
Custom Types: Complex types that don’t derive AnchorSerialize/AnchorDeserialize may require manual handling.

Best Practices

Commit IDL files to version control to ensure reproducible builds:
Always verify the program ID matches the expected address:
Document where IDL files come from:
Thoroughly test all CPI interactions:

Troubleshooting

IDL Not Found

Error:
Solution: Ensure the IDL file exists in an /idls directory:

Module Not Found

Error:
Solution: Add the declare_program!() invocation:

Type Mismatch

Error:
Solution: Use the generated account types:

Resources