1
1
import { useState } from "react" ;
2
2
import CopyIcon from "./icons/CopyIcon" ;
3
3
import { mapValues } from "../utils/ObjectHelpers" ;
4
+ // Direct YAML import with webpack yaml-loader
5
+ import sponsoredFeedsData from "../pages/price-feeds/sponsored-feeds/data/sponsored_feeds_by_network.yaml" ;
4
6
5
- interface UpdateParameters {
6
- heartbeatLength : number ;
7
- heartbeatUnit : "second" | "minute" | "hour" ;
8
- priceDeviation : number ;
7
+ interface SponsoredFeed {
8
+ alias : string ; // name of the feed
9
+ id : string ; // price feed id
10
+ time_difference : number ; // in seconds
11
+ price_deviation : number ;
12
+ confidence_ratio : number ;
9
13
}
10
14
11
- interface SponsoredFeed {
12
- name : string ;
13
- priceFeedId : string ;
14
- updateParameters : UpdateParameters ;
15
+ interface SponsoredFeedsData {
16
+ [ networkKey : string ] : SponsoredFeed [ ] ;
15
17
}
16
18
17
19
interface SponsoredFeedsTableProps {
18
- feeds : SponsoredFeed [ ] ;
20
+ networkKey : string ;
19
21
networkName : string ;
20
22
}
21
23
22
24
/**
23
25
* Helper functions
24
26
*/
27
+ // Convert time_difference (seconds) to human readable format
28
+ const formatTimeUnit = ( seconds : number ) : { value : number ; unit : string } => {
29
+ if ( seconds >= 3600 ) {
30
+ return { value : seconds / 3600 , unit : "hour" } ;
31
+ } else if ( seconds >= 60 ) {
32
+ return { value : seconds / 60 , unit : "minute" } ;
33
+ } else {
34
+ return { value : seconds , unit : "second" } ;
35
+ }
36
+ } ;
25
37
26
38
// Format update parameters as a string for grouping
27
- const formatUpdateParams = ( params : UpdateParameters ) : string => {
28
- return `${ params . heartbeatLength } ${ params . heartbeatUnit } heartbeat / ${ params . priceDeviation } % price deviation` ;
39
+ const formatUpdateParams = ( feed : SponsoredFeed ) : string => {
40
+ const timeFormat = formatTimeUnit ( feed . time_difference ) ;
41
+ const timeStr = `${ timeFormat . value } ${ timeFormat . unit } ${
42
+ timeFormat . value !== 1 ? "s" : ""
43
+ } `;
44
+ return `${ timeStr } heartbeat / ${ feed . price_deviation } % price deviation` ;
29
45
} ;
30
46
31
47
// Render update parameters with proper styling
32
- const renderUpdateParams = ( params : UpdateParameters , isDefault : boolean ) => (
33
- < div className = "flex items-start gap-1.5" >
34
- < div
35
- className = { `w-1.5 h-1.5 rounded-full mt-1 flex-shrink-0 ${
36
- isDefault ? "bg-green-500" : "bg-orange-500"
37
- } `}
38
- > </ div >
39
- < span
40
- className = { `text-xs leading-relaxed font-medium ${
41
- isDefault
42
- ? "text-gray-700 dark:text-gray-300"
43
- : "text-orange-600 dark:text-orange-400"
44
- } `}
45
- >
46
- < strong > { params . heartbeatLength } </ strong > { params . heartbeatUnit } heartbeat
47
- < br />
48
- < strong > { params . priceDeviation } %</ strong > price deviation
49
- </ span >
50
- </ div >
51
- ) ;
48
+ const renderUpdateParams = ( feed : SponsoredFeed , isDefault : boolean ) => {
49
+ const timeFormat = formatTimeUnit ( feed . time_difference ) ;
50
+ const timeStr =
51
+ timeFormat . value === 1 ? timeFormat . unit : `${ timeFormat . unit } s` ;
52
+
53
+ return (
54
+ < div className = "flex items-start gap-1.5" >
55
+ < div
56
+ className = { `w-1.5 h-1.5 rounded-full mt-1 flex-shrink-0 ${
57
+ isDefault ? "bg-green-500" : "bg-orange-500"
58
+ } `}
59
+ > </ div >
60
+ < span
61
+ className = { `text-xs leading-relaxed font-medium ${
62
+ isDefault
63
+ ? "text-gray-700 dark:text-gray-300"
64
+ : "text-orange-600 dark:text-orange-400"
65
+ } `}
66
+ >
67
+ < strong > { timeFormat . value } </ strong > { timeStr } heartbeat
68
+ < br />
69
+ < strong > { feed . price_deviation } %</ strong > price deviation
70
+ </ span >
71
+ </ div >
72
+ ) ;
73
+ } ;
52
74
53
75
export const SponsoredFeedsTable = ( {
54
- feeds ,
76
+ networkKey ,
55
77
networkName,
56
78
} : SponsoredFeedsTableProps ) => {
57
79
const [ copiedId , setCopiedId ] = useState < string | null > ( null ) ;
@@ -63,15 +85,32 @@ export const SponsoredFeedsTable = ({
63
85
} ) ;
64
86
} ;
65
87
88
+ // Load feeds from YAML data
89
+ const data = sponsoredFeedsData as SponsoredFeedsData ;
90
+ const feeds = data [ networkKey ] || [ ] ;
91
+
92
+ // Handle empty feeds
93
+ if ( ! feeds || feeds . length === 0 ) {
94
+ return (
95
+ < div className = "my-6" >
96
+ < p className = "mb-3" >
97
+ No sponsored price feeds are currently available for{ " " }
98
+ < strong > { networkName } </ strong > .
99
+ </ p >
100
+ </ div >
101
+ ) ;
102
+ }
103
+
66
104
// Calculate parameter statistics
67
105
const paramCounts = mapValues (
68
- Object . groupBy ( feeds , ( feed ) => formatUpdateParams ( feed . updateParameters ) ) ,
106
+ Object . groupBy ( feeds , ( feed ) => formatUpdateParams ( feed ) ) ,
69
107
( feeds : SponsoredFeed [ ] ) => feeds . length
70
108
) ;
71
109
72
- const defaultParams = Object . entries ( paramCounts ) . sort (
110
+ const paramEntries = Object . entries ( paramCounts ) . sort (
73
111
( [ , a ] , [ , b ] ) => b - a
74
- ) [ 0 ] [ 0 ] ;
112
+ ) ;
113
+ const defaultParams = paramEntries . length > 0 ? paramEntries [ 0 ] [ 0 ] : "" ;
75
114
76
115
return (
77
116
< div className = "my-6" >
@@ -124,32 +163,30 @@ export const SponsoredFeedsTable = ({
124
163
</ thead >
125
164
< tbody className = "bg-white dark:bg-gray-900" >
126
165
{ feeds . map ( ( feed , index ) => {
127
- const formattedParams = formatUpdateParams (
128
- feed . updateParameters
129
- ) ;
166
+ const formattedParams = formatUpdateParams ( feed ) ;
130
167
const isDefault = formattedParams === defaultParams ;
131
168
132
169
return (
133
170
< tr
134
- key = { feed . priceFeedId }
171
+ key = { feed . id }
135
172
className = "border-b border-gray-100 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800/30"
136
173
>
137
174
< td className = "px-3 py-2 align-top" >
138
175
< span className = "font-medium text-gray-900 dark:text-gray-100" >
139
- { feed . name }
176
+ { feed . alias }
140
177
</ span >
141
178
</ td >
142
179
< td className = "px-3 py-2 align-top" >
143
180
< div className = "flex items-start gap-2" >
144
181
< code className = "text-xs font-mono text-gray-600 dark:text-gray-400 flex-1 break-all leading-relaxed" >
145
- { feed . priceFeedId }
182
+ { feed . id }
146
183
</ code >
147
184
< button
148
- onClick = { ( ) => copyToClipboard ( feed . priceFeedId ) }
185
+ onClick = { ( ) => copyToClipboard ( feed . id ) }
149
186
className = "p-1 hover:bg-gray-200 dark:hover:bg-gray-600 rounded flex-shrink-0 mt-0.5"
150
187
title = "Copy Price Feed ID"
151
188
>
152
- { copiedId === feed . priceFeedId ? (
189
+ { copiedId === feed . id ? (
153
190
< span className = "text-green-500 text-xs font-bold" >
154
191
✓
155
192
</ span >
@@ -160,7 +197,7 @@ export const SponsoredFeedsTable = ({
160
197
</ div >
161
198
</ td >
162
199
< td className = "px-3 py-2 align-top" >
163
- { renderUpdateParams ( feed . updateParameters , isDefault ) }
200
+ { renderUpdateParams ( feed , isDefault ) }
164
201
</ td >
165
202
</ tr >
166
203
) ;
0 commit comments