Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tests Manager (Anchor + Rust)

Beginner-friendly Solana smart contract project using the Anchor framework.

The program acts like a tiny CRUD backend:

  • create_testCreate a test record
  • update_testUpdate title/score
  • delete_testDelete a record (close account)
  • get_testRead one record (and emit an event)
  • get_user_testsRead many records (demonstration; real listing is off-chain)

Folder structure

.
├── Anchor.toml
├── Cargo.toml
├── README.md
├── migrations
│   └── deploy.js
├── package.json
├── programs
│   └── tests_manager
│       ├── Cargo.toml
│       └── src
│           └── lib.rs
├── tests
│   └── tests_manager.js

What each file does

  • Anchor.toml: Anchor config (cluster, wallet, program id for localnet).
  • Cargo.toml: Rust workspace (Anchor expects programs under programs/*).
  • programs/tests_manager/Cargo.toml: Program crate config + dependencies.
  • programs/tests_manager/src/lib.rs: The on-chain program (accounts + instructions).
  • tests/tests_manager.js: JavaScript tests showing CRUD calls.
  • migrations/deploy.js: Anchor deploy hook (not needed for this simple program).
  • package.json: Node tooling for Anchor tests.

How unique IDs are handled (simple)

On-chain programs don’t have “auto-increment integers” like SQL. We implement the simplest on-chain version:

  • A single Config account stores next_id: u64
  • Every create_test:
    1. reads config.next_id as the new ID
    2. creates the Test account at a deterministic PDA using that ID
    3. increments config.next_id

This is easy to understand, but note:

  • It’s a global counter (IDs are unique across the whole program).
  • The program uses a PDA for Config and for each Test, so addresses are deterministic.

Program design (CRUD mindset)

Accounts (database tables)

In programs/tests_manager/src/lib.rs:

  • Config (1 row total)

    • next_id: u64
  • Test (many rows)

    • id: u64 (unique)
    • owner: Pubkey (who owns this record)
    • title: String
    • score: u8
    • created_at: i64 (Unix timestamp)

PDAs (record “primary keys”)

Each test lives at a PDA derived from:

["test", owner_pubkey, id_le_bytes]

So the same (owner, id) always points to the same on-chain address.

Instructions (what each one does)

1) create_test(title, score)

  • Creates Config if missing (first-ever create)
  • Uses config.next_id as the new id
  • Creates a new Test PDA for (owner, id)
  • Saves: id, owner, title, score, created_at

2) update_test(id, title, score)

  • Derives the Test PDA from (owner, id)
  • Checks test.owner == signer
  • Updates title and score

3) delete_test(id)

  • Derives the Test PDA from (owner, id)
  • Checks test.owner == signer
  • Closes the account and returns rent to the owner

4) get_test(id)

Solana programs can’t “return” data like a normal web server.

So get_test is implemented as:

  • verify the correct PDA was provided for (owner, id)
  • emit an event containing the test fields

In practice, clients usually do:

  • program.account.test.fetch(testPda) (RPC read)

5) get_user_tests(owner_pubkey)

Programs also can’t scan the chain and find “all accounts for this user”.

Recommended approach (off-chain):

  • program.account.test.all([{ memcmp: { offset: 16, bytes: ownerBase58 } }])

This project still includes a get_user_tests instruction as a demo:

  • the client passes candidate Test accounts in remaining_accounts
  • the program filters them and emits events for those owned by owner_pubkey

Run locally

Prereqs:

  • Solana CLI installed + configured
  • Anchor installed (avm recommended)
  • Node.js installed

Commands:

npm install
anchor build
anchor test

If you change the program id, sync it:

anchor keys sync

Example requests (client-side)

All examples below are in tests/tests_manager.js.

  • Create:
    • program.methods.createTest("Intro to Anchor", 95).accounts({ ... }).rpc()
  • Update:
    • program.methods.updateTest(new anchor.BN(id), "New title", 91).accounts({ ... }).rpc()
  • Delete:
    • program.methods.deleteTest(new anchor.BN(id)).accounts({ ... }).rpc()
  • Fetch one:
    • program.account.test.fetch(testPda)
  • Fetch all for user (recommended):
    • program.account.test.all([{ memcmp: { offset: 16, bytes: ownerBase58 } }])

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages