1
1
import { useState , useEffect } from "react" ;
2
- import { TextField , Select , MenuItem , Box } from "@mui/material" ;
3
- import { HermesClient } from "@pythnetwork/hermes-client" ;
2
+ import { TextField , Select , MenuItem } from "@mui/material" ;
3
+ import { HermesClient , PriceFeedMetadata } from "@pythnetwork/hermes-client" ;
4
4
import { Table , Td , Th , Tr , CopyToClipboard } from "nextra/components" ;
5
5
6
- interface PriceFeed {
7
- id : string ;
8
- name : string ;
9
- assetType : string ;
6
+ const fetchStablePriceFeeds = async ( ) => {
7
+ const priceFeeds = await new HermesClient (
8
+ "https://hermes.pyth.network"
9
+ ) . getPriceFeeds ( ) ;
10
+ const assetTypes = Array . from (
11
+ new Set ( priceFeeds . map ( ( feed ) => feed . attributes . asset_type ?? "" ) )
12
+ ) ;
13
+ console . log ( priceFeeds ) ;
14
+ return { priceFeeds, assetTypes } ;
15
+ } ;
16
+
17
+ const fetchBetaPriceFeeds = async ( ) => {
18
+ const priceFeeds = await new HermesClient (
19
+ "https://hermes-beta.pyth.network"
20
+ ) . getPriceFeeds ( ) ;
21
+ const assetTypes = Array . from (
22
+ new Set ( priceFeeds . map ( ( feed ) => feed . attributes . asset_type ?? "" ) )
23
+ ) ;
24
+ console . log ( priceFeeds ) ;
25
+ return { priceFeeds, assetTypes } ;
26
+ } ;
27
+
28
+ type AssetTypesSelectorProps = {
29
+ priceFeedsState : PriceFeedsState ;
30
+ selectedAssetType : string ;
31
+ setSelectedAssetType : ( assetType : string ) => void ;
32
+ } ;
33
+
34
+ enum PriceFeedsStateType {
35
+ NotLoaded ,
36
+ Loading ,
37
+ Loaded ,
38
+ Error ,
10
39
}
11
40
12
- export function PriceFeedTable ( ) {
13
- const [ priceFeeds , setPriceFeeds ] = useState < PriceFeed [ ] > ( [ ] ) ;
14
- const [ searchTerm , setSearchTerm ] = useState ( "" ) ;
15
- const [ selectedAssetType , setSelectedAssetType ] = useState ( "All" ) ;
41
+ const PriceFeedsState = {
42
+ NotLoaded : ( ) => ( { type : PriceFeedsStateType . NotLoaded as const } ) ,
43
+ Loading : ( ) => ( { type : PriceFeedsStateType . Loading as const } ) ,
44
+ Loaded : ( priceFeeds : PriceFeedMetadata [ ] , assetTypes : string [ ] ) => ( {
45
+ type : PriceFeedsStateType . Loaded as const ,
46
+ priceFeeds,
47
+ assetTypes,
48
+ } ) ,
49
+ Error : ( error : unknown ) => ( {
50
+ type : PriceFeedsStateType . Error as const ,
51
+ error,
52
+ } ) ,
53
+ } ;
54
+
55
+ type PriceFeedsState = ReturnType <
56
+ typeof PriceFeedsState [ keyof typeof PriceFeedsState ]
57
+ > ;
16
58
17
- // enum AssetTypesStateType {
18
- // NotLoaded,
19
- // Loading,
20
- // Loaded,
21
- // Error,
22
- // }
23
-
24
- // const AssetTypesState = {
25
- // NotLoaded: () => ({ type: AssetTypesStateType.NotLoaded as const }),
26
- // Loading: () => ({ type: AssetTypesStateType.Loading as const }),
27
- // Loaded: (assetTypes: string[]) => ({
28
- // type: AssetTypesStateType.Loaded as const,
29
- // assetTypes,
30
- // }),
31
- // Error: (error: unknown) => ({
32
- // type: AssetTypesStateType.Error as const,
33
- // error,
34
- // }),
35
- // };
36
-
37
- const fetchAssetTypes = async ( ) => {
38
- const hermesClient = new HermesClient ( "https://hermes.pyth.network" ) ;
39
- const feeds = await hermesClient . getPriceFeeds ( ) ;
40
- return Array . from (
41
- new Set ( feeds . map ( ( feed ) => feed . attributes . asset_type ?? "" ) )
42
- ) ;
43
- } ;
44
-
45
- // const useAssetTypes = (): typeof AssetTypesState => {
46
- // const [assetTypes, setAssetTypes] = useState<typeof AssetTypesState>(
47
- // AssetTypesState.NotLoaded()
48
- // );
49
-
50
- // useEffect(() => {
51
- // setAssetTypes(AssetTypesState.Loading());
52
- // }, []);
53
-
54
- // return assetTypes;
55
- // };
56
-
57
- // type AssetTypesSelectorProps = {
58
- // selectedAssetType: string;
59
- // setSelectedAssetType: (assetType: string) => void;
60
- // };
61
-
62
- // const AssetTypesSelector = ({
63
- // selectedAssetType,
64
- // setSelectedAssetType,
65
- // }: AssetTypesSelectorProps) => {
66
- // const assetTypes = useAssetTypes();
67
- // };
59
+ const usePriceFeeds = ( ) : PriceFeedsState => {
60
+ const [ priceFeeds , setPriceFeeds ] = useState < PriceFeedsState > (
61
+ PriceFeedsState . NotLoaded ( )
62
+ ) ;
68
63
69
64
useEffect ( ( ) => {
70
- const fetchPriceFeeds = async ( ) => {
71
- const hermesClient = new HermesClient ( "https://hermes.pyth.network" ) ;
72
- const feeds = await hermesClient . getPriceFeeds ( ) ;
73
- const transformedFeeds = feeds . map ( ( feed ) => ( {
74
- id : feed . id ,
75
- name : feed . attributes . display_symbol || "" ,
76
- assetType : feed . attributes . asset_type || "" ,
77
- } ) ) ;
78
- setPriceFeeds ( transformedFeeds ) ;
79
- } ;
80
-
81
- fetchPriceFeeds ( ) ;
65
+ setPriceFeeds ( PriceFeedsState . Loading ( ) ) ;
66
+ fetchStablePriceFeeds ( )
67
+ . then ( ( { priceFeeds, assetTypes } ) => {
68
+ setPriceFeeds ( PriceFeedsState . Loaded ( priceFeeds , assetTypes ) ) ;
69
+ } )
70
+ . catch ( ( error ) => {
71
+ setPriceFeeds ( PriceFeedsState . Error ( error ) ) ;
72
+ } ) ;
82
73
} , [ ] ) ;
83
74
84
- const filteredData = priceFeeds . filter ( ( feed ) => {
85
- const matchesSearch =
86
- feed . id . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
87
- feed . name . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ;
88
- const matchesAssetType =
89
- selectedAssetType === "All" || feed . assetType === selectedAssetType ;
90
- return matchesSearch && matchesAssetType ;
91
- } ) ;
92
- return (
93
- < Box >
94
- < Box sx = { { mb : 3 , display : "flex" , gap : 2 } } >
95
- < TextField
96
- label = "Search price feeds"
97
- variant = "outlined"
98
- size = "small"
99
- value = { searchTerm }
100
- onChange = { ( e ) => setSearchTerm ( e . target . value ) }
101
- sx = { { width : 300 } }
102
- />
75
+ return priceFeeds ;
76
+ } ;
77
+
78
+ const AssetTypesSelect = ( {
79
+ priceFeedsState,
80
+ selectedAssetType,
81
+ setSelectedAssetType,
82
+ } : AssetTypesSelectorProps ) => {
83
+ const priceFeeds = usePriceFeeds ( ) ;
84
+
85
+ switch ( priceFeeds . type ) {
86
+ case PriceFeedsStateType . NotLoaded :
87
+ case PriceFeedsStateType . Loading :
88
+ case PriceFeedsStateType . Error :
89
+ return < p > Error loading asset types</ p > ;
90
+ case PriceFeedsStateType . Loaded :
91
+ return (
103
92
< Select
104
93
value = { selectedAssetType }
105
94
onChange = { ( e ) => setSelectedAssetType ( e . target . value ) }
106
95
size = "small"
107
96
sx = { { width : 200 } }
108
97
>
109
98
< MenuItem value = "All" > All Asset Types</ MenuItem >
110
- { Array . from ( new Set ( priceFeeds . map ( ( feed ) => feed . assetType ) ) ) . map (
111
- ( type ) => (
112
- < MenuItem key = { type } value = { type } >
113
- { type || "Uncategorized" }
114
- </ MenuItem >
115
- )
116
- ) }
99
+ { priceFeeds . assetTypes . map ( ( type ) => (
100
+ < MenuItem key = { type } value = { type } >
101
+ { type ?? "Uncategorized" }
102
+ </ MenuItem >
103
+ ) ) }
117
104
</ Select >
118
- </ Box >
105
+ ) ;
106
+ }
107
+ } ;
108
+
109
+ export function PriceFeedTable ( ) {
110
+ const [ searchTerm , setSearchTerm ] = useState ( "" ) ;
111
+ const [ selectedAssetType , setSelectedAssetType ] = useState ( "All" ) ;
112
+ const priceFeedsState = usePriceFeeds ( ) ;
113
+
114
+ < AssetTypesSelect
115
+ priceFeedsState = { priceFeedsState }
116
+ selectedAssetType = { selectedAssetType }
117
+ setSelectedAssetType = { setSelectedAssetType }
118
+ /> ;
119
+
120
+ let filteredData : PriceFeedMetadata [ ] = [ ] ;
121
+ if ( priceFeedsState . type === PriceFeedsStateType . Loaded ) {
122
+ filteredData = priceFeedsState . priceFeeds . filter ( ( feed ) => {
123
+ const matchesSearch =
124
+ feed . id . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
125
+ feed . attributes . display_symbol
126
+ . toLowerCase ( )
127
+ . includes ( searchTerm . toLowerCase ( ) ) ;
128
+ const matchesAssetType =
129
+ selectedAssetType === "All" ||
130
+ feed . attributes . asset_type === selectedAssetType ;
131
+ return matchesSearch && matchesAssetType ;
132
+ } ) ;
133
+ }
134
+
135
+ return (
136
+ < >
137
+ < TextField
138
+ label = "Search price feeds"
139
+ variant = "outlined"
140
+ size = "small"
141
+ value = { searchTerm }
142
+ onChange = { ( e ) => setSearchTerm ( e . target . value ) }
143
+ sx = { { width : 300 } }
144
+ />
145
+ < Select
146
+ value = { selectedAssetType }
147
+ onChange = { ( e ) => setSelectedAssetType ( e . target . value ) }
148
+ size = "small"
149
+ sx = { { width : 200 } }
150
+ >
151
+ < MenuItem value = "All" > All Asset Types</ MenuItem >
152
+ { priceFeedsState . type === PriceFeedsStateType . Loaded &&
153
+ priceFeedsState . assetTypes . map ( ( type ) => (
154
+ < MenuItem key = { type } value = { type } >
155
+ { type || "Uncategorized" }
156
+ </ MenuItem >
157
+ ) ) }
158
+ </ Select >
119
159
120
160
< Table >
121
161
< thead >
@@ -128,16 +168,16 @@ export function PriceFeedTable() {
128
168
< tbody >
129
169
{ filteredData . map ( ( feed ) => (
130
170
< Tr key = { feed . id } >
131
- < Td > { feed . name } </ Td >
132
- < Td > { feed . assetType } </ Td >
133
- < Td >
171
+ < Td > { feed . attributes . display_symbol } </ Td >
172
+ < Td > { feed . attributes . asset_type } </ Td >
173
+ < Td className = "font-mono whitespace-nowrap" >
134
174
{ feed . id }
135
- < CopyToClipboard getValue = { ( ) => feed . id } />
175
+ < CopyToClipboard className = "ml-2" getValue = { ( ) => feed . id } />
136
176
</ Td >
137
177
</ Tr >
138
178
) ) }
139
179
</ tbody >
140
180
</ Table >
141
- </ Box >
181
+ </ >
142
182
) ;
143
183
}
0 commit comments