Skip to content

Phase 4: Trading Interface - COMPLETE

Note: This page is a point-in-time status snapshot and may be out of date relative to the current source. PRs welcome.

Overview

Status: ✅ COMPLETE Implementation Date: January 2, 2026 Location: apps/web/src/components/trading/, apps/web/src/services/solanaProgram.ts

This document describes the trading interface implementation for Mentat Protocol, completing the M3 milestone.


What Was Implemented

1. Solana Program Compilation Fix

Problem: The Solana programs had compilation errors due to version incompatibility between Anchor 0.31.1 and Solana 3.x CLI tools.

Solution: 1. Installed Anchor CLI 0.32.1 via avm install 0.32.1 2. Updated anchor-lang dependency in both programs from 0.31.1 to 0.32.1 3. Installed Solana CLI stable (3.0+) 4. Successfully ran anchor build

Results: - target/idl/market_factory.json - 31KB IDL - target/idl/market_settlement.json - 32KB IDL - target/deploy/mentat_programs.so - 367KB binary - target/deploy/market_settlement.so - 353KB binary

2. Trading UI Components

TradingPanel.vue

Main trading interface for buying and selling outcome shares.

Features: - Buy/Sell toggle with visual state - Outcome selection grid showing prices and probabilities - Amount input with quick buttons ($10, $50, $100, Max) - Real-time trade estimation - Slippage tolerance slider (0.1% - 10%) - Execute trade button with loading state - Error display

Usage:

<TradingPanel
  :market-id="market.id"
  :market-public-key="market.publicKey"
  :outcomes="market.outcomes"
  @trade-success="handleSuccess"
  @trade-error="handleError"
/>

Styling: - Dark theme with CSS variables - Gradient buttons for buy/sell actions - Responsive grid layout - Smooth transitions and hover effects

PositionCard.vue

Displays user position for a specific outcome.

Features: - Outcome badge with color coding - Share count - PnL display (positive=green, negative=red) - Entry price vs current price - Invested amount and current value - Sell position button

Usage:

<PositionCard
  :position="position"
  @sell="handleSell"
/>

TradeConfirmModal.vue

Confirmation modal before executing a trade.

Features: - Trade summary icon (buy/sell) - Outcome label and expected price - Fee breakdown - Price impact warning (>2%) - Confirm/Cancel buttons - Loading state during confirmation - Teleport to body for z-index handling

Usage:

<TradeConfirmModal
  :show="showModal"
  :is-buy="true"
  :outcome-label="'YES'"
  :amount="BigInt(10000000)"
  :expected-shares="BigInt(9500000)"
  :expected-price="0.55"
  :price-impact="0.5"
  :trading-fee="BigInt(100000)"
  :total-cost="BigInt(10100000)"
  :max-slippage="1"
  :loading="isSubmitting"
  @close="closeModal"
  @confirm="submitTrade"
/>

3. Solana Program Service

Complete rewrite of apps/web/src/services/solanaProgram.ts with IDL integration.

Initialization

import { solanaProgramService } from '@/services/solanaProgram';

// Initialize with connection
solanaProgramService.initialize(connection);

// Initialize with wallet for transactions
solanaProgramService.initialize(connection, {
  publicKey: wallet.publicKey,
  signTransaction: wallet.signTransaction,
});

PDA Derivation

// Derive market PDA
const [marketPda, bump] = solanaProgramService.deriveMarketPda(new BN(123));

// Derive pool PDA
const [poolPda] = solanaProgramService.derivePoolPda(marketPublicKey);

// Derive position PDA
const [positionPda] = solanaProgramService.derivePositionPda(
  marketPublicKey,
  walletPublicKey
);

// Derive LP position PDA
const [lpPda] = solanaProgramService.deriveLpPositionPda(
  poolPublicKey,
  walletPublicKey
);

Trading Operations

// Execute trade
const result = await solanaProgramService.executeTrade({
  marketId: 'market-123',
  marketPublicKey: new PublicKey('...'),
  outcomeIndex: 0,
  isBuy: true,
  amount: BigInt(10_000_000), // 10 USDC
  maxSlippage: 1,
}, walletPublicKey);

// Estimate trade
const estimate = solanaProgramService.estimateTrade({
  marketId: 'market-123',
  marketPublicKey: new PublicKey('...'),
  outcomeIndex: 0,
  isBuy: true,
  amount: BigInt(10_000_000),
  maxSlippage: 1,
});
console.log('Expected shares:', estimate.expectedShares);
console.log('Price impact:', estimate.priceImpact);

Liquidity Operations

// Add liquidity
const signature = await solanaProgramService.addLiquidity({
  marketId: 'market-123',
  marketPublicKey: new PublicKey('...'),
  amount: BigInt(100_000_000), // 100 USDC
}, walletPublicKey);

// Remove liquidity
const signature = await solanaProgramService.removeLiquidity(
  marketPublicKey,
  BigInt(50_000_000), // LP tokens to burn
  walletPublicKey
);

Position Queries

// Get user positions
const positions = await solanaProgramService.getUserPositions(
  marketPublicKey,
  walletPublicKey
);

// Get liquidity position
const lpPosition = await solanaProgramService.getLiquidityPosition(
  marketPublicKey,
  walletPublicKey
);

// Get outcome prices
const prices = await solanaProgramService.getOutcomePrices(marketPublicKey);
console.log('YES:', prices[0], 'NO:', prices[1]);

Utility Methods

// USDC conversion
const lamports = solanaProgramService.usdcToLamports(10); // 10 USDC -> 10000000
const usdc = solanaProgramService.lamportsToUsdc(BigInt(10_000_000)); // 10.0

// Formatting
const priceStr = solanaProgramService.formatPrice(0.5523); // "0.5523"
const sharesStr = solanaProgramService.formatShares(BigInt(1_500_000)); // "1.50"

// Fee breakdown
const fees = solanaProgramService.calculateFeeBreakdown(
  BigInt(1_000_000), // amount
  100 // 1% fee (100 bps)
);
console.log('Trading fee:', fees.tradingFee);
console.log('LP share:', fees.lpShare); // 60%
console.log('Creator share:', fees.creatorShare); // 30%
console.log('Treasury share:', fees.treasuryShare); // 10%

File Structure

apps/web/src/
├── components/
│   └── trading/
│       ├── index.ts                  # Component exports
│       ├── TradingPanel.vue          # Main trading interface (350 LOC)
│       ├── PositionCard.vue          # Position display (180 LOC)
│       └── TradeConfirmModal.vue     # Confirmation modal (280 LOC)
├── idl/
│   ├── market_factory.json           # Market Factory IDL (31KB)
│   └── market_settlement.json        # Market Settlement IDL (32KB)
├── services/
│   └── solanaProgram.ts              # Solana service (558 LOC)
├── stores/
│   └── trading.ts                    # Trading store (526 LOC)
└── types/
    └── trading.ts                    # Type definitions (317 LOC)

Integration Guide

Import Components

import {
  TradingPanel,
  PositionCard,
  TradeConfirmModal
} from '@/components/trading';

Use in MarketDetail View

<template>
  <div class="market-detail">
    <div class="market-info">
      <!-- Existing market info -->
    </div>

    <div class="trading-section">
      <TradingPanel
        :market-id="market.id"
        :market-public-key="market.publicKey.toString()"
        :outcomes="market.outcomes"
        @trade-success="onTradeSuccess"
        @trade-error="onTradeError"
      />

      <div class="positions">
        <h3>Your Positions</h3>
        <PositionCard
          v-for="position in userPositions"
          :key="position.outcomeIndex"
          :position="position"
          @sell="openSellModal"
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { TradingPanel, PositionCard } from '@/components/trading';
import { useTradingStore } from '@/stores/trading';

const tradingStore = useTradingStore();

function onTradeSuccess(signature: string) {
  console.log('Trade confirmed:', signature);
  // Refresh positions
  tradingStore.refresh(walletStore.publicKey);
}

function onTradeError(error: Error) {
  console.error('Trade failed:', error);
}
</script>

Build Verification

# Build Solana programs
cd apps/solana-programs
anchor build
# ✅ Build successful

# Build web app
cd apps/web
npm run build
# ✅ 353 modules transformed
# ✅ Build time: 4.87s

# Verify trading components
ls -la src/components/trading/
# index.ts
# TradingPanel.vue
# PositionCard.vue
# TradeConfirmModal.vue

# Verify IDLs
ls -la src/idl/
# market_factory.json
# market_settlement.json

Next Steps

1. Deploy Solana Programs

cd apps/solana-programs

# Configure for devnet
solana config set --url devnet

# Create keypair (if needed)
solana-keygen new -o ~/.config/solana/id.json

# Fund account
solana airdrop 2

# Deploy
anchor deploy --provider.cluster devnet

# Note the program IDs from output

2. Update Environment

# apps/web/.env
VITE_SOLANA_NETWORK=devnet
VITE_MARKET_FACTORY_PROGRAM_ID=<deployed_id>
VITE_MARKET_SETTLEMENT_PROGRAM_ID=<deployed_id>

3. Start Development Server

cd apps/web
npm run dev
# Server at http://localhost:5173

4. Test Trading Flow

  1. Connect wallet (Phantom/Solflare)
  2. Navigate to a market
  3. Select outcome (YES/NO)
  4. Enter amount
  5. Click Buy/Sell
  6. Confirm in wallet
  7. Verify position updated

Summary

Phase 4 Implementation: ✅ COMPLETE

Component Status LOC
TradingPanel.vue 350
PositionCard.vue 180
TradeConfirmModal.vue 280
solanaProgram.ts 558
IDL Files 63KB

Total New Code: ~1,500 LOC Build Status: ✅ Passing


Created: January 2, 2026 Author: Claude Code