Sol.College

Full lesson

carsa-solana

What it does

From the project README:

[](https://solana.com/) [](https://www.anchor-lang.com/) [](https://nextjs.org/) [](https://www.typescriptlang.org/)
Original
Candra0x6/carsa-solana
SuperStack fork
Blockchains/carsa-solana @ 1c61eed
Stars
0
Last push (original)
2025-10-29
Languages
TypeScript, Rust, Shell, JavaScript, CSS, Makefile
Detected stack
AnchorMetaplexNext.jsweb3.js
Categories
Other
Licence
MIT

Architecture

129 source files. By extension: .tsx ×63, .ts ×56, .rs ×8, .js ×2.

Top-level directorySource files
carsa-frontend102
carsa-contracts27

On-chain programs: carsa-contracts/programs/carsa

Dapp: devnet deploy pending

⏳ Devnet deploy pending — goes live when the devnet wallet is topped up. This fork's own dapp is not deployed yet, so there is no program address or transaction to show. It is queued in dapp-queue.txt; once devnet SOL is available it is deployed, exercised with real devnet transactions, and this section switches to the live dapp at /dapps/carsa-solana/. Until then, the lesson and step-by-step guide below are complete, and you can run it locally with the commands below.

Key code, explained

Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.

Candra0x6/carsa-solana · carsa-contracts/programs/carsa/src/instructions/voucher_pool.rs L217–240 @ 1c61eed · MIT · original
                to: ctx.accounts.pool_vault_ata.to_account_info(),                authority: ctx.accounts.pool_delegate.to_account_info(),            },        );        token::transfer(transfer_ctx, amount)?;         // Update pool state        pool_state.total_voucher_staked = pool_state            .total_voucher_staked            .checked_add(amount)            .ok_or(CarsaError::Overflow)?;         // Update user stake record        user_stake_record.staked_amount = new_user_total;        user_stake_record.last_action_at = clock.unix_timestamp;         msg!("Voucher deposited successfully");        msg!("User: {}", ctx.accounts.user.key());        msg!("Amount: {}", amount);        msg!("New user total: {}", new_user_total);        msg!("Pool total staked: {}", pool_state.total_voucher_staked);         // Emit event        emit!(VoucherDepositedEvent {
  1. Line 221 token::transfer(transfer_ctx, amount)?;: An SPL token transfer through the Token Program.
  2. Line 226 .checked_add(amount): Uses checked arithmetic so overflow fails instead of wrapping.
  3. Line 229 // Update user stake record: Touches staking state: the amount locked and when it was locked.
  4. Line 231 user_stake_record.last_action_at = clock.unix_timestamp;: Reads on-chain time from the Clock sysvar.
  5. Line 233 msg!("Voucher deposited successfully");: Logs to the program log, which is visible in explorers and the test output.
  6. Line 240 emit!(VoucherDepositedEvent {: Emits an Anchor event that clients can parse from the logs.
Candra0x6/carsa-solana · carsa-contracts/programs/carsa/src/instructions/mint_tokens.rs L13–36 @ 1c61eed · MIT · original
    pub update_authority: Signer<'info>,        /// The mint account for Lokal tokens    /// This will be created and owned by the SPL Token program    #[account(        init,        payer = update_authority,        mint::decimals = 9, // Standard SPL token decimals        mint::authority = mint_authority, // PDA as mint authority        mint::freeze_authority = mint_authority, // PDA can freeze tokens if needed    )]    pub mint: Account<'info, Mint>,        /// Program Derived Address that acts as the mint authority    /// This ensures only the program can mint new tokens    /// CHECK: This account is derived using seeds and verified in constraints    #[account(        seeds = [MINT_AUTHORITY_SEED],        bump,    )]    pub mint_authority: UncheckedAccount<'info>,        /// Configuration account that stores mint metadata and settings    /// Uses PDA to ensure uniqueness and program ownership
  1. Line 13 pub update_authority: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 18 init,: Creates and pays for a new account. Space and payer must be specified.
  3. Line 30 seeds = [MINT_AUTHORITY_SEED],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 31 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
Candra0x6/carsa-solana · carsa-contracts/programs/carsa/src/instructions/rewards.rs L1–24 @ 1c61eed · MIT · original
use anchor_lang::prelude::*;use anchor_spl::token::{self as token, Mint, Token, TokenAccount};use crate::state::*;use crate::error::CarsaError; /// Register a new merchant in the Carsa loyalty program/// This instruction creates a merchant account with specific cashback rates#[derive(Accounts)]#[instruction(name: String, category: String, cashback_rate: u16)]pub struct RegisterMerchant<'info> {    /// The merchant's wallet that will own this merchant account    #[account(mut)]    pub merchant_owner: Signer<'info>,        /// The merchant account to be created    /// Uses PDA for security and to prevent duplicate merchants    #[account(        init,        payer = merchant_owner,        space = MerchantAccount::LEN,        seeds = [MERCHANT_SEED, merchant_owner.key().as_ref()],        bump,    )]    pub merchant_account: Account<'info, MerchantAccount>,
  1. Line 13 pub merchant_owner: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 18 init,: Creates and pays for a new account. Space and payer must be specified.
  3. Line 21 seeds = [MERCHANT_SEED, merchant_owner.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 22 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
Candra0x6/carsa-solana · carsa-contracts/programs/carsa/src/instructions/transfers.rs L13–36 @ 1c61eed · MIT · original
    pub sender: Signer<'info>,        /// The sender's token account    #[account(        mut,        constraint = sender_token_account.owner == sender.key(),        constraint = sender_token_account.mint == config.mint @ CarsaError::MintAuthorityMismatch    )]    pub sender_token_account: Account<'info, TokenAccount>,        /// The recipient's token account    #[account(        mut,        constraint = recipient_token_account.mint == config.mint @ CarsaError::MintAuthorityMismatch,        constraint = recipient_token_account.key() != sender_token_account.key() @ CarsaError::SelfTransferNotAllowed    )]    pub recipient_token_account: Account<'info, TokenAccount>,        /// Configuration account containing mint settings    #[account(        seeds = [CONFIG_SEED],        bump = config.config_bump,    )]    pub config: Account<'info, LokalMintConfig>,
  1. Line 13 pub sender: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 33 seeds = [CONFIG_SEED],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 34 bump = config.config_bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.

How to run, build and test

These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.

DirectoryCommandRuns
carsa-contractsnpm run lintprettier */*.js "*/**/*{.js,.ts}" --check
carsa-frontendnpm run devnext dev --turbopack
carsa-frontendnpm run startnext start
carsa-frontendnpm run buildprisma generate && next build --turbopack
carsa-frontendnpm run linteslint
carsa-contractsanchor buildcompile programs and generate IDLs
carsa-contractsanchor testlocal validator + test suite
carsa-contractsanchor run testyarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts
.make helpMakefile target
.make setupMakefile target
.make setup-localMakefile target
.make redeployMakefile target
.make redeploy-localMakefile target
.make devMakefile target
.make dev-localMakefile target
.make testMakefile target

What you could build from it