From 204b14341ad05cba201a9b108aa8fe8485f0c244 Mon Sep 17 00:00:00 2001 From: Mike Shultz Date: Thu, 7 Mar 2024 15:28:36 -0700 Subject: [PATCH 1/3] fix(test): anvil behavior changed with --block-base-fee-per-gas (#95) --- tests/test_provider.py | 17 ++++++++++++++++- tests/test_pytest.py | 14 ++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/tests/test_provider.py b/tests/test_provider.py index cf827c1..466c887 100644 --- a/tests/test_provider.py +++ b/tests/test_provider.py @@ -289,11 +289,26 @@ def test_base_fee(connected_provider, temp_config, networks, accounts): data = {"foundry": {"base_fee": new_base_fee, "host": "http://127.0.0.1:8555"}} with temp_config(data): with networks.ethereum.local.use_provider("foundry") as provider: - assert provider.base_fee == new_base_fee + # Verify the block has the right base fee + block_one = provider.get_block("latest") + assert block_one.base_fee == new_base_fee + + # Make sure the command has the right base fee + cmd = provider.build_command() + idx = -1 + for i, part in enumerate(cmd): + if part == "--block-base-fee-per-gas": + idx = i + 1 + assert idx > -1 # option was found + assert cmd[idx] == str(new_base_fee) # option val is correct # Show can transact with this base_fee acct1.transfer(acct2, "1 eth") + # Verify the block still has the right base fee + block_two = provider.get_block("latest") + assert block_two.base_fee == new_base_fee + def test_auto_mine(connected_provider): assert connected_provider.auto_mine is True diff --git a/tests/test_pytest.py b/tests/test_pytest.py index 13650ec..4a83cd7 100644 --- a/tests/test_pytest.py +++ b/tests/test_pytest.py @@ -87,8 +87,8 @@ def run_gas_test(result, expected_report: str = EXPECTED_GAS_REPORT): @pytest.mark.fork -def test_gas_flag_in_tests(ape_pytester): - result = ape_pytester.runpytest("--gas") +def test_gas_flag_in_tests(ape_pytester, sender): + result = ape_pytester.runpytest("--gas", "--network", "ethereum:local:foundry") run_gas_test(result) # Verify can happen more than once. @@ -101,7 +101,13 @@ def test_gas_flag_exclude_method_using_cli_option(ape_pytester): expected = filter_expected_methods("fooAndBar", "myNumber") # Also ensure can filter out whole class expected = expected.replace(TOKEN_B_GAS_REPORT, "") - result = ape_pytester.runpytest("--gas", "--gas-exclude", "*:fooAndBar,*:myNumber,tokenB:*") + result = ape_pytester.runpytest( + "--gas", + "--gas-exclude", + "*:fooAndBar,*:myNumber,tokenB:*", + "--network", + "ethereum:local:foundry", + ) run_gas_test(result, expected_report=expected) @@ -112,7 +118,7 @@ def test_coverage(ape_pytester): verifying Foundry in coverage. TODO: Write + Run tests in an env with both vyper and foundry. """ - result = ape_pytester.runpytest("--coverage") + result = ape_pytester.runpytest("--coverage", "--network", "ethereum:local:foundry") result.assert_outcomes(passed=NUM_TESTS) assert any("Coverage Profile" in ln for ln in result.outlines) assert any("WARNING: No coverage data found." in ln for ln in result.outlines) From e90b82c7d6abcd4a0a0218ef940dac0931635ea5 Mon Sep 17 00:00:00 2001 From: Mike Shultz Date: Fri, 8 Mar 2024 11:31:35 -0700 Subject: [PATCH 2/3] feat: show call trace on transaction revert (#92) --- ape_foundry/provider.py | 44 +++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/ape_foundry/provider.py b/ape_foundry/provider.py index b87060a..07029f8 100644 --- a/ape_foundry/provider.py +++ b/ape_foundry/provider.py @@ -718,6 +718,27 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa if not message: return VirtualMachineError(base_err=exception, **kwargs) + def _handle_execution_reverted( + exception: Exception, revert_message: Optional[str] = None, **kwargs + ): + if revert_message in ("", "0x", None): + revert_message = TransactionError.DEFAULT_MESSAGE + + enriched = self.compiler_manager.enrich_error( + ContractLogicError(base_err=exception, revert_message=message, **kwargs) + ) + + # Show call trace if availble + if enriched.txn: + # Unlikely scenario where a transaction is on the error even though a receipt + # exists. + if isinstance(enriched.txn, TransactionAPI) and enriched.txn.receipt: + enriched.txn.receipt.show_trace() + elif isinstance(enriched.txn, ReceiptAPI): + enriched.txn.show_trace() + + return enriched + # Handle `ContactLogicError` similarly to other providers in `ape`. # by stripping off the unnecessary prefix that foundry has on reverts. foundry_prefix = ( @@ -725,24 +746,19 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa ) if message.startswith(foundry_prefix): message = message.replace(foundry_prefix, "").strip("'") - err = ContractLogicError(base_err=exception, revert_message=message, **kwargs) - return self.compiler_manager.enrich_error(err) + return _handle_execution_reverted(exception, message, **kwargs) elif "Transaction reverted without a reason string" in message: - err = ContractLogicError(base_err=exception, **kwargs) - return self.compiler_manager.enrich_error(err) + return _handle_execution_reverted(exception, **kwargs) elif message.lower() == "execution reverted": - err = ContractLogicError(TransactionError.DEFAULT_MESSAGE, base_err=exception, **kwargs) + message = TransactionError.DEFAULT_MESSAGE if isinstance(exception, Web3ContractLogicError) and ( msg := _extract_custom_error(self, **kwargs) ): - err.message = msg - - err.message = ( - TransactionError.DEFAULT_MESSAGE if err.message in ("", "0x", None) else err.message - ) - return self.compiler_manager.enrich_error(err) + if msg not in ("", "0x", None): + message = msg + return _handle_execution_reverted(exception, revert_message=message, **kwargs) elif message == "Transaction ran out of gas" or "OutOfGas" in message: return OutOfGasError(base_err=exception, **kwargs) @@ -752,14 +768,12 @@ def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMa message.replace("execution reverted: ", "").strip() or TransactionError.DEFAULT_MESSAGE ) - err = ContractLogicError(message, base_err=exception, **kwargs) - return self.compiler_manager.enrich_error(err) + return _handle_execution_reverted(exception, revert_message=message, **kwargs) elif isinstance(exception, ContractCustomError): # Is raw hex (custom exception) message = TransactionError.DEFAULT_MESSAGE if message in ("", None, "0x") else message - err = ContractLogicError(message, base_err=exception, **kwargs) - return self.compiler_manager.enrich_error(err) + return _handle_execution_reverted(exception, revert_message=message, **kwargs) return VirtualMachineError(message, **kwargs) From 5c3ca5e687ef353995b9a4c67a129aa0bee2c498 Mon Sep 17 00:00:00 2001 From: banteg <4562643+banteg@users.noreply.github.com> Date: Sat, 9 Mar 2024 00:46:44 +0400 Subject: [PATCH 3/3] feat: declare base and blast support (#96) --- ape_foundry/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ape_foundry/__init__.py b/ape_foundry/__init__.py index 529778d..b0f754d 100644 --- a/ape_foundry/__init__.py +++ b/ape_foundry/__init__.py @@ -52,6 +52,15 @@ def providers(): yield "polygon", "mainnet-fork", FoundryForkProvider yield "polygon", "mumbai-fork", FoundryForkProvider + yield "base", LOCAL_NETWORK_NAME, FoundryProvider + yield "base", "mainnet-fork", FoundryForkProvider + yield "base", "goerli-fork", FoundryForkProvider + yield "base", "sepolia-fork", FoundryForkProvider + + yield "blast", LOCAL_NETWORK_NAME, FoundryProvider + yield "blast", "mainnet-fork", FoundryForkProvider + yield "blast", "sepolia-fork", FoundryForkProvider + __all__ = [ "FoundryNetworkConfig",