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 directory | Source files |
|---|---|
libs | 756 |
apps | 42 |
(root) | 3 |
tools | 1 |
๐ก 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)
- Rust:
rustc 1.99.1 (58cf8f9c3 2026-10-13) - Solana CLI (Agave):
solana-cli 1.18.17 (src:c027cfc3; feat:4215500110, client:Agave) - Node.js:
v24.22.1 - npm:
11.21.1 - pnpm:
10.34.6
2. Clone at the tested commit
git clone https://github.com/Blockchains/tokengator.git && cd tokengator
git checkout b87eed94e06bf4c0d7b959571c801428d34bef1e3. Configure
The repo reads these environment variables. Copy the example file and fill in your own values. Never commit real keys.
.env.example:API_URL,AUTH_DISCORD_ADMIN_IDS,AUTH_DISCORD_CLIENT_ID,AUTH_DISCORD_CLIENT_SECRET,AUTH_DISCORD_ENABLED,AUTH_GITHUB_ADMIN_IDS,AUTH_GITHUB_CLIENT_ID,AUTH_GITHUB_CLIENT_SECRET,AUTH_GITHUB_ENABLED,AUTH_GOOGLE_ADMIN_IDS,AUTH_GOOGLE_CLIENT_ID,AUTH_GOOGLE_CLIENT_SECRET,AUTH_GOOGLE_ENABLED,AUTH_TWITTER_ADMIN_IDS,AUTH_TWITTER_CONSUMER_KEY,AUTH_TWITTER_CONSUMER_SECRET,AUTH_TWITTER_ENABLED,AUTH_PASSWORD_ENABLED,AUTH_REGISTER_ENABLED,AUTH_SOLANA_ADMIN_IDS,AUTH_SOLANA_ENABLED,CLOAK_MASTER_KEY,CLOAK_KEYCHAIN,COOKIE_DOMAINS,DATABASE_URL,DATABASE_PROVISION,GRAPHQL_PLAYGROUND,JWT_SECRET,HOST,PORT,SESSION_SECRET,SOLANA_ENDPOINT,SOLANA_FEE_PAYER_SECRET_KEY,SOLANA_FEE_PAYER_MINIMAL_BALANCE,WEB_URL
Fixes we had to apply to the pinned commit before it would build or run, with the reason for each:
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 buildsed -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
| Directory | Command | Result | Time |
|---|---|---|---|
. | pnpm install --no-frozen-lockfile | โ pass | 21s |
5. Build
| Directory | Command | Result | Time |
|---|---|---|---|
. | pnpm run build | โ pass | 32s |
7. Deploy to a local validator
8. Run and exercise it
| Directory | Command | Result | Time |
|---|---|---|---|
. | bash /workspace/sol-college/repo/tests/flows/tokengator.sh | โ exit 7 | 183s |
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:Extend it
- Add an integration test that runs the main flow end to end and asserts every account state change.
- Add a negative test: call each instruction with the wrong signer or account and check that it fails.
- Make the frontend read the cluster from an env var, deploy it to GitHub Pages and point it at your devnet program.
- 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.
libs/api/preset/data-access/src/lib/api-preset-minter.service.ts L1โ24 @ b87eed9 ยท MIT ยท originalimport * 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,- Line 13
TOKEN_2022_PROGRAM_ID,: Targets the Token-2022 program instead of the original Token Program. - Line 22
ComputeBudgetProgram,: Sets the compute-unit limit or price (priority fee).
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), ])- Line 102
connection.getParsedTokenAccountsByOwner(address, { programId: TOKEN_2022_PROGRAM_ID }),: Targets the Token-2022 program instead of the original Token Program. - 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.
| Directory | Command | Runs |
|---|---|---|
. | npm run start | NODE_ENV=production node dist/apps/api/main.js |
. | npm run build | pnpm build:web && pnpm prisma generate && pnpm build:api |
What you could build from it
- Fork the SuperStack copy and extend it within the Other gaps listed on the idea page.
- Learn the pattern first: Program Derived Addresses (PDAs)
- Learn the pattern first: Cross-Program Invocations (CPIs)