230 lines
8.8 KiB
TypeScript
230 lines
8.8 KiB
TypeScript
|
|
/**
|
||
|
|
* HardwareWalletSupport Component - Hardware wallet connection and management
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { useState } from 'react'
|
||
|
|
import { useAccount, useConnect, useDisconnect } from 'wagmi'
|
||
|
|
import { useAdmin } from '../../contexts/AdminContext'
|
||
|
|
import toast from 'react-hot-toast'
|
||
|
|
|
||
|
|
interface HardwareWalletInfo {
|
||
|
|
type: 'ledger' | 'trezor' | 'other'
|
||
|
|
name: string
|
||
|
|
address: string
|
||
|
|
connected: boolean
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function HardwareWalletSupport() {
|
||
|
|
const { address, isConnected, connector } = useAccount()
|
||
|
|
const { connect, connectors } = useConnect()
|
||
|
|
const { disconnect } = useDisconnect()
|
||
|
|
const { addAuditLog } = useAdmin()
|
||
|
|
const [walletInfo, setWalletInfo] = useState<HardwareWalletInfo | null>(null)
|
||
|
|
|
||
|
|
// Filter for hardware wallet connectors
|
||
|
|
const hardwareConnectors = connectors.filter((c) => {
|
||
|
|
const name = c.name.toLowerCase()
|
||
|
|
return name.includes('ledger') || name.includes('trezor') || name.includes('hardware')
|
||
|
|
})
|
||
|
|
|
||
|
|
const handleConnect = async (connectorId: string) => {
|
||
|
|
try {
|
||
|
|
const connector = connectors.find((c) => c.id === connectorId)
|
||
|
|
if (!connector) {
|
||
|
|
toast.error('Connector not found')
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
await connect({ connector })
|
||
|
|
|
||
|
|
// Detect hardware wallet type
|
||
|
|
const walletType = detectHardwareWalletType(connector.name)
|
||
|
|
if (walletType) {
|
||
|
|
setWalletInfo({
|
||
|
|
type: walletType,
|
||
|
|
name: connector.name,
|
||
|
|
address: address || '',
|
||
|
|
connected: true,
|
||
|
|
})
|
||
|
|
|
||
|
|
addAuditLog({
|
||
|
|
user: address || 'unknown',
|
||
|
|
action: 'connect_hardware_wallet',
|
||
|
|
resourceType: 'wallet',
|
||
|
|
resourceId: connectorId,
|
||
|
|
details: { type: walletType, name: connector.name },
|
||
|
|
status: 'success',
|
||
|
|
})
|
||
|
|
|
||
|
|
toast.success(`Connected to ${connector.name}`)
|
||
|
|
}
|
||
|
|
} catch (error: any) {
|
||
|
|
toast.error(`Connection failed: ${error.message}`)
|
||
|
|
console.error('Hardware wallet connection error:', error)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const handleDisconnect = () => {
|
||
|
|
disconnect()
|
||
|
|
setWalletInfo(null)
|
||
|
|
toast.success('Hardware wallet disconnected')
|
||
|
|
}
|
||
|
|
|
||
|
|
const detectHardwareWalletType = (connectorName: string): 'ledger' | 'trezor' | 'other' | null => {
|
||
|
|
const name = connectorName.toLowerCase()
|
||
|
|
if (name.includes('ledger')) return 'ledger'
|
||
|
|
if (name.includes('trezor')) return 'trezor'
|
||
|
|
if (name.includes('hardware')) return 'other'
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
|
||
|
|
const isHardwareWallet = walletInfo !== null || (connector && detectHardwareWalletType(connector.name) !== null)
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="space-y-6">
|
||
|
|
<div className="bg-black/20 rounded-xl p-6 border border-white/10">
|
||
|
|
<h2 className="text-xl font-bold text-white mb-4">Hardware Wallet Support</h2>
|
||
|
|
<p className="text-white/70 text-sm mb-4">
|
||
|
|
Connect and use hardware wallets (Ledger, Trezor) for enhanced security in admin operations.
|
||
|
|
</p>
|
||
|
|
|
||
|
|
{/* Connection Status */}
|
||
|
|
{isConnected && isHardwareWallet && (
|
||
|
|
<div className="bg-green-500/20 border border-green-500/50 rounded-lg p-4 mb-4">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<div>
|
||
|
|
<p className="text-green-200 font-semibold">Hardware Wallet Connected</p>
|
||
|
|
<p className="text-green-200/70 text-sm mt-1">
|
||
|
|
{connector?.name || 'Hardware Wallet'} - {address?.slice(0, 10)}...{address?.slice(-8)}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<button
|
||
|
|
onClick={handleDisconnect}
|
||
|
|
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg font-semibold transition-colors"
|
||
|
|
>
|
||
|
|
Disconnect
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* Hardware Wallet Connectors */}
|
||
|
|
{hardwareConnectors.length > 0 ? (
|
||
|
|
<div className="space-y-3">
|
||
|
|
{hardwareConnectors.map((connector) => {
|
||
|
|
const isActive = isConnected && connector.id === connector.id
|
||
|
|
const walletType = detectHardwareWalletType(connector.name)
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
key={connector.id}
|
||
|
|
className={`p-4 rounded-lg border ${
|
||
|
|
isActive
|
||
|
|
? 'bg-green-500/20 border-green-500/50'
|
||
|
|
: 'bg-white/5 border-white/10'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<div className="flex items-center gap-3">
|
||
|
|
<div className="text-2xl">
|
||
|
|
{walletType === 'ledger' && '🔷'}
|
||
|
|
{walletType === 'trezor' && '🔶'}
|
||
|
|
{!walletType && '💼'}
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<p className="text-white font-semibold">{connector.name}</p>
|
||
|
|
<p className="text-white/60 text-xs">
|
||
|
|
{walletType === 'ledger' && 'Hardware wallet - Ledger'}
|
||
|
|
{walletType === 'trezor' && 'Hardware wallet - Trezor'}
|
||
|
|
{!walletType && 'Hardware wallet'}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{!isActive ? (
|
||
|
|
<button
|
||
|
|
onClick={() => handleConnect(connector.id)}
|
||
|
|
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-semibold transition-colors"
|
||
|
|
>
|
||
|
|
Connect
|
||
|
|
</button>
|
||
|
|
) : (
|
||
|
|
<span className="text-green-300 text-sm font-semibold">Connected</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<div className="bg-yellow-500/20 border border-yellow-500/50 rounded-lg p-4">
|
||
|
|
<p className="text-yellow-200 text-sm mb-2">
|
||
|
|
<strong>No hardware wallet connectors detected</strong>
|
||
|
|
</p>
|
||
|
|
<p className="text-yellow-200/70 text-xs">
|
||
|
|
To enable hardware wallet support:
|
||
|
|
</p>
|
||
|
|
<ul className="list-disc list-inside text-yellow-200/70 text-xs mt-2 space-y-1">
|
||
|
|
<li>Install Ledger Live or Trezor Bridge</li>
|
||
|
|
<li>Install browser extensions if available</li>
|
||
|
|
<li>Connect your hardware wallet to your computer</li>
|
||
|
|
<li>Unlock your hardware wallet</li>
|
||
|
|
<li>Refresh this page</li>
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* Instructions */}
|
||
|
|
<div className="mt-6 bg-blue-500/20 border border-blue-500/50 rounded-lg p-4">
|
||
|
|
<h3 className="text-blue-200 font-semibold mb-2">Hardware Wallet Instructions</h3>
|
||
|
|
<div className="space-y-2 text-blue-200/80 text-sm">
|
||
|
|
<div>
|
||
|
|
<strong>For Ledger:</strong>
|
||
|
|
<ul className="list-disc list-inside ml-4 mt-1">
|
||
|
|
<li>Install Ledger Live application</li>
|
||
|
|
<li>Connect your Ledger device via USB</li>
|
||
|
|
<li>Unlock your device and open Ethereum app</li>
|
||
|
|
<li>Enable "Contract Data" in Ethereum app settings</li>
|
||
|
|
<li>Click Connect above</li>
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<strong>For Trezor:</strong>
|
||
|
|
<ul className="list-disc list-inside ml-4 mt-1">
|
||
|
|
<li>Install Trezor Bridge or use Trezor Connect</li>
|
||
|
|
<li>Connect your Trezor device via USB</li>
|
||
|
|
<li>Unlock your device</li>
|
||
|
|
<li>Click Connect above</li>
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
<div className="mt-3 text-blue-200/70 text-xs">
|
||
|
|
<strong>Security Note:</strong> Hardware wallets provide the highest level of security for admin operations.
|
||
|
|
All transactions will require physical confirmation on your device.
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Current Connection Info */}
|
||
|
|
{isConnected && address && (
|
||
|
|
<div className="mt-4 bg-white/5 rounded-lg p-4 border border-white/10">
|
||
|
|
<h3 className="text-white font-semibold mb-2">Current Connection</h3>
|
||
|
|
<div className="space-y-2 text-white/70 text-sm">
|
||
|
|
<div>
|
||
|
|
<span className="font-semibold">Address:</span>{' '}
|
||
|
|
<span className="font-mono">{address}</span>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<span className="font-semibold">Connector:</span> {connector?.name || 'Unknown'}
|
||
|
|
</div>
|
||
|
|
{walletInfo && (
|
||
|
|
<div>
|
||
|
|
<span className="font-semibold">Type:</span> {walletInfo.type}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|