feat(admin): refactor admin console to focus on superadmin tier, graduation and dispute governance

This commit is contained in:
Bot
2026-08-31 01:58:15 +08:00
parent 4890329f99
commit 7c59c2e9fc
6 changed files with 499 additions and 126 deletions
+4 -6
View File
@@ -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<string>('faucet');
const [activeTab, setActiveTab] = useState<string>('governance');
return (
<Web3Provider>
@@ -17,14 +16,13 @@ export default function AdminPage() {
<AdminNavbar activeTab={activeTab} setActiveTab={setActiveTab} />
<main className="flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-8">
{activeTab === 'faucet' && <FaucetPanel />}
{activeTab === 'wizard' && <MarketWizard />}
{activeTab === 'governance' && <GovernancePanel />}
{activeTab === 'adjudication' && <AdjudicationPanel />}
{activeTab === 'faucet' && <FaucetPanel />}
</main>
<footer className="border-t border-slate-900 py-6 text-center text-xs text-slate-600">
WTFX Protocol Governance &amp; Administration Console &bull; Robinhood Chain Testnet (ID: 46630)
WTFX Protocol SuperAdmin Console &bull; Robinhood Chain Testnet (ID: 46630)
</footer>
</div>
</Web3Provider>
@@ -12,10 +12,9 @@ export const AdminNavbar: React.FC<NavbarProps> = ({ 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 (
@@ -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<string>('0.6');
const [creatorShare, setCreatorShare] = useState<string>('50');
const [centralWallet, setCentralWallet] = useState<string>('');
// Tier 与 毕业阈值 & 争议窗口
const [selectedTier, setSelectedTier] = useState<number>(1);
const [tierStartAmount, setTierStartAmount] = useState<string>('1.1');
const [tierPower, setTierPower] = useState<string>('1.0');
const [graduationPoolCap, setGraduationPoolCap] = useState<string>('100000');
const [graduationMaxSupply, setGraduationMaxSupply] = useState<string>('1000000');
const [disputeWindowDays, setDisputeWindowDays] = useState<string>('1');
const [loading, setLoading] = useState<boolean>(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 (
<div className="max-w-4xl mx-auto space-y-6">
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-6 shadow-xl space-y-6">
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 shadow-xl space-y-6">
<div className="border-b border-slate-800 pb-5 flex items-center justify-between">
<div className="flex items-center space-x-3">
<div className="p-3 bg-amber-500/10 rounded-xl border border-amber-500/20 text-amber-400">
<div className="p-3 bg-amber-500/10 rounded-2xl border border-amber-500/20 text-amber-400">
<Sliders className="w-6 h-6" />
</div>
<div>
<h2 className="text-xl font-bold text-white"> (Protocol Governance)</h2>
<p className="text-sm text-slate-400 mt-0.5">
<h2 className="text-xl font-bold text-white"> (SuperAdmin Protocol Governance)</h2>
<p className="text-xs text-slate-400 mt-0.5">
WTFControllerV2 DEFAULT_ADMIN_ROLE / OPERATOR_ROLE
</p>
</div>
@@ -128,100 +135,135 @@ export const GovernancePanel: React.FC = () => {
<button
onClick={fetchGovParams}
className="p-2 text-slate-400 hover:text-slate-200 hover:bg-slate-800 rounded-lg transition-colors"
className="p-2 text-slate-400 hover:text-slate-200 hover:bg-slate-800 rounded-xl transition-colors"
>
<RefreshCw className="w-4 h-4" />
</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Treasury Setting */}
<div className="bg-slate-950/50 border border-slate-800 rounded-xl p-4 space-y-3">
<div className="flex items-center space-x-2 text-sm font-semibold text-slate-200">
<ShieldCheck className="w-4 h-4 text-cyan-400" />
<span> (Treasury Address)</span>
{/* 1. Tier 治理档位配置 */}
<div className="bg-slate-950/60 border border-slate-800 rounded-2xl p-5 space-y-4">
<div className="flex items-center space-x-2 text-sm font-bold text-white">
<Layers className="w-4 h-4 text-cyan-400" />
<span>1. (Market Tier Curves Config)</span>
</div>
<p className="text-xs text-slate-400"> Tier Power LDA 线</p>
<div className="grid grid-cols-3 gap-3">
{[1, 2, 3].map((t) => (
<button
key={t}
onClick={() => setSelectedTier(t)}
className={`py-2 text-xs font-bold rounded-xl border transition-all ${
selectedTier === t
? 'bg-cyan-950/60 border-cyan-500 text-cyan-300'
: 'bg-slate-900 border-slate-800 text-slate-400 hover:border-slate-700'
}`}
>
Tier {t}
</button>
))}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-medium text-slate-400 mb-1"> (Start - &gt;= 1.1)</label>
<input
type="number"
step="0.1"
value={tierStartAmount}
onChange={(e) => 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"
/>
</div>
<p className="text-xs text-slate-400">/</p>
<div>
<label className="block text-xs font-medium text-slate-400 mb-1"> (Power - 1.0)</label>
<input
type="number"
step="0.1"
value={tierPower}
onChange={(e) => 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"
/>
</div>
</div>
<button
onClick={handleUpdateTierConfig}
disabled={loading || !account}
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-xl text-xs font-bold transition-colors"
>
Tier {selectedTier}
</button>
</div>
{/* 2. 毕业阈值与争议公示期 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-slate-950/60 border border-slate-800 rounded-2xl p-5 space-y-3">
<div className="flex items-center space-x-2 text-sm font-bold text-white">
<Award className="w-4 h-4 text-amber-400" />
<span>2. (Graduation Pool Cap)</span>
</div>
<p className="text-xs text-slate-400"></p>
<input
type="number"
value={graduationPoolCap}
onChange={(e) => 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"
/>
<button
onClick={handleUpdateGraduation}
disabled={loading || !account}
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-xl text-xs font-bold transition-colors"
>
</button>
</div>
<div className="bg-slate-950/60 border border-slate-800 rounded-2xl p-5 space-y-3">
<div className="flex items-center space-x-2 text-sm font-bold text-white">
<Clock className="w-4 h-4 text-indigo-400" />
<span>3. (Dispute Window)</span>
</div>
<p className="text-xs text-slate-400"></p>
<input
type="number"
value={disputeWindowDays}
onChange={(e) => 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"
/>
<button
onClick={handleUpdateDisputeWindow}
disabled={loading || !account}
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-xl text-xs font-bold transition-colors"
>
()
</button>
</div>
</div>
{/* 3. 国库与安全地址 */}
<div className="bg-slate-950/60 border border-slate-800 rounded-2xl p-5 space-y-3">
<div className="flex items-center space-x-2 text-sm font-bold text-white">
<ShieldCheck className="w-4 h-4 text-emerald-400" />
<span>4. (Treasury Address)</span>
</div>
<p className="text-xs text-slate-400"> 50% /</p>
<div className="flex space-x-2">
<input
type="text"
value={treasury}
onChange={(e) => 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"
/>
<button
onClick={handleUpdateTreasury}
disabled={loading || !account}
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-lg text-xs font-medium transition-colors"
className="px-5 py-2 bg-cyan-600 hover:bg-cyan-500 text-white rounded-lg text-xs font-bold transition-colors disabled:opacity-50"
>
</button>
</div>
{/* Central Wallet */}
<div className="bg-slate-950/50 border border-slate-800 rounded-xl p-4 space-y-3">
<div className="flex items-center space-x-2 text-sm font-semibold text-slate-200">
<Wallet className="w-4 h-4 text-indigo-400" />
<span> (Central Wallet)</span>
</div>
<p className="text-xs text-slate-400"></p>
<input
type="text"
value={centralWallet}
onChange={(e) => 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"
/>
<button
onClick={handleUpdateCentralWallet}
disabled={loading || !account}
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-lg text-xs font-medium transition-colors"
>
</button>
</div>
{/* Default Fee Rate */}
<div className="bg-slate-950/50 border border-slate-800 rounded-xl p-4 space-y-3">
<div className="flex items-center space-x-2 text-sm font-semibold text-slate-200">
<Percent className="w-4 h-4 text-emerald-400" />
<span> (Fee Rate %)</span>
</div>
<p className="text-xs text-slate-400"> [0.1%, 3.0%] 0.6%</p>
<input
type="number"
step="0.01"
value={defaultFeeRate}
onChange={(e) => 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"
/>
<button
onClick={handleUpdateFeeRate}
disabled={loading || !account}
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-lg text-xs font-medium transition-colors"
>
</button>
</div>
{/* Creator Share */}
<div className="bg-slate-950/50 border border-slate-800 rounded-xl p-4 space-y-3">
<div className="flex items-center space-x-2 text-sm font-semibold text-slate-200">
<Percent className="w-4 h-4 text-pink-400" />
<span> (Creator Share %)</span>
</div>
<p className="text-xs text-slate-400"> 50%</p>
<input
type="number"
step="1"
value={creatorShare}
onChange={(e) => 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"
/>
<button
onClick={handleUpdateCreatorShare}
disabled={loading || !account}
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-lg text-xs font-medium transition-colors"
>
</button>
</div>
</div>
@@ -229,16 +271,16 @@ export const GovernancePanel: React.FC = () => {
{/* Status Message */}
{statusMsg && (
<div
className={`p-4 rounded-xl border flex items-start space-x-3 text-sm ${
className={`p-4 rounded-2xl border flex items-start space-x-3 text-xs ${
statusMsg.type === 'success'
? 'bg-emerald-950/30 border-emerald-800/50 text-emerald-300'
: 'bg-rose-950/30 border-rose-800/50 text-rose-300'
}`}
>
{statusMsg.type === 'success' ? (
<Check className="w-5 h-5 flex-shrink-0 text-emerald-400" />
<Check className="w-4 h-4 flex-shrink-0 text-emerald-400" />
) : (
<AlertCircle className="w-5 h-5 flex-shrink-0 text-rose-400" />
<AlertCircle className="w-4 h-4 flex-shrink-0 text-rose-400" />
)}
<div className="flex-1 overflow-hidden break-words">
<p>{statusMsg.text}</p>