1
- import { Connection , PublicKey , clusterApiUrl , Cluster , Commitment , AccountInfo , Account } from '@solana/web3.js'
1
+ import { Connection , PublicKey , Commitment , AccountInfo } from '@solana/web3.js'
2
2
import {
3
- Base ,
4
- Magic ,
5
- parseMappingData ,
6
3
parseBaseData ,
7
4
parsePriceData ,
8
5
parseProductData ,
9
- Price ,
10
6
PriceData ,
11
7
Product ,
12
8
ProductData ,
13
- Version ,
14
9
AccountType ,
15
- MAX_SLOT_DIFFERENCE ,
16
- PriceStatus ,
17
10
} from './index'
18
11
19
12
const ONES = '11111111111111111111111111111111'
20
13
14
+ /** An update to the content of the solana account at `key` that occurred at `slot`. */
15
+ export type AccountUpdate < T > = {
16
+ key : PublicKey
17
+ accountInfo : AccountInfo < T >
18
+ slot : number
19
+ }
20
+
21
21
/**
22
22
* Type of callback invoked whenever a pyth price account changes. The callback additionally
23
- * gets access product, which contains the metadata for this price account (e.g., that the symbol is "BTC/USD")
23
+ * gets ` product` , which contains the metadata for this price account (e.g., that the symbol is "BTC/USD")
24
24
*/
25
25
export type PythPriceCallback = ( product : Product , price : PriceData ) => void
26
26
27
+ /**
28
+ * A price callback that additionally includes the raw solana account information. Use this if you need
29
+ * access to account keys and such.
30
+ */
31
+ export type PythVerbosePriceCallback = ( product : AccountUpdate < ProductData > , price : AccountUpdate < PriceData > ) => void
32
+
27
33
/**
28
34
* Reads Pyth price data from a solana web3 connection. This class uses a callback-driven model,
29
35
* similar to the solana web3 methods for tracking updates to accounts.
@@ -33,22 +39,29 @@ export class PythConnection {
33
39
pythProgramKey : PublicKey
34
40
commitment : Commitment
35
41
36
- productAccountKeyToProduct : Record < string , Product > = { }
42
+ productAccountKeyToProduct : Record < string , AccountUpdate < ProductData > > = { }
37
43
priceAccountKeyToProductAccountKey : Record < string , string > = { }
38
44
39
- callbacks : PythPriceCallback [ ] = [ ]
45
+ callbacks : PythVerbosePriceCallback [ ] = [ ]
40
46
41
- private handleProductAccount ( key : PublicKey , account : AccountInfo < Buffer > ) {
42
- const { priceAccountKey, type, product } = parseProductData ( account . data )
43
- this . productAccountKeyToProduct [ key . toString ( ) ] = product
44
- if ( priceAccountKey . toString ( ) !== ONES ) {
45
- this . priceAccountKeyToProductAccountKey [ priceAccountKey . toString ( ) ] = key . toString ( )
47
+ private handleProductAccount ( key : PublicKey , account : AccountInfo < Buffer > , slot : number ) {
48
+ const productData = parseProductData ( account . data )
49
+ this . productAccountKeyToProduct [ key . toString ( ) ] = {
50
+ key,
51
+ slot,
52
+ accountInfo : {
53
+ ...account ,
54
+ data : productData ,
55
+ } ,
56
+ }
57
+ if ( productData . priceAccountKey . toString ( ) !== ONES ) {
58
+ this . priceAccountKeyToProductAccountKey [ productData . priceAccountKey . toString ( ) ] = key . toString ( )
46
59
}
47
60
}
48
61
49
62
private handlePriceAccount ( key : PublicKey , account : AccountInfo < Buffer > , slot : number ) {
50
- const product = this . productAccountKeyToProduct [ this . priceAccountKeyToProductAccountKey [ key . toString ( ) ] ]
51
- if ( product === undefined ) {
63
+ const productUpdate = this . productAccountKeyToProduct [ this . priceAccountKeyToProductAccountKey [ key . toString ( ) ] ]
64
+ if ( productUpdate === undefined ) {
52
65
// This shouldn't happen since we're subscribed to all of the program's accounts,
53
66
// but let's be good defensive programmers.
54
67
throw new Error (
@@ -57,9 +70,17 @@ export class PythConnection {
57
70
}
58
71
59
72
const priceData = parsePriceData ( account . data , slot )
73
+ const priceUpdate = {
74
+ key,
75
+ slot,
76
+ accountInfo : {
77
+ ...account ,
78
+ data : priceData ,
79
+ } ,
80
+ }
60
81
61
82
for ( const callback of this . callbacks ) {
62
- callback ( product , priceData )
83
+ callback ( productUpdate , priceUpdate )
63
84
}
64
85
}
65
86
@@ -72,7 +93,7 @@ export class PythConnection {
72
93
// We can skip these because we're going to get every account owned by this program anyway.
73
94
break
74
95
case AccountType . Product :
75
- this . handleProductAccount ( key , account )
96
+ this . handleProductAccount ( key , account , slot )
76
97
break
77
98
case AccountType . Price :
78
99
if ( ! productOnly ) {
@@ -117,6 +138,11 @@ export class PythConnection {
117
138
118
139
/** Register callback to receive price updates. */
119
140
public onPriceChange ( callback : PythPriceCallback ) {
141
+ this . callbacks . push ( ( product , price ) => callback ( product . accountInfo . data . product , price . accountInfo . data ) )
142
+ }
143
+
144
+ /** Register a verbose callback to receive price updates. */
145
+ public onPriceChangeVerbose ( callback : PythVerbosePriceCallback ) {
120
146
this . callbacks . push ( callback )
121
147
}
122
148
0 commit comments