Skip to content

Commit

Permalink
feat: support raise_on_revert flag for calls and transactions (#107)
Browse files Browse the repository at this point in the history
approved from core, changes are really no different.
  • Loading branch information
antazoey authored Jul 26, 2024
1 parent aacb85a commit bbf676b
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 13 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ repos:
name: black

- repo: https://github.com/pycqa/flake8
rev: 7.0.0
rev: 7.1.0
hooks:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
rev: v1.11.0
hooks:
- id: mypy
additional_dependencies: [types-PyYAML, types-requests, types-setuptools, pydantic]
Expand Down
24 changes: 16 additions & 8 deletions ape_foundry/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
if sender:
sender = self.conversion_manager.convert(txn.sender, AddressType)

vm_err = None
if sender and sender in self.unlocked_accounts:
# Allow for an unsigned transaction
txn = self.prepare_transaction(txn)
Expand All @@ -526,7 +527,7 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
if "nonce too low" in str(vm_err):
# Add additional nonce information
new_err_msg = f"Nonce '{txn.nonce}' is too low"
raise VirtualMachineError(
vm_err = VirtualMachineError(
new_err_msg,
base_err=vm_err.base_err,
code=vm_err.code,
Expand All @@ -536,7 +537,9 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
contract_address=vm_err.contract_address,
)

raise vm_err from err
txn_hash = txn.txn_hash
if txn.raise_on_revert:
raise vm_err from err

receipt = self.get_receipt(
txn_hash.hex(),
Expand All @@ -546,6 +549,8 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
else self.network.required_confirmations
),
)
if vm_err:
receipt.error = vm_err

if receipt.failed:
txn_dict = receipt.transaction.model_dump(mode="json", by_alias=True)
Expand All @@ -563,11 +568,14 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
self.web3.eth.call(txn_params)
except Exception as err:
vm_err = self.get_virtual_machine_error(err, txn=receipt)
raise vm_err from err
receipt.error = vm_err
if txn.raise_on_revert:
raise vm_err from err

# If we get here, for some reason the tx-replay did not produce
# a VM error.
receipt.raise_for_status()
if txn.raise_on_revert:
# If we get here, for some reason the tx-replay did not produce
# a VM error.
receipt.raise_for_status()

self.chain_manager.history.append(receipt)
return receipt
Expand Down Expand Up @@ -709,15 +717,15 @@ def set_storage(self, address: AddressType, slot: int, value: HexBytes):
],
)

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

txn_dict.pop("chainId", None)
arguments[0] = txn_dict
return super()._eth_call(arguments)
return super()._eth_call(arguments, raise_on_revert=raise_on_revert)


class FoundryForkProvider(FoundryProvider):
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
],
"lint": [
"black>=24.4.2,<25", # Auto-formatter and linter
"mypy>=1.10.0,<2", # Static type analyzer
"mypy>=1.11.0,<2", # Static type analyzer
"types-setuptools", # Needed for mypy type shed
"types-requests", # Needed for mypy type shed
"types-PyYAML", # Needed for mypy type shed
"flake8>=7.0.0,<8", # Style linter
"flake8>=7.1.0,<8", # Style linter
"flake8-breakpoint>=1.1.0,<2", # Detect breakpoints left in code
"flake8-print>=5.0.0,<6", # Detect print statements left in code
"isort>=5.13.2,<6", # Import sorting linter
Expand Down Expand Up @@ -72,7 +72,7 @@
url="https://github.com/ApeWorX/ape-foundry",
include_package_data=True,
install_requires=[
"eth-ape>=0.8.9,<0.9",
"eth-ape>=0.8.10,<0.9",
"ethpm-types", # Use same version as eth-ape
"eth-pydantic-types", # Use same version as eth-ape
"evm-trace", # Use same version as ape
Expand Down
15 changes: 15 additions & 0 deletions tests/test_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,21 @@ def test_revert_error_using_impersonated_account(error_contract, accounts, conne
with pytest.raises(error_contract.Unauthorized):
error_contract.withdraw(sender=acct)

# Show we can "allow" reverts using impersonated accounts.
# NOTE: This is extra because of the lack of tx-hash available.
receipt = error_contract.withdraw(sender=acct, raise_on_revert=False)
assert receipt.failed


def test_revert_allow(error_contract, not_owner, contract_instance):
# 'sender' is not the owner so it will revert (with a message)
receipt = error_contract.withdraw(sender=not_owner, raise_on_revert=False)
assert receipt.error is not None
assert isinstance(receipt.error, error_contract.Unauthorized)

# Ensure this also works for calls.
contract_instance.setNumber.call(5, raise_on_revert=False)


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

0 comments on commit bbf676b

Please sign in to comment.