Skip to content

ADR-019: NFT Minting Infrastructure Integration ​

Date: 2025-11-09 Status: πŸ€” Proposed Decision Makers: Architecture Team Related ADRs: ADR-008 (EmProps UI Monorepo Integration)


Executive Summary ​

This ADR proposes integrating the EmProps NFT minting infrastructure (three standalone packages: emprops-hardhat, emprops-ponder, emprops-react-web3) from the nft_investigation workspace into the emerge-turbo monorepo. The integration enables users to launch NFT collections from AI-generated content, creating a proof-of-concept for blockchain-based content monetization.

Current State:

  • βœ… Smart Contracts - Complete Solidity implementation in /Users/the_dusky/code/emprops/nft_investigation/emprops-hardhat
  • βœ… Blockchain Indexer - Ponder-based event indexing in /Users/the_dusky/code/emprops/nft_investigation/emprops-ponder
  • βœ… React Web3 SDK - Demo implementation in /Users/the_dusky/code/emprops/nft_investigation/emprops-react-web3
  • ❌ Integration - No connection to emerge-turbo platform

Target State:

  • βœ… Complete NFT Stack - Smart contracts, indexer, and launchpad UI integrated into monorepo
  • βœ… User Flow - "Launch as NFT" button in emprops-studio β†’ NFT launchpad β†’ payment β†’ content generation β†’ NFT minting
  • βœ… Proof of Concept - Self-contained NFT modules with minimal changes to existing apps

Key Decision: How do we integrate three separate NFT packages into emerge-turbo while maintaining isolation and creating a working proof-of-concept?


Table of Contents ​

  1. Context
  2. Problem Statement
  3. Decision
  4. Technical Architecture
  5. Implementation Strategy
  6. Monorepo Structure
  7. Integration Points
  8. Migration Challenges
  9. Consequences
  10. Alternatives Considered
  11. Related Documentation

Context ​

Current NFT Infrastructure ​

The EmProps NFT system was developed 8 months ago as a proof-of-concept for blockchain-based ownership of AI-generated content. It consists of three packages:

nft_investigation/
β”œβ”€β”€ emprops-hardhat/        # Smart contract development & deployment
β”‚   β”œβ”€β”€ contracts/           # Solidity contracts (in backup/)
β”‚   β”œβ”€β”€ deploy/              # Hardhat deployment scripts
β”‚   β”œβ”€β”€ scripts/             # DB integration & simulation
β”‚   └── backup/hardhat/      # βœ… COMPLETE CONTRACT IMPLEMENTATIONS
β”‚       └── contracts/
β”‚           β”œβ”€β”€ OwnerTokenContract.sol          # Master NFT for collection ownership
β”‚           β”œβ”€β”€ AppFactoryContract.sol          # CREATE2 factory for NFT collections
β”‚           β”œβ”€β”€ SimpleAppContract.sol           # ERC721A NFT collection
β”‚           └── SimpleAppInitializerContract.sol # Collection configuration
β”‚
β”œβ”€β”€ emprops-ponder/         # Blockchain event indexing & API
β”‚   β”œβ”€β”€ src/api/             # Hono HTTP API + WebSocket server
β”‚   β”œβ”€β”€ src/handlers/        # Event handlers for contract monitoring
β”‚   β”œβ”€β”€ ponder.schema.ts     # 8-table PostgreSQL schema
β”‚   └── ponder.config.ts     # Dynamic contract configuration
β”‚
└── emprops-react-web3/     # React hooks & components (DEMO)
    β”œβ”€β”€ src/hooks/           # useGetSimpleApps, useMintToken, etc.
    β”œβ”€β”€ src/adapter/         # WagmiAdapter + PonderAdapter
    β”œβ”€β”€ src/provider/        # EmPropsProvider context
    └── src/test/manual/     # Manual testing components

Existing emerge-turbo Architecture ​

The monorepo already has relevant infrastructure:

emerge-turbo/
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ emprops-studio/      # Next.js UI (target for "Launch NFT" button)
β”‚   β”œβ”€β”€ emprops-api/         # Express API (could handle NFT operations)
β”‚   β”œβ”€β”€ monitor/             # Real-time dashboard (could show NFT metrics)
β”‚   └── api/                 # Core job orchestration
β”‚
└── packages/
    β”œβ”€β”€ @emp/core/           # Shared utilities, Redis, OTEL
    β”œβ”€β”€ @emp/database/       # Prisma client
    └── component-library/   # Shared React components

Business Context ​

Goal: Enable users to monetize AI-generated content through NFT collections

User Journey:

  1. User creates AI content in emprops-studio (existing flow)
  2. User clicks "Launch as NFT Collection" button (NEW)
  3. NFT launchpad opens with collection configuration form (NEW)
  4. User configures: name, symbol, max supply, mint price, etc.
  5. Payment validation (future: could require payment or NFT ownership)
  6. Content generation using existing ComfyUI workflows
  7. NFT collection deployed on-chain
  8. NFTs minted with AI-generated content as metadata

Constraints:

  • βœ… Proof of concept - self-contained, minimal impact on existing code
  • βœ… New button in emprops-studio is the ONLY change to existing apps
  • βœ… All NFT functionality isolated in new modules
  • βœ… Can be tested independently

Problem Statement ​

Challenges ​

  1. Three Separate Repositories

    • How do we consolidate without losing functionality?
    • How do we maintain the contract deployment pipeline?
    • How do we integrate blockchain indexing (Ponder) with existing infrastructure?
  2. React Web3 SDK is a Demo

    • The current emprops-react-web3 is a demo/testing harness
    • Need to extract useful patterns and integrate into actual UI
    • Don't want to duplicate Web3 libraries (Wagmi, RainbowKit)
  3. Database Management

    • emprops-hardhat uses PostgreSQL for deployment tracking
    • emprops-ponder uses PostgreSQL for event indexing
    • emerge-turbo already has Prisma + PostgreSQL
    • How do we consolidate database usage?
  4. Minimal Integration

    • Only a single button should change in emprops-studio
    • All NFT features must be self-contained
    • Need clear boundaries between existing platform and NFT modules
  5. Smart Contract State

    • Contracts are in backup/ folder, not main contracts folder
    • Need to validate they compile and work
    • Deployment scripts reference database storage

Decision ​

Integration Approach: Modular Proof-of-Concept Architecture ​

We will integrate the NFT infrastructure as three new modules in the monorepo with minimal coupling to existing services:

  1. packages/nft-contracts - Smart contract package (from emprops-hardhat)
  2. apps/nft-indexer - Ponder-based blockchain indexer (from emprops-ponder)
  3. apps/nft-launchpad - Next.js NFT collection launchpad UI (NEW, inspired by emprops-react-web3)

Key Principles ​

βœ… Isolation First - NFT modules are self-contained with clear APIs βœ… Minimal Touch Points - Single button in emprops-studio, everything else is new βœ… Proof of Concept - Not production-critical, can iterate independently βœ… Reuse Patterns - Learn from emprops-react-web3 but don't copy it wholesale βœ… Database Coexistence - NFT indexer uses separate schema, not Prisma


Technical Architecture ​

High-Level Architecture ​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     emerge-turbo Monorepo                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚  β”‚ emprops-studio  │────────▢│ nft-launchpad    β”‚              β”‚
β”‚  β”‚                 β”‚ "Launch β”‚ (Next.js)        β”‚              β”‚
β”‚  β”‚ (existing app)  β”‚  NFT"   β”‚ - Collection UI  β”‚              β”‚
β”‚  β”‚                 β”‚ button  β”‚ - Minting UI     β”‚              β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚ - Payment flow   β”‚              β”‚
β”‚                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚                                        β”‚                         β”‚
β”‚                                        β”‚ Web3 calls              β”‚
β”‚                                        β–Ό                         β”‚
β”‚                               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚                               β”‚ Blockchain       β”‚              β”‚
β”‚                               β”‚ (Base/Ethereum)  β”‚              β”‚
β”‚                               β”‚                  β”‚              β”‚
β”‚                               β”‚ - OwnerToken     β”‚              β”‚
β”‚                               β”‚ - AppFactory     β”‚              β”‚
β”‚                               β”‚ - SimpleApp      β”‚              β”‚
β”‚                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚                                        β”‚                         β”‚
β”‚                                        β”‚ Events                  β”‚
β”‚                                        β–Ό                         β”‚
β”‚                               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚                               β”‚ nft-indexer      β”‚              β”‚
β”‚                               β”‚ (Ponder)         β”‚              β”‚
β”‚                               β”‚ - Event handlers β”‚              β”‚
β”‚                               β”‚ - PostgreSQL     β”‚              β”‚
β”‚                               β”‚ - WebSocket API  β”‚              β”‚
β”‚                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚                                        β”‚                         β”‚
β”‚                                        β”‚ Real-time data          β”‚
β”‚                                        β–Ό                         β”‚
β”‚                               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚                               β”‚ monitor          β”‚              β”‚
β”‚                               β”‚ (existing)       β”‚              β”‚
β”‚                               β”‚ + NFT metrics    β”‚              β”‚
β”‚                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β”‚                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ packages/nft-contracts                                    β”‚  β”‚
β”‚  β”‚ - Solidity contracts                                      β”‚  β”‚
β”‚  β”‚ - Hardhat config                                          β”‚  β”‚
β”‚  β”‚ - Deployment scripts                                      β”‚  β”‚
β”‚  β”‚ - TypeChain generated types                               β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Smart Contract Architecture ​

Contract Hierarchy:

OwnerTokenContract (ERC721A Upgradeable)
    β”‚
    └─── Represents ownership of an NFT collection
         Each token ID = ownership of one collection

AppFactoryContract (UUPS Upgradeable)
    β”‚
    β”œβ”€β”€β”€ Creates new NFT collections using CREATE2
    β”œβ”€β”€β”€ Deterministic addresses (same on all chains)
    └─── Mints OwnerToken when collection is created

SimpleAppContract (ERC721A, Minimal Proxy)
    β”‚
    β”œβ”€β”€β”€ Individual NFT collection
    β”œβ”€β”€β”€ Controlled by OwnerToken holder
    β”œβ”€β”€β”€ Configurable: maxSupply, mintPrice, maxPerMint, startTime
    └─── Public minting with payment

Key Features:

  • ERC721A - Gas-optimized batch minting
  • UUPS Upgradeable - Can upgrade factory and owner token
  • ERC1167 Minimal Proxy - Cheap collection deployment (~$5 vs ~$200)
  • Cross-chain Compatible - Same collection address on all chains (CREATE2)
  • Owner Token Gating - Only OwnerToken holder can manage collection

Monorepo Structure ​

Proposed Directory Layout ​

emerge-turbo/
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ emprops-studio/          # EXISTING (minimal change)
β”‚   β”‚   └── pages/
β”‚   β”‚       └── studio/apps/v2/
β”‚   β”‚           └── [id].tsx     # ✏️ ADD: "Launch as NFT" button
β”‚   β”‚
β”‚   β”œβ”€β”€ nft-launchpad/           # πŸ†• NEW APP
β”‚   β”‚   β”œβ”€β”€ pages/
β”‚   β”‚   β”‚   β”œβ”€β”€ _app.tsx         # Wagmi + RainbowKit providers
β”‚   β”‚   β”‚   β”œβ”€β”€ index.tsx        # Collection gallery
β”‚   β”‚   β”‚   β”œβ”€β”€ create.tsx       # Create collection form
β”‚   β”‚   β”‚   └── [collection]/
β”‚   β”‚   β”‚       β”œβ”€β”€ index.tsx    # Collection details
β”‚   β”‚   β”‚       └── mint.tsx     # Minting interface
β”‚   β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”‚   β”œβ”€β”€ CollectionCard.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ MintButton.tsx
β”‚   β”‚   β”‚   └── CreateCollectionForm.tsx
β”‚   β”‚   β”œβ”€β”€ hooks/
β”‚   β”‚   β”‚   β”œβ”€β”€ useCreateCollection.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ useMintNFT.ts
β”‚   β”‚   β”‚   └── useCollections.ts
β”‚   β”‚   β”œβ”€β”€ lib/
β”‚   β”‚   β”‚   β”œβ”€β”€ wagmi.ts         # Wagmi configuration
β”‚   β”‚   β”‚   └── contracts.ts     # Contract addresses & ABIs
β”‚   β”‚   └── package.json
β”‚   β”‚
β”‚   β”œβ”€β”€ nft-indexer/             # πŸ†• NEW APP (from emprops-ponder)
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ index.ts     # Hono API routes
β”‚   β”‚   β”‚   β”‚   └── sockets/     # WebSocket server
β”‚   β”‚   β”‚   β”œβ”€β”€ handlers/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ OwnerToken.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ AppFactory.ts
β”‚   β”‚   β”‚   β”‚   └── SimpleApp.ts
β”‚   β”‚   β”‚   └── utils/
β”‚   β”‚   β”‚       └── db.ts        # Contract registry
β”‚   β”‚   β”œβ”€β”€ ponder.config.ts     # Dynamic contract loading
β”‚   β”‚   β”œβ”€β”€ ponder.schema.ts     # 8-table schema
β”‚   β”‚   └── package.json
β”‚   β”‚
β”‚   β”œβ”€β”€ monitor/                 # EXISTING (optional enhancement)
β”‚   β”‚   └── pages/
β”‚   β”‚       └── nft-metrics.tsx  # πŸ”§ OPTIONAL: NFT dashboard
β”‚   β”‚
β”‚   └── emprops-api/             # EXISTING (optional enhancement)
β”‚       └── src/routes/
β”‚           └── nft.ts           # πŸ”§ OPTIONAL: NFT metadata API
β”‚
β”œβ”€β”€ packages/
β”‚   β”œβ”€β”€ nft-contracts/           # πŸ†• NEW PACKAGE (from emprops-hardhat)
β”‚   β”‚   β”œβ”€β”€ contracts/
β”‚   β”‚   β”‚   β”œβ”€β”€ OwnerTokenContract.sol
β”‚   β”‚   β”‚   β”œβ”€β”€ AppFactoryContract.sol
β”‚   β”‚   β”‚   β”œβ”€β”€ SimpleAppContract.sol
β”‚   β”‚   β”‚   β”œβ”€β”€ SimpleAppInitializerContract.sol
β”‚   β”‚   β”‚   β”œβ”€β”€ interfaces/
β”‚   β”‚   β”‚   └── proxy/
β”‚   β”‚   β”œβ”€β”€ deploy/
β”‚   β”‚   β”‚   β”œβ”€β”€ 00_owner_token.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ 01_app_factory.ts
β”‚   β”‚   β”‚   └── 02_simple_app_impl.ts
β”‚   β”‚   β”œβ”€β”€ scripts/
β”‚   β”‚   β”‚   β”œβ”€β”€ db/              # Database storage scripts
β”‚   β”‚   β”‚   └── simulate/        # Contract testing scripts
β”‚   β”‚   β”œβ”€β”€ test/
β”‚   β”‚   β”œβ”€β”€ hardhat.config.ts
β”‚   β”‚   β”œβ”€β”€ tsconfig.json
β”‚   β”‚   └── package.json
β”‚   β”‚
β”‚   └── @emp/core/              # EXISTING (no changes)
β”‚
└── pnpm-workspace.yaml         # EXISTING (already includes packages/*, apps/*)

Package Naming Conventions ​

  • nft-contracts - Contains smart contracts (not @emp/nft-contracts to keep Hardhat independent)
  • nft-indexer - Ponder application (app, not package)
  • nft-launchpad - Next.js frontend application

Implementation Strategy ​

Phase 1: Documentation & Validation (Week 1) ​

Goal: Understand what we have and validate it works

  1. Create comprehensive documentation for each of the three repos:

    • Document contract architecture and deployment flow
    • Document Ponder indexing setup and API
    • Document React Web3 patterns worth keeping
  2. Validate contracts compile and test:

    bash
    cd emprops-hardhat
    pnpm install
    pnpm compile
    pnpm test
  3. Review and create final integration plan

Phase 2: Contract Package Migration (Week 1-2) ​

Goal: Get smart contracts into monorepo and deployable

  1. Create packages/nft-contracts:

    • Copy contracts from backup/hardhat/contracts/ to packages/nft-contracts/contracts/
    • Copy deployment scripts
    • Update hardhat.config.ts for monorepo context
    • Update database storage scripts to use shared PostgreSQL
  2. Test compilation:

    bash
    pnpm --filter nft-contracts compile
    pnpm --filter nft-contracts test
  3. Deploy to testnet (Base Sepolia):

    bash
    pnpm --filter nft-contracts deploy --network baseSepolia
  4. Store deployment info in database for Ponder to read

Phase 3: Indexer App Migration (Week 2) ​

Goal: Get Ponder indexer running and tracking events

  1. Create apps/nft-indexer:

    • Copy from emprops-ponder
    • Update ponder.config.ts to read contracts from monorepo database
    • Ensure PostgreSQL schema doesn't conflict with Prisma
  2. Test locally:

    bash
    pnpm --filter nft-indexer dev
  3. Verify API endpoints:

    • GET /api/apps - List all NFT collections
    • GET /api/owner-tokens - List owner tokens
    • WebSocket connection for real-time events

Phase 4: Launchpad App Creation (Week 2-3) ​

Goal: Create NFT launchpad UI inspired by emprops-react-web3

  1. Create apps/nft-launchpad (new Next.js app):

    • Set up Wagmi + RainbowKit configuration
    • Create collection creation form
    • Create minting interface
    • Connect to nft-indexer API for data
  2. Extract useful patterns from emprops-react-web3:

    • Adapter pattern (WagmiAdapter for on-chain, API for indexed data)
    • React Query integration
    • Transaction hooks pattern
    • Error handling patterns
  3. DO NOT copy wholesale:

    • Don't create another provider package
    • Build hooks directly in nft-launchpad
    • Use established patterns from emprops-studio

Phase 5: emprops-studio Integration (Week 3) ​

Goal: Add "Launch as NFT" button - THE ONLY CHANGE to existing apps

  1. Add button to app pages:

    tsx
    // apps/emprops-studio/pages/studio/apps/v2/[id].tsx
    <Button onClick={() => router.push('/nft-launchpad/create?sourceApp=' + id)}>
      Launch as NFT Collection
    </Button>
  2. Pass context to launchpad:

    • App ID
    • Generated content reference
    • User wallet (if connected)
  3. That's it - all other NFT functionality is in nft-launchpad

Phase 6: Testing & Documentation (Week 3-4) ​

Goal: Validate end-to-end flow and document

  1. Test complete flow:

    • Click "Launch as NFT" in emprops-studio
    • Configure collection in nft-launchpad
    • Deploy collection on testnet
    • Verify indexer picks up events
    • Mint NFTs
    • Verify ownership
  2. Create documentation:

    • User guide for NFT launching
    • Developer guide for contracts
    • API documentation for indexer
  3. Optional: Add metrics to monitor:

    • Collections created
    • NFTs minted
    • Total revenue

Integration Points ​

1. emprops-studio β†’ nft-launchpad ​

Touch Point: Single button in app pages

tsx
// apps/emprops-studio/pages/studio/apps/v2/[id].tsx
import Link from 'next/link';

// Inside component render:
<Link href={`/nft-launchpad/create?sourceApp=${appId}`}>
  <Button>Launch as NFT Collection</Button>
</Link>

Data Flow:

  • App ID passed via query parameter
  • nft-launchpad reads app details (could call emprops-api)
  • User configures NFT collection
  • Collection deployed independently

2. nft-launchpad β†’ nft-indexer ​

Touch Point: REST API + WebSocket for data

typescript
// apps/nft-launchpad/lib/api.ts
const NFT_INDEXER_URL = process.env.NEXT_PUBLIC_NFT_INDEXER_URL;

export async function getCollections() {
  const response = await fetch(`${NFT_INDEXER_URL}/api/apps`);
  return response.json();
}

// WebSocket for real-time updates
const socket = io(NFT_INDEXER_URL);
socket.on('emprops:appToken:minted', (data) => {
  // Update UI
});

3. nft-indexer β†’ Blockchain ​

Touch Point: Ponder reads contract events

typescript
// apps/nft-indexer/ponder.config.ts
export default {
  networks: {
    baseSepolia: {
      chainId: 84532,
      transport: http(process.env.PONDER_RPC_URL_84532)
    }
  },
  contracts: {
    AppFactory: {
      // Dynamically loaded from database
      abi: AppFactoryAbi,
      address: await getContractAddress('AppFactoryProxyContract'),
      network: 'baseSepolia',
      startBlock: deploymentBlock
    }
  }
}

4. nft-contracts β†’ Database ​

Touch Point: Deployment info stored for indexer

typescript
// packages/nft-contracts/scripts/db/storeDeployments.ts
await pool.query(`
  INSERT INTO contract_deployments (name, address, network, abi)
  VALUES ($1, $2, $3, $4)
`, [contractName, address, networkName, JSON.stringify(abi)]);

5. Optional: monitor β†’ nft-indexer ​

Touch Point: Display NFT metrics (future enhancement)

tsx
// apps/monitor/pages/nft-metrics.tsx
import useSWR from 'swr';

export default function NFTMetrics() {
  const { data } = useSWR('/api/nft-stats', fetcher);

  return (
    <div>
      <Metric label="Collections Created" value={data?.collections} />
      <Metric label="NFTs Minted" value={data?.minted} />
    </div>
  );
}

Migration Challenges ​

1. Database Coexistence ​

Challenge: emprops-hardhat and emprops-ponder both use PostgreSQL, but emerge-turbo uses Prisma

Solution:

  • NFT packages use separate schemas in same PostgreSQL instance
  • Don't integrate with Prisma (keep isolated)
  • Create nft_contracts and nft_indexer schemas
  • Ponder manages its own schema (already does this)
sql
-- Database structure
postgres://
β”œβ”€β”€ public                  # emerge-turbo Prisma schema
β”œβ”€β”€ nft_contracts          # Contract deployments (Hardhat)
└── ponder_*               # Ponder indexer tables (auto-managed)

2. Contract Location ​

Challenge: Contracts currently in backup/ folder

Solution:

  • Move from backup/hardhat/contracts/ to packages/nft-contracts/contracts/
  • Validate compilation
  • Update deployment scripts
  • Run tests to ensure nothing broke

3. React Web3 SDK is a Demo ​

Challenge: emprops-react-web3 is a testing harness, not production code

Solution:

  • Don't migrate emprops-react-web3 as a package
  • Extract useful patterns:
    • Adapter pattern for dual data sources
    • Transaction hooks structure
    • React Query integration
  • Build fresh in nft-launchpad using these patterns
  • Reference emprops-react-web3 as documentation only

4. Dependency Duplication ​

Challenge: NFT packages have many overlapping dependencies with emerge-turbo

Solution:

  • Use workspace protocol: "@emp/core": "workspace:*"
  • Share where possible: TypeScript, Prettier, ESLint configs
  • Keep separate where needed: Wagmi, Viem versions for Web3 compatibility

5. Port Conflicts ​

Challenge: Multiple apps need to run simultaneously

Solution:

emprops-studio:  3336 (existing)
nft-launchpad:   3337 (new)
nft-indexer:     3338 (new)
monitor:         3334 (existing)

6. Environment Variables ​

Challenge: Each package needs different config

Solution:

  • Create .env.nft for NFT-specific variables
  • Use dotenv-cli to load: dotenv -e .env.nft -- pnpm dev
  • Document required variables:
bash
# .env.nft
NFT_INDEXER_URL=http://localhost:3338
NEXT_PUBLIC_NFT_INDEXER_URL=http://localhost:3338
PONDER_RPC_URL_84532=https://base-sepolia.g.alchemy.com/v2/YOUR_KEY
PONDER_DATABASE_URL=postgresql://localhost/nft_investigation
HARDHAT_DATABASE_URL=postgresql://localhost/nft_investigation
DEPLOYER_PRIVATE_KEY=0x...

Consequences ​

Positive Consequences ​

βœ… Unified Codebase

  • Single repo for entire platform (AI + NFT)
  • Easier to coordinate releases
  • Shared tooling and CI/CD

βœ… Proof of Concept Isolation

  • NFT features are self-contained
  • Can iterate independently
  • Minimal risk to existing platform

βœ… Clear Boundaries

  • One button in emprops-studio
  • All NFT complexity isolated in new modules
  • Easy to remove if POC fails

βœ… Reusable Infrastructure

  • Indexer pattern can be reused for other on-chain features
  • Contract deployment pipeline established
  • Web3 integration patterns documented

βœ… Learning Foundation

  • Team learns blockchain integration
  • Establishes patterns for future Web3 features
  • Creates monetization pathway

Negative Consequences ​

⚠️ Increased Complexity

  • More apps to manage (3 new)
  • Blockchain dependencies add uncertainty
  • Need to understand Solidity, Hardhat, Ponder

⚠️ External Dependencies

  • Blockchain RPC reliability
  • Gas costs for deployment
  • Smart contract security risks

⚠️ Database Proliferation

  • Multiple schemas in PostgreSQL
  • Not using Prisma for NFT data
  • More database management overhead

⚠️ Separate Deployment

  • Smart contracts deployed independently
  • Indexer needs to be running
  • Launchpad depends on both

Mitigation Strategies ​

  1. Gradual Rollout

    • Start with testnet only
    • Internal testing first
    • Limited beta with select users
  2. Documentation

    • Comprehensive setup guides
    • Architecture diagrams
    • Troubleshooting playbook
  3. Monitoring

    • Contract event tracking
    • Indexer health checks
    • Transaction failure alerts
  4. Fallback Plan

    • Can remove NFT modules without impacting platform
    • Contracts are immutable once deployed
    • Keep emprops-react-web3 as reference

Alternatives Considered ​

Alternative 1: Keep NFT Infrastructure Separate ​

Approach: Leave emprops-hardhat, emprops-ponder, emprops-react-web3 as separate repos

Pros:

  • No migration effort
  • Clear separation of concerns
  • Independent versioning

Cons:

  • Harder to integrate with emprops-studio
  • Duplicate tooling and setup
  • Split deployment pipeline
  • Rejected: Goal is integration, not separation

Alternative 2: Full Integration with Shared Infrastructure ​

Approach: Integrate NFT data into Prisma, use emprops-api for all NFT operations

Pros:

  • Single API
  • Unified database
  • Consistent patterns

Cons:

  • High coupling
  • Blockchain data doesn't fit Prisma model well
  • Changes Ponder's auto-managed schema approach
  • Rejected: Too much coupling for a proof of concept

Alternative 3: Microservices with Separate Deployment ​

Approach: Deploy NFT services independently, integrate via API only

Pros:

  • True isolation
  • Independent scaling
  • Can use different tech stack

Cons:

  • Deployment complexity
  • Cross-service coordination
  • Network latency
  • Rejected: Overkill for proof of concept

Alternative 4: Serverless Functions for NFT Operations ​

Approach: Use Vercel/Netlify functions instead of dedicated apps

Pros:

  • Auto-scaling
  • Pay-per-use
  • Simpler deployment

Cons:

  • Ponder can't run serverless (needs persistent process)
  • Smart contract deployment needs local setup
  • Cold start issues for blockchain calls
  • Rejected: Ponder requires long-running process

ADRs ​

  • ADR-008: EmProps UI Monorepo Integration (similar migration pattern)
  • ADR-006: Logging Architecture (shared observability approach)

Technical Docs ​

External References ​


Decision Log ​

DateDecisionRationale
2025-11-09Integrate as three separate modulesMaintains isolation for POC
2025-11-09Don't migrate emprops-react-web3 as packageIt's a demo, extract patterns instead
2025-11-09Use separate database schemasAvoid Prisma/Ponder conflicts
2025-11-09Single button in emprops-studio onlyMinimal touch point reduces risk

Next Steps ​

  1. Create comprehensive documentation of three repos (emprops-hardhat, emprops-ponder, emprops-react-web3)
  2. Validate contracts compile and test in current location
  3. Review and approve this ADR
  4. Create detailed implementation plan with week-by-week tasks
  5. Begin Phase 1: Documentation & Validation

Appendix: Contract Details ​

OwnerTokenContract ​

Purpose: Master NFT representing ownership of an NFT collection

Key Functions:

  • factoryMint(address to) - Mint new owner token (factory only)
  • getTokensByOwner(address owner) - Get all tokens for an address
  • setFactory(address factory) - Set authorized factory (one-time)

Upgrade Pattern: UUPS (owner can upgrade)

AppFactoryContract ​

Purpose: Factory for creating NFT collections with deterministic addresses

Key Functions:

  • registerImplementation(address impl, address initializer) - Register new app type
  • createApp(bytes32 appType, string appId, ...) - Create new collection
  • predictAppAddress(bytes32 appType, bytes32 salt) - Preview deployment address

Upgrade Pattern: UUPS (owner can upgrade)

SimpleAppContract ​

Purpose: Individual ERC721A NFT collection

Key Functions:

  • mint(uint256 quantity) - Public minting with payment
  • withdraw() - Owner withdraws sales proceeds
  • setPaused(bool paused) - Owner pauses/unpauses minting
  • setMaxSupply(uint256), setMintPrice(uint256), etc. - Owner/initializer config

Upgrade Pattern: Minimal Proxy (cheap deployment, not upgradeable)

SimpleAppInitializerContract ​

Purpose: One-time initialization of SimpleApp parameters

Key Functions:

  • initializeApp(address app, bytes initData) - Set minting parameters

Pattern: Immutable (deployed per factory)


Status: πŸ€” Awaiting approval

Approvers:

  • [ ] Technical Lead
  • [ ] Product Owner
  • [ ] DevOps Lead

Approval Date: Pending

Released under the MIT License.