1717_VERSION_1 = 1
1818_VERSION_2 = 2
1919_SUPPORTED_VERSIONS = set ((_VERSION_1 , _VERSION_2 ))
20- _ACCOUNT_HEADER_BYTES = 16 # magic + version + type + size, u32 * 4
20+ ACCOUNT_HEADER_BYTES = 16 # magic + version + type + size, u32 * 4
2121_NULL_KEY_BYTES = b'\x00 ' * SolanaPublicKey .LENGTH
22- MAX_SLOT_DIFFERENCE = 25
22+ DEFAULT_MAX_LATENCY = 25
2323
2424
2525class PythAccountType (Enum ):
@@ -81,7 +81,7 @@ def _read_attribute_string(buffer: bytes, offset: int) -> Tuple[Optional[str], i
8181
8282
8383def _parse_header (buffer : bytes , offset : int = 0 , * , key : SolanaPublicKeyOrStr ) -> Tuple [PythAccountType , int , int ]:
84- if len (buffer ) - offset < _ACCOUNT_HEADER_BYTES :
84+ if len (buffer ) - offset < ACCOUNT_HEADER_BYTES :
8585 raise ValueError ("Pyth account data too short" )
8686
8787 # Pyth magic (u32) == MAGIC
@@ -141,7 +141,7 @@ def update_with_rpc_response(self, slot: int, value: Dict[str, Any]) -> None:
141141 f"wrong Pyth account type { type_ } for { type (self )} " )
142142
143143 try :
144- self .update_from (data [:size ], version = version , offset = _ACCOUNT_HEADER_BYTES )
144+ self .update_from (data [:size ], version = version , offset = ACCOUNT_HEADER_BYTES )
145145 except Exception as e :
146146 logger .exception ("error while parsing account" , exception = e )
147147
@@ -482,6 +482,7 @@ class PythPriceAccount(PythAccount):
482482 aggregate price is composed of
483483 slot (int): the slot time when this account was last fetched
484484 product (Optional[PythProductAccount]): the product this price is for, if loaded
485+ max_latency (int): the maximum allowed slot difference for this feed
485486 """
486487
487488 def __init__ (self , key : SolanaPublicKey , solana : SolanaClient , * , product : Optional [PythProductAccount ] = None ) -> None :
@@ -503,6 +504,7 @@ def __init__(self, key: SolanaPublicKey, solana: SolanaClient, *, product: Optio
503504 self .prev_price : float = field (init = False )
504505 self .prev_conf : float = field (init = False )
505506 self .prev_timestamp : int = 0 # unix timestamp in seconds
507+ self .max_latency : int = 0 # maximum allowed slot difference for this feed
506508
507509 @property
508510 def aggregate_price (self ) -> Optional [float ]:
@@ -537,7 +539,7 @@ def get_aggregate_price_status_with_slot(self, slot: int) -> Optional[PythPriceS
537539 You might consider using this function with the latest solana slot to make sure the price has not gone stale.
538540 """
539541 if self .aggregate_price_info .price_status == PythPriceStatus .TRADING and \
540- slot - self .aggregate_price_info .pub_slot > MAX_SLOT_DIFFERENCE :
542+ slot - self .aggregate_price_info .pub_slot > self . max_latency :
541543 return PythPriceStatus .UNKNOWN
542544
543545 return self .aggregate_price_info .price_status
@@ -571,9 +573,12 @@ def update_from(self, buffer: bytes, *, version: int, offset: int = 0) -> None:
571573 derivations = list (struct .unpack_from ("<6q" , buffer , offset ))
572574 self .derivations = dict ((type_ , derivations [type_ .value - 1 ]) for type_ in [EmaType .EMA_CONFIDENCE_VALUE , EmaType .EMA_PRICE_VALUE ])
573575 offset += 48 # struct.calcsize("6q")
574- # drv[2-4]_ fields are currently unused
575576 timestamp , min_publishers = struct .unpack_from ("<qB" , buffer , offset )
576- offset += 16 # struct.calcsize("qBbhi") ("bhi" is drv_2, drv_3, drv_4)
577+ offset += 9 # struct.calcsize("qB")
578+ _message_sent , max_latency = struct .unpack_from ("<bB" , buffer , offset )
579+ offset += 2 # struct.calcsize("bB")
580+ _drv_3 , _drv_4 = struct .unpack_from ("<bi" , buffer , offset )
581+ offset += 5 # struct.calcsize("bi")
577582 product_account_key_bytes , next_price_account_key_bytes = struct .unpack_from ("32s32s" , buffer , offset )
578583 offset += 64 # struct.calcsize("32s32s")
579584 prev_slot , prev_price , prev_conf , prev_timestamp = struct .unpack_from ("<QqQq" , buffer , offset )
@@ -620,6 +625,8 @@ def update_from(self, buffer: bytes, *, version: int, offset: int = 0) -> None:
620625 self .prev_price = prev_price
621626 self .prev_conf = prev_conf
622627 self .prev_timestamp = prev_timestamp
628+ # a max latency of 0 is the default max latency
629+ self .max_latency = max_latency if max_latency != 0 else DEFAULT_MAX_LATENCY
623630
624631 def __str__ (self ) -> str :
625632 if self .product :
0 commit comments