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_identifier(self):"""This works most of the time"""signature=f'{self.name}({",".join(self.get_inputs())})'returnf"0x{keccak_256(signature.encode('utf-8')).hex()[:8]}"
defencode_call(self,*,inputs:T)->HexStr:from..typesimportStructidentifier=self.get_identifier()# TODO: this is hardifinputs==():returnidentifierifisinstance(inputs,BaseModel):ifisinstance(inputs,Struct):input_data=inputs.to_bytes().hex()else:input_data=encode(self.get_inputs(),list(inputs.model_dump().values()),).hex()elifisinstance(inputs,tuple):input_data=encode(self.get_inputs(),[self._encode(val)forvalininputs]).hex()elifisinstance(inputs,list):input_data=encode(self.get_inputs(),[[self._encode(val)forvalininputs]]).hex()else:input_data=encode(self.get_inputs(),[inputs]).hex()returnHexStr(f"{identifier}{input_data}")
defdecode_result(self,result:HexStr)->U:ifisclass(self._output)andissubclass(self._output,Struct):returnself._output.from_bytes(result)output=self.get_output()ifoutputisNone:# return None if the expected return type is Nonereturnoutput# type: ignoreifnotisinstance(output,list):output=[output]decoded_output=decode(output,bytes.fromhex(result.removeprefix("0x")))[0]ifget_origin(self._output)==list:output_list_type=get_args(self._output)[0]ifisclass(output_list_type)andissubclass(output_list_type,Struct):decoded_arr:U=cast(U,[output_list_type.from_tuple(item)foritemindecoded_output],)returndecoded_arrelse:ifisclass(self._output)andissubclass(self._output,Struct):returnself._output.from_bytes(bytes.fromhex(result.removeprefix("0x")))else:decoded_output=decode(output,bytes.fromhex(result.removeprefix("0x")))# NOTE: https://github.com/pydantic/pydantic/discussions/5970# TODO: this is discussed to see if its a bug or not. Annotations are a class but can't be checked as a subclassif(isclass(self._output)andnotisinstance(self._output,GenericAlias)andissubclass(self._output,BaseModel)):decoded=decoded_outputoutput=self._outputresponse={}for(name,field),valueinzip(output.model_fields.items(),decoded):response[name]=Struct.cast(field.annotation,value)returnoutput(**response)returndecoded_output