17
17
_VERSION_1 = 1
18
18
_VERSION_2 = 2
19
19
_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
21
21
_NULL_KEY_BYTES = b'\x00 ' * SolanaPublicKey .LENGTH
22
- MAX_SLOT_DIFFERENCE = 25
22
+ DEFAULT_MAX_LATENCY = 25
23
23
24
24
25
25
class PythAccountType (Enum ):
@@ -81,7 +81,7 @@ def _read_attribute_string(buffer: bytes, offset: int) -> Tuple[Optional[str], i
81
81
82
82
83
83
def _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 :
85
85
raise ValueError ("Pyth account data too short" )
86
86
87
87
# Pyth magic (u32) == MAGIC
@@ -141,7 +141,7 @@ def update_with_rpc_response(self, slot: int, value: Dict[str, Any]) -> None:
141
141
f"wrong Pyth account type { type_ } for { type (self )} " )
142
142
143
143
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 )
145
145
except Exception as e :
146
146
logger .exception ("error while parsing account" , exception = e )
147
147
@@ -482,6 +482,7 @@ class PythPriceAccount(PythAccount):
482
482
aggregate price is composed of
483
483
slot (int): the slot time when this account was last fetched
484
484
product (Optional[PythProductAccount]): the product this price is for, if loaded
485
+ max_latency (int): the maximum allowed slot difference for this feed
485
486
"""
486
487
487
488
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
503
504
self .prev_price : float = field (init = False )
504
505
self .prev_conf : float = field (init = False )
505
506
self .prev_timestamp : int = 0 # unix timestamp in seconds
507
+ self .max_latency : int = 0 # maximum allowed slot difference for this feed
506
508
507
509
@property
508
510
def aggregate_price (self ) -> Optional [float ]:
@@ -537,7 +539,7 @@ def get_aggregate_price_status_with_slot(self, slot: int) -> Optional[PythPriceS
537
539
You might consider using this function with the latest solana slot to make sure the price has not gone stale.
538
540
"""
539
541
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 :
541
543
return PythPriceStatus .UNKNOWN
542
544
543
545
return self .aggregate_price_info .price_status
@@ -571,9 +573,12 @@ def update_from(self, buffer: bytes, *, version: int, offset: int = 0) -> None:
571
573
derivations = list (struct .unpack_from ("<6q" , buffer , offset ))
572
574
self .derivations = dict ((type_ , derivations [type_ .value - 1 ]) for type_ in [EmaType .EMA_CONFIDENCE_VALUE , EmaType .EMA_PRICE_VALUE ])
573
575
offset += 48 # struct.calcsize("6q")
574
- # drv[2-4]_ fields are currently unused
575
576
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")
577
582
product_account_key_bytes , next_price_account_key_bytes = struct .unpack_from ("32s32s" , buffer , offset )
578
583
offset += 64 # struct.calcsize("32s32s")
579
584
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:
620
625
self .prev_price = prev_price
621
626
self .prev_conf = prev_conf
622
627
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
623
630
624
631
def __str__ (self ) -> str :
625
632
if self .product :
0 commit comments