declare-program.mdx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. ---
  2. title: Dependency Free Composability
  3. description:
  4. Learn how to use Anchor's declare_program macro to interact with programs
  5. without additional dependencies.
  6. ---
  7. The
  8. [`declare_program!()`](https://github.com/coral-xyz/anchor/tree/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program)
  9. macro simplifies the process of interacting with Anchor programs by generating
  10. Rust modules (from a program's IDL) that can be used in both on-chain and
  11. off-chain code. You can find an example program
  12. [here](https://github.com/coral-xyz/anchor/tree/master/tests/declare-program).
  13. The following modules are generated by the `declare_program!()` macro:
  14. | Module | Description |
  15. | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
  16. | [`cpi`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/cpi.rs) | Helper functions for making cross-program invocations (CPIs) to the program from other on-chain programs |
  17. | [`client`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/client.rs) | Accounts and arguments required to build program instructions to add to client-side transactions |
  18. | [`account`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/accounts.rs) | Account data types (program state) defined in the program |
  19. | [`program`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/program.rs) | Program ID constant used to identify the program |
  20. | [`constants`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/constants.rs) | Program constants defined in the program |
  21. | [`events`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/events.rs) | Program events defined in the program |
  22. | [`types`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/types.rs) | Program types defined in the program |
  23. ## Examples
  24. The following examples demonstrate how to use the `declare_program!()` macro in
  25. two scenarios:
  26. 1. Making Cross Program Invocations (CPIs) from one program to another program
  27. 2. Building client-side transactions to invoke a program's instructions
  28. Both examples show how the modules generated by the `declare_program!()` macro
  29. simplify program interactions, whether you're writing on-chain or off-chain
  30. code.
  31. ### On-chain CPI
  32. To use the `declare_program!()` macro, you need the IDL file for the target
  33. program. The IDL file must be placed in a directory named `/idls` in your
  34. project. The `/idls` directory can be located at any level in your project
  35. structure. For example, your project could have this layout:
  36. <Files>
  37. <Folder name="idls" defaultOpen={true}>
  38. <File name="example.json" />
  39. </Folder>
  40. <Folder name="programs" defaultOpen={true}>
  41. <Folder name="example-cpi" defaultOpen={true}>
  42. <Folder name="src" defaultOpen={true}>
  43. <File name="lib.rs" />
  44. </Folder>
  45. <File name="Cargo.toml" />
  46. </Folder>
  47. </Folder>
  48. </Files>
  49. Below is the source code (`lib.rs`) for the target (callee) program that
  50. generates the `example.json` IDL file shown above.
  51. Using the program's IDL file, another program can use the `declare_program!()`
  52. macro to generate a CPI module, enabling it to make CPIs to this program's
  53. instructions.
  54. <Tabs items={["Callee Program", "IDL"]}>
  55. ```rust tab="Callee Program"
  56. use anchor_lang::prelude::*;
  57. declare_id!("8HupNBr7SBhBLcBsLhbtes3tCarBm6Bvpqp5AfVjHuj8");
  58. #[program]
  59. pub mod example {
  60. use super::*;
  61. pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
  62. let counter = &ctx.accounts.counter;
  63. msg!("Counter account created! Current count: {}", counter.count);
  64. Ok(())
  65. }
  66. pub fn increment(ctx: Context<Increment>) -> Result<()> {
  67. let counter = &mut ctx.accounts.counter;
  68. msg!("Previous counter: {}", counter.count);
  69. counter.count += 1;
  70. msg!("Counter incremented! Current count: {}", counter.count);
  71. Ok(())
  72. }
  73. }
  74. #[derive(Accounts)]
  75. pub struct Initialize<'info> {
  76. #[account(mut)]
  77. pub payer: Signer<'info>,
  78. #[account(
  79. init,
  80. payer = payer,
  81. space = 8 + 8
  82. )]
  83. pub counter: Account<'info, Counter>,
  84. pub system_program: Program<'info, System>,
  85. }
  86. #[derive(Accounts)]
  87. pub struct Increment<'info> {
  88. #[account(mut)]
  89. pub counter: Account<'info, Counter>,
  90. }
  91. #[account]
  92. pub struct Counter {
  93. pub count: u64,
  94. }
  95. ```
  96. ```ts tab="IDL"
  97. {
  98. "address": "8HupNBr7SBhBLcBsLhbtes3tCarBm6Bvpqp5AfVjHuj8",
  99. "metadata": {
  100. "name": "example",
  101. "version": "0.1.0",
  102. "spec": "0.1.0",
  103. "description": "Created with Anchor"
  104. },
  105. "instructions": [
  106. {
  107. "name": "increment",
  108. "discriminator": [
  109. 11,
  110. 18,
  111. 104,
  112. 9,
  113. 104,
  114. 174,
  115. 59,
  116. 33
  117. ],
  118. "accounts": [
  119. {
  120. "name": "counter",
  121. "writable": true
  122. }
  123. ],
  124. "args": []
  125. },
  126. {
  127. "name": "initialize",
  128. "discriminator": [
  129. 175,
  130. 175,
  131. 109,
  132. 31,
  133. 13,
  134. 152,
  135. 155,
  136. 237
  137. ],
  138. "accounts": [
  139. {
  140. "name": "payer",
  141. "writable": true,
  142. "signer": true
  143. },
  144. {
  145. "name": "counter",
  146. "writable": true,
  147. "signer": true
  148. },
  149. {
  150. "name": "system_program",
  151. "address": "11111111111111111111111111111111"
  152. }
  153. ],
  154. "args": []
  155. }
  156. ],
  157. "accounts": [
  158. {
  159. "name": "Counter",
  160. "discriminator": [
  161. 255,
  162. 176,
  163. 4,
  164. 245,
  165. 188,
  166. 253,
  167. 124,
  168. 25
  169. ]
  170. }
  171. ],
  172. "types": [
  173. {
  174. "name": "Counter",
  175. "type": {
  176. "kind": "struct",
  177. "fields": [
  178. {
  179. "name": "count",
  180. "type": "u64"
  181. }
  182. ]
  183. }
  184. }
  185. ]
  186. }
  187. ```
  188. </Tabs>
  189. Below is the source code (`lib.rs`) for the caller program (example-cpi) that
  190. uses the `declare_program!()` macro to generate a CPI module to invoke the
  191. instructions defined in the callee program above.
  192. <Tabs items={["Caller Program", "Test"]}>
  193. ```rust tab="Caller Program"
  194. use anchor_lang::prelude::*;
  195. declare_id!("GENmb1D59wqCKRwujq4PJ8461EccQ5srLHrXyXp4HMTH");
  196. // [!code word:declare_program]
  197. // [!code highlight:9]
  198. declare_program!(example);
  199. use example::{
  200. accounts::Counter,
  201. cpi::{
  202. self,
  203. accounts::{Increment, Initialize},
  204. },
  205. program::Example,
  206. };
  207. #[program]
  208. pub mod example_cpi {
  209. use super::*;
  210. pub fn initialize_cpi(ctx: Context<InitializeCpi>) -> Result<()> {
  211. // Create CPI context for initialize
  212. let cpi_ctx = CpiContext::new(
  213. ctx.accounts.example_program.to_account_info(),
  214. Initialize {
  215. payer: ctx.accounts.payer.to_account_info(),
  216. counter: ctx.accounts.counter.to_account_info(),
  217. system_program: ctx.accounts.system_program.to_account_info(),
  218. },
  219. );
  220. // Invoke the initialize instruction
  221. cpi::initialize(cpi_ctx)?;
  222. Ok(())
  223. }
  224. pub fn increment_cpi(ctx: Context<IncrementCpi>) -> Result<()> {
  225. // Create CPI context for increment
  226. let cpi_ctx = CpiContext::new(
  227. ctx.accounts.example_program.to_account_info(),
  228. Increment {
  229. counter: ctx.accounts.counter.to_account_info(),
  230. },
  231. );
  232. // Invoke the increment instruction
  233. cpi::increment(cpi_ctx)?;
  234. Ok(())
  235. }
  236. }
  237. #[derive(Accounts)]
  238. pub struct InitializeCpi<'info> {
  239. #[account(mut)]
  240. pub payer: Signer<'info>,
  241. #[account(mut)]
  242. pub counter: Signer<'info>,
  243. pub system_program: Program<'info, System>,
  244. pub example_program: Program<'info, Example>,
  245. }
  246. #[derive(Accounts)]
  247. pub struct IncrementCpi<'info> {
  248. #[account(mut)]
  249. pub counter: Account<'info, Counter>,
  250. pub example_program: Program<'info, Example>,
  251. }
  252. ```
  253. ```ts tab="Test"
  254. import * as anchor from "@coral-xyz/anchor";
  255. import { Program } from "@coral-xyz/anchor";
  256. import { Example } from "../target/types/example";
  257. import { ExampleCpi } from "../target/types/example_cpi";
  258. import { Keypair } from "@solana/web3.js";
  259. describe("example", () => {
  260. anchor.setProvider(anchor.AnchorProvider.env());
  261. const program = anchor.workspace.Example as Program<Example>;
  262. const cpiProgram = anchor.workspace.ExampleCpi as Program<ExampleCpi>;
  263. const counterAccount = Keypair.generate();
  264. it("Is initialized!", async () => {
  265. const transactionSignature = await cpiProgram.methods
  266. .initializeCpi()
  267. .accounts({
  268. counter: counterAccount.publicKey,
  269. })
  270. .signers([counterAccount])
  271. .rpc({ skipPreflight: true });
  272. const accountData = await program.account.counter.fetch(
  273. counterAccount.publicKey,
  274. );
  275. console.log(`Transaction Signature: ${transactionSignature}`);
  276. console.log(`Count: ${accountData.count}`);
  277. });
  278. it("Increment", async () => {
  279. const transactionSignature = await cpiProgram.methods
  280. .incrementCpi()
  281. .accounts({
  282. counter: counterAccount.publicKey,
  283. })
  284. .rpc();
  285. const accountData = await program.account.counter.fetch(
  286. counterAccount.publicKey,
  287. );
  288. console.log(`Transaction Signature: ${transactionSignature}`);
  289. console.log(`Count: ${accountData.count}`);
  290. });
  291. });
  292. ```
  293. </Tabs>
  294. #### Explanation
  295. <Steps>
  296. <Step>
  297. The `declare_program!()` macro takes a single argument - the name of the
  298. program's IDL file (e.g. `example.json`):
  299. ```rust
  300. declare_program!(example); // Looks for /idls/example.json
  301. ```
  302. </Step>
  303. <Step>
  304. Bring into scope the generated modules:
  305. ```rust
  306. use example::{
  307. accounts::Counter, // Account types
  308. cpi::{ // Cross program invocation helpers
  309. self,
  310. accounts::{Increment, Initialize},
  311. },
  312. program::Example, // Program type
  313. };
  314. ```
  315. </Step>
  316. <Step>
  317. Use the imported types in the account validation structs:
  318. ```rust
  319. #[derive(Accounts)]
  320. pub struct IncrementCpi<'info> {
  321. // Counter type from accounts module
  322. #[account(mut)]
  323. // [!code word:Counter]
  324. // [!code highlight]
  325. pub counter: Account<'info, Counter>,
  326. // Example type from program module
  327. // [!code word:Example]
  328. // [!code highlight]
  329. pub example_program: Program<'info, Example>,
  330. }
  331. ```
  332. </Step>
  333. <Step>
  334. Use the CPI module to invoke the program's instructions:
  335. ```rust
  336. pub fn initialize_cpi(ctx: Context<InitializeCpi>) -> Result<()> {
  337. // Create CPI context for initialize
  338. let cpi_ctx = CpiContext::new(
  339. ctx.accounts.example_program.to_account_info(),
  340. Initialize {
  341. payer: ctx.accounts.payer.to_account_info(),
  342. counter: ctx.accounts.counter.to_account_info(),
  343. system_program: ctx.accounts.system_program.to_account_info(),
  344. },
  345. );
  346. // Invoke the initialize instruction
  347. // [!code highlight]
  348. cpi::initialize(cpi_ctx)?;
  349. Ok(())
  350. }
  351. ```
  352. ```rust
  353. pub fn increment_cpi(ctx: Context<IncrementCpi>) -> Result<()> {
  354. // Create CPI context for increment
  355. let cpi_ctx = CpiContext::new(
  356. ctx.accounts.example_program.to_account_info(),
  357. Increment {
  358. counter: ctx.accounts.counter.to_account_info(),
  359. },
  360. );
  361. // Invoke the increment instruction
  362. // [!code highlight]
  363. cpi::increment(cpi_ctx)?;
  364. Ok(())
  365. }
  366. ```
  367. </Step>
  368. </Steps>
  369. ### Off-chain Client
  370. To use the `declare_program!()` macro, you need the IDL file for the target
  371. program. The IDL file must be placed in a directory named `/idls` in your
  372. project. The `/idls` directory can be located at any level in your project
  373. structure. For example, your project could have this layout:
  374. <Files>
  375. <Folder name="idls" defaultOpen={true}>
  376. <File name="example.json" />
  377. </Folder>
  378. <Folder name="src" defaultOpen={true}>
  379. <File name="main.rs" />
  380. </Folder>
  381. <File name="Cargo.toml" />
  382. </Files>
  383. Below is the source code (`lib.rs`) for the target program that generates the
  384. `example.json` IDL file shown above. The program's IDL can then be used in a
  385. client script along with the `declare_program!()` macro to generate a Client
  386. module to build the program's instructions.
  387. <Tabs items={["Callee Program", "IDL"]}>
  388. ```rust tab="Callee Program"
  389. use anchor_lang::prelude::*;
  390. declare_id!("6khKp4BeJpCjBY1Eh39ybiqbfRnrn2UzWeUARjQLXYRC");
  391. #[program]
  392. pub mod example {
  393. use super::*;
  394. pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
  395. let counter = &ctx.accounts.counter;
  396. msg!("Counter account created! Current count: {}", counter.count);
  397. Ok(())
  398. }
  399. pub fn increment(ctx: Context<Increment>) -> Result<()> {
  400. let counter = &mut ctx.accounts.counter;
  401. msg!("Previous counter: {}", counter.count);
  402. counter.count += 1;
  403. msg!("Counter incremented! Current count: {}", counter.count);
  404. Ok(())
  405. }
  406. }
  407. #[derive(Accounts)]
  408. pub struct Initialize<'info> {
  409. #[account(mut)]
  410. pub payer: Signer<'info>,
  411. #[account(
  412. init,
  413. payer = payer,
  414. space = 8 + 8
  415. )]
  416. pub counter: Account<'info, Counter>,
  417. pub system_program: Program<'info, System>,
  418. }
  419. #[derive(Accounts)]
  420. pub struct Increment<'info> {
  421. #[account(mut)]
  422. pub counter: Account<'info, Counter>,
  423. }
  424. #[account]
  425. pub struct Counter {
  426. pub count: u64,
  427. }
  428. ```
  429. ```ts tab="IDL"
  430. {
  431. "address": "6khKp4BeJpCjBY1Eh39ybiqbfRnrn2UzWeUARjQLXYRC",
  432. "metadata": {
  433. "name": "example",
  434. "version": "0.1.0",
  435. "spec": "0.1.0",
  436. "description": "Created with Anchor"
  437. },
  438. "instructions": [
  439. {
  440. "name": "increment",
  441. "discriminator": [11, 18, 104, 9, 104, 174, 59, 33],
  442. "accounts": [
  443. {
  444. "name": "counter",
  445. "writable": true
  446. }
  447. ],
  448. "args": []
  449. },
  450. {
  451. "name": "initialize",
  452. "discriminator": [175, 175, 109, 31, 13, 152, 155, 237],
  453. "accounts": [
  454. {
  455. "name": "payer",
  456. "writable": true,
  457. "signer": true
  458. },
  459. {
  460. "name": "counter",
  461. "writable": true,
  462. "signer": true
  463. },
  464. {
  465. "name": "system_program",
  466. "address": "11111111111111111111111111111111"
  467. }
  468. ],
  469. "args": []
  470. }
  471. ],
  472. "accounts": [
  473. {
  474. "name": "Counter",
  475. "discriminator": [255, 176, 4, 245, 188, 253, 124, 25]
  476. }
  477. ],
  478. "types": [
  479. {
  480. "name": "Counter",
  481. "type": {
  482. "kind": "struct",
  483. "fields": [
  484. {
  485. "name": "count",
  486. "type": "u64"
  487. }
  488. ]
  489. }
  490. }
  491. ]
  492. }
  493. ```
  494. </Tabs>
  495. Below is the client script (main.rs) that uses the `declare_program!()` macro to
  496. generate a Client module to build the program's instructions.
  497. <Tabs items={["Client Script", "Dependencies"]}>
  498. ```rust tab="Client Script"
  499. use anchor_client::{
  500. solana_client::rpc_client::RpcClient,
  501. solana_sdk::{
  502. commitment_config::CommitmentConfig, native_token::LAMPORTS_PER_SOL, signature::Keypair,
  503. signer::Signer, system_program,
  504. },
  505. Client, Cluster,
  506. };
  507. use anchor_lang::prelude::*;
  508. use std::rc::Rc;
  509. declare_program!(example);
  510. use example::{accounts::Counter, client::accounts, client::args};
  511. #[tokio::main]
  512. async fn main() -> anyhow::Result<()> {
  513. let connection = RpcClient::new_with_commitment(
  514. "http://127.0.0.1:8899", // Local validator URL
  515. CommitmentConfig::confirmed(),
  516. );
  517. // Generate Keypairs and request airdrop
  518. let payer = Keypair::new();
  519. let counter = Keypair::new();
  520. println!("Generated Keypairs:");
  521. println!(" Payer: {}", payer.pubkey());
  522. println!(" Counter: {}", counter.pubkey());
  523. println!("\nRequesting 1 SOL airdrop to payer");
  524. let airdrop_signature = connection.request_airdrop(&payer.pubkey(), LAMPORTS_PER_SOL)?;
  525. // Wait for airdrop confirmation
  526. while !connection.confirm_transaction(&airdrop_signature)? {
  527. std::thread::sleep(std::time::Duration::from_millis(100));
  528. }
  529. println!(" Airdrop confirmed!");
  530. // Create program client
  531. let provider = Client::new_with_options(
  532. Cluster::Localnet,
  533. Rc::new(payer),
  534. CommitmentConfig::confirmed(),
  535. );
  536. let program = provider.program(example::ID)?;
  537. // Build and send instructions
  538. println!("\nSend transaction with initialize and increment instructions");
  539. let initialize_ix = program
  540. .request()
  541. .accounts(accounts::Initialize {
  542. counter: counter.pubkey(),
  543. payer: program.payer(),
  544. system_program: system_program::ID,
  545. })
  546. .args(args::Initialize)
  547. .instructions()?
  548. .remove(0);
  549. let increment_ix = program
  550. .request()
  551. .accounts(accounts::Increment {
  552. counter: counter.pubkey(),
  553. })
  554. .args(args::Increment)
  555. .instructions()?
  556. .remove(0);
  557. let signature = program
  558. .request()
  559. .instruction(initialize_ix)
  560. .instruction(increment_ix)
  561. .signer(&counter)
  562. .send()
  563. .await?;
  564. println!(" Transaction confirmed: {}", signature);
  565. println!("\nFetch counter account data");
  566. let counter_account: Counter = program.account::<Counter>(counter.pubkey()).await?;
  567. println!(" Counter value: {}", counter_account.count);
  568. Ok(())
  569. }
  570. ```
  571. ```toml tab="Dependencies"
  572. [package]
  573. name = "rs"
  574. version = "0.1.0"
  575. edition = "2021"
  576. [dependencies]
  577. anchor-client = { version = "0.31.1", features = ["async"] }
  578. anchor-lang = "0.31.1"
  579. anyhow = "1.0.93"
  580. tokio = { version = "1.0", features = ["full"] }
  581. ```
  582. </Tabs>
  583. <Steps>
  584. <Step>
  585. The `declare_program!()` macro takes a single argument - the name of the
  586. program's IDL file (e.g. `example.json`):
  587. ```rust
  588. declare_program!(example); // Looks for /idls/example.json
  589. ```
  590. </Step>
  591. <Step>
  592. Bring into scope the generated modules:
  593. ```rust
  594. use example::{
  595. accounts::Counter, // Program Account types
  596. client::accounts, // Accounts for program instructions
  597. client::args, // Arguments for program instructions
  598. };
  599. ```
  600. </Step>
  601. <Step>
  602. Use the Client module to build the program's instructions:
  603. ```rust
  604. // Build initialize instruction
  605. let initialize_ix = program
  606. .request()
  607. // Accounts required for initialize instruction
  608. .accounts(accounts::Initialize {
  609. counter: counter.pubkey(),
  610. payer: program.payer(),
  611. system_program: system_program::ID,
  612. })
  613. // Arguments for initialize instruction (discriminator)
  614. .args(args::Initialize)
  615. .instructions()?
  616. .remove(0);
  617. ```
  618. ```rust
  619. // Build increment instruction
  620. let increment_ix = program
  621. .request()
  622. // Accounts required for increment instruction
  623. .accounts(accounts::Increment {
  624. counter: counter.pubkey(),
  625. })
  626. // Arguments for increment instruction (discriminator)
  627. .args(args::Increment)
  628. .instructions()?
  629. .remove(0);
  630. ```
  631. </Step>
  632. <Step>
  633. Add the program's instructions to a transaction and send the transaction:
  634. ```rust
  635. let signature = program
  636. .request()
  637. .instruction(initialize_ix)
  638. .instruction(increment_ix)
  639. .signer(&counter)
  640. .send()
  641. .await?;
  642. ```
  643. </Step>
  644. <Step>
  645. Use the Account module to fetch and deserialize the program's account types:
  646. ```rust
  647. // Counter type from accounts module
  648. let counter_account: Counter = program.account::<Counter>(counter.pubkey()).await?;
  649. ```
  650. </Step>
  651. </Steps>