Skip to content

Commit 3101010

Browse files
dbfreempacrob
authored and
pacrob
committed
Feature/asyncify contract (ethereum#2387)
* asyncify eth.contract * Contract build_transaction method
1 parent 543b9dd commit 3101010

File tree

7 files changed

+110
-55
lines changed

7 files changed

+110
-55
lines changed

ens/main.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ def resolver(self, normal_name: str) -> Optional['Contract']:
269269
resolver_addr = self.ens.caller.resolver(normal_name_to_hash(normal_name))
270270
if is_none_or_zero_address(resolver_addr):
271271
return None
272-
return self._resolverContract(address=resolver_addr)
272+
# TODO: look at possibly removing type ignore when AsyncENS is written
273+
return self._resolverContract(address=resolver_addr) # type: ignore
273274

274275
def reverser(self, target_address: ChecksumAddress) -> Optional['Contract']:
275276
reversed_domain = address_to_reverse_domain(target_address)
@@ -449,7 +450,8 @@ def _set_resolver(
449450
namehash,
450451
resolver_addr
451452
).transact(transact)
452-
return self._resolverContract(address=resolver_addr)
453+
# TODO: look at possibly removing type ignore when AsyncENS is written
454+
return self._resolverContract(address=resolver_addr) # type: ignore
453455

454456
def _setup_reverse(
455457
self, name: str, address: ChecksumAddress, transact: Optional["TxParams"] = None

ethpm/package.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ def get_contract_instance(self, name: ContractName, address: Address) -> Contrac
309309
contract_instance = self.w3.eth.contract(
310310
address=address, **contract_kwargs
311311
)
312-
return contract_instance
312+
# TODO: type ignore may be able to be removed after more of AsynContract is finished
313+
return contract_instance # type: ignore
313314

314315
#
315316
# Build Dependencies

tests/core/contracts/conftest.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,16 +1052,15 @@ def buildTransaction(request):
10521052
return functools.partial(invoke_contract, api_call_desig='buildTransaction')
10531053

10541054

1055-
@pytest_asyncio.fixture()
1056-
async def async_deploy(web3, Contract, apply_func=identity, args=None):
1055+
async def async_deploy(async_web3, Contract, apply_func=identity, args=None):
10571056
args = args or []
10581057
deploy_txn = await Contract.constructor(*args).transact()
1059-
deploy_receipt = await web3.eth.wait_for_transaction_receipt(deploy_txn)
1058+
deploy_receipt = await async_web3.eth.wait_for_transaction_receipt(deploy_txn)
10601059
assert deploy_receipt is not None
10611060
address = apply_func(deploy_receipt['contractAddress'])
10621061
contract = Contract(address=address)
10631062
assert contract.address == address
1064-
assert len(await web3.eth.get_code(contract.address)) > 0
1063+
assert len(await async_web3.eth.get_code(contract.address)) > 0
10651064
return contract
10661065

10671066

tests/core/contracts/test_contract_call_interface.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,3 +879,31 @@ def test_call_revert_contract(revert_contract):
879879
# which does not contain the revert reason. Avoid that by giving a gas
880880
# value.
881881
revert_contract.functions.revertWithMessage().call({'gas': 100000})
882+
883+
884+
@pytest.mark.asyncio
885+
async def test_async_call_with_no_arguments(async_math_contract, call):
886+
result = await async_math_contract.functions.return13().call()
887+
assert result == 13
888+
889+
890+
@pytest.mark.asyncio
891+
async def test_async_call_with_one_argument(async_math_contract, call):
892+
result = await async_math_contract.functions.multiply7(3).call()
893+
assert result == 21
894+
895+
896+
@pytest.mark.asyncio
897+
async def test_async_returns_data_from_specified_block(async_w3, async_math_contract):
898+
start_num = await async_w3.eth.get_block('latest')
899+
await async_w3.provider.make_request(method='evm_mine', params=[5])
900+
await async_math_contract.functions.increment().transact()
901+
await async_math_contract.functions.increment().transact()
902+
903+
output1 = await async_math_contract.functions.counter().call(
904+
block_identifier=start_num.number + 6)
905+
output2 = await async_math_contract.functions.counter().call(
906+
block_identifier=start_num.number + 7)
907+
908+
assert output1 == 1
909+
assert output2 == 2

web3/_utils/module_testing/web3_module.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def test_solidityKeccak(
179179
self, w3: "Web3", types: Sequence[TypeStr], values: Sequence[Any], expected: HexBytes
180180
) -> None:
181181
if isinstance(expected, type) and issubclass(expected, Exception):
182-
with pytest.raises(expected):
182+
with pytest.raises(expected): # type: ignore
183183
w3.solidityKeccak(types, values)
184184
return
185185

web3/contract.py

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -840,25 +840,10 @@ def _get_transaction(self, transaction: Optional[TxParams] = None) -> TxParams:
840840
return transact_transaction
841841

842842
@combomethod
843-
def buildTransaction(self, transaction: Optional[TxParams] = None) -> TxParams:
844-
"""
845-
Build the transaction dictionary without sending
846-
"""
847-
848-
if transaction is None:
849-
built_transaction: TxParams = {}
850-
else:
851-
built_transaction = cast(TxParams, dict(**transaction))
852-
self.check_forbidden_keys_in_transaction(built_transaction,
853-
["data", "to"])
854-
855-
if self.w3.eth.default_account is not empty:
856-
# type ignored b/c check prevents an empty default_account
857-
built_transaction.setdefault('from', self.w3.eth.default_account) # type: ignore
858-
859-
built_transaction['data'] = self.data_in_transaction
843+
def _build_transaction(self, transaction: Optional[TxParams] = None) -> TxParams:
844+
built_transaction = self._get_transaction(transaction)
860845
built_transaction['to'] = Address(b'')
861-
return fill_transaction_defaults(self.w3, built_transaction)
846+
return built_transaction
862847

863848
@staticmethod
864849
def check_forbidden_keys_in_transaction(
@@ -877,6 +862,22 @@ class ContractConstructor(BaseContractConstructor):
877862
def transact(self, transaction: Optional[TxParams] = None) -> HexBytes:
878863
return self.w3.eth.send_transaction(self._get_transaction(transaction))
879864

865+
@combomethod
866+
def build_transaction(self, transaction: Optional[TxParams] = None) -> TxParams:
867+
"""
868+
Build the transaction dictionary without sending
869+
"""
870+
built_transaction = self._build_transaction(transaction)
871+
return fill_transaction_defaults(self.w3, built_transaction)
872+
873+
@combomethod
874+
@deprecated_for("build_transaction")
875+
def buildTransaction(self, transaction: Optional[TxParams] = None) -> TxParams:
876+
"""
877+
Build the transaction dictionary without sending
878+
"""
879+
return self.build_transaction(transaction)
880+
880881

881882
class AsyncContractConstructor(BaseContractConstructor):
882883

@@ -885,6 +886,14 @@ async def transact(self, transaction: Optional[TxParams] = None) -> HexBytes:
885886
return await self.w3.eth.send_transaction( # type: ignore
886887
self._get_transaction(transaction))
887888

889+
@combomethod
890+
async def build_transaction(self, transaction: Optional[TxParams] = None) -> TxParams:
891+
"""
892+
Build the transaction dictionary without sending
893+
"""
894+
built_transaction = self._build_transaction(transaction)
895+
return fill_transaction_defaults(self.w3, built_transaction)
896+
888897

889898
class ConciseMethod:
890899
ALLOWED_MODIFIERS = {'call', 'estimateGas', 'transact', 'buildTransaction'}
@@ -1364,7 +1373,7 @@ async def call(
13641373
self._return_data_normalizers,
13651374
self.function_identifier,
13661375
call_transaction,
1367-
block_id,
1376+
block_id, # type: ignore
13681377
self.contract_abi,
13691378
self.abi,
13701379
state_override,
@@ -2020,11 +2029,11 @@ def parse_block_identifier(w3: 'Web3', block_identifier: BlockIdentifier) -> Blo
20202029

20212030
async def async_parse_block_identifier(w3: 'Web3',
20222031
block_identifier: BlockIdentifier
2023-
) -> BlockIdentifier:
2032+
) -> Awaitable[BlockIdentifier]:
20242033
if isinstance(block_identifier, int):
2025-
return parse_block_identifier_int(w3, block_identifier)
2034+
return await async_parse_block_identifier_int(w3, block_identifier)
20262035
elif block_identifier in ['latest', 'earliest', 'pending']:
2027-
return block_identifier
2036+
return block_identifier # type: ignore
20282037
elif isinstance(block_identifier, bytes) or is_hex_encoded_block_hash(block_identifier):
20292038
return await w3.eth.get_block(block_identifier)['number'] # type: ignore
20302039
else:
@@ -2042,6 +2051,18 @@ def parse_block_identifier_int(w3: 'Web3', block_identifier_int: int) -> BlockNu
20422051
return BlockNumber(block_num)
20432052

20442053

2054+
async def async_parse_block_identifier_int(w3: 'Web3', block_identifier_int: int
2055+
) -> Awaitable[BlockNumber]:
2056+
if block_identifier_int >= 0:
2057+
block_num = block_identifier_int
2058+
else:
2059+
last_block = await w3.eth.get_block('latest')['number'] # type: ignore
2060+
block_num = last_block + block_identifier_int + 1
2061+
if block_num < 0:
2062+
raise BlockNumberOutofRange
2063+
return BlockNumber(block_num) # type: ignore
2064+
2065+
20452066
def transact_with_contract_function(
20462067
address: ChecksumAddress,
20472068
w3: 'Web3',
@@ -2167,7 +2188,7 @@ def async_find_functions_by_identifier(
21672188

21682189
def get_function_by_identifier(
21692190
fns: Sequence[ContractFunction], identifier: str
2170-
) -> ContractFunction:
2191+
) -> Union[ContractFunction, AsyncContractFunction]:
21712192
if len(fns) > 1:
21722193
raise ValueError(
21732194
f'Found multiple functions with matching {identifier}. '

web3/eth.py

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@
6969
replace_transaction,
7070
)
7171
from web3.contract import (
72+
AsyncContract,
73+
AsyncContractCaller,
7274
ConciseContract,
7375
Contract,
7476
ContractCaller,
@@ -116,6 +118,8 @@ class BaseEth(Module):
116118
_default_block: BlockIdentifier = "latest"
117119
_default_chain_id: Optional[int] = None
118120
gasPriceStrategy = None
121+
defaultContractFactory: Type[Union[Contract, AsyncContract,
122+
ConciseContract, ContractCaller, AsyncContractCaller]] = Contract
119123

120124
_gas_price: Method[Callable[[], Wei]] = Method(
121125
RPC.eth_gasPrice,
@@ -343,6 +347,30 @@ def call_munger(
343347
mungers=[default_root_munger]
344348
)
345349

350+
@overload
351+
def contract(self, address: None = None, **kwargs: Any) -> Union[Type[Contract], Type[AsyncContract]]: ... # noqa: E704,E501
352+
353+
@overload # noqa: F811
354+
def contract(self, address: Union[Address, ChecksumAddress, ENS], **kwargs: Any) -> Union[Contract, AsyncContract]: ... # noqa: E704,E501
355+
356+
def contract( # noqa: F811
357+
self, address: Optional[Union[Address, ChecksumAddress, ENS]] = None, **kwargs: Any
358+
) -> Union[Type[Contract], Contract, Type[AsyncContract], AsyncContract]:
359+
ContractFactoryClass = kwargs.pop('ContractFactoryClass', self.defaultContractFactory)
360+
361+
ContractFactory = ContractFactoryClass.factory(self.w3, **kwargs)
362+
363+
if address:
364+
return ContractFactory(address)
365+
else:
366+
return ContractFactory
367+
368+
def set_contract_factory(
369+
self, contractFactory: Type[Union[Contract, AsyncContract,
370+
ConciseContract, ContractCaller, AsyncContractCaller]]
371+
) -> None:
372+
self.defaultContractFactory = contractFactory
373+
346374

347375
class AsyncEth(BaseEth):
348376
is_async = True
@@ -561,7 +589,6 @@ async def call(
561589

562590
class Eth(BaseEth):
563591
account = Account()
564-
defaultContractFactory: Type[Union[Contract, ConciseContract, ContractCaller]] = Contract # noqa: E704,E501
565592
iban = Iban
566593

567594
def namereg(self) -> NoReturn:
@@ -956,35 +983,12 @@ def filter_munger(
956983
mungers=[default_root_munger],
957984
)
958985

959-
@overload
960-
def contract(self, address: None = None, **kwargs: Any) -> Type[Contract]: ... # noqa: E704,E501
961-
962-
@overload # noqa: F811
963-
def contract(self, address: Union[Address, ChecksumAddress, ENS], **kwargs: Any) -> Contract: ... # noqa: E704,E501
964-
965-
def contract( # noqa: F811
966-
self, address: Optional[Union[Address, ChecksumAddress, ENS]] = None, **kwargs: Any
967-
) -> Union[Type[Contract], Contract]:
968-
ContractFactoryClass = kwargs.pop('ContractFactoryClass', self.defaultContractFactory)
969-
970-
ContractFactory = ContractFactoryClass.factory(self.w3, **kwargs)
971-
972-
if address:
973-
return ContractFactory(address)
974-
else:
975-
return ContractFactory
976-
977986
@deprecated_for("set_contract_factory")
978987
def setContractFactory(
979988
self, contractFactory: Type[Union[Contract, ConciseContract, ContractCaller]]
980989
) -> None:
981990
return self.set_contract_factory(contractFactory)
982991

983-
def set_contract_factory(
984-
self, contractFactory: Type[Union[Contract, ConciseContract, ContractCaller]]
985-
) -> None:
986-
self.defaultContractFactory = contractFactory
987-
988992
def getCompilers(self) -> NoReturn:
989993
raise DeprecationWarning("This method has been deprecated as of EIP 1474.")
990994

0 commit comments

Comments
 (0)