Package: pypechain
Type-safe Python bindings for Ethereum smart contracts!Used byFeaturesStatic Python bindings for ethereum smart contracts.Parses JSON ABIs to create typesafe web3.py contract instancesFunctions have typesafe function parameters and return valuesSmart Contract internal types are exposed as dataclassesContract event interfaces are exposed as typesafe classesThis project is a work-in-progress. All code is provided as is and without guarantee.Installpipinstall--upgradepypechainFor development install instructions, see toplevelINSTALL.mdPackages 📦Package NameVersionDescriptionpypechainCodegen python interfaces for web3.py contracts.autoflakeRemoves unused imports and unused variablesblackThe uncompromising code formatter.isortA Python utility / library to sort Python imports.jinja2A very fast and expressive template engine.web3web3.pyUsagePypechain is primarily to be used via the CLI:❯❯ pypechain -h

usage: pypechain [-h] [--output-dir OUTPUT_DIR] [--line-length LINE_LENGTH] abi_file_path

Generates class files for a given abi.

positional arguments:
  abi_file_path         Path to the abi JSON file or directory containing multiple JSON files.

options:
  -h, --help            show this help message and exit
  --output-dir OUTPUT_DIR
                        Path to the directory where files will be generated. Defaults to pypechain_types.
  --line-length LINE_LENGTH
                        Optional argument for the output file's maximum line length. Defaults to 80.However, you can also run themainscript directly from Python:frompypechainimportpypechainabi_dir="some/abi/dir"output_dir="some/output/dir"pypechain(abi_dir,output_dir)ExamplesPypechain generates a Python module from compiled Solidity code in ABI format.
This enables access to typesafe contract, struct, and event objects, which greatly improves
the developer experience.Accessing contract balancesUsing web3:fromweb3importWeb3web3=Web3()base_token_address="0xSomeAddress"user_address="0xUserAddress"# Contract construction takes an ABI filepath stringbase_token_contract=web3.eth.contract(abi=base_contract_abi,address=web3.to_checksum_address(base_token_address))# Arbitrary function arguments and names forces one to examine the ABI JSON to know the values & types# Additionally, the types are not specified as Python types in the ABIfn_args=[user_address]fn_kwargs={}# HARD TO DISCOVER ALL FUNCTIONS AND THEIR ARGUMENTScontract_function=base_token_contract.get_function_by_name("balanceOf")(*fn_args,**fn_kwargs)# The function call also takes arbitrary args and kwargscall_args=[]call_kwargs={}# UNTYPED RETURN VALUE!!return_values:dict[str,Any]=contract_function.call(*call_args,**call_kwargs)Using Pypechain generated objects:fromweb3importWeb3frompypechain_typesimportERC20MintableContractweb3=Web3()base_token_address="0xSomeAddress"user_address="0xUserAddress"# Contracts include a factory function to initialize with your given web3 providerbase_token_contract:ERC20MintableContract=ERC20MintableContract.deploy(w3=web3,signer=user_address)# balanceOf is a class function, enabling IDE tab-completion, intuitive inspection, typed inputs and typed outputsuser_base_balance:int=base_token_contract.functions.balanceOf(user_address).call()Understanding contractsSolidity files can be difficult to read for native Python programmers that have little exposure to smart contract code.// SPDX-License-Identifier: MITpragmasolidity^0.8.0;contractReturnTypes{structSimpleStruct{uintintVal;stringstrVal;}structInnerStruct{boolboolVal;}structNestedStruct{uintintVal;stringstrVal;InnerStructinnerStruct;}functionmixStructsAndPrimitives()publicpurereturns(SimpleStructmemorysimpleStruct,NestedStructmemory,uint,stringmemoryname,boolYesOrNo){simpleStruct=SimpleStruct({intVal:1,strVal:"You are number 1"});NestedStructmemorynestedStruct=NestedStruct({intVal:2,strVal:"You are number 2",innerStruct:InnerStruct({boolVal:true})});return(simpleStruct,nestedStruct,1,"ReturnTypesContract",false);}}Running pypechain on the compiled ABI from this contract produces code that is more intuitive for Python programmers....# imports@dataclassclassSimpleStruct:"""SimpleStruct struct."""intVal:intstrVal:str@dataclassclassInnerStruct:"""InnerStruct struct."""boolVal:bool@dataclassclassNestedStruct:"""NestedStruct struct."""intVal:intstrVal:strinnerStruct:InnerStructclassReturnTypesMixStructsAndPrimitivesContractFunction(ContractFunction):"""ContractFunction for the mixStructsAndPrimitives method."""classReturnValues(NamedTuple):"""The return named tuple for MixStructsAndPrimitives."""simpleStruct:SimpleStructarg2:NestedStructarg3:intname:strYesOrNo:booldef__call__(self)->ReturnTypesMixStructsAndPrimitivesContractFunction:clone=super().__call__()self.kwargs=clone.kwargsself.args=clone.argsreturnselfdefcall(self,transaction:TxParams|None=None,block_identifier:BlockIdentifier="latest",state_override:CallOverride|None=None,ccip_read_enabled:bool|None=None,)->ReturnValues:"""returns ReturnValues."""# Define the expected return types from the smart contract callreturn_types=[SimpleStruct,NestedStruct,int,str,bool]# Call the functionraw_values=super().call(transaction,block_identifier,state_override,ccip_read_enabled)returnself.ReturnValues(*rename_returned_types(return_types,raw_values))classReturnTypesContractFunctions(ContractFunctions):"""ContractFunctions for the ReturnTypes contract."""mixStructsAndPrimitives:ReturnTypesMixStructsAndPrimitivesContractFunctiondef__init__(self,abi:ABI,w3:"Web3",address:ChecksumAddress|None=None,decode_tuples:bool|None=False,)->None:super().__init__(abi,w3,address,decode_tuples)self.mixStructsAndPrimitives=ReturnTypesMixStructsAndPrimitivesContractFunction.factory("mixStructsAndPrimitives",w3=w3,contract_abi=abi,address=address,decode_tuples=decode_tuples,function_identifier="mixStructsAndPrimitives",)classReturnTypesContract(Contract):"""A web3.py Contract class for the ReturnTypes contract."""abi:ABI=returntypes_abibytecode:bytes=HexBytes(returntypes_bytecode)def__init__(self,address:ChecksumAddress|None=None)->None:try:# Initialize parent Contract classsuper().__init__(address=address)self.functions=ReturnTypesContractFunctions(returntypes_abi,self.w3,address)exceptFallbackNotFound:print("Fallback function not found. Continuing...")functions:ReturnTypesContractFunctions@classmethoddefdeploy(cls,w3:Web3,signer:ChecksumAddress)->Self:"""Deploys and instance of the contract.Parameters----------w3 : Web3A web3 instance.signer : ChecksumAddressThe address to deploy the contract from.Returns-------SelfA deployed instance of the contract."""deployer=cls.factory(w3=w3)tx_hash=deployer.constructor().transact({"from":signer})tx_receipt=w3.eth.wait_for_transaction_receipt(tx_hash)deployed_contract=deployer(address=tx_receipt.contractAddress)# type: ignorereturndeployed_contract@classmethoddeffactory(cls,w3:Web3,class_name:str|None=None,**kwargs:Any)->Type[Self]:contract=super().factory(w3,class_name,**kwargs)contract.functions=ReturnTypesContractFunctions(returntypes_abi,w3,None)returncontract