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:
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¶
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¶
4. Test Trading Flow¶
- Connect wallet (Phantom/Solflare)
- Navigate to a market
- Select outcome (YES/NO)
- Enter amount
- Click Buy/Sell
- Confirm in wallet
- 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