"use client"; import React, { useState, useEffect, Suspense } from "react"; import { useAccount } from 'wagmi'; import { use0GBroker } from '../../hooks/use0GBroker'; import { useSearchParams } from 'next/navigation'; function LedgerContent() { const { isConnected } = useAccount(); const searchParams = useSearchParams(); const { broker, isInitializing, error: brokerError, ledgerInfo, refreshLedgerInfo, addLedger: createLedger, depositFund, } = use0GBroker(); const [addAmount, setAddAmount] = useState(""); const [isAdding, setIsAdding] = useState(false); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState<'overview' | 'detail'>('overview'); const [isLockedExpanded, setIsLockedExpanded] = useState(false); const [expandedRefunds, setExpandedRefunds] = useState<{[key: string]: boolean}>({}); const [refundDetails, setRefundDetails] = useState<{[key: string]: {amount: bigint, remainTime: bigint}[]}>({}); const [loadingRefunds, setLoadingRefunds] = useState<{[key: string]: boolean}>({}); const [isRetrieving, setIsRetrieving] = useState<{[key: string]: boolean}>({}); const [isRetrievingAll, setIsRetrievingAll] = useState(false); const [showSuccessAlert, setShowSuccessAlert] = useState<{message: string, show: boolean}>({message: '', show: false}); const [showWithdrawModal, setShowWithdrawModal] = useState(false); const [withdrawAmount, setWithdrawAmount] = useState(""); // Handle tab parameter from URL useEffect(() => { const tab = searchParams.get('tab'); if (tab === 'transactions') { setActiveTab('detail'); } else { setActiveTab('overview'); } }, [searchParams]); // Auto-refresh ledger info when component mounts and when wallet connection changes useEffect(() => { if (isConnected && refreshLedgerInfo) { refreshLedgerInfo(); } }, [isConnected, refreshLedgerInfo]); // Helper function to format numbers and avoid scientific notation const formatNumber = (value: string | number) => { if (!value || value === "0" || value === 0) return "0"; const num = parseFloat(value.toString()); if (isNaN(num)) return "0"; // Convert to string to avoid scientific notation // This preserves all significant digits return num.toLocaleString('en-US', { useGrouping: false, minimumFractionDigits: 0, maximumFractionDigits: 20 }); }; // Helper function to format time from seconds const formatTime = (totalSeconds: number) => { const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const secs = totalSeconds % 60; return `${hours}h ${minutes}min ${secs}s`; }; // Function to fetch refund details const fetchRefundDetails = async (provider: string, type: 'inference' | 'fine-tuning') => { if (!broker) return; const key = `${type}-${provider}`; setLoadingRefunds(prev => ({ ...prev, [key]: true })); try { let refunds: {amount: bigint, remainTime: bigint}[] = []; if (type === 'inference') { const [, refundData] = await broker.inference.getAccountWithDetail(provider); refunds = refundData; } else { if (!broker.fineTuning) { throw new Error('Fine-tuning broker is not available'); } const { refunds: refundData } = await broker.fineTuning.getAccountWithDetail(provider); refunds = refundData; } setRefundDetails(prev => ({ ...prev, [key]: refunds })); } catch { // Silently handle refund details fetching errors } finally { setLoadingRefunds(prev => ({ ...prev, [key]: false })); } }; // Function to toggle refund details expansion const toggleRefundDetails = async (provider: string, type: 'inference' | 'fine-tuning') => { const key = `${type}-${provider}`; const isCurrentlyExpanded = expandedRefunds[key]; if (!isCurrentlyExpanded && !refundDetails[key]) { await fetchRefundDetails(provider, type); } setExpandedRefunds(prev => ({ ...prev, [key]: !isCurrentlyExpanded })); }; // Use real ledger info if available, otherwise show placeholder const displayLedgerInfo = ledgerInfo || { totalBalance: "0.000000", availableBalance: "0.000000", locked: "0.000000", inferences: [], fineTunings: [], }; const handleAddFunds = async () => { if (!addAmount || !broker) return; setIsAdding(true); setError(null); try { const amount = parseFloat(addAmount); // Use depositFund which now handles both creation and deposit intelligently await depositFund(amount); alert(`Successfully added ${addAmount} A0GI tokens to your account!`); setAddAmount(""); } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : 'Failed to add funds'; setError(errorMessage); } finally { setIsAdding(false); } }; const handleRefund = async () => { if (!broker) return; setIsAdding(true); setError(null); try { // Check if broker has a refund method, otherwise show a placeholder message if (typeof broker.ledger.refund === 'function') { const availableAmount = parseFloat(displayLedgerInfo.availableBalance); if (availableAmount > 0) { await broker.ledger.refund(availableAmount); alert('Successfully refunded available balance!'); await refreshLedgerInfo(); } } else { // Placeholder for refund functionality alert('Refund functionality will be implemented when the broker supports it.'); } } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : 'Failed to refund'; setError(errorMessage); } finally { setIsAdding(false); } }; if (!isConnected) { return (

Wallet Not Connected

Please connect your wallet to access account management features.

); } return (

Account

Manage your account balance and funding

{/* Tab Navigation */}
{/* Main Content */}
{activeTab === 'overview' && (
{/* Total Balance Section */}

Total Balance

{isInitializing && (
Loading...
)}
{formatNumber(displayLedgerInfo.totalBalance)} A0GI
{/* Add Funds Section */}

Add Funds

setAddAmount(e.target.value)} placeholder="0.1" step="0.01" min="0" className="w-full px-4 py-3 pr-12 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition-colors" />
A0GI

How it works

  • • Funds are deposited to your account
  • • They are used automatically for AI service payments
  • • Unused funds can be withdrawn anytime
)} {activeTab === 'detail' && (
{/* Account Overview */}

How fund management works

Available Balance: Funds for provider services and withdrawals

Provider Funds: Auto-allocated to AI service providers when used

Retrieval: Transfer unused provider funds back to Available Balance

{/* Display success message for retrieve operations */} {showSuccessAlert.show && (
Success
)} {/* Total Balance Container */}

Total Balance:

{formatNumber(displayLedgerInfo.totalBalance)} A0GI
{/* Available Balance */}

Available Balance

Available for provider funding and withdrawal

{formatNumber(displayLedgerInfo.availableBalance)} A0GI
{/* Fund Flow Visualization */}
{/* Auto-funding flow (downward) */}
Auto-funds on usage {/* Tooltip */}

Auto-funding Process

For each AI service provider you use, the system creates a separate sub-account under your control that holds funds specifically allocated for paying that provider's services.

{/* Arrow */}
{/* Manual retrieve flow (upward) */}
{/* Tooltip */}

Retrieve Unused Funds

Transfer unused funds from provider sub-accounts back to your Available Balance for withdrawal or other uses.

{/* Arrow */}
{/* Provider Funds with integrated details */}

Distributed Provider Funds

Funds currently distributed to service providers

{formatNumber(displayLedgerInfo.locked)} A0GI
{/* Integrated Sub-accounts Details - Full width when expanded */} {isLockedExpanded && (
{/* Vertical layout for sub-accounts when expanded */}
{/* Inference Sub-accounts */}
Inference Providers
{ledgerInfo?.inferences && ledgerInfo.inferences.length > 0 ? (
{ledgerInfo.inferences.map((inference, index) => (
Provider Address
{inference.provider}
Current Fund
{formatNumber(inference.balance)} A0GI
Pending Retrieval
{/* Refund Details Expansion */} {expandedRefunds[`inference-${inference.provider}`] && (
Retrieval Details
{loadingRefunds[`inference-${inference.provider}`] ? (
Loading...
) : refundDetails[`inference-${inference.provider}`]?.length > 0 ? (
{refundDetails[`inference-${inference.provider}`].map((refund, refundIndex) => (
Amount
{formatNumber((Number(refund.amount) / 1e18).toString())} A0GI
Lock Time
{formatTime(Number(refund.remainTime))}
))}
) : (
No pending refunds
)}
)}
))}
) : (
No inference services have been used yet
)}
{/* Fine-tuning Sub-accounts */}
Fine-tuning Providers
{ledgerInfo?.fineTunings && ledgerInfo.fineTunings.length > 0 ? (
{ledgerInfo.fineTunings.map((fineTuning, index) => (
Provider Address
{fineTuning.provider}
Current Fund
{formatNumber(fineTuning.balance)} A0GI
Pending Retrieval
{/* Refund Details Expansion */} {expandedRefunds[`fine-tuning-${fineTuning.provider}`] && (
Retrieval Details
{loadingRefunds[`fine-tuning-${fineTuning.provider}`] ? (
Loading...
) : refundDetails[`fine-tuning-${fineTuning.provider}`]?.length > 0 ? (
{refundDetails[`fine-tuning-${fineTuning.provider}`].map((refund, refundIndex) => (
Amount
{formatNumber((Number(refund.amount) / 1e18).toString())} A0GI
Lock Time
{formatTime(Number(refund.remainTime))}
))}
) : (
No pending refunds
)}
)}
))}
) : (
Fine-tuning services details are temporarily unavailable. Support coming soon.
)}
)}
{/* Display error message if any */} {error && (

Error

{error}

)}
)} {/* Withdraw Modal */} {showWithdrawModal && (

Withdraw Funds

setWithdrawAmount(e.target.value)} placeholder="0.0" step="0.01" min="0" max={displayLedgerInfo.availableBalance} className="w-full px-4 py-3 pr-12 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition-colors" />
A0GI

Available: {formatNumber(displayLedgerInfo.availableBalance)} A0GI

{error && (

{error}

)}
)}
); } export default function LedgerPage() { return ( Loading...
}> ); }