← Index
Source: docs/archive/nostra/designs/task-1-create-market.md (auto-generated by scripts/generate-docs-html.mjs — edit the .md, not this file)

Task 1: Create Market Implementation Design

Objective

Enable the "Create Market" page to functional correctly by implementing the backend endpoint to create markets on-chain and store them in the database.

Current State

Important Note: Multi-Choice Market Limitation

MarketFactory.createMultipleChoiceMarket does not register tokens with the CTFExchange, making them untradable on the current exchange implementation.

Liquidity Provisioning

2. Frontend Implementation (web/src/app/create-market/page.tsx)

Proposed Solution

1. Backend Implementation (api)

New Endpoint: POST /api/markets/grouped

Request Body:

{
  "groupQuestion": "Who will win...?",
  "category": "Sports",
  "players": [
    { "name": "Player A", "description": "...", "imageUrl": "https://..." },
    { "name": "Player B", "description": "...", "imageUrl": "" }
  ],
  "resolutionTime": 1735689600, // Unix timestamp
  "endTime": 1735603200, // Unix timestamp
  "marketType": "grouped-binary"
}

Logic:

  1. Validation: Check inputs.
  2. Blockchain Interaction:
    • Load Admin Private Key from environment variables (ADMIN_PRIVATE_KEY).
    • Initialize BlockchainService with signer.
    • For each player:
      • Generate questionId (using ethers.id + timestamp/salt).
      • Call marketFactory.createBinaryMarket(...).
      • Wait for transaction confirmation.
      • Parse MarketCreated event to get conditionId and tokenIds.
    • Retry Logic:
      • Wrap the blockchain call in a retry loop.
      • MAX_RETRIES = 3.
      • RETRY_DELAY = 2000 ms.
      • If a transaction fails (e.g., network timeout, RPC error), wait and retry.
      • If it fails after 3 attempts, mark this specific market creation as FAILED in the response/DB but continue processing other players if possible (or abort depending on strictness). Recommendation: Abort and return partial success details.
  3. Database Storage:
    • Create MarketGroup record.
    • For each player, create Market record with the returned conditionId and the provided imageUrl.
    • Create Outcome records (YES/NO) with returned tokenIds.
  4. Response: Return the created market group details.

File Changes:

2. Frontend Implementation (web)

Updates:

File Changes:

Database Schema

model Market {
  // ... existing fields
  imageUrl String? @map("image_url") // URL for market/player image
}

model Outcome {
  // ... existing fields
  imageUrl String? @map("image_url") // URL for outcome image (for future multi-choice support)
}

Considerations

Step-by-Step Plan

  1. Backend: Implement MarketService.createGroupedMarket.
  2. Backend: Add route POST /api/markets/grouped.
  3. Frontend: Update create-market/page.tsx.
  4. Test: Create a market via UI and verify it appears in DB and on-chain (via logs).

Image Upload Implementation

We use Multer, a node.js middleware for handling multipart/form-data, which is primarily used for uploading files.

Implementation Details

  1. Backend (api):

    • Middleware: multer is configured to store uploaded files in the local api/uploads directory.
    • Route: POST /api/upload accepts a single file, saves it with a unique name (timestamp + random), and returns the relative URL (e.g., /uploads/12345.png).
    • Static Serving: express.static serves the uploads directory at the /uploads path.
    • Market Creation: The POST /api/markets/grouped endpoint was updated to accept an imageUrl field and store it in the MarketGroup record.
  2. Frontend (web):

    • UI: Added a file input field for "Market Image".
    • Logic: When a file is selected, it is immediately uploaded to /api/upload. The returned URL is stored in state and then sent as part of the market creation payload.