ADR: EmProps Studio NFT Revival
Date: 2026-02-07 Status: Proposed Depends On: NFT Diamond Standard on ArbitrumDecision Makers: Architecture Team
Executive Summary
Restore full NFT capabilities in emprops-studio using the Diamond/Arbitrum infrastructure built for the Emerge miniapp. This replaces the legacy microservices architecture (~10 services) with the turbo monorepo's emprops-api + emprops-studio.
Success criteria: emprops-studio is fully operational as an NFT platform — creators can deploy collections, mint tokens, manage their marketplace presence, and collectors can discover and view NFTs. All powered by the Diamond/Arbitrum backend instead of the legacy Java API and blockchain syncers.
Context
What Exists Today
EmProps Studio already has ~80 NFT files (~12,600 LOC) — marketplace pages, mint buttons, collection management, publishing flows, contract clients, and wallet integration. This code was the core product when EmProps was an NFT platform. See Studio Code Inventory.
The problem: this UI is connected to a legacy backend consisting of:
- emprops-api (Java/Spring) — marketplace REST API, still running
- emprops-syncer-long-* — blockchain event watchers (Tezos, Ethereum, Base)
- emprops-open-generator — polling-based token generation
- emprops-puppeteer-api — p5.js/JS rendering
- IPFS server — metadata storage
See Legacy Architecture for the full service map.
What Changes
The Emerge miniapp ADR builds:
- Diamond contracts on Arbitrum (factory + shared facets)
- NFT API endpoints in emprops-api (setup, deploy, generate, mint, metadata)
- Database schema updates (NFT fields on collection model)
This ADR takes that infrastructure and wires it into emprops-studio, replacing the legacy services.
What Gets Eliminated
| Legacy Service | Replaced By |
|---|---|
| emprops-api (Java) marketplace endpoints | emprops-api (Node) NFT routes |
| emprops-syncer-long-* (all 4 syncers) | Not needed — custodial mintTo means we know all mints |
| emprops-open-generator (polling) | Job queue + ComfyUI workers (already built) |
| emprops-open-generator-curated | Job queue handles priority natively |
| IPFS server | GCS + CDN |
emprops-puppeteer-api stays — still needed for p5.js/JavaScript rendering.
Decision
Architecture: Two-App Stack
┌──────────────────────────────────────────────────────┐
│ emprops-studio (Next.js) │
│ │
│ Marketplace ←──── Collection CRUD ────→ Publishing │
│ Token Detail Collection Settings Mint Flow │
│ Profile Pages Wallet Integration Deploy Flow │
└────────────────────────┬─────────────────────────────┘
│ All API calls
▼
┌──────────────────────────────────────────────────────┐
│ emprops-api (Node) │
│ │
│ /nft/collections/setup → Gemini LLM │
│ /nft/collections/:id/deploy → Diamond on Arbitrum │
│ /nft/collections/:id/generate → Job queue + ComfyUI │
│ /nft/collections/:id/mint → mintTo on Diamond │
│ /nft/collections/:id/metadata/:tokenId → GCS + CDN │
│ │
│ + existing collection CRUD, auth, projects, etc. │
└──────────────────────────────────────────────────────┘No syncers. No Java API. No IPFS. The studio talks to emprops-api, which talks to Arbitrum and GCS.
What Changes in the Studio
1. Blockchain Types
File: types/wallet.ts
Add "ARBITRUM" to Blockchain type. Add Arbitrum chain IDs (421614 Sepolia, 42161 mainnet).
File: apps/emprops-api/src/lib/collections.ts
Add "ARBITRUM" to Zod blockchain enum.
2. Contract Client
File: clients/contract-client.ts (1,275 LOC)
Add Diamond read functions for Arbitrum:
getArbitrumCollection(contractAddress)— read collection data from Diamond via viemgetArbitrumTokenMetadata(contractAddress, tokenId)— read tokenURI
Write operations (deploy, mint) go through the API — not direct contract calls. The platform wallet signs transactions server-side.
3. Mint Button
New: components/MintButton/ArbitrumMint.tsx
For the initial revival, this is a custodial mint button — it calls the API /nft/collections/:id/mint endpoint. The platform signs the transaction, not the user.
The existing MintButton/index.tsx router dispatches to this component when blockchain === "ARBITRUM".
Later: add user-signed minting via wagmi for users who want to pay gas directly.
4. Publish Collection Modal
Modify: components/PublishCollectionModal/index.tsx (267 LOC)
Add Arbitrum as a deployment target:
- User selects Arbitrum in blockchain selector
- Configures: max supply, royalty bps, royalty receiver
- Modal calls
/nft/collections/:id/deploy - Shows deployment progress (tx hash → contract address → Arbiscan link)
5. Marketplace Pages
Existing pages already work — they list collections from the API. The changes:
- Marketplace browse: include Arbitrum collections in results
- Collection detail: show Arbitrum-specific info (contract address, Arbiscan link)
- Token detail: fetch metadata from
/nft/collections/:id/metadata/:tokenId
6. Market Actions
Modify: components/MarketActions/index.tsx (100 LOC)
Currently Tezos-only (line 60 blockchain check). For Arbitrum:
- Initial: "View on OpenSea" link (external marketplace)
- Future: Native marketplace facet on Diamond
7. Collection Management
Modify: components/CollectionSettings/
For Arbitrum collections, show:
- Contract address + Arbiscan link
- Tokens minted / max supply
- Mint status (active/paused/completed)
- Pause/unpause minting (calls API → MintFacet)
8. Profile Pages
Existing pages at pages/profile/[address]/ — collections, sales, creations. Need to:
- Include Arbitrum collections in queries
- Show Arbitrum tokens with correct metadata
- Link to Arbiscan for on-chain verification
API Endpoints Consumed by Studio
All of these are built as part of the Emerge miniapp ADR. The studio reuses the same endpoints:
| Endpoint | Studio Usage |
|---|---|
POST /nft/collections/setup | Collection creation wizard (Gemini generates instruction set) |
POST /nft/collections/:id/deploy | PublishCollectionModal deploys Diamond |
POST /nft/collections/:id/generate | Generate token artwork |
POST /nft/collections/:id/mint | ArbitrumMint button |
GET /nft/collections/:id/metadata/:tokenId | Token detail page |
GET /nft/collections/:id/tokens | Collection items grid |
No new API endpoints are needed. The studio is a client of the same API the miniapp uses.
Legacy Feature Restoration Roadmap
Features from the original Java emprops-api, mapped to the new system:
| Feature | Legacy Implementation | Revival Approach | Priority |
|---|---|---|---|
| Collection CRUD | Java endpoints | Already in turbo monorepo | Done |
| Token minting | User-initiated + syncer tracking | Custodial mintTo via API | P1 |
| Marketplace browse | Java endpoints + platform API client | Existing studio pages + emprops-api | P1 |
| Collection publishing | Studio UI → Java API | Studio UI → emprops-api /nft/deploy | P1 |
| Token metadata | IPFS | GCS + CDN | P1 |
| Royalties | Off-chain rates in Java API | On-chain ERC-2981 (RoyaltyFacet) | P1 |
| Marketplace listings | Java SalesEntity + endpoints | OpenSea links (v1), native facet (future) | P2 |
| Secondary sales tracking | Java PrivateSalesController | Not for v1 — add Ponder indexer later | P3 |
| Floor/ceiling prices | Java ProjectController.stats | OpenSea API or Ponder aggregation | P3 |
| Event audit trail | Java EventEntity | API event logging + chain events (Ponder) | P3 |
| Token trait filtering | token_features table | Metadata attributes from GCS JSON | P2 |
| Profile pages | Java endpoints + studio pages | Already in studio, add Arbitrum data | P1 |
Implementation Strategy
Prerequisites
The Emerge miniapp ADR must be complete:
- Diamond contracts deployed on Arbitrum Sepolia
- NFT API endpoints working in emprops-api
- Database schema updated
Phase 1: Core Wiring (P1 features)
Connect the existing studio UI to the new API endpoints.
Files to modify:
| File | Change |
|---|---|
types/wallet.ts | Add ARBITRUM |
clients/contract-client.ts | Add Diamond read functions |
hooks/contract-client.ts | Add Arbitrum chain support |
hooks/collections.ts | Support Arbitrum in publish flow |
components/MintButton/index.tsx | Route ARBITRUM to ArbitrumMint |
components/PublishCollectionModal/index.tsx | Add Diamond deployment flow |
components/BlockchainSelector/ | Add Arbitrum option |
components/CollectionSettings/ | Show Arbitrum collection state |
components/MarketActions/index.tsx | OpenSea link for Arbitrum |
pages/tokens/emprops/[id]/[tokenId].tsx | Render Arbitrum token metadata |
Files to create:
| File | Purpose |
|---|---|
components/MintButton/ArbitrumMint.tsx | Custodial mint button |
hooks/diamond.ts | Diamond-specific read hooks |
lib/arbitrum.ts | Chain config + utilities |
contracts/ethereum/diamond-factory.json | Diamond Factory ABI |
contracts/ethereum/diamond-collection.json | Diamond facet ABIs |
Phase 2: Enhanced Features (P2)
- Token trait filtering from metadata attributes
- Collection analytics (tokens minted, revenue)
- Marketplace listing links (OpenSea integration)
- Batch operations UI
Phase 3: Full Independence (P3)
- Retire Java emprops-api completely
- Add Ponder indexer for secondary market tracking
- Add native marketplace facet to Diamond
- Historical sales data and floor prices
- Event audit trail from chain events
Consequences
Positive
- Single stack: Everything in the turbo monorepo (Node + Next.js) — no Java, no microservices
- Reuses existing UI: ~80 files of studio NFT code gets reconnected, not rewritten
- Same API for all clients: Miniapp and studio share the same emprops-api endpoints
- Better NFT system: Diamond upgradeability, Arbitrum gas costs, GCS metadata (updateable)
- Eliminates ~6 legacy services: No syncers, no Java API, no IPFS, no polling generators
Negative
- Tezos collections orphaned: Existing Tezos collections won't work with the new system without keeping legacy services
- emprops-clients package:
@stakeordie/emprops-clientswas built for ERC721A/ERC1167 — needs replacement or bypass - Feature gap during transition: Some features (secondary sales, floor prices) won't exist until P3
Mitigations
- Keep legacy Tezos services running until all active Tezos collections are concluded
- Create Diamond-specific hooks directly in studio, bypassing emprops-clients
- Use OpenSea APIs/links as bridge for marketplace features until native facet is built
Success Criteria
- [ ] Creators can deploy NFT collections to Arbitrum from emprops-studio
- [ ] Marketplace browse shows Arbitrum collections alongside existing ones
- [ ] Token detail pages render metadata from GCS/CDN correctly
- [ ] Collection management (pause, settings) works for Arbitrum collections
- [ ] Profile pages show Arbitrum tokens
- [ ] Java emprops-api can be taken offline without breaking Arbitrum features
- [ ] All existing Tezos functionality continues to work during transition
Related Documentation
- NFT Diamond Standard on Arbitrum (Miniapp ADR)
- Miniapp Implementation Plan
- Studio Code Inventory — All ~80 NFT files
- Legacy Architecture — What we're replacing
- External Repos — Reference material from prior attempts
