diff --git a/pyproject.toml b/pyproject.toml index a8e8f1e835..cbd46d5481 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ classifiers = [ dependencies = [ "dill", "docker", + "eth-abi", "eth-typing", "eth-account", "fixedpointmath", diff --git a/scripts/invariant_checks.py b/scripts/invariant_checks.py index e0dd23eaea..efa6a9b920 100644 --- a/scripts/invariant_checks.py +++ b/scripts/invariant_checks.py @@ -22,6 +22,7 @@ from agent0 import Chain, Hyperdrive from agent0.ethpy.hyperdrive import get_hyperdrive_registry_from_artifacts +from agent0.hyperfuzz import FuzzAssertionException from agent0.hyperfuzz.system_fuzz.invariant_checks import run_invariant_checks from agent0.hyperlogs.rollbar_utilities import initialize_rollbar, log_rollbar_exception, log_rollbar_message from agent0.utils import async_runner @@ -29,6 +30,22 @@ LOOKBACK_BLOCK_LIMIT = 1000 +def _sepolia_ignore_errors(exc: Exception) -> bool: + # Ignored fuzz exceptions + if isinstance(exc, FuzzAssertionException): + # LP rate invariance check + if ( + # Only ignore steth pools + "STETH" in exc.exception_data["pool_name"] + and len(exc.args) >= 2 + and exc.args[0] == "Continuous Fuzz Bots Invariant Checks" + and "actual_vault_shares=" in exc.args[1] + and "is expected to be greater than expected_vault_shares=" in exc.args[1] + ): + return True + return False + + def main(argv: Sequence[str] | None = None) -> None: """Check Hyperdrive invariants each block. @@ -41,6 +58,11 @@ def main(argv: Sequence[str] | None = None) -> None: parsed_args = parse_arguments(argv) + if parsed_args.sepolia: + invariance_ignore_func = _sepolia_ignore_errors + else: + invariance_ignore_func = None + if parsed_args.infra: # TODO Abstract this method out for infra scripts # Get the rpc uri from env variable @@ -113,6 +135,7 @@ def main(argv: Sequence[str] | None = None) -> None: simulation_mode=False, log_to_rollbar=log_to_rollbar, rollbar_log_level_threshold=chain.config.rollbar_log_level_threshold, + rollbar_log_filter_func=invariance_ignore_func, pool_name=hyperdrive_obj.name, ) for hyperdrive_obj in deployed_pools @@ -133,6 +156,7 @@ class Args(NamedTuple): infra: bool registry_addr: str rpc_uri: str + sepolia: bool def namespace_to_args(namespace: argparse.Namespace) -> Args: @@ -153,6 +177,7 @@ def namespace_to_args(namespace: argparse.Namespace) -> Args: infra=namespace.infra, registry_addr=namespace.registry_addr, rpc_uri=namespace.rpc_uri, + sepolia=namespace.sepolia, ) @@ -199,6 +224,13 @@ def parse_arguments(argv: Sequence[str] | None = None) -> Args: help="The RPC URI of the chain.", ) + parser.add_argument( + "--sepolia", + default=False, + action="store_true", + help="Running on Sepolia Testnet. If True, will ignore some known errors.", + ) + # Use system arguments if none were passed if argv is None: argv = sys.argv diff --git a/src/agent0/ethpy/hyperdrive/interface/read_interface.py b/src/agent0/ethpy/hyperdrive/interface/read_interface.py index fdc967a3e9..b9dec5f89a 100644 --- a/src/agent0/ethpy/hyperdrive/interface/read_interface.py +++ b/src/agent0/ethpy/hyperdrive/interface/read_interface.py @@ -8,6 +8,7 @@ from enum import Enum from typing import TYPE_CHECKING, Any, cast +import eth_abi from fixedpointmath import FixedPoint from web3 import Web3 from web3.exceptions import BadFunctionCallOutput, ContractLogicError @@ -96,12 +97,6 @@ pathlib.Path(__file__).parent.parent.parent.parent / "packages" / "external" / "IMorpho.sol" / "IMorpho.json" ).resolve() -# TODO morpho hyperdrive doesn't expose this -# MORPHO_LOAN_TOKEN_ADDR = "0x6B175474E89094C44Da98b954EedeAC495271d0F" -# TODO generate this id from the variables exposed by morpho hyperdrive contract -MORPHO_MARKET_PARAMS_ID = "0x39d11026eae1c6ec02aa4c0910778664089cdd97c3fd23f68f7cd05e2e95af48" - - # We expect to have many instance attributes & public methods since this is a large API. # pylint: disable=too-many-lines # pylint: disable=too-many-instance-attributes @@ -152,6 +147,7 @@ def __init__( txn_signature: bytes | None, optional The signature for transactions. Defaults to `0xa0`. """ + # pylint: disable=too-many-locals if txn_signature is None: self.txn_signature = AGENT0_SIGNATURE else: @@ -231,19 +227,22 @@ def __init__( self.morpho_contract = web3.eth.contract( address=web3.to_checksum_address(morpho_contract_addr), abi=morpho_blue_abi["abi"] ) - # TODO ideally we would build the id, but something below is incorrect. - # We hard code for now. - # self.morpho_market_id = web3.solidity_keccak( - # abi_types=["address", "address", "address", "address", "uint256"], - # values=[ - # MORPHO_LOAN_TOKEN_ADDR, - # morpho_hyperdrive_contract.functions.collateralToken().call(), - # morpho_hyperdrive_contract.functions.oracle().call(), - # morpho_hyperdrive_contract.functions.irm().call(), - # morpho_hyperdrive_contract.functions.lltv().call(), - # ], - # ) - self.morpho_market_id = MORPHO_MARKET_PARAMS_ID + + values = ( + base_token_contract_address, + morpho_hyperdrive_contract.functions.collateralToken().call(), + morpho_hyperdrive_contract.functions.oracle().call(), + morpho_hyperdrive_contract.functions.irm().call(), + morpho_hyperdrive_contract.functions.lltv().call(), + ) + + # Typing is reporting `encode` is not exposed in `eth_abi` + encoded_market_id = eth_abi.encode( # type: ignore + ("address", "address", "address", "address", "uint256"), + values, + ) + + self.morpho_market_id = web3.keccak(encoded_market_id) else: # We default to erc4626, but print a warning if we don't recognize the kind diff --git a/src/agent0/hyperfuzz/system_fuzz/invariant_checks.py b/src/agent0/hyperfuzz/system_fuzz/invariant_checks.py index 1c06e20055..e7b05e8f70 100644 --- a/src/agent0/hyperfuzz/system_fuzz/invariant_checks.py +++ b/src/agent0/hyperfuzz/system_fuzz/invariant_checks.py @@ -79,6 +79,8 @@ def run_invariant_checks( # pylint: disable=too-many-locals # pylint: disable=too-many-arguments + logging.info("Running invariant checks on pool %s", pool_name) + if rollbar_log_level_threshold is None: rollbar_log_level_threshold = logging.DEBUG @@ -146,6 +148,7 @@ def run_invariant_checks( exception_data = exception_data_template.copy() exception_data.update(data) exception_data["block_number"] = pool_state.block_number + exception_data["pool_name"] = pool_name # Log exception to rollbar assert log_level is not None