How I Built a Counter Program in Anchor and Finally Trusted My Tests

rust dev.to

The first thing that surprised me about Anchor was how much work happens in the accounts struct. My Initialize struct has three fields, but the real heavy lifting sits on the counter:

#[account(init, payer = authority, space = 8 + Counter::INIT_SPACE)]
pub counter: Account<'info, Counter>,
Enter fullscreen mode Exit fullscreen mode

init tells Anchor to create a brand new account right in the transaction. payer says who pays the rent. space calculates the exact number of bytes needed. In a traditional backend I would write all that allocation logic by hand. Here Anchor does it for me from one annotation. The handler itself is almost boring: grab the counter from ctx.accounts, set the authority, set count to zero, and return. That’s it.

The increment handler is even smaller-just a checked_add call to bump the count. But the real security lives in the Increment accounts struct, on a single line:

#[account(mut, has_one = authority)]
pub counter: Account<'info, Counter>,
Enter fullscreen mode Exit fullscreen mode

has_one = authority means Anchor checks, before my handler runs, that the authority stored on-chain matches the signer of this transaction. If someone else tries to increment my counter, the transaction fails immediately. I didn't write an if statement or compare keys. I declared the rule, and Anchor enforces it. That’s the biggest mental shift from a Web2 backend: authorization becomes a declarative constraint, not a middleware check I might forget.

I tested the whole flow with LiteSVM, a local Solana simulator that runs my compiled program in milliseconds. I wrote one test that calls initialize then increment, then reads the account and asserts count == 1. I wrote another test where a different wallet tries to increment and the transaction must fail. If the second test ever passes, someone removed the has_one constraint and my program is wide open. On Day 61 I planted a bug on purpose-changing checked_add(1) to checked_add(2)-and the test caught it instantly. Before this week I was deploying to devnet and refreshing Explorer like a detective. Now I have a feedback loop that feels as fast as a normal unit test, and that’s why I trust my code.

If I had another week I’d add a decrement handler and a transfer_authority instruction. The pattern is the same: an accounts struct with constraints, a tiny handler, and a LiteSVM test. The template scales. If you're starting with Anchor, set up the test harness first. The moment you can change a line and see green or red in under a second is the moment you stop guessing and start understanding.

Source: dev.to

arrow_back Back to Tutorials