Skip to content

Commit bbf676b

Browse files
authored
feat: support raise_on_revert flag for calls and transactions (#107)
approved from core, changes are really no different.
1 parent aacb85a commit bbf676b

File tree

4 files changed

+36
-13
lines changed

4 files changed

+36
-13
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ repos:
1616
name: black
1717

1818
- repo: https://github.com/pycqa/flake8
19-
rev: 7.0.0
19+
rev: 7.1.0
2020
hooks:
2121
- id: flake8
2222

2323
- repo: https://github.com/pre-commit/mirrors-mypy
24-
rev: v1.10.0
24+
rev: v1.11.0
2525
hooks:
2626
- id: mypy
2727
additional_dependencies: [types-PyYAML, types-requests, types-setuptools, pydantic]

ape_foundry/provider.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
504504
if sender:
505505
sender = self.conversion_manager.convert(txn.sender, AddressType)
506506

507+
vm_err = None
507508
if sender and sender in self.unlocked_accounts:
508509
# Allow for an unsigned transaction
509510
txn = self.prepare_transaction(txn)
@@ -526,7 +527,7 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
526527
if "nonce too low" in str(vm_err):
527528
# Add additional nonce information
528529
new_err_msg = f"Nonce '{txn.nonce}' is too low"
529-
raise VirtualMachineError(
530+
vm_err = VirtualMachineError(
530531
new_err_msg,
531532
base_err=vm_err.base_err,
532533
code=vm_err.code,
@@ -536,7 +537,9 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
536537
contract_address=vm_err.contract_address,
537538
)
538539

539-
raise vm_err from err
540+
txn_hash = txn.txn_hash
541+
if txn.raise_on_revert:
542+
raise vm_err from err
540543

541544
receipt = self.get_receipt(
542545
txn_hash.hex(),
@@ -546,6 +549,8 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
546549
else self.network.required_confirmations
547550
),
548551
)
552+
if vm_err:
553+
receipt.error = vm_err
549554

550555
if receipt.failed:
551556
txn_dict = receipt.transaction.model_dump(mode="json", by_alias=True)
@@ -563,11 +568,14 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
563568
self.web3.eth.call(txn_params)
564569
except Exception as err:
565570
vm_err = self.get_virtual_machine_error(err, txn=receipt)
566-
raise vm_err from err
571+
receipt.error = vm_err
572+
if txn.raise_on_revert:
573+
raise vm_err from err
567574

568-
# If we get here, for some reason the tx-replay did not produce
569-
# a VM error.
570-
receipt.raise_for_status()
575+
if txn.raise_on_revert:
576+
# If we get here, for some reason the tx-replay did not produce
577+
# a VM error.
578+
receipt.raise_for_status()
571579

572580
self.chain_manager.history.append(receipt)
573581
return receipt
@@ -709,15 +717,15 @@ def set_storage(self, address: AddressType, slot: int, value: HexBytes):
709717
],
710718
)
711719

712-
def _eth_call(self, arguments: list) -> HexBytes:
720+
def _eth_call(self, arguments: list, raise_on_revert: bool = True) -> HexBytes:
713721
# Overridden to handle unique Foundry pickiness.
714722
txn_dict = copy(arguments[0])
715723
if isinstance(txn_dict.get("type"), int):
716724
txn_dict["type"] = HexBytes(txn_dict["type"]).hex()
717725

718726
txn_dict.pop("chainId", None)
719727
arguments[0] = txn_dict
720-
return super()._eth_call(arguments)
728+
return super()._eth_call(arguments, raise_on_revert=raise_on_revert)
721729

722730

723731
class FoundryForkProvider(FoundryProvider):

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
],
1515
"lint": [
1616
"black>=24.4.2,<25", # Auto-formatter and linter
17-
"mypy>=1.10.0,<2", # Static type analyzer
17+
"mypy>=1.11.0,<2", # Static type analyzer
1818
"types-setuptools", # Needed for mypy type shed
1919
"types-requests", # Needed for mypy type shed
2020
"types-PyYAML", # Needed for mypy type shed
21-
"flake8>=7.0.0,<8", # Style linter
21+
"flake8>=7.1.0,<8", # Style linter
2222
"flake8-breakpoint>=1.1.0,<2", # Detect breakpoints left in code
2323
"flake8-print>=5.0.0,<6", # Detect print statements left in code
2424
"isort>=5.13.2,<6", # Import sorting linter
@@ -72,7 +72,7 @@
7272
url="https://github.com/ApeWorX/ape-foundry",
7373
include_package_data=True,
7474
install_requires=[
75-
"eth-ape>=0.8.9,<0.9",
75+
"eth-ape>=0.8.10,<0.9",
7676
"ethpm-types", # Use same version as eth-ape
7777
"eth-pydantic-types", # Use same version as eth-ape
7878
"evm-trace", # Use same version as ape

tests/test_provider.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,21 @@ def test_revert_error_using_impersonated_account(error_contract, accounts, conne
268268
with pytest.raises(error_contract.Unauthorized):
269269
error_contract.withdraw(sender=acct)
270270

271+
# Show we can "allow" reverts using impersonated accounts.
272+
# NOTE: This is extra because of the lack of tx-hash available.
273+
receipt = error_contract.withdraw(sender=acct, raise_on_revert=False)
274+
assert receipt.failed
275+
276+
277+
def test_revert_allow(error_contract, not_owner, contract_instance):
278+
# 'sender' is not the owner so it will revert (with a message)
279+
receipt = error_contract.withdraw(sender=not_owner, raise_on_revert=False)
280+
assert receipt.error is not None
281+
assert isinstance(receipt.error, error_contract.Unauthorized)
282+
283+
# Ensure this also works for calls.
284+
contract_instance.setNumber.call(5, raise_on_revert=False)
285+
271286

272287
@pytest.mark.parametrize("host", ("https://example.com", "example.com"))
273288
def test_host(project, local_network, host):

0 commit comments

Comments
 (0)