11import React , { useState , useEffect , useContext } from 'react' ;
22import { AppContext } from '@/contexts/AppContext' ;
33import { usePhantomWallet } from '@/contexts/PhantomWalletProvider' ;
4+ import { useProgram } from '../hooks/useProgram' ;
5+ import { useProgramStatistics , useOffers , useUserHistory } from '../hooks/useOnChainData' ;
6+ import { useRealPriceData } from '../hooks/usePriceData' ;
47import OverviewPanel from '@/components/analytics/OverviewPanel' ;
58import RecentTrades from '@/components/analytics/RecentTrades' ;
69import VolumePerDayChart from '@/components/analytics/VolumePerDayChart' ;
710import TopTraders from '@/components/analytics/TopTraders' ;
811
912export default function AnalyticsDashboard ( ) {
1013 const { network, selectedNetwork, networks } = useContext ( AppContext ) ;
11- const { connected, publicKey } = usePhantomWallet ( ) ;
14+ const { connected, publicKey, connection } = usePhantomWallet ( ) ;
1215 const [ timeframe , setTimeframe ] = useState ( '24h' ) ;
13- const [ refreshInterval , setRefreshInterval ] = useState ( null ) ;
1416
15- // Real-time protocol data states
17+ // Initialize program with connection and wallet
18+ const program = useProgram ( connection , { publicKey, signTransaction : ( ) => { } } ) ;
19+
20+ // Real blockchain data hooks
21+ const { statistics : programStats , loading : statsLoading , error : statsError } = useProgramStatistics ( program ) ;
22+ const { offers : allOffers , loading : offersLoading , error : offersError } = useOffers ( program ) ;
23+ const { prices : solPrices } = useRealPriceData ( ) ;
24+
25+ // Real-time protocol data states - derived from blockchain data
1626 const [ protocolOverview , setProtocolOverview ] = useState ( {
1727 totalTrades : 0 ,
1828 protocolVolume : 0 ,
@@ -28,105 +38,91 @@ export default function AnalyticsDashboard() {
2838 const [ volumeData , setVolumeData ] = useState ( [ ] ) ;
2939 const [ topTradersData , setTopTradersData ] = useState ( [ ] ) ;
3040
31- // Initialize real-time data fetching
41+ // Process real blockchain data when it becomes available
3242 useEffect ( ( ) => {
33- // Initial data fetch
34- fetchAllProtocolData ( ) ;
35-
36- // Set up real-time updates every 5 seconds
37- const interval = setInterval ( fetchAllProtocolData , 5000 ) ;
38- setRefreshInterval ( interval ) ;
39-
40- return ( ) => {
41- if ( interval ) {
42- clearInterval ( interval ) ;
43- }
44- } ;
45- } , [ selectedNetwork , timeframe ] ) ;
46-
47- const fetchAllProtocolData = async ( ) => {
48- try {
49- // Simulate API calls to fetch real-time protocol data
50- await Promise . all ( [
51- fetchProtocolOverview ( ) ,
52- fetchRecentTradesData ( ) ,
53- fetchVolumeData ( ) ,
54- fetchTopTradersData ( )
55- ] ) ;
56- } catch ( error ) {
57- console . error ( 'Error fetching protocol analytics data:' , error ) ;
43+ if ( programStats && allOffers && solPrices ) {
44+ const solPrice = solPrices . usd || 150 ; // Use USD price, fallback to 150
45+ // Calculate protocol overview from real blockchain data
46+ const overview = calculateProtocolOverview ( programStats , allOffers , solPrice ) ;
47+ setProtocolOverview ( overview ) ;
48+
49+ // Process recent trades from real offers
50+ const trades = processRecentTrades ( allOffers , solPrice ) ;
51+ setRecentTrades ( trades ) ;
52+
53+ // Generate volume data from real offers
54+ const volume = calculateVolumeData ( allOffers , timeframe , solPrice ) ;
55+ setVolumeData ( volume ) ;
56+
57+ // Calculate top traders from real reputation data
58+ const topTraders = calculateTopTraders ( allOffers ) ;
59+ setTopTradersData ( topTraders ) ;
5860 }
59- } ;
61+ } , [ programStats , allOffers , solPrices , timeframe ] ) ;
6062
61- const fetchProtocolOverview = async ( ) => {
62- // Simulate fetching protocol overview metrics
63- const mockData = {
64- totalTrades : Math . floor ( Math . random ( ) * 5000 ) + 1000 , // 1000-6000 trades
65- protocolVolume : Math . random ( ) * 50000 + 10000 , // 10K-60K SOL
66- totalFees : Math . random ( ) * 50000 + 5000 , // $5K-$55K in fees
67- completionRate : 85 + ( Math . random ( ) * 10 ) , // 85-95%
68- tradesChange : Math . random ( ) * 20 - 5 , // -5% to +15%
69- volumeChange : Math . random ( ) * 30 - 10 , // -10% to +20%
70- feesChange : Math . random ( ) * 25 - 5 , // -5% to +20%
71- completionChange : Math . random ( ) * 5 - 2 // -2% to +3%
63+ // Calculate protocol overview from real blockchain data
64+ const calculateProtocolOverview = ( stats , offers , price ) => {
65+ const completedOffers = offers . filter ( offer => offer . status === 'Completed' ) ;
66+ const totalVolume = completedOffers . reduce ( ( sum , offer ) => sum + offer . solAmount , 0 ) ;
67+ const totalFiatValue = totalVolume * price ;
68+ const totalFees = totalFiatValue * 0.005 ; // 0.5% protocol fee
69+
70+ return {
71+ totalTrades : stats . offers . total ,
72+ protocolVolume : totalVolume ,
73+ totalFees : totalFees ,
74+ completionRate : stats . offers . completionRate ,
75+ tradesChange : 0 , // Would need historical data to calculate
76+ volumeChange : 0 , // Would need historical data to calculate
77+ feesChange : 0 , // Would need historical data to calculate
78+ completionChange : 0 // Would need historical data to calculate
7279 } ;
73- setProtocolOverview ( mockData ) ;
7480 } ;
7581
76- const fetchRecentTradesData = async ( ) => {
77- // Simulate fetching recent protocol trades (last 100)
78- const mockTrades = Array . from ( { length : 100 } , ( _ , i ) => {
79- const tradeId = `T${ Date . now ( ) . toString ( ) . slice ( - 6 ) } _${ i . toString ( ) . padStart ( 3 , '0' ) } ` ;
80- const types = [ 'buy' , 'sell' ] ;
81- const statuses = [ 'completed' , 'in_progress' , 'cancelled' , 'disputed' ] ;
82- const currencies = [ 'USD' , 'EUR' , 'GBP' ] ;
83-
84- const solAmount = Math . random ( ) * 10 + 0.1 ; // 0.1-10 SOL
85- const rate = 130 + ( Math . random ( ) * 40 ) ; // $130-170 per SOL
86- const fiatAmount = solAmount * rate ;
87-
88- return {
89- tradeId,
90- type : types [ Math . floor ( Math . random ( ) * types . length ) ] ,
91- status : statuses [ Math . floor ( Math . random ( ) * statuses . length ) ] ,
92- buyer : `${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) } ` ,
93- seller : `${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) } ` ,
94- solAmount,
95- fiatAmount,
96- currency : currencies [ Math . floor ( Math . random ( ) * currencies . length ) ] ,
97- rate,
98- timestamp : new Date ( Date . now ( ) - Math . random ( ) * 86400000 * 7 ) , // Last 7 days
99- completionTime : Math . random ( ) > 0.7 ? `${ Math . floor ( Math . random ( ) * 30 + 5 ) } min` : null ,
100- protocolFee : solAmount * 0.005 // 0.5% protocol fee
101- } ;
102- } ) ;
103-
104- // Sort by timestamp, newest first
105- mockTrades . sort ( ( a , b ) => b . timestamp - a . timestamp ) ;
106- setRecentTrades ( mockTrades ) ;
82+ // Process real offers into recent trades format
83+ const processRecentTrades = ( offers , price ) => {
84+ return offers
85+ . filter ( offer => offer . status !== 'Created' )
86+ . sort ( ( a , b ) => b . updatedAt - a . updatedAt )
87+ . slice ( 0 , 100 )
88+ . map ( offer => ( {
89+ tradeId : offer . id . slice ( - 8 ) ,
90+ type : offer . buyer ? 'sell' : 'buy' ,
91+ status : offer . status . toLowerCase ( ) . replace ( / ( [ A - Z ] ) / g, '_$1' ) . toLowerCase ( ) ,
92+ buyer : offer . buyer || 'pending' ,
93+ seller : offer . seller ,
94+ solAmount : offer . solAmount ,
95+ fiatAmount : offer . fiatAmount ,
96+ currency : offer . fiatCurrency ,
97+ rate : offer . fiatAmount / offer . solAmount ,
98+ timestamp : new Date ( offer . updatedAt ) ,
99+ completionTime : offer . status === 'Completed' ?
100+ `${ Math . floor ( ( offer . updatedAt - offer . createdAt ) / 60000 ) } min` : null ,
101+ protocolFee : offer . solAmount * 0.005
102+ } ) ) ;
107103 } ;
108104
109- const fetchVolumeData = async ( ) => {
110- // Simulate fetching volume data based on timeframe
111- let dataPointsCount ;
112- let timeIncrement ;
105+ // Calculate volume data from real offers
106+ const calculateVolumeData = ( offers , timeframe , price ) => {
107+ const completedOffers = offers . filter ( offer => offer . status === 'Completed' ) ;
113108
109+ let dataPointsCount , timeIncrement ;
114110 switch ( timeframe ) {
115111 case '1h' :
116- dataPointsCount = 60 ; // 60 minutes
117- timeIncrement = 60 * 1000 ; // 1 minute
112+ dataPointsCount = 60 ;
113+ timeIncrement = 60 * 1000 ;
118114 break ;
119115 case '24h' :
120- dataPointsCount = 24 ; // 24 hours
121- timeIncrement = 60 * 60 * 1000 ; // 1 hour
116+ dataPointsCount = 24 ;
117+ timeIncrement = 60 * 60 * 1000 ;
122118 break ;
123119 case '7d' :
124- dataPointsCount = 7 ; // 7 days
125- timeIncrement = 24 * 60 * 60 * 1000 ; // 1 day
120+ dataPointsCount = 7 ;
121+ timeIncrement = 24 * 60 * 60 * 1000 ;
126122 break ;
127123 case '30d' :
128- dataPointsCount = 30 ; // 30 days
129- timeIncrement = 24 * 60 * 60 * 1000 ; // 1 day
124+ dataPointsCount = 30 ;
125+ timeIncrement = 24 * 60 * 60 * 1000 ;
130126 break ;
131127 default :
132128 dataPointsCount = 24 ;
@@ -135,41 +131,85 @@ export default function AnalyticsDashboard() {
135131
136132 const now = new Date ( ) ;
137133 const volumePoints = Array . from ( { length : dataPointsCount } , ( _ , i ) => {
138- const time = new Date ( now . getTime ( ) - ( dataPointsCount - 1 - i ) * timeIncrement ) ;
139- const baseVolume = 1000 + Math . random ( ) * 5000 ; // Base volume
140- const volume = baseVolume + Math . sin ( i / 5 ) * 1000 ; // Add some wave pattern
134+ const timeStart = new Date ( now . getTime ( ) - ( dataPointsCount - i ) * timeIncrement ) ;
135+ const timeEnd = new Date ( timeStart . getTime ( ) + timeIncrement ) ;
136+
137+ const volumeInPeriod = completedOffers
138+ . filter ( offer => {
139+ const offerTime = new Date ( offer . updatedAt ) ;
140+ return offerTime >= timeStart && offerTime < timeEnd ;
141+ } )
142+ . reduce ( ( sum , offer ) => sum + offer . solAmount , 0 ) ;
141143
142144 return {
143- time : time . toISOString ( ) ,
144- volume : Math . max ( 0 , volume ) // Ensure non-negative
145+ time : timeStart . toISOString ( ) ,
146+ volume : volumeInPeriod
145147 } ;
146148 } ) ;
147149
148- setVolumeData ( volumePoints ) ;
150+ return volumePoints ;
149151 } ;
150152
151- const fetchTopTradersData = async ( ) => {
152- // Simulate fetching top 100 traders data
153- const mockTraders = Array . from ( { length : 100 } , ( _ , i ) => {
154- const baseAddress = Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) ;
155- const address = `${ baseAddress } ...${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 6 ) } ` ;
153+ // Calculate top traders from real offer data
154+ const calculateTopTraders = ( offers ) => {
155+ const traderStats = { } ;
156+
157+ offers . forEach ( offer => {
158+ // Process seller stats
159+ if ( ! traderStats [ offer . seller ] ) {
160+ traderStats [ offer . seller ] = {
161+ address : offer . seller ,
162+ tradeCount : 0 ,
163+ volume : 0 ,
164+ successfulTrades : 0 ,
165+ disputedTrades : 0
166+ } ;
167+ }
156168
157- const tradeCount = Math . floor ( Math . random ( ) * 500 ) + 10 ; // 10-510 trades
158- const volume = Math . random ( ) * 100000 + 1000 ; // 1K-101K SOL
159- const pnl = ( Math . random ( ) - 0.3 ) * 50000 ; // -15K to +35K (bias towards positive)
160- const successRate = 60 + Math . random ( ) * 35 ; // 60-95%
169+ traderStats [ offer . seller ] . tradeCount ++ ;
170+ traderStats [ offer . seller ] . volume += offer . solAmount ;
161171
162- return {
163- address,
164- tradeCount,
165- volume,
166- pnl,
167- successRate,
168- verified : Math . random ( ) > 0.8 // 20% are verified
169- } ;
172+ if ( offer . status === 'Completed' ) {
173+ traderStats [ offer . seller ] . successfulTrades ++ ;
174+ } else if ( offer . status === 'DisputeOpened' ) {
175+ traderStats [ offer . seller ] . disputedTrades ++ ;
176+ }
177+
178+ // Process buyer stats if exists
179+ if ( offer . buyer && offer . buyer !== 'pending' ) {
180+ if ( ! traderStats [ offer . buyer ] ) {
181+ traderStats [ offer . buyer ] = {
182+ address : offer . buyer ,
183+ tradeCount : 0 ,
184+ volume : 0 ,
185+ successfulTrades : 0 ,
186+ disputedTrades : 0
187+ } ;
188+ }
189+
190+ traderStats [ offer . buyer ] . tradeCount ++ ;
191+ traderStats [ offer . buyer ] . volume += offer . solAmount ;
192+
193+ if ( offer . status === 'Completed' ) {
194+ traderStats [ offer . buyer ] . successfulTrades ++ ;
195+ } else if ( offer . status === 'DisputeOpened' ) {
196+ traderStats [ offer . buyer ] . disputedTrades ++ ;
197+ }
198+ }
170199 } ) ;
171-
172- setTopTradersData ( mockTraders ) ;
200+
201+ // Convert to array and calculate derived metrics
202+ return Object . values ( traderStats )
203+ . map ( trader => ( {
204+ ...trader ,
205+ successRate : trader . tradeCount > 0 ?
206+ ( trader . successfulTrades / trader . tradeCount ) * 100 : 0 ,
207+ pnl : 0 , // Would need more complex calculation with buy/sell price differences
208+ verified : false // Would need verification system
209+ } ) )
210+ . filter ( trader => trader . tradeCount > 0 )
211+ . sort ( ( a , b ) => b . volume - a . volume )
212+ . slice ( 0 , 100 ) ;
173213 } ;
174214
175215 const timeframeOptions = [
@@ -179,6 +219,10 @@ export default function AnalyticsDashboard() {
179219 { value : '30d' , label : '30D' }
180220 ] ;
181221
222+ // Show loading state while fetching real blockchain data
223+ const isLoading = statsLoading || offersLoading ;
224+ const hasError = statsError || offersError ;
225+
182226 return (
183227 < div className = "analytics-dashboard" >
184228 < div className = "analytics-header" >
@@ -189,6 +233,8 @@ export default function AnalyticsDashboard() {
189233 </ h1 >
190234 < p className = "analytics-subtitle" >
191235 Monitor svmp2p trading performance and user metrics on { network . name }
236+ { isLoading && " [LOADING BLOCKCHAIN DATA...]" }
237+ { hasError && " [ERROR LOADING DATA]" }
192238 </ p >
193239 </ div >
194240
@@ -208,7 +254,7 @@ export default function AnalyticsDashboard() {
208254 < div className = "connection-status" >
209255 { connected ? (
210256 < span className = "status-connected" >
211- [ONLINE] { network . name }
257+ [ONLINE] { network . name } - BLOCKCHAIN DATA
212258 </ span >
213259 ) : (
214260 < span className = "status-disconnected" >
@@ -221,26 +267,30 @@ export default function AnalyticsDashboard() {
221267 </ div >
222268
223269 < div className = "analytics-content" >
224- { /* Protocol Overview Panel - KPI Summary */ }
270+ { /* Protocol Overview Panel - Real KPI Summary from Blockchain */ }
225271 < OverviewPanel
226272 data = { protocolOverview }
227273 network = { network }
228274 timeframe = { timeframe }
275+ loading = { isLoading }
276+ error = { hasError }
229277 />
230278
231279 < div className = "analytics-grid" >
232280 { /* Left Column - Volume Chart and Top Traders */ }
233281 < div className = "analytics-column-left" >
234- { /* Volume Per Day Chart */ }
282+ { /* Volume Per Day Chart - Real Blockchain Data */ }
235283 < VolumePerDayChart
236284 data = { volumeData }
237285 network = { network }
238286 timeframe = { timeframe }
287+ loading = { isLoading }
239288 />
240289
241- { /* Top Traders Rankings */ }
290+ { /* Top Traders Rankings - Real Blockchain Data */ }
242291 < TopTraders
243292 tradersData = { topTradersData }
293+ loading = { isLoading }
244294 />
245295 </ div >
246296
@@ -249,6 +299,7 @@ export default function AnalyticsDashboard() {
249299 < RecentTrades
250300 trades = { recentTrades }
251301 network = { network }
302+ loading = { isLoading }
252303 />
253304 </ div >
254305 </ div >
0 commit comments