11import json
2+ import logging
3+
4+ from functools import wraps
25from typing import Any , Dict
3- from loguru import logger
46
57from web3 import Web3
68from web3 .contract import Contract as Web3Contract
79from web3 .exceptions import TimeExhausted
810from web3 .types import FilterParams
9- from util .logging import get_logger
10- from web3utils .wallet import Wallet
1111
12- logger = get_logger ()
12+ from web3utils . wallet import Wallet
1313
1414class Contract :
1515
16- FOUNDRY_OUT = "./abi "
16+ FOUNDRY_OUT = "../out "
1717 SOLIDITY_EXT = "sol"
1818 GAS = 1000000
1919 TX_TIMEOUT_SECONDS = 120
@@ -51,10 +51,10 @@ def _setup_functions(self) -> None:
5151 mutability = func .get ('stateMutability' )
5252
5353 if mutability in ['nonpayable' , 'payable' ]:
54- logger .info (f"creating { func_name } (...) tx" )
54+ logging .info (f"creating { func_name } (...) tx" )
5555 setattr (self , func_name , self ._create_write_method (func_name ))
5656 elif mutability in ['view' , 'pure' ]:
57- logger .info (f"creating { func_name } (...) call" )
57+ logging .info (f"creating { func_name } (...) call" )
5858 setattr (self , func_name , self ._create_read_method (func_name ))
5959
6060 def _create_read_method (self , func_name : str ):
@@ -63,14 +63,26 @@ def read_method(*args, **kwargs) -> Any:
6363 modified_args = [arg .address if isinstance (arg , Wallet ) else arg for arg in args ]
6464 return getattr (self .contract .functions , func_name )(* modified_args , ** kwargs ).call ()
6565 except Exception as e :
66- logger .warning (f"Error calling function '{ func_name } ': { e } " )
66+ logging .warning (f"Error calling function '{ func_name } ': { e } " )
6767 return None
6868
69- # Optionally, add docstrings or additional attributes here
70- read_method . __name__ = func_name
71- read_method . __doc__ = f"Calls the ' { func_name } ' function of the contract."
69+ # add docstrings signature and selector
70+ self . _amend_method ( read_method , func_name )
71+
7272 return read_method
7373
74+ def _amend_method (self , method , name ):
75+ method .__name__ = name
76+ method .__doc__ = f"Calls the '{ name } ' contract function."
77+
78+ signature = getattr (self .contract .functions , name ).signature
79+ method .signature = signature
80+ method .argument_names = getattr (self .contract .functions , name ).argument_names
81+ method .inputs = getattr (self .contract .functions , name ).abi ['inputs' ]
82+ method .outputs = getattr (self .contract .functions , name ).abi ['outputs' ]
83+ method .selector = Web3 .keccak (text = signature )[:4 ]
84+ method .selector_hex = method .selector .hex ()
85+
7486 def _get_tx_params (self , args :tuple ) -> Dict [str , Any ]:
7587 if len (args ) == 0 :
7688 raise ValueError ("No transaction parameters provided." )
@@ -95,38 +107,31 @@ def write_method(*args) -> str:
95107
96108 try :
97109 wallet = tx_params ['from' ]
98- logger .info (f"Sending transaction for function '{ func_name } ' with wallet: { wallet .address } " )
99110
100111 # create tx properties
101112 chain_id = self .w3 .eth .chain_id
102113 gas = tx_params .get ('gas' , self .GAS )
103114 gas_price = tx_params .get ('gasPrice' , self .w3 .eth .gas_price )
104- gas_limit = tx_params .get ('gasLimit' , None )
105115 nonce = self .w3 .eth .get_transaction_count (wallet .address )
106116
107117 # transform wallet args to addresses (str)
108118 modified_args = [arg .address if isinstance (arg , Wallet ) else arg for arg in function_args ]
109119
110- options = {
120+ # create tx
121+ txn = getattr (self .contract .functions , func_name )(* modified_args ).build_transaction ({
111122 'chainId' : chain_id ,
112123 'gas' : gas ,
113124 'gasPrice' : gas_price ,
114125 'nonce' : nonce ,
115- }
116-
117- if gas_limit :
118- options ['gas' ] = gas_limit
119-
120- # create tx
121- txn = getattr (self .contract .functions , func_name )(* modified_args ).build_transaction (options )
126+ })
122127
123128 # sign tx
124129 private_key = bytes (wallet .account .key )
125130 signed_txn = self .w3 .eth .account .sign_transaction (txn , private_key = private_key )
126131
127132 # send signed transaction
128133 tx_hash = self .w3 .eth .send_raw_transaction (signed_txn .raw_transaction )
129- logger .info (f"Transaction sent: { tx_hash .hex ()} " )
134+ logging .info (f"Transaction sent: { tx_hash .hex ()} " )
130135
131136 if 'timeout' not in tx_params :
132137 timeout = self .TX_TIMEOUT_SECONDS
@@ -136,25 +141,23 @@ def write_method(*args) -> str:
136141 try :
137142 receipt = self .w3 .eth .wait_for_transaction_receipt (tx_hash , timeout = timeout )
138143 except TimeExhausted :
139- logger .warning (f"Transaction timeout after { timeout } seconds." )
144+ logging .warning (f"Transaction timeout after { timeout } seconds." )
140145 return tx_hash .hex ()
141146
142147 if receipt ['status' ] == 1 :
143- logger .info (f"Transaction successful: { receipt } " )
148+ logging .info (f"Transaction successful: { receipt } " )
144149 else :
145- logger .warning (f"Transaction failed: { receipt } " )
150+ logging .warning (f"Transaction failed: { receipt } " )
146151
147152 return tx_hash .hex ()
148153
149154 except Exception as e :
150- logger .warning (f"Error sending transaction for function '{ func_name } ': { e } " )
151- if ' tx_hash' in locals ():
152- logger . warning ( f"Transaction hash: { tx_hash . hex () } " )
153- return tx_hash . hex ()
154- return None
155+ logging .warning (f"Error sending transaction for function '{ func_name } ': { e } " )
156+ return tx_hash . hex ()
157+
158+ # add docstrings signature and selector
159+ self . _amend_method ( write_method , func_name )
155160
156- write_method .__name__ = func_name
157- write_method .__doc__ = f"Sends a transaction to the '{ func_name } ' function of the contract."
158161 return write_method
159162
160163 def _load_abi (self , contract :str , out_path :str ) -> Dict [str , Any ]:
0 commit comments