feat(vault): add user vault factory, non-custodial deposit/withdraw and session key UI

This commit is contained in:
Bot
2026-08-31 01:41:57 +08:00
parent 7e554402b2
commit 4890329f99
6 changed files with 607 additions and 9 deletions
@@ -1,9 +1,51 @@
import React from 'react';
import React, { useState } from 'react';
import { useWeb3 } from '../../context/Web3Context';
import { PieChart, Clock, ArrowUpRight, ArrowDownLeft, ShieldCheck, History } from 'lucide-react';
import { PieChart, Clock, ArrowUpRight, ArrowDownLeft, ShieldCheck, History, KeyRound, ArrowRightLeft, Loader2, Check } from 'lucide-react';
export const PortfolioView: React.FC = () => {
const { account, wusdBalance } = useWeb3();
const { account, wusdBalance, vaultAddress, isSessionActive, depositToVault, withdrawFromVault, enableSessionKey } = useWeb3();
const [depositAmount, setDepositAmount] = useState<string>('500');
const [withdrawAmount, setWithdrawAmount] = useState<string>('100');
const [loading, setLoading] = useState<boolean>(false);
const [actionMsg, setActionMsg] = useState<string | null>(null);
const handleDeposit = async () => {
try {
setLoading(true);
setActionMsg(null);
const hash = await depositToVault(depositAmount);
setActionMsg(`🎉 成功存入 ${depositAmount} WUSD 到专属非托管金库!Tx: ${hash.slice(0, 10)}...`);
} catch (e: any) {
alert(e.message || '充值失败');
} finally {
setLoading(false);
}
};
const handleWithdraw = async () => {
try {
setLoading(true);
setActionMsg(null);
const hash = await withdrawFromVault(withdrawAmount);
setActionMsg(`🎉 成功从金库提现 ${withdrawAmount} WUSD 到钱包!Tx: ${hash.slice(0, 10)}...`);
} catch (e: any) {
alert(e.message || '提现失败');
} finally {
setLoading(false);
}
};
const handleEnableSession = async () => {
try {
setLoading(true);
await enableSessionKey();
setActionMsg('⚡ Session Key 免密连击授权成功!24小时内高频交易零弹窗!');
} catch (e: any) {
alert(e.message || '授权失败');
} finally {
setLoading(false);
}
};
const MOCK_POSITIONS = [
{
@@ -79,12 +121,96 @@ export const PortfolioView: React.FC = () => {
</div>
<div className="bg-[#0f111d] border border-slate-800 rounded-2xl p-6 shadow-xl">
<span className="text-xs text-slate-400 block font-medium"> (Active Positions)</span>
<span className="text-2xl font-black font-mono text-slate-200 mt-1 block">2 </span>
<div className="text-xs text-slate-500 mt-2"></div>
<span className="text-xs text-slate-400 block font-medium"> (AA User Vault)</span>
<span className="text-xs font-mono text-slate-300 truncate block mt-1">
{vaultAddress || '连接钱包后自动生成'}
</span>
<div className="flex items-center space-x-1 text-xs text-emerald-400 font-semibold mt-2">
<ShieldCheck className="w-3.5 h-3.5" />
<span>100% </span>
</div>
</div>
</div>
{/* Non-Custodial Vault Operations Panel */}
<div className="bg-[#0f111d] border border-cyan-900/30 rounded-2xl p-6 shadow-xl space-y-4">
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 border-b border-slate-800 pb-4">
<div className="flex items-center space-x-3">
<div className="p-2.5 bg-cyan-500/10 rounded-xl border border-cyan-500/20 text-cyan-400">
<KeyRound className="w-5 h-5" />
</div>
<div>
<h2 className="font-bold text-base text-white"> (AA Smart Vault)</h2>
<p className="text-xs text-slate-400"> Session Key 0 </p>
</div>
</div>
<button
onClick={handleEnableSession}
disabled={loading || isSessionActive || !account}
className={`px-4 py-2 rounded-xl text-xs font-bold transition-all flex items-center space-x-1.5 ${
isSessionActive
? 'bg-emerald-950/60 text-emerald-400 border border-emerald-800/50 cursor-default'
: 'bg-gradient-to-r from-cyan-500 to-indigo-600 hover:from-cyan-400 hover:to-indigo-500 text-white shadow-md shadow-cyan-500/20'
}`}
>
<KeyRound className="w-3.5 h-3.5" />
<span>{isSessionActive ? '⚡ 免密 Session 已激活 (24H 有效)' : '开启免密连击交易 (Session Key)'}</span>
</button>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 pt-2">
{/* Deposit */}
<div className="bg-slate-950/60 border border-slate-800 rounded-xl p-4 space-y-3">
<span className="text-xs font-bold text-slate-300 block"> (Deposit to Vault)</span>
<div className="flex space-x-2">
<input
type="number"
value={depositAmount}
onChange={(e) => setDepositAmount(e.target.value)}
placeholder="500"
className="flex-1 bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-100 focus:outline-none focus:border-cyan-500"
/>
<button
onClick={handleDeposit}
disabled={loading || !account || !depositAmount}
className="px-4 py-2 bg-cyan-600 hover:bg-cyan-500 text-white rounded-lg text-xs font-bold transition-colors disabled:opacity-50"
>
{loading ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : '立即充值'}
</button>
</div>
</div>
{/* Withdraw */}
<div className="bg-slate-950/60 border border-slate-800 rounded-xl p-4 space-y-3">
<span className="text-xs font-bold text-slate-300 block"> EOA (Instant Withdraw)</span>
<div className="flex space-x-2">
<input
type="number"
value={withdrawAmount}
onChange={(e) => setWithdrawAmount(e.target.value)}
placeholder="100"
className="flex-1 bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-100 focus:outline-none focus:border-cyan-500"
/>
<button
onClick={handleWithdraw}
disabled={loading || !account || !withdrawAmount}
className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-lg text-xs font-bold transition-colors disabled:opacity-50"
>
{loading ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : '提取本金'}
</button>
</div>
</div>
</div>
{actionMsg && (
<div className="p-3 bg-emerald-950/30 border border-emerald-800/40 rounded-xl text-xs text-emerald-300 flex items-center space-x-2">
<Check className="w-4 h-4 text-emerald-400" />
<span>{actionMsg}</span>
</div>
)}
</div>
{/* Positions Table */}
<div className="bg-[#0f111d] border border-slate-800 rounded-2xl p-6 shadow-xl space-y-4">
<div className="flex items-center space-x-2 border-b border-slate-800/80 pb-4">