Skip to main content

What you will learn

  • How an Anchor program, its IDL, and a generated client fit together
  • How to read program accounts and send instructions from a mobile app
  • How to regenerate the client after changing your program
  • How to deploy your own program and point the app at it

Prerequisites

Create the project

The expo-kit-anchor template ships an Expo app and an Anchor program in one project, already wired together.
1

Generate the app

Select the expo-kit-anchor template when prompted.
2

Run it

The app connects to a hello_world counter program already deployed on devnet. Connect a wallet, initialize a counter, and increment it before changing anything — that confirms your toolchain works end to end.

How the pieces fit together

Everything after the program is generated:
You write the program. Building it writes the IDL. Codama reads the IDL and generates a typed client. The app imports that client. When the program changes, you rebuild and regenerate, and the instructions and types the app uses always match what the program declares.
The IDL is the contract between the two sides. You never hand-write the client — every account fetcher and instruction builder the app calls is generated from it.

The Anchor program

The program lives in anchor/programs/hello_world/src/. Its entrypoint declares the program address and the instructions:
state.rs defines the account the program stores data in:
Each wallet gets its own counter, at a PDA derived from the program address and the wallet:
Because the address is derived, the app can always find a wallet’s counter without storing the address anywhere.

Generating the client

anchor/codama.mjs points Codama at the IDL and tells it where to write the client:
Regenerate with:
The generated code is re-exported from anchor/src/index.ts, and tsconfig.json maps that directory to @project/anchor, so the app imports from one place rather than reaching into generated files:

Reading program accounts

findCounterPda and fetchMaybeCounter are both generated. The hook derives the PDA for the connected wallet and fetches the account, which may not exist yet:
fetchMaybeCounter returns a result with an exists flag rather than throwing, which is what lets the UI distinguish “not initialized” from a counter at zero.

Sending instructions

Instruction builders are generated too — getInitializeInstructionAsync and getIncrementInstructionAsync. The hook builds a transaction message with @solana/kit and hands it to the wallet through Mobile Wallet Adapter:
The wallet’s sending signer must be the only signer in the transaction. Use the same signer instance as fee payer and pass it to the instruction builder, which is why getTransactionSigner is called once and reused above.
Each instruction is exposed as a mutation, so the UI gets pending and error state for free:
Because the wallet submits the transaction rather than the app, the hook polls for confirmation afterwards and invalidates the counter query on settle — including on failure, since the wallet may have submitted after the app stopped waiting.

Changing the program

Add an instruction to the program, then rebuild and regenerate:
The new instruction appears in the IDL, the generated client picks up a builder for it, and the app can call it. Add a mutation alongside the existing ones and wire it to the UI.

Deploying your own program

The template points at a program someone else deployed, so you can run the app before you have a program of your own. To change program behavior you need your own deployment.
1

Take ownership of the program

This generates a program keypair, writes the new address into lib.rs and Anchor.toml, rebuilds the program, and reruns Codama — so the address changes everywhere at once.
2

Fund the deploying wallet

Deploying costs SOL. Check the wallet paying for it:
If it has no SOL, fund it at the Solana Faucet.
3

Deploy

After deploying to a new address the app shows not initialized again. The counter PDA is derived from the program address, so a new program means a new counter.
For local development, npm run anchor:localnet runs a validator and npm run anchor:deploy:localnet deploys against it.

Common issues

“Attempt to debit an account but found no record of a prior credit” on deploy — the deploying wallet has never held SOL. Fund the address from solana address at the faucet. “Invalid instruction data” or an unknown instruction — the client is out of step with the program. Run npm run anchor:build then npm run codama:js. “Account does not exist” — the PDA has not been initialized for this wallet, or the program address changed and the derived address moved with it. Types not resolving from @project/anchor — the client has not been generated yet. Run npm run codama:js, which writes into anchor/src/client/js.
Last modified on August 27, 2026