Skip to content

Commit 356c425

Browse files
committed
Add tests for set code transactions
- Add to the required formatter changes for eth-tester support - Add integration test for set code tx - Add set code core test to test `send_transaction` via eth-tester
1 parent 9c42d85 commit 356c425

File tree

3 files changed

+310
-1
lines changed

3 files changed

+310
-1
lines changed

tests/core/eth-module/test_transactions.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,25 @@
22
import collections
33
import itertools
44

5+
from eth_account import (
6+
Account,
7+
)
58
from eth_utils import (
69
to_checksum_address,
710
to_int,
811
)
912
from hexbytes import (
1013
HexBytes,
1114
)
15+
import pytest_asyncio
1216

17+
from tests.core.contracts.utils import (
18+
async_deploy,
19+
deploy,
20+
)
21+
from web3._utils.contract_sources.contract_data.math_contract import (
22+
MATH_CONTRACT_DATA,
23+
)
1324
from web3._utils.ens import (
1425
ens_addresses,
1526
)
@@ -29,6 +40,12 @@
2940
RECEIPT_TIMEOUT = 0.2
3041

3142

43+
@pytest.fixture
44+
def math_contract(w3):
45+
factory = w3.eth.contract(**MATH_CONTRACT_DATA)
46+
return deploy(w3, factory)
47+
48+
3249
def _tx_indexing_response_iterator():
3350
while True:
3451
yield {"error": {"message": "transaction indexing in progress"}}
@@ -371,9 +388,86 @@ def test_eth_send_raw_blob_transaction(w3):
371388
)
372389

373390

391+
def test_send_set_code_transaction(w3, math_contract):
392+
pkey = w3.provider.ethereum_tester.backend.account_keys[0]
393+
acct = Account.from_key(pkey)
394+
395+
nonce = w3.eth.get_transaction_count(acct.address)
396+
chain_id = w3.eth.chain_id
397+
398+
auth = {
399+
"chainId": chain_id,
400+
"address": math_contract.address,
401+
"nonce": nonce + 1,
402+
}
403+
signed_auth = acct.sign_authorization(auth)
404+
405+
# get current math counter and increase it only in the delegation by n
406+
math_counter = math_contract.functions.counter().call()
407+
built_tx = math_contract.functions.incrementCounter(
408+
math_counter + 1337
409+
).build_transaction({})
410+
txn = {
411+
"chainId": chain_id,
412+
"to": acct.address,
413+
"value": 0,
414+
"gas": 200_000,
415+
"nonce": nonce,
416+
"maxPriorityFeePerGas": 10**9,
417+
"maxFeePerGas": 10**9,
418+
"data": built_tx["data"],
419+
"authorizationList": [signed_auth],
420+
}
421+
422+
tx_hash = w3.eth.send_transaction(txn)
423+
get_tx = w3.eth.get_transaction(tx_hash)
424+
w3.eth.wait_for_transaction_receipt(tx_hash, timeout=10)
425+
426+
code = w3.eth.get_code(acct.address)
427+
428+
assert code.to_0x_hex().lower() == f"0xef0100{math_contract.address[2:].lower()}"
429+
delegated = w3.eth.contract(address=acct.address, abi=math_contract.abi)
430+
# assert the math counter is increased by 1337 only in delegated acct
431+
assert math_contract.functions.counter().call() == math_counter
432+
delegated_call = delegated.functions.counter().call()
433+
assert delegated_call == math_counter + 1337
434+
435+
assert len(get_tx["authorizationList"]) == 1
436+
get_auth = get_tx["authorizationList"][0]
437+
assert get_auth["chainId"] == chain_id
438+
assert get_auth["address"].lower() == math_contract.address.lower()
439+
assert get_auth["nonce"] == nonce + 1
440+
assert isinstance(get_auth["yParity"], int)
441+
assert isinstance(get_auth["r"], HexBytes)
442+
assert isinstance(get_auth["s"], HexBytes)
443+
444+
# reset code
445+
reset_auth = {
446+
"chainId": chain_id,
447+
"address": "0x" + ("00" * 20),
448+
"nonce": nonce + 3,
449+
}
450+
signed_reset_auth = acct.sign_authorization(reset_auth)
451+
new_txn = dict(txn)
452+
new_txn["authorizationList"] = [signed_reset_auth]
453+
new_txn["nonce"] = nonce + 2
454+
455+
reset_tx_hash = w3.eth.send_transaction(new_txn)
456+
w3.eth.wait_for_transaction_receipt(reset_tx_hash, timeout=10)
457+
458+
reset_code = w3.eth.get_code(acct.address)
459+
assert reset_code == HexBytes("0x")
460+
461+
374462
# --- async --- #
375463

376464

465+
@pytest_asyncio.fixture
466+
async def async_math_contract(async_w3):
467+
factory = async_w3.eth.contract(**MATH_CONTRACT_DATA)
468+
return await async_deploy(async_w3, factory)
469+
470+
377471
@pytest.mark.asyncio
378472
async def test_async_wait_for_transaction_receipt_transaction_indexing_in_progress(
379473
async_w3, request_mocker
@@ -420,3 +514,77 @@ async def test_async_send_raw_blob_transaction(async_w3):
420514
assert transaction["blobVersionedHashes"][0] == HexBytes(
421515
"0x0127c38bcad458d932e828b580b9ad97310be01407dfa0ed88118735980a3e9a"
422516
)
517+
518+
519+
@pytest.mark.asyncio
520+
async def test_async_send_set_code_transaction(async_w3, async_math_contract):
521+
pkey = async_w3.provider.ethereum_tester.backend.account_keys[0]
522+
acct = Account.from_key(pkey)
523+
524+
nonce = await async_w3.eth.get_transaction_count(acct.address)
525+
chain_id = await async_w3.eth.chain_id
526+
527+
auth = {
528+
"chainId": chain_id,
529+
"address": async_math_contract.address,
530+
"nonce": nonce + 1,
531+
}
532+
signed_auth = acct.sign_authorization(auth)
533+
534+
# get current math counter and increase it only in the delegation by n
535+
math_counter = await async_math_contract.functions.counter().call()
536+
built_tx = await async_math_contract.functions.incrementCounter(
537+
math_counter + 1337
538+
).build_transaction({})
539+
txn = {
540+
"chainId": chain_id,
541+
"to": acct.address,
542+
"value": 0,
543+
"gas": 200_000,
544+
"nonce": nonce,
545+
"maxPriorityFeePerGas": 10**9,
546+
"maxFeePerGas": 10**9,
547+
"data": built_tx["data"],
548+
"authorizationList": [signed_auth],
549+
}
550+
551+
tx_hash = await async_w3.eth.send_transaction(txn)
552+
get_tx = await async_w3.eth.get_transaction(tx_hash)
553+
await async_w3.eth.wait_for_transaction_receipt(tx_hash, timeout=10)
554+
555+
code = await async_w3.eth.get_code(acct.address)
556+
557+
assert (
558+
code.to_0x_hex().lower() == f"0xef0100{async_math_contract.address[2:].lower()}"
559+
)
560+
delegated = async_w3.eth.contract(address=acct.address, abi=async_math_contract.abi)
561+
# assert the math counter is increased by 1337 only in delegated acct
562+
assert await async_math_contract.functions.counter().call() == math_counter
563+
delegated_call = await delegated.functions.counter().call()
564+
assert delegated_call == math_counter + 1337
565+
566+
assert len(get_tx["authorizationList"]) == 1
567+
get_auth = get_tx["authorizationList"][0]
568+
assert get_auth["chainId"] == chain_id
569+
assert get_auth["address"].lower() == async_math_contract.address.lower()
570+
assert get_auth["nonce"] == nonce + 1
571+
assert isinstance(get_auth["yParity"], int)
572+
assert isinstance(get_auth["r"], HexBytes)
573+
assert isinstance(get_auth["s"], HexBytes)
574+
575+
# reset code
576+
reset_auth = {
577+
"chainId": chain_id,
578+
"address": "0x" + ("00" * 20),
579+
"nonce": nonce + 3,
580+
}
581+
signed_reset_auth = acct.sign_authorization(reset_auth)
582+
new_txn = dict(txn)
583+
new_txn["authorizationList"] = [signed_reset_auth]
584+
new_txn["nonce"] = nonce + 2
585+
586+
reset_tx_hash = await async_w3.eth.send_transaction(new_txn)
587+
await async_w3.eth.wait_for_transaction_receipt(reset_tx_hash, timeout=10)
588+
589+
reset_code = await async_w3.eth.get_code(acct.address)
590+
assert reset_code == HexBytes("0x")

web3/_utils/module_testing/eth_module.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,74 @@ async def test_async_sign_and_send_raw_middleware(
720720
# clean up
721721
async_w3.middleware_onion.remove("signing")
722722

723+
@pytest.mark.asyncio
724+
async def test_async_sign_authorization_and_send_raw_set_code_transaction(
725+
self,
726+
async_w3: "AsyncWeb3",
727+
keyfile_account_pkey: HexStr,
728+
async_math_contract: "AsyncContract",
729+
) -> None:
730+
keyfile_account = async_w3.eth.account.from_key(keyfile_account_pkey)
731+
732+
chain_id = await async_w3.eth.chain_id
733+
nonce = await async_w3.eth.get_transaction_count(keyfile_account.address)
734+
735+
auth = {
736+
"chainId": chain_id,
737+
"address": async_math_contract.address,
738+
"nonce": nonce + 1,
739+
}
740+
signed_auth = keyfile_account.sign_authorization(auth)
741+
742+
txn: TxParams = {
743+
"chainId": chain_id,
744+
"to": keyfile_account.address,
745+
"value": Wei(0),
746+
"gas": 200_000,
747+
"nonce": nonce,
748+
"maxPriorityFeePerGas": Wei(10**9),
749+
"maxFeePerGas": Wei(10**9),
750+
"data": HexBytes("0x"),
751+
"authorizationList": [signed_auth],
752+
}
753+
754+
signed = keyfile_account.sign_transaction(txn)
755+
tx_hash = await async_w3.eth.send_raw_transaction(signed.raw_transaction)
756+
get_tx = await async_w3.eth.get_transaction(tx_hash)
757+
await async_w3.eth.wait_for_transaction_receipt(tx_hash, timeout=10)
758+
759+
code = await async_w3.eth.get_code(keyfile_account.address)
760+
assert code.to_0x_hex() == f"0xef0100{async_math_contract.address[2:].lower()}"
761+
762+
assert len(get_tx["authorizationList"]) == 1
763+
get_auth = get_tx["authorizationList"][0]
764+
assert get_auth["chainId"] == chain_id
765+
assert get_auth["address"] == async_math_contract.address
766+
assert get_auth["nonce"] == nonce + 1
767+
assert isinstance(get_auth["yParity"], int)
768+
assert isinstance(get_auth["r"], HexBytes)
769+
assert isinstance(get_auth["s"], HexBytes)
770+
771+
# reset code
772+
reset_auth = {
773+
"chainId": chain_id,
774+
"address": "0x" + ("00" * 20),
775+
"nonce": nonce + 3,
776+
}
777+
signed_reset_auth = keyfile_account.sign_authorization(reset_auth)
778+
new_txn = dict(txn)
779+
new_txn["authorizationList"] = [signed_reset_auth]
780+
new_txn["nonce"] = nonce + 2
781+
782+
signed_reset = keyfile_account.sign_transaction(new_txn)
783+
reset_tx_hash = await async_w3.eth.send_raw_transaction(
784+
signed_reset.raw_transaction
785+
)
786+
await async_w3.eth.wait_for_transaction_receipt(reset_tx_hash, timeout=10)
787+
788+
reset_code = await async_w3.eth.get_code(keyfile_account.address)
789+
assert reset_code == HexBytes("0x")
790+
723791
@pytest.mark.asyncio
724792
async def test_GasPriceStrategyMiddleware(
725793
self,
@@ -3778,6 +3846,68 @@ def test_sign_and_send_raw_middleware(
37783846
# cleanup
37793847
w3.middleware_onion.remove("signing")
37803848

3849+
def test_sign_authorization_and_send_raw_set_code_transaction(
3850+
self, w3: "Web3", keyfile_account_pkey: HexStr, math_contract: "Contract"
3851+
) -> None:
3852+
keyfile_account = w3.eth.account.from_key(keyfile_account_pkey)
3853+
3854+
chain_id = w3.eth.chain_id
3855+
nonce = w3.eth.get_transaction_count(keyfile_account.address)
3856+
3857+
auth = {
3858+
"chainId": chain_id,
3859+
"address": math_contract.address,
3860+
"nonce": nonce + 1,
3861+
}
3862+
signed_auth = keyfile_account.sign_authorization(auth)
3863+
3864+
txn: TxParams = {
3865+
"chainId": chain_id,
3866+
"to": keyfile_account.address,
3867+
"value": Wei(0),
3868+
"gas": 200_000,
3869+
"nonce": nonce,
3870+
"maxPriorityFeePerGas": Wei(10**9),
3871+
"maxFeePerGas": Wei(10**9),
3872+
"data": HexBytes("0x"),
3873+
"authorizationList": [signed_auth],
3874+
}
3875+
3876+
signed = keyfile_account.sign_transaction(txn)
3877+
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
3878+
get_tx = w3.eth.get_transaction(tx_hash)
3879+
w3.eth.wait_for_transaction_receipt(tx_hash, timeout=10)
3880+
3881+
code = w3.eth.get_code(keyfile_account.address)
3882+
assert code.to_0x_hex() == f"0xef0100{math_contract.address[2:].lower()}"
3883+
3884+
assert len(get_tx["authorizationList"]) == 1
3885+
get_auth = get_tx["authorizationList"][0]
3886+
assert get_auth["chainId"] == chain_id
3887+
assert get_auth["address"] == math_contract.address
3888+
assert get_auth["nonce"] == nonce + 1
3889+
assert isinstance(get_auth["yParity"], int)
3890+
assert isinstance(get_auth["r"], HexBytes)
3891+
assert isinstance(get_auth["s"], HexBytes)
3892+
3893+
# reset code
3894+
reset_auth = {
3895+
"chainId": chain_id,
3896+
"address": "0x" + ("00" * 20),
3897+
"nonce": nonce + 3,
3898+
}
3899+
signed_reset_auth = keyfile_account.sign_authorization(reset_auth)
3900+
new_txn = dict(txn)
3901+
new_txn["authorizationList"] = [signed_reset_auth]
3902+
new_txn["nonce"] = nonce + 2
3903+
3904+
signed_reset = keyfile_account.sign_transaction(new_txn)
3905+
reset_tx_hash = w3.eth.send_raw_transaction(signed_reset.raw_transaction)
3906+
w3.eth.wait_for_transaction_receipt(reset_tx_hash, timeout=10)
3907+
3908+
reset_code = w3.eth.get_code(keyfile_account.address)
3909+
assert reset_code == HexBytes("0x")
3910+
37813911
def test_eth_call(self, w3: "Web3", math_contract: "Contract") -> None:
37823912
txn_params = math_contract._prepare_transaction(
37833913
abi_element_identifier="add",

web3/providers/eth_tester/middleware.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,18 @@ def is_hexstr(value: Any) -> bool:
9696
apply_key_map({"storageKeys": "storage_keys"})
9797
),
9898
"authorizationList": apply_list_to_array_formatter(
99-
apply_key_map({"chainId": "chain_id", "yParity": "y_parity"})
99+
compose(
100+
apply_formatters_to_dict(
101+
{
102+
"chain_id": to_integer_if_hex,
103+
"nonce": to_integer_if_hex,
104+
"y_parity": to_integer_if_hex,
105+
"r": to_integer_if_hex,
106+
"s": to_integer_if_hex,
107+
},
108+
),
109+
apply_key_map({"chainId": "chain_id", "yParity": "y_parity"}),
110+
)
100111
),
101112
}
102113
transaction_request_formatter = apply_formatters_to_dict(TRANSACTION_REQUEST_FORMATTERS)

0 commit comments

Comments
 (0)