Skip to content

Commit 9194222

Browse files
authored
Allow ContractFunctions to be called via combomethod style (#3444)
* Allow ContractFunction to be called via reference * Add docs
1 parent 6e5a554 commit 9194222

File tree

8 files changed

+129
-16
lines changed

8 files changed

+129
-16
lines changed

docs/web3.contract.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,16 @@ If you have the function name in a variable, you might prefer this alternative:
679679
contract_func = myContract.functions[func_to_call]
680680
twentyone = contract_func(3).call()
681681
682+
You can also interact with contract functions without parentheses if the function doesn't
683+
take any arguments. For example:
684+
685+
.. code-block:: python
686+
687+
>>> myContract.functions.return13.call()
688+
13
689+
>>> myContract.functions.return13().call()
690+
13
691+
682692
:py:class:`ContractFunction` provides methods to interact with contract functions.
683693
Positional and keyword arguments supplied to the contract function subclass
684694
will be used to find the contract function by signature,

newsfragments/3444.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow user to call ContractFunctions without parentheses

tests/core/contracts/test_contract_build_transaction.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ def test_build_transaction_with_contract_no_arguments(
5353
}
5454

5555

56+
def test_build_transaction_with_contract_no_arguments_no_parens(
57+
w3, math_contract, build_transaction
58+
):
59+
txn = math_contract.functions.incrementCounter.build_transaction()
60+
assert dissoc(txn, "gas") == {
61+
"to": math_contract.address,
62+
"data": "0x5b34b966",
63+
"value": 0,
64+
"maxFeePerGas": 2750000000,
65+
"maxPriorityFeePerGas": 10**9,
66+
"chainId": 131277322940537,
67+
}
68+
69+
5670
def test_build_transaction_with_contract_fallback_function(
5771
w3, fallback_function_contract
5872
):
@@ -309,6 +323,21 @@ async def test_async_build_transaction_with_contract_no_arguments(
309323
}
310324

311325

326+
@pytest.mark.asyncio
327+
async def test_async_build_transaction_with_contract_no_arguments_no_parens(
328+
async_w3, async_math_contract, async_build_transaction
329+
):
330+
txn = await async_math_contract.functions.incrementCounter.build_transaction()
331+
assert dissoc(txn, "gas") == {
332+
"to": async_math_contract.address,
333+
"data": "0x5b34b966",
334+
"value": 0,
335+
"maxFeePerGas": 2750000000,
336+
"maxPriorityFeePerGas": 10**9,
337+
"chainId": 131277322940537,
338+
}
339+
340+
312341
@pytest.mark.asyncio
313342
async def test_async_build_transaction_with_contract_fallback_function(
314343
async_w3, async_fallback_function_contract

tests/core/contracts/test_contract_call_interface.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ def test_call_with_no_arguments(math_contract, call):
145145
assert result == 13
146146

147147

148+
def test_call_no_arguments_no_parens(math_contract):
149+
assert math_contract.functions.return13.call() == 13
150+
151+
148152
def test_call_with_one_argument(math_contract, call):
149153
result = call(contract=math_contract, contract_function="multiply7", func_args=[3])
150154
assert result == 21
@@ -2287,6 +2291,12 @@ async def test_async_call_with_no_arguments(async_math_contract, call):
22872291
assert result == 13
22882292

22892293

2294+
@pytest.mark.asyncio
2295+
async def test_async_call_with_no_arguments_no_parens(async_math_contract, call):
2296+
result = await async_math_contract.functions.return13.call()
2297+
assert result == 13
2298+
2299+
22902300
@pytest.mark.asyncio
22912301
async def test_async_call_with_one_argument(async_math_contract, call):
22922302
result = await async_math_contract.functions.multiply7(3).call()

tests/core/contracts/test_contract_estimate_gas.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ def test_contract_estimate_gas(w3, math_contract, estimate_gas, transact):
1818
assert abs(gas_estimate - gas_used) < 21000
1919

2020

21+
def test_estimate_gas_can_be_called_without_parens(
22+
w3, math_contract, estimate_gas, transact
23+
):
24+
gas_estimate = math_contract.functions.incrementCounter.estimate_gas()
25+
26+
txn_hash = transact(contract=math_contract, contract_function="incrementCounter")
27+
28+
txn_receipt = w3.eth.wait_for_transaction_receipt(txn_hash)
29+
gas_used = txn_receipt.get("gasUsed")
30+
31+
assert abs(gas_estimate - gas_used) < 21000
32+
33+
2134
def test_contract_fallback_estimate_gas(w3, fallback_function_contract):
2235
gas_estimate = fallback_function_contract.fallback.estimate_gas()
2336

@@ -197,6 +210,24 @@ async def test_async_estimate_gas_accepts_latest_block(
197210
assert abs(gas_estimate - gas_used) < 21000
198211

199212

213+
@pytest.mark.asyncio
214+
async def test_async_estimate_gas_can_be_called_without_parens(
215+
async_w3, async_math_contract, async_transact
216+
):
217+
gas_estimate = await async_math_contract.functions.counter.estimate_gas(
218+
block_identifier="latest"
219+
)
220+
221+
txn_hash = await async_transact(
222+
contract=async_math_contract, contract_function="incrementCounter"
223+
)
224+
225+
txn_receipt = await async_w3.eth.wait_for_transaction_receipt(txn_hash)
226+
gas_used = txn_receipt.get("gasUsed")
227+
228+
assert abs(gas_estimate - gas_used) < 21000
229+
230+
200231
@pytest.mark.asyncio
201232
async def test_async_estimate_gas_block_identifier_unique_estimates(
202233
async_w3, async_math_contract, async_transact

tests/core/contracts/test_contract_transact_interface.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,17 @@ def test_receive_contract_with_fallback_function(receive_function_contract, call
285285
assert final_value == "receive"
286286

287287

288+
def test_contract_can_transact_without_fn_parens(w3, math_contract, call):
289+
initial_value = call(contract=math_contract, contract_function="counter")
290+
txn_hash = math_contract.functions.incrementCounter.transact()
291+
txn_receipt = w3.eth.wait_for_transaction_receipt(txn_hash)
292+
assert txn_receipt is not None
293+
294+
final_value = call(contract=math_contract, contract_function="counter")
295+
296+
assert final_value - initial_value == 1
297+
298+
288299
@pytest.mark.asyncio
289300
async def test_async_transacting_with_contract_no_arguments(
290301
async_w3, async_math_contract, async_transact, async_call
@@ -306,6 +317,25 @@ async def test_async_transacting_with_contract_no_arguments(
306317
assert final_value - initial_value == 1
307318

308319

320+
@pytest.mark.asyncio
321+
async def test_async_transacting_with_contract_no_arguments_no_parens(
322+
async_w3, async_math_contract, async_transact, async_call
323+
):
324+
initial_value = await async_call(
325+
contract=async_math_contract, contract_function="counter"
326+
)
327+
328+
txn_hash = await async_math_contract.functions.incrementCounter.transact()
329+
txn_receipt = await async_w3.eth.wait_for_transaction_receipt(txn_hash)
330+
assert txn_receipt is not None
331+
332+
final_value = await async_call(
333+
contract=async_math_contract, contract_function="counter"
334+
)
335+
336+
assert final_value - initial_value == 1
337+
338+
309339
@pytest.mark.asyncio
310340
async def test_async_transact_not_sending_ether_to_nonpayable_function(
311341
async_w3, async_payable_tester_contract, async_transact, async_call

web3/contract/async_contract.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,8 @@ async def call(
319319
state_override,
320320
ccip_read_enabled,
321321
self.decode_tuples,
322-
*self.args,
323-
**self.kwargs,
322+
*self.args or (),
323+
**self.kwargs or {},
324324
)
325325

326326
async def transact(self, transaction: Optional[TxParams] = None) -> HexBytes:
@@ -332,8 +332,8 @@ async def transact(self, transaction: Optional[TxParams] = None) -> HexBytes:
332332
setup_transaction,
333333
self.contract_abi,
334334
self.abi,
335-
*self.args,
336-
**self.kwargs,
335+
*self.args or (),
336+
**self.kwargs or {},
337337
)
338338

339339
async def estimate_gas(
@@ -352,8 +352,8 @@ async def estimate_gas(
352352
self.abi,
353353
block_identifier,
354354
state_override,
355-
*self.args,
356-
**self.kwargs,
355+
*self.args or (),
356+
**self.kwargs or {},
357357
)
358358

359359
async def build_transaction(
@@ -367,8 +367,8 @@ async def build_transaction(
367367
built_transaction,
368368
self.contract_abi,
369369
self.abi,
370-
*self.args,
371-
**self.kwargs,
370+
*self.args or (),
371+
**self.kwargs or {},
372372
)
373373

374374
@staticmethod

web3/contract/contract.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -318,21 +318,22 @@ def call(
318318
state_override,
319319
ccip_read_enabled,
320320
self.decode_tuples,
321-
*self.args,
322-
**self.kwargs,
321+
*self.args or (),
322+
**self.kwargs or {},
323323
)
324324

325325
def transact(self, transaction: Optional[TxParams] = None) -> HexBytes:
326326
setup_transaction = self._transact(transaction)
327+
327328
return transact_with_contract_function(
328329
self.address,
329330
self.w3,
330331
self.abi_element_identifier,
331332
setup_transaction,
332333
self.contract_abi,
333334
self.abi,
334-
*self.args,
335-
**self.kwargs,
335+
*self.args or (),
336+
**self.kwargs or {},
336337
)
337338

338339
def estimate_gas(
@@ -351,21 +352,22 @@ def estimate_gas(
351352
self.abi,
352353
block_identifier,
353354
state_override,
354-
*self.args,
355-
**self.kwargs,
355+
*self.args or (),
356+
**self.kwargs or {},
356357
)
357358

358359
def build_transaction(self, transaction: Optional[TxParams] = None) -> TxParams:
359360
built_transaction = self._build_transaction(transaction)
361+
360362
return build_transaction_for_function(
361363
self.address,
362364
self.w3,
363365
self.abi_element_identifier,
364366
built_transaction,
365367
self.contract_abi,
366368
self.abi,
367-
*self.args,
368-
**self.kwargs,
369+
*self.args or (),
370+
**self.kwargs or {},
369371
)
370372

371373
@staticmethod

0 commit comments

Comments
 (0)