Skip to content

soladdev/teddies-nft

Repository files navigation

Teddies Platform

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)


teddis

Architecture overview

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
Loading

Repository layout

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

Technology stack (in depth)

Monorepo & developer experience

  • Turborepo orchestrates build, dev, lint, format, and test across packages with task caching and dependency ordering (dependsOn: ["^build"] where applicable).
  • pnpm workspaces (pnpm-workspace.yaml) deduplicate dependencies and link @repo/* packages efficiently.
  • Husky + Commitlint (Conventional Commits) support consistent commits; lint-staged + Prettier keep formatting aligned.

Frontend (apps/frontend)

  • Next.js 14 delivers server/client rendering, routing, and optimized assets (including sharp for image pipelines where configured).
  • React 18 for UI composition.
  • Tailwind CSS with tailwind-merge, clsx, and class-variance-authority for scalable styling; tailwindcss-animate for 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.js is 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 use useSession().
  • Sentry Next.js SDK captures errors and performance signals in production-like setups.
  • GSAP + @gsap/react and @studio-freight/react-lenis support 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, and file-saver for downloads.

Backend (apps/backend)

  • Express exposes HTTP APIs under /auth and /api, with cors restricted via ALLOWED_ORIGINS, cookie-parser, express-session, and morgan logs routed through Winston.
  • Prisma maps the domain to PostgreSQL (DATABASE_URL, directUrl for migrations). The schema models users, profiles, NFT catalogue rows, owned mints per profile, badges, conditions, and earned badge state.
  • tsup bundles TypeScript to dist/ for production; ts-node + nodemon power local development.
  • jsonwebtoken issues tokens after wallet verification; tweetnacl / bs58 support cryptographic verification aligned with Solana wallet signatures (see signature.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-cli inject and upload source maps from the build pipeline.

Business logic

Identity and access

  1. NoncePOST /auth/nonce stores a server-generated nonce on the User row for the given wallet address (upsert).
  2. Wallet login — The user signs the nonce with their Solana wallet. POST /auth/login verifies 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 OwnedNftMint rows with current holdings (including resolving duplicates across profiles where applicable).
    • Ensures UserProfile exists with a generated username if missing and seeds displayed NFT URLs when appropriate.
    • Issues a JWT and sets an httpOnly cookie (token).
  3. Frontend session — NextAuth Credentials provider mirrors backend success into an app session (JWT strategy, configurable max age in auth.ts).

Profile and leaderboard

  • Public profileGET /api/profiles/:wallet returns showcase fields (username, colours, displayed mints, points, rarest badge, Twitter fields). Rank is computed relative to other collectors (getUserRank).
  • UpdatesPUT /api/profiles (authenticated) accepts whitelisted fields (background, color, username, displayed mints, Twitter identifiers). Usernames are unique and length-limited; changing username marks changedUsername.
  • LeaderboardGET /api/leaderboard returns ranked collectors (top profiles by engagement rules defined in service layer); optional authBypass allows read patterns suited for both anonymous and authenticated views.

NFT catalogue and holder enrichment

  • Catalogue readsGET /api/nfts combines database NFT metadata with optional wallet-scoped filtering: when walletAddress is supplied, responses merge OwnedNftMint fields (streak, neverListed, latestSignature) into each document for richer UI.
  • Indexer integrationprocessNftsOfUser calls an external BACKEND_WEB3_URL endpoint with mints and a webhook URL pointing at POST /api/nfts/webhook. That webhook applies batched updates to OwnedNftMint so traits used by badge rules stay aligned with indexer-backed signals.

Badge system

  • ModelsBadge defines name, rarity, imagery, and Condition rows with point weights. UserBadge / UserBadgeCondition persist what was satisfied and which mints contributed.
  • EvaluationcalculateBadges loads 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.

Twitter linking

  • Authenticated users obtain a Twitter authorization URL (GET /auth/twitter/auth). State and PKCE verifier are persisted on User, then GET /auth/twitter/callback validates state, exchanges the code, and updates profile Twitter fields before redirecting to the frontend collector route.

Observability and ops

  • Structured logging (Winston) and HTTP logging (Morgan).
  • Sentry on API and Next.js for error tracking and optional profiling.

Prerequisites

  • Node.js ≥ 18 (see root package.json engines)
  • pnpm (recommended for this workspace)
  • PostgreSQL reachable via DATABASE_URL (and DIRECT_URL for 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).


Getting started

Install dependencies from the repository root:

pnpm install

Run database migrations and generate Prisma Client for the backend (from apps/backend):

cd apps/backend
pnpm prisma migrate deploy
pnpm prisma generate

Development — run both apps via Turborepo from the root:

pnpm dev

Build everything:

pnpm build

Quality gates:

pnpm lint
pnpm format:check
pnpm test

API surface (summary)

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

License and naming

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.

About

A one-of-a-kind Web3 platform built to power the Teddies ecosystem. A unique web3-native platform for the Teddies ecosystem. Earn points, unlock badges, flex your profile, participate in upcoming drops.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors