7 from __future__
import annotations
10 from web3._utils.contracts
import find_matching_event_abi
11 from eth_utils
import event_abi_to_log_topic
13 from typing
import Dict, List, Iterator, Optional, Union, Iterable, Any, cast
16 SYNC_BLOCKS_PER_BATCH = 1000
18 Interface = Dict[str, Any]
23 Minimal data required to instantiate the in-memory interface to a contract.
25 def __init__(self, address: str, abi: Dict[str, Any]):
44 deployer_eth_address: str,
45 deployer_eth_private_key: Optional[bytes],
46 deployment_gas: Optional[int],
47 compiler_flags: Dict[str, Any] =
None,
48 args: Iterable[Any] =
None) -> InstanceDescription:
50 Compile and deploy a contract, returning the live instance and an instance
51 description (which the caller should save in order to access the
52 instance in the future).
54 compiled = InstanceDescription.compile(
55 source_file, contract_name, compiler_flags)
57 instance_desc = InstanceDescription.deploy_from_compiled(
60 deployer_eth_private_key,
65 f
"deploy: contract: {contract_name} "
66 f
"to address: {instance_desc.address}")
72 deployer_eth_address: str,
73 deployer_eth_private_key: Optional[bytes],
74 deployment_gas: Optional[int],
76 *args: Any) -> InstanceDescription:
77 contract = web3.eth.contract(
78 abi=compiled[
'abi'], bytecode=compiled[
'bin'])
79 construct_call = contract.constructor(*args)
84 deployer_eth_private_key,
88 tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash, 10000)
89 contract_address = tx_receipt[
'contractAddress']
91 f
"deploy: tx_hash={tx_hash[0:8].hex()}, " +
92 f
" gasUsed={tx_receipt.gasUsed}, status={tx_receipt.status}")
99 compiler_flags: Dict[str, Any] =
None) \
101 compiled_all =
compile_files([source_file], **(compiler_flags
or {}))
103 compiled = compiled_all[f
"{source_file}:{contract_name}"]
109 Return the instantiated contract
111 return web3.eth.contract(address=self.
address, abi=self.
abi)
115 return web3.eth.blockNumber
119 solcx.install_solc(SOL_COMPILER_VERSION)
124 Wrapper around solcx which ensures the required version of the compiler is
127 solcx.set_solc_version(SOL_COMPILER_VERSION)
128 return solcx.compile_files(files, optimize=
True, **kwargs)
134 sender_eth_addr: str,
135 sender_eth_private_key: Optional[bytes] =
None,
136 value: Optional[EtherValue] =
None,
137 gas: Optional[int] =
None) -> bytes:
139 Broadcast a transaction for a contract call, handling the difference
140 between hosted keys (sender_eth_private_key is None) and local keys
141 (sender_eth_private_key is not None). Returns the hash of the broadcast
144 tx_desc: Dict[str, Union[str, int]] = {
'from': sender_eth_addr}
146 tx_desc[
"value"] = value.wei
149 if sender_eth_private_key:
150 tx_desc[
"gasPrice"] = web3.eth.gasPrice
151 tx_desc[
"nonce"] = web3.eth.getTransactionCount(sender_eth_addr)
152 transaction = call.buildTransaction(tx_desc)
153 signed_tx = web3.eth.account.signTransaction(
154 transaction, sender_eth_private_key)
155 print(f
"send_contract_call: size={len(signed_tx.rawTransaction)}")
156 return web3.eth.sendRawTransaction(signed_tx.rawTransaction)
159 return call.transact(tx_desc)
164 sender_eth_addr: str,
165 value: Optional[EtherValue] =
None,
166 gas: Optional[int] =
None) -> Any:
168 Make a contract call locally on the RPC host and return the result. Does
169 not create a transaction.
171 tx_desc: Dict[str, Union[str, int]] = {
'from': sender_eth_addr}
173 tx_desc[
"value"] = value.wei
176 return call.call(tx_desc)
185 batch_size: Optional[int]) -> Iterator[Any]:
187 Query the attached node for all events emitted by the given contract
188 instance, with the given name. Yields an iterator of event-specific objects
189 to be decoded by the caller.
203 contract_address = instance.address
204 contract_event = instance.events[event_name]()
206 event_abi = find_matching_event_abi(instance.abi, event_name=event_name)
207 log_topic = event_abi_to_log_topic(cast(Dict[str, Any], event_abi))
209 batch_size = batch_size
or SYNC_BLOCKS_PER_BATCH
210 while start_block <= end_block:
213 to_block = min(start_block + batch_size - 1, end_block)
215 'fromBlock': start_block,
217 'address': contract_address,
219 logs = web3.eth.getLogs(filter_params)
221 if log_topic == log[
'topics'][0]:
222 yield contract_event.processLog(log)
223 start_block = to_block + 1
229 tx_receipt: Any) -> Iterator[Any]:
231 Query a transaction receipt for all events emitted by the given contract
232 instance with a given event name. Yields an iterator of event-specific
233 objects to be decoded by the caller. This function intentionally avoids
234 connecting to a node, or creating host-side filters.
236 contract_address = instance.address
237 contract_event = instance.events[event_name]()
239 event_abi = find_matching_event_abi(instance.abi, event_name=event_name)
240 log_topic = event_abi_to_log_topic(cast(Dict[str, Any], event_abi))
241 for log
in tx_receipt.logs:
242 if log.address == contract_address
and log_topic == log[
'topics'][0]:
243 yield contract_event.processLog(log)