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:
- Building for a real use case first (Emerge NFTs on Arbitrum)
- Using ERC-2535 Diamond which gives us factory deployment PLUS per-collection upgradeability
- Scoping tightly — custodial minting, GCS metadata, no indexer needed for v1
- 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
| Stack | Java, Spring, Spring Data JPA |
| Role | REST API for the Marketplace |
| Status | Legacy — 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
| Stack | Node, NestJS, Prisma |
| Role | Special-purpose minting API |
| Status | Legacy — special purpose, unlikely directly reusable |
REST-based API that was developed specifically for Memento Mori collection.
emprops-open-generator
| Stack | Node, Express |
| Role | Token artwork generation worker |
| Status | Replaced 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:
- Generate the art
- Do on-chain updates (just for Tezos)
- Call
emprops-apito update the metadata
emprops-open-generator-curated
| Stack | Node, Express |
| Role | Fast-track token generation for specific collections |
| Status | Replaced 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
| Stack | Node, NestJS |
| Role | Screenshot/video generation via Puppeteer |
| Status | Still 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
| Stack | Node |
| Role | Catch-up syncer for missed Tezos tokens |
| Status | Legacy — 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
| Stack | Node |
| Role | Real-time Base blockchain token sync |
| Status | Legacy — 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
| Stack | Node |
| Role | Real-time Ethereum blockchain token sync |
| Status | Legacy — 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
| Stack | Node |
| Role | Real-time Tezos token sync |
| Status | Active 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
| Stack | Node |
| Role | Real-time Tezos V2 contract token sync |
| Status | Active for Tezos V2 |
Syncs minted tokens from Tezos, but for the V2 contracts. Works the same as emprops-syncer-long-tezos.
emprops-open-api
| Stack | Node.js |
| Role | API layer between frontend and core services |
| Status | Predecessor 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
| Stack | Frontend (likely Next.js) |
| Role | User-facing marketplace and studio interface |
| Status | Evolved 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 Service | What It Did | New System Replacement |
|---|---|---|
| emprops-api (Java) | Marketplace REST API, collection/token CRUD | apps/emprops-api (Node) in turbo monorepo |
| emprops-syncer-long-* | Watched blockchain for mint events | Not needed — custodial mintTo means platform knows all mints |
| emprops-open-generator | Polled for pending tokens, generated art | Job queue + ComfyUI workers (already built) |
| emprops-open-generator-curated | Fast-track generation for specific collections | Job queue handles priority natively |
| emprops-puppeteer-api | Screenshot/video rendering | Still needed for p5.js/JS components |
| emprops-syncer | Catch-up sync for missed Tezos tokens | Not needed with custodial minting |
| emprops-open-api | Intermediary API layer | Consolidated into apps/emprops-api |
| emprops-open-interface | Frontend | apps/emprops-studio in turbo monorepo |
| IPFS Server | Token metadata storage | GCS + 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 UI —
emprops-studiostill 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
Related Documentation
- Legacy Tezos Implementation — Contract versions and data flow
- Contract Client — JavaScript SDK for contract interactions
- Studio Code Inventory — All NFT code in emprops-studio
- External Repos — emprops-hardhat, emprops-ponder, emprops-react-web3
- New Architecture (Diamond/Arbitrum) — The ADR for what replaces this
