@@ -8,58 +8,163 @@ import { Accordion, Button, ButtonStyle } from '@sovryn/ui';
88import { BOB_CHAIN_ID , RSK_CHAIN_ID } from '../../../../../config/chains' ;
99
1010import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer' ;
11- import { NativeTokenAmount } from '../../../../2_molecules/NativeTokenAmount/NativeTokenAmount' ;
12- import { USD } from '../../../../../constants/currencies' ;
11+ import {
12+ BITCOIN ,
13+ BTC_RENDER_PRECISION ,
14+ USD ,
15+ } from '../../../../../constants/currencies' ;
1316import { translations } from '../../../../../locales/i18n' ;
1417import { decimalic } from '../../../../../utils/math' ;
1518import { USD_VALUE_PRECISION } from './ProtocolData.constants' ;
1619import styles from './ProtocolData.module.css' ;
20+ import { pickBtcUsd , safeBucketUsd , sanitizeUsd } from './ProtocolData.utils' ;
1721import { useGetBOBVolume } from './hooks/useGetBOBVolume' ;
1822import { useGetLockedData } from './hooks/useGetLockedData' ;
1923import { useGetRSKVolume } from './hooks/useGetRSKVolume' ;
24+ import { useGetTokens } from './hooks/useGetTokens' ;
2025
2126const pageTranslations = translations . landingPage . protocolDataSection ;
2227
2328export const ProtocolData : FC = ( ) => {
24- const lockedData = useGetLockedData ( RSK_CHAIN_ID ) ;
25- const rskVolume = useGetRSKVolume ( ) ;
29+ // RSK data (TVL parts + 24h volume)
30+ const rskLocked = useGetLockedData ( RSK_CHAIN_ID ) ;
2631
27- const bobLockedData = useGetLockedData ( BOB_CHAIN_ID ) ;
28- const bobVolume = useGetBOBVolume ( ) ;
32+ const rskVolumeRaw = useGetRSKVolume ( ) ; // 24h USD (string)
33+
34+ // BOB data (TVL aggregate + 24h volume)
35+ const bobLocked = useGetLockedData ( BOB_CHAIN_ID ) ;
36+
37+ const bobVolumeRaw = useGetBOBVolume ( ) ; // 24h USD (string)
38+
39+ // Tokens for BTC price sourcing
40+ const { value : rskTokens } = useGetTokens ( RSK_CHAIN_ID ) ;
41+ const { value : bobTokens } = useGetTokens ( BOB_CHAIN_ID ) ;
2942
3043 const navigate = useNavigate ( ) ;
3144 const [ open , toggle ] = useReducer ( v => ! v , false ) ;
32-
3345 const handleClick = useCallback ( ( ) => navigate ( '/stats' ) , [ navigate ] ) ;
3446
35- const rskTVL = useMemo (
47+ // Prefer RSK RBTC/WRBTC for stability; fallback to BOB BTC wrappers.
48+ const rskBtcUsd = useMemo (
49+ ( ) => pickBtcUsd ( rskTokens , [ 'RBTC' , 'WRBTC' , 'BTC' ] ) ?? 0 ,
50+ [ rskTokens ] ,
51+ ) ;
52+ const bobBtcUsd = useMemo (
3653 ( ) =>
37- decimalic ( lockedData . tvlAmm ?. totalUsd || '0' )
38- . add ( lockedData . tvlLending ?. totalUsd || '0' )
39- . add ( lockedData . tvlMynt ?. totalUsd || '0' )
40- . add ( lockedData . tvlProtocol ?. totalUsd || '0' )
41- . add ( lockedData . tvlStaking ?. totalUsd || '0' )
42- . add ( lockedData . tvlSubprotocols ?. totalUsd || '0' )
43- . add ( lockedData . tvlZero ?. totalUsd || '0' )
44- . toString ( ) ,
45- [
46- lockedData . tvlAmm ?. totalUsd ,
47- lockedData . tvlLending ?. totalUsd ,
48- lockedData . tvlMynt ?. totalUsd ,
49- lockedData . tvlProtocol ?. totalUsd ,
50- lockedData . tvlStaking ?. totalUsd ,
51- lockedData . tvlSubprotocols ?. totalUsd ,
52- lockedData . tvlZero ?. totalUsd ,
53- ] ,
54+ pickBtcUsd ( bobTokens , [
55+ 'WBTC' ,
56+ 'tBTC' ,
57+ 'UniBTC' ,
58+ 'XSOLVBTC' ,
59+ 'SolvBTC' ,
60+ 'BTC' ,
61+ ] ) ?? 0 ,
62+ [ bobTokens ] ,
63+ ) ;
64+
65+ // Use ONE global BTC/USD for totals (avoid double-scaling differences).
66+ const globalBtcUsd = useMemo (
67+ ( ) => rskBtcUsd || bobBtcUsd || 0 ,
68+ [ rskBtcUsd , bobBtcUsd ] ,
69+ ) ;
70+
71+ const rskTvlUsd = useMemo ( ( ) => {
72+ if ( ! rskLocked ) {
73+ return '0' ;
74+ }
75+
76+ // Aggregate from buckets, applying numeric caps per bucket
77+ const fromBuckets = decimalic ( 0 )
78+ . add ( safeBucketUsd ( rskLocked . tvlAmm ) )
79+ . add ( safeBucketUsd ( rskLocked . tvlLending ) )
80+ . add ( safeBucketUsd ( rskLocked . tvlMynt ) )
81+ . add ( safeBucketUsd ( rskLocked . tvlProtocol ) )
82+ . add ( safeBucketUsd ( rskLocked . tvlStaking ) )
83+ . add ( safeBucketUsd ( rskLocked . tvlSubprotocols ) )
84+ . add ( safeBucketUsd ( rskLocked . tvlZero ) ) ;
85+
86+ //sanity-check against raw total_usd/totalUsd from backend
87+ const rawTotal = Number (
88+ ( rskLocked as any ) ?. total_usd ?? ( rskLocked as any ) ?. totalUsd ?? 0 ,
89+ ) ;
90+ const safeRawTotal =
91+ ! isFinite ( rawTotal ) || rawTotal < 0 || rawTotal > 1e9 ? 0 : rawTotal ;
92+
93+ if ( safeRawTotal > 0 ) {
94+ const diff = Math . abs ( fromBuckets . toNumber ( ) - safeRawTotal ) ;
95+ const relDiff = diff / safeRawTotal ;
96+
97+ // Log if discrepancy > ~3% (indexer vs graph-wrapper)
98+ if ( relDiff > 0.03 ) {
99+ // eslint-disable-next-line no-console
100+ console . warn ( '[ProtocolData] RSK TVL mismatch' , {
101+ buckets : fromBuckets . toString ( ) ,
102+ total_usd : safeRawTotal . toString ( ) ,
103+ relDiff,
104+ } ) ;
105+ }
106+ }
107+
108+ return fromBuckets . toString ( ) ;
109+ } , [ rskLocked ] ) ;
110+
111+ const bobTvlUsd = useMemo ( ( ) => {
112+ if ( ! bobLocked ) {
113+ return '0' ;
114+ }
115+
116+ const raw =
117+ ( bobLocked as any ) ?. total_usd ?? ( bobLocked as any ) ?. totalUsd ?? 0 ;
118+
119+ return sanitizeUsd ( raw , 1e9 ) ;
120+ } , [ bobLocked ] ) ;
121+
122+ const rskTvlBtc = useMemo (
123+ ( ) => ( rskBtcUsd ? decimalic ( rskTvlUsd ) . div ( rskBtcUsd ) . toString ( ) : '0' ) ,
124+ [ rskTvlUsd , rskBtcUsd ] ,
125+ ) ;
126+ const bobTvlBtc = useMemo (
127+ ( ) => ( bobBtcUsd ? decimalic ( bobTvlUsd ) . div ( bobBtcUsd ) . toString ( ) : '0' ) ,
128+ [ bobTvlUsd , bobBtcUsd ] ,
129+ ) ;
130+
131+ const rskVolumeUsd = useMemo (
132+ ( ) => sanitizeUsd ( rskVolumeRaw , 1e12 ) ,
133+ [ rskVolumeRaw ] ,
134+ ) ;
135+ const bobVolumeUsd = useMemo (
136+ ( ) => sanitizeUsd ( bobVolumeRaw , 1e12 ) ,
137+ [ bobVolumeRaw ] ,
138+ ) ;
139+
140+ const rskVolumeBtc = useMemo (
141+ ( ) => ( rskBtcUsd ? decimalic ( rskVolumeUsd ) . div ( rskBtcUsd ) . toString ( ) : '0' ) ,
142+ [ rskVolumeUsd , rskBtcUsd ] ,
143+ ) ;
144+ const bobVolumeBtc = useMemo (
145+ ( ) => ( bobBtcUsd ? decimalic ( bobVolumeUsd ) . div ( bobBtcUsd ) . toString ( ) : '0' ) ,
146+ [ bobVolumeUsd , bobBtcUsd ] ,
147+ ) ;
148+
149+ const totalTvlUsd = useMemo (
150+ ( ) => decimalic ( rskTvlUsd ) . add ( bobTvlUsd ) . toString ( ) ,
151+ [ rskTvlUsd , bobTvlUsd ] ,
152+ ) ;
153+ const totalVolUsd = useMemo (
154+ ( ) => decimalic ( rskVolumeUsd ) . add ( bobVolumeUsd ) . toString ( ) ,
155+ [ rskVolumeUsd , bobVolumeUsd ] ,
54156 ) ;
55157
56- const total = useMemo ( ( ) => {
57- return {
58- lockedData :
59- Number ( rskTVL || '0' ) + Number ( bobLockedData . total_usd || '0' ) ,
60- volumeData : Number ( rskVolume || '0' ) + Number ( bobVolume || '0' ) ,
61- } ;
62- } , [ bobLockedData . total_usd , bobVolume , rskTVL , rskVolume ] ) ;
158+ const totalTvlBtc = useMemo (
159+ ( ) =>
160+ globalBtcUsd ? decimalic ( totalTvlUsd ) . div ( globalBtcUsd ) . toString ( ) : '0' ,
161+ [ totalTvlUsd , globalBtcUsd ] ,
162+ ) ;
163+ const totalVolBtc = useMemo (
164+ ( ) =>
165+ globalBtcUsd ? decimalic ( totalVolUsd ) . div ( globalBtcUsd ) . toString ( ) : '0' ,
166+ [ totalVolUsd , globalBtcUsd ] ,
167+ ) ;
63168
64169 return (
65170 < div >
@@ -79,41 +184,50 @@ export const ProtocolData: FC = () => {
79184 < Accordion
80185 label = {
81186 < div className = "flex justify-between font-medium w-full" >
187+ { /* TOTAL TVL (All Networks) — BTC + USD (one BTC/USD) */ }
82188 < div className = "flex flex-col items-start sm:min-w-44" >
83189 < div className = "text-xs text-gray-30 mb-2" >
84190 { t ( pageTranslations . totalValueLockedAllNetworks ) }
85191 </ div >
192+
193+ { /* BTC total */ }
86194 < div className = "sm:text-xl text-gray-10 text-sm sm:font-medium font-semibold leading-8" >
87- < NativeTokenAmount
88- chainId = { RSK_CHAIN_ID }
89- usdValue = { total . lockedData }
90- precision = { 4 }
195+ < AmountRenderer
196+ value = { totalTvlBtc }
197+ suffix = { BITCOIN }
198+ precision = { BTC_RENDER_PRECISION }
91199 />
92200 </ div >
93201
202+ { /* USD total */ }
94203 < div className = "text-gray-50 text-sm" >
95204 < AmountRenderer
96- value = { total . lockedData }
205+ value = { totalTvlUsd }
97206 suffix = { USD }
98207 precision = { USD_VALUE_PRECISION }
99208 />
100209 </ div >
101210 </ div >
102211
212+ { /* TOTAL 24H VOLUME (All Networks) — BTC + USD (one BTC/USD) */ }
103213 < div className = "ml-10 mr-5 flex flex-col items-start sm:min-w-44" >
104214 < div className = "text-xs text-gray-30 mb-2" >
105215 { t ( pageTranslations . volumeAllNetworks ) }
106216 </ div >
107- < div className = "sm:text-2xl text-gray-10 text-sm sm:font-medium font-semibold leading-8" >
108- < NativeTokenAmount
109- chainId = { RSK_CHAIN_ID }
110- usdValue = { total . volumeData }
217+
218+ { /* 24h volume in BTC */ }
219+ < div className = "sm:text-xl text-gray-10 text-sm sm:font-medium font-semibold leading-8" >
220+ < AmountRenderer
221+ value = { totalVolBtc }
222+ suffix = { BITCOIN }
223+ precision = { BTC_RENDER_PRECISION }
111224 />
112225 </ div >
113226
227+ { /* 24h volume in USD */ }
114228 < div className = "text-gray-50 text-sm" >
115229 < AmountRenderer
116- value = { total . volumeData }
230+ value = { totalVolUsd }
117231 suffix = { USD }
118232 precision = { USD_VALUE_PRECISION }
119233 />
@@ -123,18 +237,26 @@ export const ProtocolData: FC = () => {
123237 }
124238 children = {
125239 < div className = "lg:p-4 px-0 py-4 font-medium" >
240+ { /* RSK ROW */ }
126241 < div className = "flex justify-between w-full" >
127242 < div className = "flex flex-col items-start sm:min-w-44" >
128243 < div className = "text-xs text-gray-30 mb-2" >
129244 { t ( pageTranslations . tvlRskNetwork ) }
130245 </ div >
246+
247+ { /* TVL (RSK) in BTC */ }
131248 < div className = "text-gray-10 text-sm sm:font-medium font-semibold" >
132- < NativeTokenAmount chainId = { RSK_CHAIN_ID } usdValue = { rskTVL } />
249+ < AmountRenderer
250+ value = { rskTvlBtc }
251+ suffix = { BITCOIN }
252+ precision = { BTC_RENDER_PRECISION }
253+ />
133254 </ div >
134255
256+ { /* TVL (RSK) in USD */ }
135257 < div className = "text-gray-50 text-sm" >
136258 < AmountRenderer
137- value = { rskTVL }
259+ value = { rskTvlUsd }
138260 suffix = { USD }
139261 precision = { USD_VALUE_PRECISION }
140262 />
@@ -145,37 +267,47 @@ export const ProtocolData: FC = () => {
145267 < div className = "text-xs text-gray-30 mb-2" >
146268 { t ( pageTranslations . volumeRskNetwork ) }
147269 </ div >
270+
271+ { /* 24h Volume (RSK) in BTC */ }
148272 < div className = "text-gray-10 text-sm sm:font-medium font-semibold" >
149- < NativeTokenAmount
150- chainId = { RSK_CHAIN_ID }
151- usdValue = { rskVolume }
273+ < AmountRenderer
274+ value = { rskVolumeBtc }
275+ suffix = { BITCOIN }
276+ precision = { BTC_RENDER_PRECISION }
152277 />
153278 </ div >
154279
280+ { /* 24h Volume (RSK) in USD */ }
155281 < div className = "text-gray-50 text-sm" >
156282 < AmountRenderer
157- value = { rskVolume }
283+ value = { rskVolumeUsd }
158284 suffix = { USD }
159285 precision = { USD_VALUE_PRECISION }
160286 />
161287 </ div >
162288 </ div >
163289 </ div >
164290
291+ { /* BOB ROW */ }
165292 < div className = "flex justify-between w-full mt-4" >
166293 < div className = "flex flex-col sm:min-w-44" >
167294 < div className = "text-xs text-gray-30 mb-2" >
168295 { t ( pageTranslations . tvlBobNetwork ) }
169296 </ div >
170- < div className = "text-gray-10 text-sm" >
171- < NativeTokenAmount
172- chainId = { RSK_CHAIN_ID }
173- usdValue = { bobLockedData . total_usd }
297+
298+ { /* TVL (BOB) in BTC */ }
299+ < div className = "text-gray-10 text-sm sm:font-medium font-semibold" >
300+ < AmountRenderer
301+ value = { bobTvlBtc }
302+ suffix = { BITCOIN }
303+ precision = { BTC_RENDER_PRECISION }
174304 />
175305 </ div >
306+
307+ { /* TVL (BOB) in USD */ }
176308 < div className = "text-gray-50 text-sm" >
177309 < AmountRenderer
178- value = { bobLockedData . total_usd }
310+ value = { bobTvlUsd }
179311 suffix = { USD }
180312 precision = { USD_VALUE_PRECISION }
181313 />
@@ -186,16 +318,20 @@ export const ProtocolData: FC = () => {
186318 < div className = "text-xs text-gray-30 mb-2" >
187319 { t ( pageTranslations . volumeBobNetwork ) }
188320 </ div >
321+
322+ { /* 24h Volume (BOB) in BTC */ }
189323 < div className = "text-gray-10 text-sm sm:font-medium font-semibold" >
190- < NativeTokenAmount
191- chainId = { RSK_CHAIN_ID }
192- usdValue = { bobVolume }
324+ < AmountRenderer
325+ value = { bobVolumeBtc }
326+ suffix = { BITCOIN }
327+ precision = { BTC_RENDER_PRECISION }
193328 />
194329 </ div >
195330
331+ { /* 24h Volume (BOB) in USD */ }
196332 < div className = "text-gray-50 text-sm" >
197333 < AmountRenderer
198- value = { bobVolume }
334+ value = { bobVolumeUsd }
199335 suffix = { USD }
200336 precision = { USD_VALUE_PRECISION }
201337 />
0 commit comments