From 7c59c2e9fced911a780a4ccce00cee4145f8b20d Mon Sep 17 00:00:00 2001 From: Bot Date: Mon, 31 Aug 2026 01:58:15 +0800 Subject: [PATCH] feat(admin): refactor admin console to focus on superadmin tier, graduation and dispute governance --- apps/admin/src/app/page.tsx | 10 +- .../admin/src/features/common/AdminNavbar.tsx | 7 +- .../features/governance/GovernancePanel.tsx | 254 +++++++++------ apps/web/src/app/page.tsx | 2 + apps/web/src/components/WebNavbar.tsx | 50 ++- .../src/features/create/UserMarketWizard.tsx | 302 ++++++++++++++++++ 6 files changed, 499 insertions(+), 126 deletions(-) create mode 100644 apps/web/src/features/create/UserMarketWizard.tsx diff --git a/apps/admin/src/app/page.tsx b/apps/admin/src/app/page.tsx index 5c191bd..247d32e 100644 --- a/apps/admin/src/app/page.tsx +++ b/apps/admin/src/app/page.tsx @@ -3,13 +3,12 @@ import React, { useState } from 'react'; import { Web3Provider } from '../context/Web3Context'; import { AdminNavbar } from '../features/common/AdminNavbar'; -import { FaucetPanel } from '../features/faucet/FaucetPanel'; -import { MarketWizard } from '../features/wizard/MarketWizard'; import { GovernancePanel } from '../features/governance/GovernancePanel'; import { AdjudicationPanel } from '../features/adjudication/AdjudicationPanel'; +import { FaucetPanel } from '../features/faucet/FaucetPanel'; export default function AdminPage() { - const [activeTab, setActiveTab] = useState('faucet'); + const [activeTab, setActiveTab] = useState('governance'); return ( @@ -17,14 +16,13 @@ export default function AdminPage() {
- {activeTab === 'faucet' && } - {activeTab === 'wizard' && } {activeTab === 'governance' && } {activeTab === 'adjudication' && } + {activeTab === 'faucet' && }
- WTFX Protocol Governance & Administration Console • Robinhood Chain Testnet (ID: 46630) + WTFX Protocol SuperAdmin Console • Robinhood Chain Testnet (ID: 46630)
diff --git a/apps/admin/src/features/common/AdminNavbar.tsx b/apps/admin/src/features/common/AdminNavbar.tsx index ce1dd19..5279c8b 100644 --- a/apps/admin/src/features/common/AdminNavbar.tsx +++ b/apps/admin/src/features/common/AdminNavbar.tsx @@ -12,10 +12,9 @@ export const AdminNavbar: React.FC = ({ activeTab, setActiveTab }) const { account, isCorrectNetwork, connectWallet, switchNetwork } = useWeb3(); const navItems = [ - { id: 'faucet', label: '🚰 WUSD 水龙头' }, - { id: 'wizard', label: '🚀 创建市场向导' }, - { id: 'governance', label: '⚙️ 协议参数治理' }, - { id: 'adjudication', label: '⚖️ 市场风控与裁决' }, + { id: 'governance', label: '⚙️ 协议全局治理' }, + { id: 'adjudication', label: '⚖️ 市场争议终裁与熔断' }, + { id: 'faucet', label: '🚰 超管铸币水龙头' }, ]; return ( diff --git a/apps/admin/src/features/governance/GovernancePanel.tsx b/apps/admin/src/features/governance/GovernancePanel.tsx index dde0fc7..60d31c7 100644 --- a/apps/admin/src/features/governance/GovernancePanel.tsx +++ b/apps/admin/src/features/governance/GovernancePanel.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { useWeb3 } from '../../context/Web3Context'; import { DEPLOYMENTS } from '@wtfx/contracts'; import { ethers } from 'ethers'; -import { Sliders, ShieldCheck, Percent, Wallet, Check, AlertCircle, Loader2, RefreshCw } from 'lucide-react'; +import { Sliders, ShieldCheck, Percent, Wallet, Check, AlertCircle, Loader2, RefreshCw, Layers, Award, Clock } from 'lucide-react'; export const GovernancePanel: React.FC = () => { const { account, signer, getControllerContract, isCorrectNetwork } = useWeb3(); @@ -11,16 +11,20 @@ export const GovernancePanel: React.FC = () => { const [defaultFeeRate, setDefaultFeeRate] = useState('0.6'); const [creatorShare, setCreatorShare] = useState('50'); const [centralWallet, setCentralWallet] = useState(''); + + // Tier 与 毕业阈值 & 争议窗口 + const [selectedTier, setSelectedTier] = useState(1); + const [tierStartAmount, setTierStartAmount] = useState('1.1'); + const [tierPower, setTierPower] = useState('1.0'); + const [graduationPoolCap, setGraduationPoolCap] = useState('100000'); + const [graduationMaxSupply, setGraduationMaxSupply] = useState('1000000'); + const [disputeWindowDays, setDisputeWindowDays] = useState('1'); + const [loading, setLoading] = useState(false); const [statusMsg, setStatusMsg] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const fetchGovParams = async () => { try { - const controller = getControllerContract(); - if (!controller) return; - - // In real scenario, read from getters or view functions - // We fill initialized defaults from deployment setTreasury(DEPLOYMENTS.robinhoodTestnet.governance.treasury); setCentralWallet(DEPLOYMENTS.robinhoodTestnet.governance.admin); } catch (err) { @@ -51,7 +55,7 @@ export const GovernancePanel: React.FC = () => { } }; - const handleUpdateFeeRate = async () => { + const handleUpdateTierConfig = async () => { if (!signer || !account) return; try { setLoading(true); @@ -59,11 +63,12 @@ export const GovernancePanel: React.FC = () => { const controller = getControllerContract(); if (!controller) throw new Error('Controller not found'); - const rateWei = ethers.parseUnits((Number(defaultFeeRate) / 100).toString(), 18); - const tx = await controller.setFeeRateDefault(rateWei); - setStatusMsg({ type: 'success', text: `更新默认手续费交易已广播: ${tx.hash.slice(0, 10)}...` }); + const startWei = ethers.parseUnits(tierStartAmount, 18); + const powerWei = ethers.parseUnits(tierPower, 18); + const tx = await controller.setMarketTier(selectedTier, startWei, powerWei); + setStatusMsg({ type: 'success', text: `更新 Tier ${selectedTier} 曲线参数交易已广播: ${tx.hash.slice(0, 10)}...` }); await tx.wait(); - setStatusMsg({ type: 'success', text: `🎉 全局默认协议手续费率已成功更新为: ${defaultFeeRate}%` }); + setStatusMsg({ type: 'success', text: `🎉 Tier ${selectedTier} 标准参数已成功更新 (Start: ${tierStartAmount}, Power: ${tierPower})!` }); } catch (err: any) { setStatusMsg({ type: 'error', text: err.reason || err.message || '更新失败' }); } finally { @@ -71,7 +76,7 @@ export const GovernancePanel: React.FC = () => { } }; - const handleUpdateCreatorShare = async () => { + const handleUpdateGraduation = async () => { if (!signer || !account) return; try { setLoading(true); @@ -79,11 +84,12 @@ export const GovernancePanel: React.FC = () => { const controller = getControllerContract(); if (!controller) throw new Error('Controller not found'); - const shareWei = ethers.parseUnits((Number(creatorShare) / 100).toString(), 18); - const tx = await controller.setCreatorShare(shareWei); - setStatusMsg({ type: 'success', text: `更新建盘者手续费分成交易已广播: ${tx.hash.slice(0, 10)}...` }); + const capWei = ethers.parseUnits(graduationPoolCap, 18); + const supplyWei = ethers.parseUnits(graduationMaxSupply, 18); + const tx = await controller.setGraduationThresholds(capWei, supplyWei); + setStatusMsg({ type: 'success', text: `更新毕业资金池阈值交易已广播: ${tx.hash.slice(0, 10)}...` }); await tx.wait(); - setStatusMsg({ type: 'success', text: `🎉 建盘者手续费返佣分成比例已成功更新为: ${creatorShare}%` }); + setStatusMsg({ type: 'success', text: `🎉 市场毕业门槛已更新:资金池达到 ${Number(graduationPoolCap).toLocaleString()} WUSD 自动毕业!` }); } catch (err: any) { setStatusMsg({ type: 'error', text: err.reason || err.message || '更新失败' }); } finally { @@ -91,7 +97,7 @@ export const GovernancePanel: React.FC = () => { } }; - const handleUpdateCentralWallet = async () => { + const handleUpdateDisputeWindow = async () => { if (!signer || !account) return; try { setLoading(true); @@ -99,10 +105,11 @@ export const GovernancePanel: React.FC = () => { const controller = getControllerContract(); if (!controller) throw new Error('Controller not found'); - const tx = await controller.setCentralWallet(centralWallet.trim()); - setStatusMsg({ type: 'success', text: `更新中央安全提币钱包交易已广播: ${tx.hash.slice(0, 10)}...` }); + const windowSec = Math.floor(Number(disputeWindowDays) * 86400); + const tx = await controller.setDisputeWindow(windowSec); + setStatusMsg({ type: 'success', text: `更新争议公示期窗口交易已广播: ${tx.hash.slice(0, 10)}...` }); await tx.wait(); - setStatusMsg({ type: 'success', text: `🎉 中央安全提币钱包已成功更新为: ${centralWallet}` }); + setStatusMsg({ type: 'success', text: `🎉 市场初裁后的争议公示期已成功更新为: ${disputeWindowDays} 天!` }); } catch (err: any) { setStatusMsg({ type: 'error', text: err.reason || err.message || '更新失败' }); } finally { @@ -112,15 +119,15 @@ export const GovernancePanel: React.FC = () => { return (
-
+
-
+
-

协议治理看板 (Protocol Governance)

-

+

协议全局治理控制台 (SuperAdmin Protocol Governance)

+

WTFControllerV2 智能合约超管参数配置(仅 DEFAULT_ADMIN_ROLE / OPERATOR_ROLE 可执行)

@@ -128,100 +135,135 @@ export const GovernancePanel: React.FC = () => {
-
- {/* Treasury Setting */} -
-
- - 协议国库金库 (Treasury Address) + {/* 1. Tier 治理档位配置 */} +
+
+ + 1. 市场治理分级参数 (Market Tier Curves Config) +
+

设置不同 Tier 市场启动时的 Power LDA 曲线底仓幂律参数。

+ +
+ {[1, 2, 3].map((t) => ( + + ))} +
+ +
+
+ + setTierStartAmount(e.target.value)} + className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500" + />
-

用于接收协议留存手续费分润的冷钱包/金库合约。

+
+ + setTierPower(e.target.value)} + className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500" + /> +
+
+ + +
+ + {/* 2. 毕业阈值与争议公示期 */} +
+
+
+ + 2. 市场毕业门槛 (Graduation Pool Cap) +
+

当市场累积保证金池达到该阈值时自动毕业锁定。

+ setGraduationPoolCap(e.target.value)} + placeholder="100000" + className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500" + /> + +
+ +
+
+ + 3. 争议公示期窗口 (Dispute Window) +
+

创建者初裁后允许社区提出异议的等待天数。

+ setDisputeWindowDays(e.target.value)} + placeholder="1" + className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500" + /> + +
+
+ + {/* 3. 国库与安全地址 */} +
+
+ + 4. 协议国库金库地址 (Treasury Address) +
+

接收协议留存 50% 手续费分润的冷钱包/金库合约。

+
setTreasury(e.target.value)} - className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500" + className="flex-1 bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500" /> -
- - {/* Central Wallet */} -
-
- - 中央安全提币钱包 (Central Wallet) -
-

由超管授权以自动化划拨协议手续费的专用钱包。

- setCentralWallet(e.target.value)} - className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500" - /> - -
- - {/* Default Fee Rate */} -
-
- - 默认协议手续费率 (Fee Rate %) -
-

新建市场默认费率(范围 [0.1%, 3.0%],默认 0.6%)。

- setDefaultFeeRate(e.target.value)} - className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500" - /> - -
- - {/* Creator Share */} -
-
- - 建盘者返佣分成比例 (Creator Share %) -
-

交易产生的手续费中分给市场创建者的比例(默认 50%)。

- setCreatorShare(e.target.value)} - className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500" - /> -
@@ -229,16 +271,16 @@ export const GovernancePanel: React.FC = () => { {/* Status Message */} {statusMsg && (
{statusMsg.type === 'success' ? ( - + ) : ( - + )}

{statusMsg.text}

diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 2adc890..e2b32d8 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -6,6 +6,7 @@ import { WebNavbar } from '../components/WebNavbar'; import { MarketExplore, MarketItem } from '../features/explore/MarketExplore'; import { TradingTerminal } from '../features/trading/TradingTerminal'; import { PortfolioView } from '../features/portfolio/PortfolioView'; +import { UserMarketWizard } from '../features/create/UserMarketWizard'; const DEFAULT_MARKET: MarketItem = { id: 'm-1', @@ -40,6 +41,7 @@ export default function WebApp() {
{activeTab === 'explore' && } {activeTab === 'trade' && } + {activeTab === 'create' && } {activeTab === 'portfolio' && }
diff --git a/apps/web/src/components/WebNavbar.tsx b/apps/web/src/components/WebNavbar.tsx index 7afebca..6c118ba 100644 --- a/apps/web/src/components/WebNavbar.tsx +++ b/apps/web/src/components/WebNavbar.tsx @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { useState } from 'react'; import { useWeb3 } from '../context/Web3Context'; -import { Wallet, TrendingUp, Compass, PieChart, ShieldAlert, CheckCircle2 } from 'lucide-react'; +import { Wallet, TrendingUp, Compass, PieChart, PlusCircle, Droplet, ShieldAlert } from 'lucide-react'; interface NavbarProps { activeTab: string; @@ -8,12 +8,31 @@ interface NavbarProps { } export const WebNavbar: React.FC = ({ activeTab, setActiveTab }) => { - const { account, wusdBalance, isCorrectNetwork, connectWallet, switchNetwork } = useWeb3(); + const { account, wusdBalance, isCorrectNetwork, connectWallet, switchNetwork, refreshBalance, getCollateralContract } = useWeb3(); + const [faucetLoading, setFaucetLoading] = useState(false); + + const handleQuickFaucet = async () => { + if (!account) return; + try { + setFaucetLoading(true); + const collateral = getCollateralContract(); + if (!collateral) return; + const tx = await collateral.mint(account, "100000000000000000000000000"); // 100M WUSD + await tx.wait(); + refreshBalance(); + alert('🎉 成功领取 100,000,000 WUSD 测试金!'); + } catch (e: any) { + alert('领水失败: ' + (e.message || e)); + } finally { + setFaucetLoading(false); + } + }; const navs = [ { id: 'explore', label: '🔥 市场探索', icon: Compass }, { id: 'trade', label: '⚡ 交易终端', icon: TrendingUp }, - { id: 'portfolio', label: '💼 我的持仓与账本', icon: PieChart }, + { id: 'create', label: '🚀 创建市场', icon: PlusCircle }, + { id: 'portfolio', label: '💼 持仓与金库', icon: PieChart }, ]; return ( @@ -30,7 +49,7 @@ export const WebNavbar: React.FC = ({ activeTab, setActiveTab }) => WTFX - POWER LDA + TESTNET
@@ -43,7 +62,7 @@ export const WebNavbar: React.FC = ({ activeTab, setActiveTab }) =>
- {/* Right Section / Wallet Status */} -
+ {/* Right Section / Faucet & Wallet */} +
+ {account && ( + + )} + {account ? (
{!isCorrectNetwork ? ( ) : (
diff --git a/apps/web/src/features/create/UserMarketWizard.tsx b/apps/web/src/features/create/UserMarketWizard.tsx new file mode 100644 index 0000000..4bb4545 --- /dev/null +++ b/apps/web/src/features/create/UserMarketWizard.tsx @@ -0,0 +1,302 @@ +import React, { useState } from 'react'; +import { useWeb3 } from '../../context/Web3Context'; +import { DEPLOYMENTS } from '@wtfx/contracts'; +import { ethers } from 'ethers'; +import { PlusCircle, Trash2, Rocket, Check, AlertCircle, Loader2, Sparkles, HelpCircle } from 'lucide-react'; + +export const UserMarketWizard: React.FC = () => { + const { account, signer, getCollateralContract, isCorrectNetwork } = useWeb3(); + + const [question, setQuestion] = useState(''); + const [description, setDescription] = useState(''); + const [category, setCategory] = useState('Crypto'); + const [customCategory, setCustomCategory] = useState(''); + const [endTime, setEndTime] = useState( + new Date(Date.now() + 7 * 24 * 3600 * 1000).toISOString().slice(0, 16) + ); + const [outcomes, setOutcomes] = useState(['YES', 'NO']); + const [seedAmount, setSeedAmount] = useState('100'); + const [loading, setLoading] = useState(false); + const [statusMsg, setStatusMsg] = useState<{ type: 'success' | 'error'; text: string } | null>(null); + + const addOutcome = () => { + if (outcomes.length < 255) { + setOutcomes([...outcomes, `选项 ${outcomes.length + 1}`]); + } + }; + + const removeOutcome = (index: number) => { + if (outcomes.length > 2) { + setOutcomes(outcomes.filter((_, i) => i !== index)); + } + }; + + const updateOutcome = (index: number, val: string) => { + const updated = [...outcomes]; + updated[index] = val; + setOutcomes(updated); + }; + + const handleDeployMarket = async (e: React.FormEvent) => { + e.preventDefault(); + if (!signer || !account) { + setStatusMsg({ type: 'error', text: '请先连接钱包!' }); + return; + } + if (!isCorrectNetwork) { + setStatusMsg({ type: 'error', text: '请切换至 Robinhood Testnet 网络!' }); + return; + } + + try { + setLoading(true); + setStatusMsg(null); + + const controllerAddress = DEPLOYMENTS.robinhoodTestnet.contracts.controllerProxy; + const collateralContract = getCollateralContract(); + if (!collateralContract) throw new Error('Collateral contract not loaded'); + + const seedWei = ethers.parseUnits(seedAmount || '0', 18); + + // Step 1: Approve Seed + if (seedWei > 0n) { + setStatusMsg({ type: 'success', text: '步骤 1/3: 正在授权初始底仓 WUSD 资金...' }); + const appTx = await collateralContract.approve(controllerAddress, ethers.MaxUint256); + await appTx.wait(); + } + + // Step 2: Deploy Market on Controller + setStatusMsg({ type: 'success', text: '步骤 2/3: 正在链上部署专属 Power LDA 预测市场...' }); + const deadlineSec = Math.floor(new Date(endTime).getTime() / 1000); + const questionId = ethers.keccak256( + ethers.toUtf8Bytes(`${question}-${Date.now()}-${account}`) + ); + + const controllerAbi = [ + "function deployMarket((bytes32 questionId, string question, bytes ancillaryData, address rewardToken, uint256 reward, uint256 proposalBond, uint256 earlyResolutionBond, uint256 settlementResolutionBond, uint256 resolutionTime, uint256 numOutcomes) paramsQuestion, (uint256 tier, address creator, uint80 feeRate, uint80 creatorShare, address curve, address collateral) paramsMarket, address oracle, uint256 otSeed) external returns (address)" + ]; + const controller = new ethers.Contract(controllerAddress, controllerAbi, signer); + + const questionParams = { + questionId: questionId, + question: question, + ancillaryData: "0x", // 元数据存储在后端数据库 + rewardToken: DEPLOYMENTS.robinhoodTestnet.contracts.collateral, + reward: 0, + proposalBond: 0, + earlyResolutionBond: 0, + settlementResolutionBond: 0, + resolutionTime: deadlineSec, + numOutcomes: outcomes.length, + }; + + const marketParams = { + tier: 1, // 默认标准档位 + creator: account, + feeRate: ethers.parseUnits('0.006', 18), + creatorShare: ethers.parseUnits('0.5', 18), + curve: DEPLOYMENTS.robinhoodTestnet.contracts.powerLDACurve, + collateral: DEPLOYMENTS.robinhoodTestnet.contracts.collateral, + }; + + const tx = await controller.deployMarket( + questionParams, + marketParams, + account, // 用户本人作为预言机和裁决人! + seedWei + ); + + setStatusMsg({ type: 'success', text: `步骤 3/3: 正在上链确认并同步链下元数据 (Tx: ${tx.hash.slice(0, 10)}...)...` }); + const receipt = await tx.wait(); + + setStatusMsg({ + type: 'success', + text: `🎉 预测市场创建成功!你作为创建者享有 50% 手续费返佣并可在到期后自主裁决结果。`, + }); + } catch (err: any) { + console.error('User market deploy error:', err); + setStatusMsg({ type: 'error', text: err.reason || err.message || '建盘失败,请检查参数' }); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+
+
+ +
+
+

自由创建预测市场 (Create Prediction Market)

+

+ 任何人均可自由创建 2~255 个选项的预测市场,享受 50% 交易手续费分成与自主裁决权 +

+
+
+
+ + {/* 1. 基本信息 */} +
+
+ + setQuestion(e.target.value)} + placeholder="例如:2026 年底前人类能否首次成功登月?" + className="w-full bg-slate-950 border border-slate-700 rounded-xl px-4 py-3 text-sm text-slate-100 focus:outline-none focus:border-cyan-500" + /> +
+ +
+
+ + +
+ +
+ + setEndTime(e.target.value)} + className="w-full bg-slate-950 border border-slate-700 rounded-xl px-4 py-3 text-sm text-slate-100 focus:outline-none focus:border-cyan-500" + /> +
+
+ +
+ +