1
1
import {
2
2
AccountType ,
3
+ PythCluster ,
3
4
getPythProgramKeyForCluster ,
4
5
parseBaseData ,
5
- parseMappingData ,
6
- parsePermissionData ,
7
- parsePriceData ,
8
- parseProductData ,
9
- PermissionData ,
10
- Product ,
11
6
} from '@pythnetwork/client'
12
- import { Connection , PublicKey } from '@solana/web3.js'
13
- import assert from 'assert'
7
+ import { Connection } from '@solana/web3.js'
14
8
import { useContext , useEffect , useRef , useState } from 'react'
15
9
import { ClusterContext } from '../contexts/ClusterContext'
16
10
import { deriveWsUrl , pythClusterApiUrls } from '../utils/pythClusterApiUrl'
11
+ import { getProgramAdapter , ProgramType } from '@pythnetwork/xc-admin-common'
12
+ import {
13
+ RawConfig ,
14
+ MappingRawConfig ,
15
+ ProductRawConfig ,
16
+ PriceRawConfig ,
17
+ } from '@pythnetwork/xc-admin-common/src/programs/core/core_adapter'
17
18
18
19
interface PythHookData {
19
20
isLoading : boolean
20
21
rawConfig : RawConfig
21
22
connection ?: Connection
22
23
}
23
24
24
- export type RawConfig = {
25
- mappingAccounts : MappingRawConfig [ ]
26
- permissionAccount ?: PermissionData
27
- }
28
- export type MappingRawConfig = {
29
- address : PublicKey
30
- next : PublicKey | null
31
- products : ProductRawConfig [ ]
32
- }
33
- export type ProductRawConfig = {
34
- address : PublicKey
35
- priceAccounts : PriceRawConfig [ ]
36
- metadata : Product
37
- }
38
- export type PriceRawConfig = {
39
- next : PublicKey | null
40
- address : PublicKey
41
- expo : number
42
- minPub : number
43
- maxLatency : number
44
- publishers : PublicKey [ ]
45
- }
46
-
47
25
export const usePyth = ( ) : PythHookData => {
48
26
const connectionRef = useRef < Connection | undefined > ( undefined )
49
27
const { cluster } = useContext ( ClusterContext )
50
28
const [ isLoading , setIsLoading ] = useState ( true )
51
29
const [ rawConfig , setRawConfig ] = useState < RawConfig > ( { mappingAccounts : [ ] } )
52
30
const [ urlsIndex , setUrlsIndex ] = useState ( 0 )
31
+ const pythAdapter = useRef ( getProgramAdapter ( ProgramType . PYTH_CORE ) )
53
32
54
33
useEffect ( ( ) => {
55
34
setIsLoading ( true )
@@ -72,116 +51,30 @@ export const usePyth = (): PythHookData => {
72
51
try {
73
52
const allPythAccounts = [
74
53
...( await connection . getProgramAccounts (
75
- getPythProgramKeyForCluster ( cluster )
54
+ getPythProgramKeyForCluster ( cluster as PythCluster )
76
55
) ) ,
77
56
]
78
57
if ( cancelled ) return
79
- const priceRawConfigs : { [ key : string ] : PriceRawConfig } = { }
80
-
81
- /// First pass, price accounts
82
- let i = 0
83
- while ( i < allPythAccounts . length ) {
84
- const base = parseBaseData ( allPythAccounts [ i ] . account . data )
85
- switch ( base ?. type ) {
86
- case AccountType . Price :
87
- const parsed = parsePriceData ( allPythAccounts [ i ] . account . data )
88
- priceRawConfigs [ allPythAccounts [ i ] . pubkey . toBase58 ( ) ] = {
89
- next : parsed . nextPriceAccountKey ,
90
- address : allPythAccounts [ i ] . pubkey ,
91
- publishers : parsed . priceComponents . map ( ( x ) => {
92
- return x . publisher !
93
- } ) ,
94
- expo : parsed . exponent ,
95
- minPub : parsed . minPublishers ,
96
- maxLatency : parsed . maxLatency ,
97
- }
98
- allPythAccounts [ i ] = allPythAccounts [ allPythAccounts . length - 1 ]
99
- allPythAccounts . pop ( )
100
- break
101
- default :
102
- i += 1
103
- }
104
- }
105
58
106
- if ( cancelled ) return
107
- /// Second pass, product accounts
108
- i = 0
109
- const productRawConfigs : { [ key : string ] : ProductRawConfig } = { }
110
- while ( i < allPythAccounts . length ) {
111
- const base = parseBaseData ( allPythAccounts [ i ] . account . data )
112
- switch ( base ?. type ) {
113
- case AccountType . Product :
114
- const parsed = parseProductData ( allPythAccounts [ i ] . account . data )
115
- if ( parsed . priceAccountKey ) {
116
- let priceAccountKey : string | undefined =
117
- parsed . priceAccountKey . toBase58 ( )
118
- const priceAccounts = [ ]
119
- while ( priceAccountKey ) {
120
- const toAdd : PriceRawConfig = priceRawConfigs [ priceAccountKey ]
121
- priceAccounts . push ( toAdd )
122
- delete priceRawConfigs [ priceAccountKey ]
123
- priceAccountKey = toAdd . next
124
- ? toAdd . next . toBase58 ( )
125
- : undefined
126
- }
127
- productRawConfigs [ allPythAccounts [ i ] . pubkey . toBase58 ( ) ] = {
128
- priceAccounts,
129
- metadata : parsed . product ,
130
- address : allPythAccounts [ i ] . pubkey ,
131
- }
132
- }
133
- allPythAccounts [ i ] = allPythAccounts [ allPythAccounts . length - 1 ]
134
- allPythAccounts . pop ( )
135
- break
136
- default :
137
- i += 1
138
- }
139
- }
59
+ // Use the adapter to parse the accounts
60
+ const parsedConfig = pythAdapter . current . getConfigFromRawAccounts (
61
+ allPythAccounts ,
62
+ cluster as PythCluster
63
+ )
140
64
141
- const rawConfig : RawConfig = { mappingAccounts : [ ] }
142
- if ( cancelled ) return
143
- /// Third pass, mapping accounts
144
- i = 0
145
- while ( i < allPythAccounts . length ) {
146
- const base = parseBaseData ( allPythAccounts [ i ] . account . data )
147
- switch ( base ?. type ) {
148
- case AccountType . Mapping :
149
- const parsed = parseMappingData ( allPythAccounts [ i ] . account . data )
150
- rawConfig . mappingAccounts . push ( {
151
- next : parsed . nextMappingAccount ,
152
- address : allPythAccounts [ i ] . pubkey ,
153
- products : parsed . productAccountKeys
154
- . filter ( ( key ) => productRawConfigs [ key . toBase58 ( ) ] )
155
- . map ( ( key ) => {
156
- const toAdd = productRawConfigs [ key . toBase58 ( ) ]
157
- delete productRawConfigs [ key . toBase58 ( ) ]
158
- return toAdd
159
- } ) ,
160
- } )
161
- allPythAccounts [ i ] = allPythAccounts [ allPythAccounts . length - 1 ]
162
- allPythAccounts . pop ( )
163
- break
164
- case AccountType . Permission :
165
- rawConfig . permissionAccount = parsePermissionData (
166
- allPythAccounts [ i ] . account . data
167
- )
168
- allPythAccounts [ i ] = allPythAccounts [ allPythAccounts . length - 1 ]
169
- allPythAccounts . pop ( )
170
- break
171
- default :
172
- i += 1
173
- }
174
- }
65
+ // Verify all accounts were processed
66
+ const remainingAccounts = allPythAccounts . filter ( ( account ) => {
67
+ const base = parseBaseData ( account . account . data )
68
+ return base && base . type !== AccountType . Test
69
+ } )
175
70
176
- assert (
177
- allPythAccounts . every (
178
- ( x ) =>
179
- ! parseBaseData ( x . account . data ) ||
180
- parseBaseData ( x . account . data ) ?. type == AccountType . Test
71
+ if ( remainingAccounts . length > 0 ) {
72
+ console . warn (
73
+ `${ remainingAccounts . length } accounts were not processed`
181
74
)
182
- )
75
+ }
183
76
184
- setRawConfig ( rawConfig )
77
+ setRawConfig ( parsedConfig )
185
78
setIsLoading ( false )
186
79
} catch ( e ) {
187
80
if ( cancelled ) return
@@ -205,3 +98,6 @@ export const usePyth = (): PythHookData => {
205
98
rawConfig,
206
99
}
207
100
}
101
+
102
+ // Re-export the types for compatibility
103
+ export type { RawConfig , MappingRawConfig , ProductRawConfig , PriceRawConfig }
0 commit comments