Sol.College

Full lesson

tokengator

๐ŸŠ๐Ÿ‘€

What it does

From the project README:

TokenGator is currently split into two repositories, one for the platform (API/SDK/Web UI) and one for the Anchor program.
Original
pubkeyapp/tokengator
SuperStack fork
Blockchains/tokengator @ b87eed9
Stars
3
Last push (original)
2024-04-16
Languages
TypeScript, CSS, JavaScript, Dockerfile, HTML, Shell
Detected stack
web3.js
Categories
Other
Licence
MIT

Architecture

802 source files. By extension: .ts ร—530, .tsx ร—268, .js ร—4.

Top-level directorySource files
libs756
apps42
(root)3
tools1

๐ŸŸก Builds, verified 2026-11-02 Tested on 2026-11-02: full log results.json

Build it yourself: step-by-step guide

Every command below was run on 2026-11-02 by tests/run_fork.py against the pinned commit, and the results come from that run. The full output is in the committed log.

1. Prerequisites (versions we tested with)

2. Clone at the tested commit

git clone https://github.com/Blockchains/tokengator.git && cd tokengator
git checkout b87eed94e06bf4c0d7b959571c801428d34bef1e

3. Configure

The repo reads these environment variables. Copy the example file and fill in your own values. Never commit real keys.

Fixes we had to apply to the pinned commit before it would build or run, with the reason for each:

  1. npm pkg delete dependencies.@pubkey-ui/generators. @pubkey-ui/generators is no longer on npm (404); only the Nx code generators use it, not the build
  2. sed -i 's/name={user?.username}/name={user?.username ?? undefined}/' libs/web/core/ui/src/lib/ui-header-profile.tsx libs/web/user/ui/src/lib/user-ui-avatar.tsx. TS2322 under the resolved Mantine types: null is not assignable to string | undefined

4. Install dependencies

DirectoryCommandResultTime
.pnpm install --no-frozen-lockfileโœ… pass21s

5. Build

DirectoryCommandResultTime
.pnpm run buildโœ… pass32s

7. Deploy to a local validator

8. Run and exercise it

DirectoryCommandResultTime
.bash /workspace/sol-college/repo/tests/flows/tokengator.shโŒ exit 7183s
Output tail: bash /workspace/sol-college/repo/tests/flows/tokengator.sh
2
๐Ÿš€  Your database is now in sync with your Prisma schema. Done in 77ms
uptime:

Notes: Postgres 17 runs locally on port 5433. The fee payer is a throwaway keypair generated per run.

Extend it

  1. Add an integration test that runs the main flow end to end and asserts every account state change.
  2. Add a negative test: call each instruction with the wrong signer or account and check that it fails.
  3. Make the frontend read the cluster from an env var, deploy it to GitHub Pages and point it at your devnet program.
  4. Wire the build into CI by copying this site's verify-forks workflow.

Key code, explained

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

pubkeyapp/tokengator ยท libs/api/preset/data-access/src/lib/api-preset-minter.service.ts L1โ€“24 @ b87eed9 ยท MIT ยท original
import * as anchor from '@coral-xyz/anchor'import { AnchorProvider, Program } from '@coral-xyz/anchor'import { Injectable, Logger } from '@nestjs/common'import { Preset, PresetActivity } from '@prisma/client'import {  ASSOCIATED_TOKEN_PROGRAM_ID,  createSyncNativeInstruction,  createWrappedNativeAccount,  getAssociatedTokenAddressSync,  getMint,  NATIVE_MINT,  NATIVE_MINT_2022,  TOKEN_2022_PROGRAM_ID,  TOKEN_PROGRAM_ID,} from '@solana/spl-token' import {  AddressLookupTableAccount,  AddressLookupTableProgram,  BlockhashWithExpiryBlockHeight,  Commitment,  ComputeBudgetProgram,  Keypair,  PublicKey,
  1. Line 13 TOKEN_2022_PROGRAM_ID,: Targets the Token-2022 program instead of the original Token Program.
  2. Line 22 ComputeBudgetProgram,: Sets the compute-unit limit or price (priority fee).
pubkeyapp/tokengator ยท libs/web/solana/data-access/src/lib/use-account.tsx L97โ€“119 @ b87eed9 ยท MIT ยท original
} async function getAllTokenAccounts(connection: Connection, address: PublicKey) {  const [tokenAccounts, token2022Accounts] = await Promise.all([    connection.getParsedTokenAccountsByOwner(address, { programId: TOKEN_PROGRAM_ID }),    connection.getParsedTokenAccountsByOwner(address, { programId: TOKEN_2022_PROGRAM_ID }),  ])  return [...tokenAccounts.value, ...token2022Accounts.value]} async function requestAndConfirmAirdrop({  address,  amount,  connection,}: {  connection: Connection  address: PublicKey  amount: string}) {  const [latestBlockhash, signature] = await Promise.all([    connection.getLatestBlockhash(),    connection.requestAirdrop(address, parseFloat(amount) * LAMPORTS_PER_SOL),  ])
  1. Line 102 connection.getParsedTokenAccountsByOwner(address, { programId: TOKEN_2022_PROGRAM_ID }),: Targets the Token-2022 program instead of the original Token Program.
  2. Line 117 connection.getLatestBlockhash(),: Fetches a recent blockhash. Transactions expire if it gets too old.

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
.npm run startNODE_ENV=production node dist/apps/api/main.js
.npm run buildpnpm build:web && pnpm prisma generate && pnpm build:api

What you could build from it

Used in lessons