Files
aitbc/apps/marketplace/src/components/AgentWallet.tsx
AITBC System b033923756 chore: normalize file permissions across repository
- Remove executable permissions from configuration files (.editorconfig, .env.example, .gitignore)
- Remove executable permissions from documentation files (README.md, LICENSE, SECURITY.md)
- Remove executable permissions from web assets (HTML, CSS, JS files)
- Remove executable permissions from data files (JSON, SQL, YAML, requirements.txt)
- Remove executable permissions from source code files across all apps
- Add executable permissions to Python
2026-03-08 11:26:18 +01:00

788 lines
30 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './ui/card';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
import { Alert, AlertDescription, AlertTitle } from './ui/alert';
import { Progress } from './ui/progress';
import { Separator } from './ui/separator';
import {
Wallet,
Send,
Settings,
Clock,
TrendingUp,
AlertTriangle,
CheckCircle,
ArrowUpRight,
ArrowDownLeft,
DollarSign,
Shield,
Activity
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useWallet } from '@/hooks/use-wallet';
interface WalletInfo {
agentId: string;
owner: string;
balance: string;
totalAllowance: string;
spentAmount: string;
spendingLimit: string;
transactionCount: number;
createdAt: string;
lastActivity: string;
isActive: boolean;
microTransactionEnabled: boolean;
}
interface Transaction {
id: string;
agent: string;
recipient: string;
amount: string;
purpose: string;
timestamp: string;
isMicroTransaction: boolean;
status: 'pending' | 'completed' | 'failed';
}
interface WalletStats {
balance: string;
totalAllowance: string;
spentAmount: string;
remainingAllowance: string;
transactionCount: number;
utilizationRate: number;
}
const AgentWallet: React.FC = () => {
const { toast } = useToast();
const { isConnected, address } = useWallet();
const [wallets, setWallets] = useState<WalletInfo[]>([]);
const [selectedWallet, setSelectedWallet] = useState<WalletInfo | null>(null);
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [walletStats, setWalletStats] = useState<WalletStats | null>(null);
const [activeTab, setActiveTab] = useState('overview');
const [loading, setLoading] = useState(true);
// Form states
const [allowanceAmount, setAllowanceAmount] = useState('');
const [spendingLimit, setSpendingLimit] = useState('');
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const [purpose, setPurpose] = useState('');
const [selectedAgent, setSelectedAgent] = useState('');
// Mock data for demonstration
const mockWallets: WalletInfo[] = [
{
agentId: 'agent_001',
owner: '0x1234...5678',
balance: '850.50',
totalAllowance: '1000.00',
spentAmount: '149.50',
spendingLimit: '500.00',
transactionCount: 23,
createdAt: '2024-01-15T10:30:00Z',
lastActivity: '2024-01-25T14:20:00Z',
isActive: true,
microTransactionEnabled: true
},
{
agentId: 'agent_002',
owner: '0xabcd...efgh',
balance: '1200.75',
totalAllowance: '2000.00',
spentAmount: '799.25',
spendingLimit: '1000.00',
transactionCount: 45,
createdAt: '2024-01-18T09:15:00Z',
lastActivity: '2024-01-26T16:45:00Z',
isActive: true,
microTransactionEnabled: true
},
{
agentId: 'agent_003',
owner: '0x5678...9abc',
balance: '450.25',
totalAllowance: '500.00',
spentAmount: '49.75',
spendingLimit: '250.00',
transactionCount: 12,
createdAt: '2024-01-20T11:45:00Z',
lastActivity: '2024-01-24T13:30:00Z',
isActive: true,
microTransactionEnabled: false
}
];
const mockTransactions: Transaction[] = [
{
id: 'tx_001',
agent: 'agent_001',
recipient: 'provider_gpu_001',
amount: '0.05',
purpose: 'GPU rental - text processing',
timestamp: '2024-01-25T14:20:00Z',
isMicroTransaction: true,
status: 'completed'
},
{
id: 'tx_002',
agent: 'agent_002',
recipient: 'provider_gpu_002',
amount: '0.15',
purpose: 'GPU rental - image processing',
timestamp: '2024-01-26T16:45:00Z',
isMicroTransaction: true,
status: 'completed'
},
{
id: 'tx_003',
agent: 'agent_001',
recipient: 'data_provider_001',
amount: '2.50',
purpose: 'Dataset purchase',
timestamp: '2024-01-24T10:15:00Z',
isMicroTransaction: false,
status: 'completed'
}
];
useEffect(() => {
// Load mock data
setTimeout(() => {
setWallets(mockWallets);
setTransactions(mockTransactions);
if (mockWallets.length > 0) {
setSelectedWallet(mockWallets[0]);
updateWalletStats(mockWallets[0]);
}
setLoading(false);
}, 1000);
}, []);
const updateWalletStats = (wallet: WalletInfo) => {
const stats: WalletStats = {
balance: wallet.balance,
totalAllowance: wallet.totalAllowance,
spentAmount: wallet.spentAmount,
remainingAllowance: (parseFloat(wallet.totalAllowance) - parseFloat(wallet.spentAmount)).toFixed(2),
transactionCount: wallet.transactionCount,
utilizationRate: (parseFloat(wallet.spentAmount) / parseFloat(wallet.totalAllowance)) * 100
};
setWalletStats(stats);
};
const handleGrantAllowance = async () => {
if (!isConnected || !selectedWallet || !allowanceAmount) {
toast({
title: "Missing Information",
description: "Please connect wallet and fill in all fields",
variant: "destructive"
});
return;
}
try {
// Simulate allowance grant
toast({
title: "Granting Allowance",
description: `Granting ${allowanceAmount} AITBC to ${selectedWallet.agentId}`,
variant: "default"
});
// Update wallet state
const updatedWallet = {
...selectedWallet,
totalAllowance: (parseFloat(selectedWallet.totalAllowance) + parseFloat(allowanceAmount)).toFixed(2),
balance: (parseFloat(selectedWallet.balance) + parseFloat(allowanceAmount)).toFixed(2)
};
setSelectedWallet(updatedWallet);
setWallets(wallets.map(w => w.agentId === updatedWallet.agentId ? updatedWallet : w));
updateWalletStats(updatedWallet);
setAllowanceAmount('');
toast({
title: "Allowance Granted",
description: `Successfully granted ${allowanceAmount} AITBC to ${selectedWallet.agentId}`,
variant: "default"
});
} catch (error) {
toast({
title: "Grant Failed",
description: "There was an error granting the allowance",
variant: "destructive"
});
}
};
const handleUpdateSpendingLimit = async () => {
if (!isConnected || !selectedWallet || !spendingLimit) {
toast({
title: "Missing Information",
description: "Please connect wallet and fill in all fields",
variant: "destructive"
});
return;
}
try {
// Simulate spending limit update
toast({
title: "Updating Spending Limit",
description: `Updating spending limit to ${spendingLimit} AITBC`,
variant: "default"
});
// Update wallet state
const updatedWallet = {
...selectedWallet,
spendingLimit: spendingLimit
};
setSelectedWallet(updatedWallet);
setWallets(wallets.map(w => w.agentId === updatedWallet.agentId ? updatedWallet : w));
setSpendingLimit('');
toast({
title: "Spending Limit Updated",
description: `Successfully updated spending limit to ${spendingLimit} AITBC`,
variant: "default"
});
} catch (error) {
toast({
title: "Update Failed",
description: "There was an error updating the spending limit",
variant: "destructive"
});
}
};
const handleExecuteTransaction = async () => {
if (!isConnected || !selectedWallet || !recipient || !amount || !purpose) {
toast({
title: "Missing Information",
description: "Please fill in all transaction fields",
variant: "destructive"
});
return;
}
try {
// Simulate transaction execution
toast({
title: "Executing Transaction",
description: `Sending ${amount} AITBC to ${recipient}`,
variant: "default"
});
// Create new transaction
const newTransaction: Transaction = {
id: `tx_${Date.now()}`,
agent: selectedWallet.agentId,
recipient: recipient,
amount: amount,
purpose: purpose,
timestamp: new Date().toISOString(),
isMicroTransaction: parseFloat(amount) < 0.001,
status: 'completed'
};
// Update wallet state
const updatedWallet = {
...selectedWallet,
balance: (parseFloat(selectedWallet.balance) - parseFloat(amount)).toFixed(2),
spentAmount: (parseFloat(selectedWallet.spentAmount) + parseFloat(amount)).toFixed(2),
transactionCount: selectedWallet.transactionCount + 1,
lastActivity: new Date().toISOString()
};
setSelectedWallet(updatedWallet);
setWallets(wallets.map(w => w.agentId === updatedWallet.agentId ? updatedWallet : w));
setTransactions([newTransaction, ...transactions]);
updateWalletStats(updatedWallet);
// Clear form
setRecipient('');
setAmount('');
setPurpose('');
toast({
title: "Transaction Completed",
description: `Successfully sent ${amount} AITBC to ${recipient}`,
variant: "default"
});
} catch (error) {
toast({
title: "Transaction Failed",
description: "There was an error executing the transaction",
variant: "destructive"
});
}
};
const handleToggleMicroTransactions = async () => {
if (!isConnected || !selectedWallet) return;
try {
const updatedWallet = {
...selectedWallet,
microTransactionEnabled: !selectedWallet.microTransactionEnabled
};
setSelectedWallet(updatedWallet);
setWallets(wallets.map(w => w.agentId === updatedWallet.agentId ? updatedWallet : w));
toast({
title: "Settings Updated",
description: `Micro-transactions ${updatedWallet.microTransactionEnabled ? 'enabled' : 'disabled'}`,
variant: "default"
});
} catch (error) {
toast({
title: "Update Failed",
description: "There was an error updating the settings",
variant: "destructive"
});
}
};
if (loading) {
return (
<div className="container mx-auto p-6 space-y-6">
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading agent wallets...</p>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Agent Wallet Management</h1>
<p className="text-muted-foreground mt-2">
Manage and monitor autonomous agent wallets with micro-transaction support
</p>
</div>
<div className="flex items-center space-x-2">
<Badge variant="outline" className="flex items-center space-x-1">
<Wallet className="w-4 h-4" />
<span>{wallets.length} Active Wallets</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<Activity className="w-4 h-4" />
<span>{transactions.length} Transactions</span>
</Badge>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Wallet Selection */}
<div className="lg:col-span-1">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Wallet className="w-5 h-5" />
<span>Agent Wallets</span>
</CardTitle>
<CardDescription>
Select an agent wallet to manage
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{wallets.map((wallet) => (
<div
key={wallet.agentId}
className={`p-4 border rounded-lg cursor-pointer transition-colors ${
selectedWallet?.agentId === wallet.agentId
? 'border-primary bg-primary/5'
: 'border-border hover:bg-muted/50'
}`}
onClick={() => {
setSelectedWallet(wallet);
updateWalletStats(wallet);
}}
>
<div className="flex items-center justify-between mb-2">
<h4 className="font-semibold">{wallet.agentId}</h4>
<Badge variant={wallet.isActive ? "default" : "secondary"}>
{wallet.isActive ? "Active" : "Inactive"}
</Badge>
</div>
<div className="space-y-1 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Balance:</span>
<span className="font-medium">{wallet.balance} AITBC</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Spent:</span>
<span className="font-medium">{wallet.spentAmount} AITBC</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Transactions:</span>
<span className="font-medium">{wallet.transactionCount}</span>
</div>
</div>
<div className="flex items-center space-x-2 mt-2">
{wallet.microTransactionEnabled && (
<Badge variant="secondary" className="text-xs">
<DollarSign className="w-3 h-3 mr-1" />
Micro-transactions
</Badge>
)}
</div>
</div>
))}
</CardContent>
</Card>
</div>
{/* Wallet Details */}
<div className="lg:col-span-2">
{selectedWallet ? (
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="transactions">Transactions</TabsTrigger>
<TabsTrigger value="manage">Manage</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-6">
{/* Wallet Stats */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<DollarSign className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Balance</span>
</div>
<div className="text-2xl font-bold">{walletStats?.balance} AITBC</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<TrendingUp className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Total Allowance</span>
</div>
<div className="text-2xl font-bold">{walletStats?.totalAllowance} AITBC</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<ArrowUpRight className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Spent</span>
</div>
<div className="text-2xl font-bold">{walletStats?.spentAmount} AITBC</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Activity className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Transactions</span>
</div>
<div className="text-2xl font-bold">{walletStats?.transactionCount}</div>
</CardContent>
</Card>
</div>
{/* Utilization */}
<Card>
<CardHeader>
<CardTitle>Allowance Utilization</CardTitle>
<CardDescription>
Current spending vs. total allowance
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span>Spent: {walletStats?.spentAmount} AITBC</span>
<span>Remaining: {walletStats?.remainingAllowance} AITBC</span>
</div>
<Progress value={walletStats?.utilizationRate || 0} className="w-full" />
<div className="text-center text-sm text-muted-foreground">
{walletStats?.utilizationRate?.toFixed(1)}% utilized
</div>
</div>
</CardContent>
</Card>
{/* Recent Activity */}
<Card>
<CardHeader>
<CardTitle>Recent Activity</CardTitle>
<CardDescription>
Latest transactions and wallet events
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{transactions.slice(0, 5).map((tx) => (
<div key={tx.id} className="flex items-center justify-between p-3 border rounded-lg">
<div className="flex items-center space-x-3">
<div className={`w-2 h-2 rounded-full ${
tx.status === 'completed' ? 'bg-green-500' :
tx.status === 'pending' ? 'bg-yellow-500' : 'bg-red-500'
}`}></div>
<div>
<p className="font-medium">{tx.purpose}</p>
<p className="text-sm text-muted-foreground">
To: {tx.recipient.slice(0, 8)}...{tx.recipient.slice(-6)}
</p>
</div>
</div>
<div className="text-right">
<p className="font-medium">{tx.amount} AITBC</p>
<p className="text-xs text-muted-foreground">
{new Date(tx.timestamp).toLocaleDateString()}
</p>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="transactions" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Transaction History</CardTitle>
<CardDescription>
All transactions for this agent wallet
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{transactions.map((tx) => (
<div key={tx.id} className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex items-center space-x-4">
<div className={`w-3 h-3 rounded-full ${
tx.status === 'completed' ? 'bg-green-500' :
tx.status === 'pending' ? 'bg-yellow-500' : 'bg-red-500'
}`}></div>
<div>
<p className="font-medium">{tx.purpose}</p>
<p className="text-sm text-muted-foreground">
{tx.isMicroTransaction ? 'Micro-transaction' : 'Standard transaction'}
</p>
<p className="text-xs text-muted-foreground">
To: {tx.recipient}
</p>
</div>
</div>
<div className="text-right">
<p className="font-medium">{tx.amount} AITBC</p>
<p className="text-sm text-muted-foreground">
{new Date(tx.timestamp).toLocaleString()}
</p>
<Badge variant={tx.status === 'completed' ? "default" : "secondary"}>
{tx.status}
</Badge>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="manage" className="space-y-6">
{/* Grant Allowance */}
<Card>
<CardHeader>
<CardTitle>Grant Allowance</CardTitle>
<CardDescription>
Add funds to the agent's allowance
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">Amount (AITBC)</label>
<Input
type="number"
placeholder="Enter amount"
value={allowanceAmount}
onChange={(e) => setAllowanceAmount(e.target.value)}
/>
</div>
<Button onClick={handleGrantAllowance} className="w-full">
<ArrowDownLeft className="w-4 h-4 mr-2" />
Grant Allowance
</Button>
</CardContent>
</Card>
{/* Execute Transaction */}
<Card>
<CardHeader>
<CardTitle>Execute Transaction</CardTitle>
<CardDescription>
Send funds from agent wallet
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">Recipient</label>
<Input
placeholder="Enter recipient address"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Amount (AITBC)</label>
<Input
type="number"
placeholder="Enter amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Purpose</label>
<Input
placeholder="Enter transaction purpose"
value={purpose}
onChange={(e) => setPurpose(e.target.value)}
/>
</div>
<Button onClick={handleExecuteTransaction} className="w-full">
<Send className="w-4 h-4 mr-2" />
Execute Transaction
</Button>
</CardContent>
</Card>
{/* Update Spending Limit */}
<Card>
<CardHeader>
<CardTitle>Update Spending Limit</CardTitle>
<CardDescription>
Set maximum spending limit per period
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">New Limit (AITBC)</label>
<Input
type="number"
placeholder="Enter spending limit"
value={spendingLimit}
onChange={(e) => setSpendingLimit(e.target.value)}
/>
</div>
<Button onClick={handleUpdateSpendingLimit} className="w-full">
<Settings className="w-4 h-4 mr-2" />
Update Limit
</Button>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="settings" className="space-y-6">
{/* Wallet Settings */}
<Card>
<CardHeader>
<CardTitle>Wallet Settings</CardTitle>
<CardDescription>
Configure agent wallet behavior
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Micro-transactions</p>
<p className="text-sm text-muted-foreground">
Enable transactions below 0.001 AITBC
</p>
</div>
<Button
variant={selectedWallet.microTransactionEnabled ? "default" : "outline"}
onClick={handleToggleMicroTransactions}
>
{selectedWallet.microTransactionEnabled ? "Enabled" : "Disabled"}
</Button>
</div>
<Separator />
<div className="space-y-2">
<p className="font-medium">Wallet Information</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div>
<span className="text-muted-foreground">Agent ID:</span>
<p className="font-mono">{selectedWallet.agentId}</p>
</div>
<div>
<span className="text-muted-foreground">Owner:</span>
<p className="font-mono">{selectedWallet.owner.slice(0, 8)}...{selectedWallet.owner.slice(-6)}</p>
</div>
<div>
<span className="text-muted-foreground">Created:</span>
<p>{new Date(selectedWallet.createdAt).toLocaleDateString()}</p>
</div>
<div>
<span className="text-muted-foreground">Last Activity:</span>
<p>{new Date(selectedWallet.lastActivity).toLocaleDateString()}</p>
</div>
</div>
</div>
</CardContent>
</Card>
{/* Security */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Shield className="w-5 h-5" />
<span>Security</span>
</CardTitle>
<CardDescription>
Security settings and permissions
</CardDescription>
</CardHeader>
<CardContent>
<Alert>
<CheckCircle className="h-4 w-4" />
<AlertTitle>Wallet Secured</AlertTitle>
<AlertDescription>
This agent wallet is protected by smart contract security measures and spending limits.
</AlertDescription>
</Alert>
</CardContent>
</Card>
</TabsContent>
</Tabs>
) : (
<Card>
<CardContent className="pt-6">
<div className="text-center py-8">
<Wallet className="w-12 h-12 text-muted-foreground mx-auto mb-4" />
<p className="text-muted-foreground">Select an agent wallet to manage</p>
</div>
</CardContent>
</Card>
)}
</div>
</div>
</div>
);
};
export default AgentWallet;