Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Feature 09: Economy & Marketplace

Overview

The player economy: gold as primary currency, the auction house marketplace for player-to-player trading, NPC vendors, the mail system for item/gold transfers, and gold sinks to prevent inflation. All marketplace transactions are serialized through the economy worker to prevent race conditions.

Dependencies

  • Feature 03 (Items & Inventory) — items to trade
  • Feature 01 (Authentication) — user accounts for mail

Technical Tasks

1. Database Migrations

  • Create migration 0008_create_economy.sql:
    • marketplace_listings: id, seller_id, item_id, price, listing_fee, status (active/sold/expired/cancelled), listed_at, expires_at (48h), sold_to, sold_at
    • Indexes: status WHERE active, expires_at WHERE active, seller_id
    • mail: id, sender_id (nullable — system mail), recipient_id, subject, body, gold_amount, item_ids (UUID[]), is_read, deliverable_at, sent_at, expires_at
    • Index: recipient_id

2. Economy Rules (crates/game)

  • Create src/economy.rs:
    • Constants:
      • LISTING_FEE_RATE: f64 = 0.05 (5% of list price, deducted upfront)
      • SALE_TAX_RATE: f64 = 0.10 (10% of sale price, deducted from proceeds)
      • MAIL_GOLD_FEE_RATE: f64 = 0.05 (5% fee on gold transfers via mail)
      • LISTING_DURATION: Duration = 48 hours
      • MAIL_DELIVERY_DELAY: Duration = 1 hour
      • MAIL_EXPIRY: Duration = 30 days
    • calculate_listing_fee(price: u64) -> u64
    • calculate_sale_proceeds(price: u64) -> u64: price - sale tax
    • calculate_mail_gold_fee(amount: u64) -> u64
    • NPC vendor pricing:
      • npc_sell_price(item: &Item) -> u64: base value by rarity tier
      • npc_buy_price(item: &Item, background: Background) -> u64: 50-70% of sell value (some backgrounds get better rates)

3. Marketplace Queries (crates/db)

  • Create src/queries/marketplace.rs:
    • create_listing(pool, seller_id, item_id, price, listing_fee) -> Listing
    • find_active_listings(pool, filters: MarketplaceFilters, limit, offset) -> Vec<Listing>:
      • Filters: item_type, rarity, level_range, price_range, name search
    • find_listing_by_id(pool, id) -> Option<Listing>
    • find_listings_by_seller(pool, seller_id) -> Vec<Listing>
    • complete_sale(pool, listing_id, buyer_id) — used by economy worker
    • cancel_listing(pool, listing_id) — return item to seller
    • expire_listings(pool) -> Vec<Listing> — find all WHERE expires_at < now AND status = active

4. Mail Queries (crates/db)

  • Create src/queries/mail.rs:
    • send_mail(pool, sender_id, recipient_id, subject, body, gold, item_ids) -> Mail
    • find_mail_for_character(pool, character_id) -> Vec<Mail>: WHERE deliverable_at <= now
    • find_mail_by_id(pool, id) -> Option<Mail>
    • mark_read(pool, mail_id)
    • collect_mail_attachments(pool, mail_id) — move items/gold to character
    • delete_expired_mail(pool) -> u64 — delete WHERE expires_at < now AND is_read

5. Economy Worker (crates/workers)

  • Create src/economy_worker.rs:
    • Single-consumer queue — only one instance processes the marketplace-buy queue to prevent race conditions
    • handle_marketplace_buy(job):
      1. BEGIN TRANSACTION
      2. Load listing (SELECT FOR UPDATE) — verify still active
      3. Load buyer character (SELECT FOR UPDATE) — verify gold >= price
      4. Deduct gold from buyer
      5. Add sale proceeds (price - 10% tax) to seller’s gold
      6. Transfer item ownership to buyer (set location to ‘mail’)
      7. Create mail to buyer with purchased item
      8. Create mail to seller with gold notification
      9. Update listing status to ‘sold’
      10. COMMIT
    • handle_auction_expiry():
      • Run every 5 minutes via scheduler
      • Find all expired active listings
      • Return items to sellers via mail
      • Update listing status to ‘expired’

6. Marketplace Routes (crates/api)

  • Create src/routes/marketplace.rs:
    • GET /api/marketplace:
      • Query params: type, rarity, level_min, level_max, price_min, price_max, search, page
      • Return paginated listings with item details
    • POST /api/marketplace/list:
      • Body: { character_id, item_id, price }
      • Validate: item owned, in backpack/bank, not equipped, price > 0
      • Check seller has available listing slots (default 10, up to 30 with purchases)
      • Deduct listing fee (5% of price) from seller’s gold
      • Move item to ‘listed’ location
      • Create listing record
    • POST /api/marketplace/buy/:listing_id:
      • Body: { character_id }
      • Validate: buyer has enough gold, listing is active, buyer != seller
      • Do NOT directly modify gold — enqueue marketplace-buy job
      • Return { status: "processing" } (buyer sees result on next poll)
    • DELETE /api/marketplace/:listing_id:
      • Cancel own listing, return item to backpack
      • Listing fee is NOT refunded

7. Mail Routes (crates/api)

  • Create src/routes/mail.rs:
    • GET /api/characters/:id/mail:
      • Return delivered mail (deliverable_at <= now)
      • Include: sender name, subject, body, gold amount, item summaries, is_read, sent_at
    • POST /api/mail/send:
      • Body: { sender_id, recipient_name, subject, body, gold_amount?, item_ids? }
      • Validate: sender owns items, has enough gold + transfer fee
      • Deduct gold + fee from sender, remove items from inventory
      • Create mail with deliverable_at = now + 1 hour
    • POST /api/mail/:id/collect:
      • Move attached items to character’s backpack
      • Add gold to character’s balance
      • Mark as collected (prevent double collection)

8. NPC Vendor Routes (crates/api)

  • Add to src/routes/inventory.rs (or new file):
    • POST /api/characters/:id/sell-to-vendor:
      • Body: { item_ids: [uuid] }
      • Calculate NPC price per item, add gold to character, delete items
    • NPC buy is handled via faction vendors (Feature 13)

9. Client — Marketplace & Mail

  • Create routes/(game)/marketplace/+page.svelte:
    • Search/filter bar: item type, rarity, level, price range, text search
    • Listing grid: item card with name, rarity color, stats preview, price, time remaining
    • Buy flow: click listing → confirmation modal → POST buy → show “Processing…” → poll for result
    • Sell flow: select item from inventory → set price → list
    • My Listings tab: active listings with cancel button
  • Create routes/(game)/mail/+page.svelte (or sidebar panel):
    • Inbox list: sender, subject, gold/item indicators, read status
    • Mail detail: body, attachments, “Collect” button
    • Compose: recipient name autocomplete, subject, body, attach gold/items

Tests

Unit Tests

  • calculate_listing_fee: 5% of 1000 = 50
  • calculate_sale_proceeds: 1000 - 10% = 900
  • calculate_mail_gold_fee: 5% of 500 = 25
  • npc_sell_price: scales by rarity tier
  • npc_buy_price: returns 50-70% of sell price

Integration Tests

  • POST /api/marketplace/list creates listing, deducts fee, moves item
  • POST /api/marketplace/list rejects equipped item
  • POST /api/marketplace/list rejects when insufficient gold for fee
  • POST /api/marketplace/list rejects when at listing slot limit
  • GET /api/marketplace returns active listings with filters
  • POST /api/marketplace/buy/:id enqueues job, economy worker transfers gold/item
  • Economy worker: buyer gold decreases, seller gold increases by correct amounts
  • Economy worker: item transferred to buyer via mail
  • POST /api/marketplace/buy/:id on already-sold listing → 409
  • DELETE /api/marketplace/:id returns item to seller, no fee refund
  • Auction expiry: expired listings return items to seller via mail
  • POST /api/mail/send with gold deducts gold + fee, creates deliverable mail
  • POST /api/mail/send with items removes items from sender inventory
  • GET /api/characters/:id/mail only returns mail where deliverable_at <= now
  • POST /api/mail/:id/collect moves items/gold to character
  • POST /api/mail/:id/collect twice → 409
  • Concurrent buy attempts on same listing: only one succeeds (economy worker serialization)
  • POST /api/characters/:id/sell-to-vendor grants gold and deletes items