1- import { Connection , PublicKey , clusterApiUrl , Cluster , Commitment , AccountInfo , Account } from '@solana/web3.js'
1+ import { Connection , PublicKey , Commitment , AccountInfo } from '@solana/web3.js'
22import {
3- Base ,
4- Magic ,
5- parseMappingData ,
63 parseBaseData ,
74 parsePriceData ,
85 parseProductData ,
9- Price ,
106 PriceData ,
117 Product ,
128 ProductData ,
13- Version ,
149 AccountType ,
15- MAX_SLOT_DIFFERENCE ,
16- PriceStatus ,
1710} from './index'
1811
1912const ONES = '11111111111111111111111111111111'
2013
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+
2121/**
2222 * 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")
2424 */
2525export type PythPriceCallback = ( product : Product , price : PriceData ) => void
2626
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+
2733/**
2834 * Reads Pyth price data from a solana web3 connection. This class uses a callback-driven model,
2935 * similar to the solana web3 methods for tracking updates to accounts.
@@ -33,22 +39,29 @@ export class PythConnection {
3339 pythProgramKey : PublicKey
3440 commitment : Commitment
3541
36- productAccountKeyToProduct : Record < string , Product > = { }
42+ productAccountKeyToProduct : Record < string , AccountUpdate < ProductData > > = { }
3743 priceAccountKeyToProductAccountKey : Record < string , string > = { }
3844
39- callbacks : PythPriceCallback [ ] = [ ]
45+ callbacks : PythVerbosePriceCallback [ ] = [ ]
4046
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 ( )
4659 }
4760 }
4861
4962 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 ) {
5265 // This shouldn't happen since we're subscribed to all of the program's accounts,
5366 // but let's be good defensive programmers.
5467 throw new Error (
@@ -57,9 +70,17 @@ export class PythConnection {
5770 }
5871
5972 const priceData = parsePriceData ( account . data , slot )
73+ const priceUpdate = {
74+ key,
75+ slot,
76+ accountInfo : {
77+ ...account ,
78+ data : priceData ,
79+ } ,
80+ }
6081
6182 for ( const callback of this . callbacks ) {
62- callback ( product , priceData )
83+ callback ( productUpdate , priceUpdate )
6384 }
6485 }
6586
@@ -72,7 +93,7 @@ export class PythConnection {
7293 // We can skip these because we're going to get every account owned by this program anyway.
7394 break
7495 case AccountType . Product :
75- this . handleProductAccount ( key , account )
96+ this . handleProductAccount ( key , account , slot )
7697 break
7798 case AccountType . Price :
7899 if ( ! productOnly ) {
@@ -117,6 +138,11 @@ export class PythConnection {
117138
118139 /** Register callback to receive price updates. */
119140 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 ) {
120146 this . callbacks . push ( callback )
121147 }
122148
0 commit comments