Skip to content

Commit

Permalink
feat: support raise_on_revert=False for sending calls and transacti…
Browse files Browse the repository at this point in the history
…ons (#177)

changes same as core and need his in for emergency fix
  • Loading branch information
antazoey committed Jul 26, 2024
1 parent 47d8f73 commit afe1a92
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 36 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
63 changes: 32 additions & 31 deletions ape_hardhat/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
ContractLogicError,
OutOfGasError,
RPCTimeoutError,
SignatureError,
SubprocessError,
TransactionError,
VirtualMachineError,
Expand All @@ -28,6 +29,7 @@
from ape.utils import DEFAULT_TEST_HD_PATH, cached_property
from ape_ethereum.provider import Web3Provider
from ape_ethereum.trace import TraceApproach, TransactionTrace
from ape_ethereum.transactions import TransactionStatusEnum
from ape_test import ApeTestConfig
from chompjs import parse_js_object # type: ignore
from eth_pydantic_types import HexBytes
Expand Down Expand Up @@ -761,7 +763,6 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
Creates a new message call transaction or a contract creation
for signed transactions.
"""

sender = txn.sender
if sender:
sender = self.conversion_manager.convert(txn.sender, AddressType)
Expand All @@ -775,6 +776,7 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
txn_dict["type"] = HexBytes(txn_dict["type"]).hex()

txn_params = cast(TxParams, txn_dict)
vm_err = None
try:
txn_hash = self.web3.eth.send_transaction(txn_params)
except ValueError as err:
Expand All @@ -795,43 +797,42 @@ def send_transaction(self, txn: TransactionAPI) -> ReceiptAPI:
else:
tx = txn

raise self.get_virtual_machine_error(err, txn=tx) from err
vm_err = self.get_virtual_machine_error(err, txn=tx)
if txn.raise_on_revert:
raise vm_err from err

receipt = self.get_receipt(
txn_hash.hex(), required_confirmations=txn.required_confirmations or 0, txn=txn_dict
)
receipt.raise_for_status()
try:
txn_hash = txn.txn_hash
except SignatureError:
txn_hash = None

required_confirmations = txn.required_confirmations or 0
if txn_hash is not None:
receipt = self.get_receipt(
txn_hash.hex(), required_confirmations=required_confirmations, txn=txn_dict
)
if vm_err:
receipt.error = vm_err
if txn.raise_on_revert:
receipt.raise_for_status()

else:
# If we get here, likely was a failed (but allowed-fail)
# impersonated-sender receipt.
receipt = self._create_receipt(
block_number=-1, # Not in a block.
error=vm_err,
required_confirmations=required_confirmations,
status=TransactionStatusEnum.FAILING,
txn_hash="", # No hash exists, likely from impersonated sender.
**txn_dict,
)

else:
receipt = super().send_transaction(txn)

return receipt

# def get_receipt(
# self,
# txn_hash: str,
# required_confirmations: int = 0,
# timeout: Optional[int] = None,
# **kwargs,
# ) -> ReceiptAPI:
# try:
# # Try once without waiting first.
# # NOTE: This is required for txn sent with an impersonated account.
# receipt_data = dict(self.web3.eth.get_transaction_receipt(HexStr(txn_hash)))
# except Exception:
# return super().get_receipt(
# txn_hash, required_confirmations=required_confirmations, timeout=timeout
# )
#
# txn = kwargs.get("txn", dict(self.web3.eth.get_transaction(HexStr(txn_hash))))
# data: dict = {"txn_hash": txn_hash, **receipt_data, **txn}
# if "gas_price" not in data:
# data["gas_price"] = self.gas_price
#
# receipt = self.network.ecosystem.decode_receipt(data)
# self.chain_manager.history.append(receipt)
# return receipt

def get_transaction_trace(self, transaction_hash: str, **kwargs) -> TraceAPI:
if "debug_trace_transaction_parameters" not in kwargs:
kwargs["debug_trace_transaction_parameters"] = {}
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,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 @@ -76,7 +76,7 @@
url="https://github.com/ApeWorX/ape-hardhat",
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
"evm-trace", # Use same version as eth-ape
"web3", # Use same version as eth-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 @@ -221,6 +221,21 @@ def test_revert_error_from_impersonated_account(error_contract, accounts):
# because the account is impersonated.
assert err.value.txn.txn_hash.startswith("0x")

# 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=account, 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, networks, host):
Expand Down

0 comments on commit afe1a92

Please sign in to comment.