This is how we make the ProtocolBase inject the ContractFunc via the type signature. After a lot of research, this goes slightly outside of the bounds for type hinting, but I think the improved expressiveness makes it fully worthwhile.
def__init__(self,**kwargs):""" This is how we make the ProtocolBase inject the ContractFunc via the type signature. After a lot of research, this goes slightly outside of the bounds for type hinting, but I think the improved expressiveness makes it fully worthwhile. """super().__init__(**kwargs)foralias,funcinself._func_sigs.items():name=aliasifis_annotation(func):annotation_args=get_args(func)args=annotation_args[0]forannotationinannotation_args:ifisinstance(annotation,Name):name=annotation.valueelse:args=funcT,U=get_args(args)setattr(self,alias,ContractFunc[T,U](# type: ignorefunc=FuncSignature[T,U](name=name,alias=alias),# type: ignorecontract=self,),)
defmodel_post_init(self,__context):network=self.__class__._networkobject.__setattr__(self,"_network",network)# overwrite the .rpc() classmethodobject.__setattr__(self,"rpc",self._rpc)
@classmethoddefrpc(cls)->"RPC":""" This uses the default network, unless a network has been provided """from._transportimport_force_get_global_rpcifcls._networkisNone:return_force_get_global_rpc()response=_force_get_global_rpc(cls._network)returnresponse
This is how we make the ProtocolBase inject the ContractFunc via the type signature. After a lot of research, this goes slightly outside of the bounds for type hinting, but I think the improved expressiveness makes it fully worthwhile.
def__init__(self,**kwargs):""" This is how we make the ProtocolBase inject the ContractFunc via the type signature. After a lot of research, this goes slightly outside of the bounds for type hinting, but I think the improved expressiveness makes it fully worthwhile. """super().__init__(**kwargs)foralias,funcinself._func_sigs.items():name=aliasifis_annotation(func):annotation_args=get_args(func)args=annotation_args[0]forannotationinannotation_args:ifisinstance(annotation,Name):name=annotation.valueelse:args=funcT,U=get_args(args)setattr(self,alias,ContractFunc[T,U](# type: ignorefunc=FuncSignature[T,U](name=name,alias=alias),# type: ignorecontract=self,),)
defmodel_post_init(self,__context):network=self.__class__._networkobject.__setattr__(self,"_network",network)# overwrite the .rpc() classmethodobject.__setattr__(self,"rpc",self._rpc)
@classmethoddefrpc(cls)->"RPC":""" This uses the default network, unless a network has been provided """from._transportimport_force_get_global_rpcifcls._networkisNone:return_force_get_global_rpc()response=_force_get_global_rpc(cls._network)returnresponse
defget_allowance_slot(self,owner:str,spender:str):""" Gets the allowance slot given token/owner/spender combination """response=self._get_debug_tracecall(self.address,data=f"0xdd62ed3e000000000000000000000000{owner.replace('0x','')}000000000000000000000000{spender.replace('0x','')}",)ifnotresponse:returnNonetry:storage=response[self.address.lower()]["storage"]exceptKeyError:returnNoneforkeyin[OZ_IMPLEMENTATION_SLOT,ADMIN_SLOT,EIP1967_IMPLEMENTATION_SLOT,]:ifhex(key)instorage:delstorage[hex(key)]iflen(storage)!=1:# TOOD: this isn't really a ValueError, but we need to allow the client to handle this differentlyraiseValueError(list(storage.keys()))returnlist(storage.keys())[0]
defget_balance_slot(self,owner:primitives.address)->Optional[HexStr]:""" Attempts to find the balance slot for an address. This will not work on tokens with irregular balance calculation. """balance=self.balance_of(owner).sync.call().rawresult=self._get_debug_tracecall(self.address,data=f"0x70a08231000000000000000000000000{owner.replace('0x','')}",)storage=result[self.address.lower()]["storage"]forslotinstorage:ifbalanceinstorage[slot]:returnslot# Unknown slot return NonereturnNone