Teddies Platform is a full-stack Web3 collector experience built around Solana NFT ownership. Collectors authenticate with their wallet, sync verified holdings, earn badges from rules applied to NFT metadata and on-chain behaviour, and showcase a public profile with leaderboard ranking and optional Twitter linkage.
The codebase is organised as a TypeScript monorepo (pnpm workspaces + Turborepo) with a Next.js web application and an Express API backed by PostgreSQL via Prisma.
GitHub: https://github.com/soladdev/teddis-nft (soladdev/teddis-nft)
flowchart TB
subgraph Client["Collector browser"]
UI["Next.js 14 App Router"]
WA["Solana Wallet Adapter"]
NA["NextAuth.js v5 Credentials"]
UI --> WA
UI --> NA
end
subgraph API["Backend API"]
EX["Express"]
CRON["node-cron jobs"]
EX --> CRON
end
subgraph Data["Data & assets"]
PG[("PostgreSQL")]
PR["Prisma ORM"]
S3["AWS S3"]
PG --- PR
end
subgraph Chain["Blockchain & indexing"]
SOL["Solana RPC / metadata"]
IDX["Indexer service BACKEND_WEB3_URL"]
end
subgraph External["Third parties"]
TW["Twitter OAuth 2"]
SE["Sentry"]
end
NA -->|"POST /auth/login , JWT cookie"| EX
UI -->|"REST /api , /auth"| EX
WA -.->|"Sign nonce"| SOL
EX --> PR
EX --> S3
EX --> TW
EX --> SE
CRON -->|"calculateBadges"| PR
CRON -->|"processNftsOfUser"| IDX
IDX -->|"POST /api/nfts/webhook"| EX
EX -.->|"NFT catalogue sync"| SOL
| Path | Role |
|---|---|
apps/frontend |
Next.js collector UI: wallet connection, profiles (/collectors/...), sessions, animations |
apps/backend |
Express REST API, Prisma models, badge engine, cron jobs, screenshot/export utilities |
packages/eslint-config |
Shared ESLint configuration |
packages/typescript-config |
Shared TypeScript tsconfig bases |
- Turborepo orchestrates
build,dev,lint,format, andtestacross packages with task caching and dependency ordering (dependsOn: ["^build"]where applicable). pnpmworkspaces (pnpm-workspace.yaml) deduplicate dependencies and link@repo/*packages efficiently.- Husky + Commitlint (Conventional Commits) support consistent commits; lint-staged + Prettier keep formatting aligned.
- Next.js 14 delivers server/client rendering, routing, and optimized assets (including
sharpfor image pipelines where configured). - React 18 for UI composition.
- Tailwind CSS with
tailwind-merge,clsx, andclass-variance-authorityfor scalable styling;tailwindcss-animatefor motion primitives. - Radix UI primitives (
Dialog,Select,Tooltip, etc.) provide accessible, unstyled building blocks. - Solana Wallet Adapter (
@solana/wallet-adapter-react, Phantom and multi-wallet support) connects browsers to Solana for signing. @solana/web3.jsis used where low-level Solana interactions are required alongside adapters.- NextAuth.js v5 (beta) with a Credentials provider integrates wallet login with the backend: the frontend posts wallet address, nonce, signature, and wallet type to
/auth/login; the backend returns a JWT that is mirrored into the NextAuth JWT/session so UI code can useuseSession(). - Sentry Next.js SDK captures errors and performance signals in production-like setups.
- GSAP +
@gsap/reactand@studio-freight/react-lenissupport polished motion and scroll smoothing where used in the UI. - Supporting libraries include
dayjs(dates),jwt-decode,lucide-react/bootstrap-icons(icons),react-infinite-scroll-component, andfile-saverfor downloads.
- Express exposes HTTP APIs under
/authand/api, withcorsrestricted viaALLOWED_ORIGINS,cookie-parser,express-session, andmorganlogs routed through Winston. - Prisma maps the domain to PostgreSQL (
DATABASE_URL,directUrlfor migrations). The schema models users, profiles, NFT catalogue rows, owned mints per profile, badges, conditions, and earned badge state. tsupbundles TypeScript todist/for production;ts-node+nodemonpower local development.- jsonwebtoken issues tokens after wallet verification;
tweetnacl/bs58support cryptographic verification aligned with Solana wallet signatures (seesignature.utils). - twitter-api-v2 implements OAuth-style flows to link Twitter identities to wallets (authorization URL + callback).
- node-cron schedules recurring jobs (badge recalculation and NFT status refresh—see Business logic).
- AWS SDK for JavaScript v3 — S3 client uploads assets where the profile/screenshot pipeline stores images.
- Puppeteer renders profile views for downloadable screenshots; Sharp and Jimp handle image processing.
- Sentry Node +
sentry-cliinject and upload source maps from the build pipeline.
- Nonce —
POST /auth/noncestores a server-generated nonce on theUserrow for the given wallet address (upsert). - Wallet login — The user signs the nonce with their Solana wallet.
POST /auth/loginverifies the signature (wallet-type aware), then:- Requires at least one Teddies NFT resolved from chain/catalogue logic (
getWalletTeddiesNFTs). If none are found, login fails with a clear error instead of issuing a token. - Reconciles
OwnedNftMintrows with current holdings (including resolving duplicates across profiles where applicable). - Ensures
UserProfileexists with a generated username if missing and seeds displayed NFT URLs when appropriate. - Issues a JWT and sets an
httpOnlycookie (token).
- Requires at least one Teddies NFT resolved from chain/catalogue logic (
- Frontend session — NextAuth Credentials provider mirrors backend success into an app session (JWT strategy, configurable max age in
auth.ts).
- Public profile —
GET /api/profiles/:walletreturns showcase fields (username, colours, displayed mints, points, rarest badge, Twitter fields). Rank is computed relative to other collectors (getUserRank). - Updates —
PUT /api/profiles(authenticated) accepts whitelisted fields (background,color,username, displayed mints, Twitter identifiers). Usernames are unique and length-limited; changing username markschangedUsername. - Leaderboard —
GET /api/leaderboardreturns ranked collectors (top profiles by engagement rules defined in service layer); optionalauthBypassallows read patterns suited for both anonymous and authenticated views.
- Catalogue reads —
GET /api/nftscombines database NFT metadata with optional wallet-scoped filtering: whenwalletAddressis supplied, responses mergeOwnedNftMintfields (streak,neverListed,latestSignature) into each document for richer UI. - Indexer integration —
processNftsOfUsercalls an externalBACKEND_WEB3_URLendpoint with mints and a webhook URL pointing atPOST /api/nfts/webhook. That webhook applies batched updates toOwnedNftMintso traits used by badge rules stay aligned with indexer-backed signals.
- Models —
Badgedefines name, rarity, imagery, andConditionrows with point weights.UserBadge/UserBadgeConditionpersist what was satisfied and which mints contributed. - Evaluation —
calculateBadgesloads users with profiles, owned mints, and existing badges, then scores each badge via named criteria functions (evaluateBadge), including ownership counts, trait simplicity (attributes.rank_explain), never-listed behaviour, rankings (Top 10 / 100 / 500), streak-related scoring, and other campaign-specific rules mapped by badge name. - Scheduling — A minute-level cron recomputes badges to keep points and rarity summaries fresh; a daily cron triggers
processNftsOfUser()without arguments to refresh holder metrics across distinct mints via the indexer.
- Authenticated users obtain a Twitter authorization URL (
GET /auth/twitter/auth). State and PKCE verifier are persisted onUser, thenGET /auth/twitter/callbackvalidates state, exchanges the code, and updates profile Twitter fields before redirecting to the frontend collector route.
- Structured logging (Winston) and HTTP logging (Morgan).
- Sentry on API and Next.js for error tracking and optional profiling.
- Node.js ≥ 18 (see root
package.jsonengines) - pnpm (recommended for this workspace)
- PostgreSQL reachable via
DATABASE_URL(andDIRECT_URLfor Prisma migrations if split)
Additional integrations expect environment configuration on both apps (backend URL, frontend URL, CORS origins, JWT/session secrets, AWS credentials for S3, Sentry DSN/org/project, Twitter OAuth credentials, indexer URL/API key, Solana RPC if applicable).
Install dependencies from the repository root:
pnpm installRun database migrations and generate Prisma Client for the backend (from apps/backend):
cd apps/backend
pnpm prisma migrate deploy
pnpm prisma generateDevelopment — run both apps via Turborepo from the root:
pnpm devBuild everything:
pnpm buildQuality gates:
pnpm lint
pnpm format:check
pnpm test| Area | Methods | Notes |
|---|---|---|
| Auth | POST /auth/nonce, POST /auth/login, GET /auth/twitter/auth, GET /auth/twitter/callback |
Wallet + Twitter flows |
| Profiles | GET /api/profiles/:wallet, PUT /api/profiles, GET /api/profile/screenshot, GET /api/leaderboard |
Screenshot requires auth |
| NFTs | GET /api/nfts, POST /api/nfts/webhook |
Webhook driven by indexer |
| Badges | GET /api/badges, GET /api/badges/:wallet |
Optional query flags per controller |
Package metadata uses the name teddies-platform. This repository may appear under paths such as teddis-platform depending on checkout—the application behaviour and documentation above refer to the Teddies collector product implemented here.