Skip to content

Legacy Platform Architecture

This documents the original EmProps microservices architecture that powered NFT minting, marketplace, and token generation before the turbo monorepo consolidation. Understanding this architecture is essential for restoring NFT capabilities in the new Diamond/Arbitrum system.

The Contract Evolution

Understanding the history explains why the external repos exist and what we're solving now.

Phase 1: Single Monolithic Contract (Original)

EmProps NFTs originally ran on a single massive contract per chain — all collections lived inside one contract. Every collection, every token, all managed together.

This worked initially but created problems as the platform grew:

  • No per-collection upgradeability
  • No isolation between collections
  • Complex state management
  • Limited customization per collection

Phase 2: Factory Pattern Attempt (emprops-hardhat/ponder/web3)

The emprops-hardhat, emprops-ponder, and emprops-react-web3 repos were the attempt to move to a contract factory approach — one contract per collection, deployed via a factory. This would give each collection its own address, its own state, and its own lifecycle.

The scope ballooned. Building the factory contracts, the blockchain indexer, and the React integration library all at once — without a concrete production use case driving it — meant the work never shipped.

Phase 3: Diamond Standard on Arbitrum (Current)

Now we're getting the factory pattern right by:

  1. Building for a real use case first (Emerge NFTs on Arbitrum)
  2. Using ERC-2535 Diamond which gives us factory deployment PLUS per-collection upgradeability
  3. Scoping tightly — custodial minting, GCS metadata, no indexer needed for v1
  4. Then generalizing — the battle-tested Diamond system becomes what emprops-hardhat/ponder/web3 were trying to be

Architecture Diagram

┌─────────────────────┐     ┌──────────────────────────────────────┐
│ Ethereum Blockchain  │     │  emprops-syncer-long-base            │
│  ├─ RPC Node         │◄────│  emprops-syncer-long-ethereum        │
│  └─ Infura WS API    │     └──────────────────────────────────────┘
└─────────────────────┘                      │

┌─────────────────────┐                      ▼
│  Tezos Blockchain    │           ┌──────────────────┐
│  ├─ RPC Node         │◄──────── │                  │       ┌─────────────────────┐
│  ├─ tzkt.io REST API │◄──────── │   emprops-api    │◄──────│ emprops-open-       │
│  └─ tzkt.io WS API   │◄──┐     │   (Java/Spring)  │       │ generator-curated   │
└─────────────────────┘    │     │                  │       └─────────────────────┘
                           │     └────────▲─────────┘
┌─────────────────────┐    │              │              ┌─────────────────────┐
│ emprops-syncer-long- │    │              │◄─────────────│ emprops-open-       │
│ token-tezos          │────┘              │              │ generator           │
│ emprops-syncer-long- │                   │              └─────────────────────┘
│ tezos-v2             │                   │
└─────────────────────┘                   │              ┌─────────────────────┐
                                          │◄─────────────│ emprops-syncer      │
┌─────────────────────┐                   │              └─────────────────────┘
│ emprops-open-        │                   │
│ interface            │                   │◄─────────────┌─────────────────────┐
│ (Frontend)           │                   │              │ emprops-minter-api  │
└────────┬────────────┘                   │              └─────────────────────┘
         │                                │
         ▼                                │
┌────────────────────┐                    │
│ emprops-open-api   │────────────────────┘
│ (Node.js)          │──────────┬──────────────────────┐
└────────────────────┘          │                      │
                                ▼                      ▼
                    ┌──────────────────┐    ┌──────────────┐
                    │ emprops-         │    │ IPFS Server  │
                    │ puppeteer-api    │    └──────────────┘
                    └────────┬─────────┘


                    ┌──────────────┐
                    │ Discord API  │
                    └──────────────┘

Services

emprops-api

StackJava, Spring, Spring Data JPA
RoleREST API for the Marketplace
StatusLegacy — not in monorepo, reference for feature spec
Location/Users/the_dusky/code/emprops/core-services/emprops-api

REST-based API for the Marketplace. It exposes endpoints to collections and tokens for published collections (both Curated and published through the Studio). Pretty much https://emprops.ai/marketplace uses all its endpoints.

Key capabilities:

  • Collection CRUD (create, read, update, delete)
  • Token management (mint tracking, metadata, ownership)
  • Published collection management (curated + studio)
  • Sales and marketplace listings
  • Royalty configuration
  • Event audit trail
  • Profile management
  • Contract registry

emprops-minter-api

StackNode, NestJS, Prisma
RoleSpecial-purpose minting API
StatusLegacy — special purpose, unlikely directly reusable

REST-based API that was developed specifically for Memento Mori collection.


emprops-open-generator

StackNode, Express
RoleToken artwork generation worker
StatusReplaced by job queue + ComfyUI workers in turbo monorepo

Once a token is synced from the blockchain by a emprops-syncer-*, a generator picks pending-generation tokens by a polling mechanism. Then it will generate the tokens by calling the emprops-open-api generation endpoints. That will:

  1. Generate the art
  2. Do on-chain updates (just for Tezos)
  3. Call emprops-api to update the metadata

emprops-open-generator-curated

StackNode, Express
RoleFast-track token generation for specific collections
StatusReplaced by job queue + ComfyUI workers in turbo monorepo

This works the same as emprops-open-generator but polls data for a specified collection. Usually used for "curated" collections so tokens can be generated faster (since it will just listen for pending-generation tokens on that collection).


emprops-puppeteer-api

StackNode, NestJS
RoleScreenshot/video generation via Puppeteer
StatusStill needed for p5.js and JavaScript token rendering

It has endpoints to send a URL and take a screenshot or video using puppeteer. This is primarily used in p5.js and javascript EmProps Studio components.


emprops-syncer

StackNode
RoleCatch-up syncer for missed Tezos tokens
StatusLegacy — backup mechanism

This was a component developed to sync tokens that could've been missed by emprops-syncer-long-tezos* artifacts. It fetches data from tzkt.io and updates tokens to emprops-api.


emprops-syncer-long-base

StackNode
RoleReal-time Base blockchain token sync
StatusLegacy — pattern relevant for EVM chain syncing

Syncs minted tokens from the Base blockchain. It listens to Infura websocket events to create pending-generation tokens on emprops-api.


emprops-syncer-long-ethereum

StackNode
RoleReal-time Ethereum blockchain token sync
StatusLegacy — pattern relevant for EVM chain syncing

Syncs minted tokens from the Ethereum blockchain. Works the same as emprops-syncer-long-base.


emprops-syncer-long-tezos

StackNode
RoleReal-time Tezos token sync
StatusActive for Tezos

Syncs minted tokens from the Tezos blockchain. It uses tzkt.io websocket events to create pending-generation tokens on emprops-api.


emprops-syncer-long-tezos-v2

StackNode
RoleReal-time Tezos V2 contract token sync
StatusActive for Tezos V2

Syncs minted tokens from Tezos, but for the V2 contracts. Works the same as emprops-syncer-long-tezos.


emprops-open-api

StackNode.js
RoleAPI layer between frontend and core services
StatusPredecessor to turbo monorepo's emprops-api

Intermediary API that the frontend (emprops-open-interface) calls. Routes requests to emprops-api, handles metadata and IPFS storage, and connects to external services (Puppeteer, Discord, IPFS).


emprops-open-interface

StackFrontend (likely Next.js)
RoleUser-facing marketplace and studio interface
StatusEvolved into apps/emprops-studio in turbo monorepo

The original frontend application. Predecessor to the current emprops-studio.


The NFT Token Lifecycle (Original Architecture)

This is the complete flow from a user minting to artwork appearing:

┌──────────────────────────────────────────────────────────────────┐
│ 1. USER MINTS ON MARKETPLACE                                     │
│    User connects wallet → selects collection → pays → mints      │
└──────────────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ 2. BLOCKCHAIN EVENT EMITTED                                      │
│    Mint transaction confirmed → Transfer/TokensMinted event       │
└──────────────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ 3. SYNCER CATCHES EVENT                                          │
│    emprops-syncer-long-{chain} listening via WebSocket            │
│    ├─ Ethereum/Base: Infura WS API                               │
│    └─ Tezos: tzkt.io WS API                                     │
└──────────────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ 4. PENDING-GENERATION TOKEN CREATED                              │
│    Syncer calls emprops-api to create token with status:          │
│    "pending-generation"                                           │
└──────────────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ 5. GENERATOR PICKS UP TOKEN                                      │
│    emprops-open-generator polls emprops-api for pending tokens    │
│    (or emprops-open-generator-curated for specific collection)    │
└──────────────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ 6. ARTWORK GENERATED                                             │
│    Generator calls emprops-open-api generation endpoints          │
│    ├─ AI/generative artwork created                              │
│    ├─ p5.js/JS: emprops-puppeteer-api renders + screenshots      │
│    └─ Assets stored (IPFS / GCS)                                 │
└──────────────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ 7. ON-CHAIN METADATA UPDATED (Tezos only)                        │
│    Generator updates token metadata URI on-chain via RPC          │
└──────────────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ 8. emprops-api UPDATED                                           │
│    Generator calls emprops-api to update token metadata           │
│    Token status: "pending-generation" → "completed"               │
└──────────────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ 9. TOKEN VISIBLE ON MARKETPLACE                                  │
│    User sees their minted NFT with generated artwork              │
│    Marketplace displays metadata, image, traits                   │
└──────────────────────────────────────────────────────────────────┘

Key Architectural Pattern: Mint-Then-Generate

The original architecture used a mint-then-generate pattern:

  • Users minted a token first (paying gas + mint price)
  • The token existed on-chain with no artwork yet
  • A syncer detected the mint event
  • A generator created the artwork asynchronously
  • Metadata was updated after generation

This is fundamentally different from the new Diamond/Arbitrum approach which uses generate-then-mint:

  • Platform generates artwork first via the job queue
  • Metadata is written to GCS
  • Platform mints the token to the user (custodial mintTo)
  • Token is born with its artwork already complete

How the New System Replaces Legacy Services

Legacy ServiceWhat It DidNew System Replacement
emprops-api (Java)Marketplace REST API, collection/token CRUDapps/emprops-api (Node) in turbo monorepo
emprops-syncer-long-*Watched blockchain for mint eventsNot needed — custodial mintTo means platform knows all mints
emprops-open-generatorPolled for pending tokens, generated artJob queue + ComfyUI workers (already built)
emprops-open-generator-curatedFast-track generation for specific collectionsJob queue handles priority natively
emprops-puppeteer-apiScreenshot/video renderingStill needed for p5.js/JS components
emprops-syncerCatch-up sync for missed Tezos tokensNot needed with custodial minting
emprops-open-apiIntermediary API layerConsolidated into apps/emprops-api
emprops-open-interfaceFrontendapps/emprops-studio in turbo monorepo
IPFS ServerToken metadata storageGCS + CDN (faster, cheaper, updateable)

What's Eliminated

With custodial mintTo minting on Arbitrum:

  • All syncers are eliminated — the platform initiates every mint, so there's nothing to "catch up" on
  • Polling-based generators are eliminated — the job queue directly triggers generation
  • IPFS is replaced — GCS + CDN provides faster, cheaper, updateable metadata storage
  • The mint-then-generate pattern is inverted — artwork is generated first, then minted

What's Preserved

  • Marketplace UIemprops-studio still has all the marketplace pages and components
  • Collection management — CRUD, publishing, settings all exist in the studio
  • Wallet integration — Dynamic Labs + wagmi for EVM wallet support
  • Puppeteer rendering — Still needed for JavaScript-based generative art

Released under the MIT License.