11from __future__ import annotations
2- from typing import List , Dict , Tuple , Optional , Any
2+ from typing import List , Dict , Tuple , Optional , Any , ClassVar
33import base64
44from enum import Enum
5+ from dataclasses import dataclass , field
56import struct
67
78from loguru import logger
@@ -205,7 +206,6 @@ class PythProductAccount(PythAccount):
205206 first_price_account_key (SolanaPublicKey): the public key of the first price account (the price accounts form a linked list)
206207 attrs (dict): a dictionary of metadata attributes
207208 """
208-
209209 def __init__ (self , key : SolanaPublicKey , solana : SolanaClient ) -> None :
210210 super ().__init__ (key , solana )
211211 self ._prices : Optional [Dict [PythPriceType , PythPriceAccount ]] = None
@@ -229,7 +229,6 @@ def symbol(self) -> str:
229229 """
230230 Gets this account's symbol, or 'Unknown' if there is no 'symbol' attribute.
231231 """
232-
233232 return self .attrs .get ("symbol" , "Unknown" )
234233
235234 async def get_prices (self ) -> Dict [PythPriceType , PythPriceAccount ]:
@@ -258,7 +257,10 @@ async def refresh_prices(self) -> Dict[PythPriceType, PythPriceAccount]:
258257 self ._prices = prices
259258 return prices
260259
261- async def check_price_changes (self , update_accounts : bool = True ) -> Tuple [List [PythPriceAccount ], List [PythPriceAccount ]]:
260+ async def check_price_changes (
261+ self ,
262+ update_accounts : bool = True
263+ ) -> Tuple [List [PythPriceAccount ], List [PythPriceAccount ]]:
262264 """
263265 Checks for changes to the list of price accounts of this product.
264266
@@ -351,6 +353,7 @@ def __iter__(self):
351353 yield key , val
352354
353355
356+ @dataclass
354357class PythPriceInfo :
355358 """
356359 Contains price information.
@@ -365,15 +368,18 @@ class PythPriceInfo:
365368 exponent (int): the power-of-10 order of the price
366369 """
367370
368- LENGTH = 32
371+ LENGTH : ClassVar [ int ] = 32
369372
370- def __init__ (self , raw_price : int , raw_confidence_interval : int , price_status : PythPriceStatus , slot : int , exponent : int ) -> None :
371- self .raw_price = raw_price
372- self .raw_confidence_interval = raw_confidence_interval
373- self .price_status = price_status
374- self .slot = slot
375- self .exponent = exponent
373+ raw_price : int
374+ raw_confidence_interval : int
375+ price_status : PythPriceStatus
376+ slot : int
377+ exponent : int
376378
379+ price : float = field (init = False )
380+ confidence_interval : float = field (init = False )
381+
382+ def __post_init__ (self ):
377383 self .price = self .raw_price * (10 ** self .exponent )
378384 self .confidence_interval = self .raw_confidence_interval * \
379385 (10 ** self .exponent )
@@ -401,11 +407,8 @@ def __str__(self) -> str:
401407 def __repr__ (self ) -> str :
402408 return str (self )
403409
404- def __iter__ (self ):
405- for key , val in self .__dict__ .items ():
406- yield key , val
407-
408410
411+ @dataclass
409412class PythPriceComponent :
410413 """
411414 Represents a price component. This is the individual prices each
@@ -421,13 +424,12 @@ class PythPriceComponent:
421424 in this price component
422425 """
423426
424- LENGTH = SolanaPublicKey .LENGTH + 2 * PythPriceInfo .LENGTH
427+ LENGTH : ClassVar [ int ] = SolanaPublicKey .LENGTH + 2 * PythPriceInfo .LENGTH
425428
426- def __init__ (self , publisher_key : SolanaPublicKey , last_aggregate_price_info : PythPriceInfo , latest_price_info : PythPriceInfo , exponent : int ) -> None :
427- self .publisher_key = publisher_key
428- self .last_aggregate_price_info = last_aggregate_price_info
429- self .latest_price_info = latest_price_info
430- self .exponent = exponent
429+ publisher_key : SolanaPublicKey
430+ last_aggregate_price_info : PythPriceInfo
431+ latest_price_info : PythPriceInfo
432+ exponent : int
431433
432434 @staticmethod
433435 def deserialise (buffer : bytes , offset : int = 0 , * , exponent : int ) -> Optional [PythPriceComponent ]:
@@ -449,12 +451,6 @@ def deserialise(buffer: bytes, offset: int = 0, *, exponent: int) -> Optional[Py
449451 latest_price = PythPriceInfo .deserialise (buffer , offset , exponent = exponent )
450452 return PythPriceComponent (key , last_aggregate_price , latest_price , exponent )
451453
452- def __iter__ (self ):
453- for key , val in self .__dict__ .items ():
454- if isinstance (val , PythPriceInfo ):
455- val = dict (val )
456- yield key , val
457-
458454
459455class PythPriceAccount (PythAccount ):
460456 """
@@ -528,22 +524,22 @@ def update_from(self, buffer: bytes, *, version: int, offset: int = 0) -> None:
528524 """
529525 if version == _VERSION_2 :
530526 price_type , exponent , num_components = struct .unpack_from ("<IiI" , buffer , offset )
531- offset += 16 # struct.calcsize("IiII") (last I is the number of quoters that make up the aggregate)
527+ offset += 16 # struct.calcsize("IiII") (last I is the number of quoters that make up the aggregate)
532528 last_slot , valid_slot = struct .unpack_from ("<QQ" , buffer , offset )
533- offset += 16 # QQ
529+ offset += 16 # QQ
534530 derivations = list (struct .unpack_from ("<6q" , buffer , offset ))
535531 self .derivations = dict ((type_ , derivations [type_ .value - 1 ]) for type_ in [TwEmaType .TWACVALUE , TwEmaType .TWAPVALUE ])
536- offset += 48 # 6q
532+ offset += 48 # 6q
537533 # All drv*_ fields sans min_publishers are currently unused
538534 _ , min_publishers = struct .unpack_from ("<qQ" , buffer , offset )
539- offset += 16 # <qQ
535+ offset += 16 # <qQ
540536 product_account_key_bytes , next_price_account_key_bytes = struct .unpack_from ("32s32s" , buffer , offset )
541- offset += 96 # 32s32s32s
537+ offset += 96 # 32s32s32s
542538 elif version == _VERSION_1 :
543539 price_type , exponent , num_components , _ , last_slot , valid_slot , product_account_key_bytes , next_price_account_key_bytes , aggregator_key_bytes = struct .unpack_from (
544540 "<IiIIQQ32s32s32s" , buffer , offset )
545541 self .derivations = {}
546- offset += 128 # struct.calcsize("<IiIIQQ32s32s32s")
542+ offset += 128 # struct.calcsize("<IiIIQQ32s32s32s")
547543 else :
548544 assert False
549545
0 commit comments