diff --git a/account.env.sample b/account.env.sample deleted file mode 100644 index b653fd3c6a..0000000000 --- a/account.env.sample +++ /dev/null @@ -1,4 +0,0 @@ -export USER_KEY="0xUSER_PRIVATE_KEY" -export AGENT_KEYS='["0xAGENT_PRIVATE_KEY"]' -export AGENT_BASE_BUDGETS='[3396163194603698651136]' -export AGENT_ETH_BUDGETS='[1000000000000000000]' diff --git a/eth.env.sample b/eth.env.sample deleted file mode 100644 index 22d21aaaa5..0000000000 --- a/eth.env.sample +++ /dev/null @@ -1,6 +0,0 @@ -# Local connection -RPC_URI="http://localhost:8545" -ARTIFACTS_URI="http://localhost:8080" -DATABASE_API_URI="http://localhost:5002" -ABI_DIR="./packages/hyperdrive/src/abis" -PREVIEW_BEFORE_TRADE="false" \ No newline at end of file diff --git a/scripts/checkpoint_bot.py b/scripts/checkpoint_bot.py deleted file mode 100644 index 6c37a1e98a..0000000000 --- a/scripts/checkpoint_bot.py +++ /dev/null @@ -1,325 +0,0 @@ -"""A checkpoint bot for Hyperdrive""" - -from __future__ import annotations - -import argparse -import datetime -import logging -import os -import sys -import time -from typing import NamedTuple, Sequence - -from eth_account.account import Account -from fixedpointmath import FixedPoint - -from agent0.core.base import PolicyAgent -from agent0.core.base.config import EnvironmentConfig -from agent0.ethpy import EthConfig, build_eth_config -from agent0.ethpy.base import initialize_web3_with_http_provider, set_anvil_account_balance, smart_contract_transact -from agent0.ethpy.hyperdrive import get_hyperdrive_addresses_from_artifacts, get_hyperdrive_pool_config -from agent0.hyperlogs import setup_logging -from agent0.hypertypes import IHyperdriveContract - -# Checkpoint bot has a lot going on -# pylint: disable=too-many-locals -# pylint: disable=too-many-statements - -# The portion of the checkpoint that the bot will wait before attempting to -# mint a new checkpoint. -CHECKPOINT_WAITING_PERIOD = 0.5 - - -def does_checkpoint_exist(hyperdrive_contract: IHyperdriveContract, checkpoint_time: int) -> bool: - """Checks whether or not a given checkpoint exists. - - Arguments - --------- - hyperdrive_contract: IHyperdriveContract - The hyperdrive contract. - checkpoint_time: int - The checkpoint time in epoch seconds. - - Returns - ------- - bool - Whether or not the checkpoint exists. - """ - checkpoint = hyperdrive_contract.functions.getCheckpoint(checkpoint_time).call() - logging.info("%s", checkpoint) - return checkpoint.vaultSharePrice > 0 - - -def get_config() -> tuple[EthConfig, EnvironmentConfig]: - """Gets the hyperdrive configuration. - - Returns - ------- - tuple[EthConfig, EnvironmentConfig] - The output EthConfig and EnvironmentConfig objects. - """ - # Get the configuration and initialize the web3 provider. - eth_config = build_eth_config() - - # The configuration for the checkpoint bot halts on errors and logs to stdout. - env_config = EnvironmentConfig( - # Errors - halt_on_errors=True, - # Logging - log_stdout=True, - log_level=logging.INFO, - ) - return (eth_config, env_config) - - -def main(argv: Sequence[str] | None = None) -> None: - """Runs the checkpoint bot. - - Arguments - --------- - argv: Sequence[str] - The argv values returned from argparser. - """ - # pylint: disable=too-many-branches - - parsed_args = parse_arguments(argv) - eth_config, env_config = get_config() - - # Get environment variables for block time and block timestamp interval - # TODO can't seem to get block time or block timestamp interval from anvil, so we get env vars passed in here - # Real time, passed in anvil as `--block-time` - # Default values, while not necessarily correct, is fine because we're looking at the ratio - # between block time and block timestamp interval - block_time = os.environ.get("BLOCK_TIME") - if block_time is None: - block_time = 1 - else: - block_time = int(block_time) - # Interval between blocks, passed in anvil through RPC `anvil_setBlockTimestampInterval` - block_timestamp_interval = os.environ.get("BLOCK_TIMESTAMP_INTERVAL") - if block_timestamp_interval is None: - block_timestamp_interval = 1 - else: - block_timestamp_interval = int(block_timestamp_interval) - - if parsed_args.rpc_uri == "": - web3 = initialize_web3_with_http_provider(eth_config.rpc_uri, reset_provider=False) - else: - web3 = initialize_web3_with_http_provider(parsed_args.rpc_uri, reset_provider=False) - - # Setup logging - setup_logging( - log_filename=env_config.log_filename, - max_bytes=env_config.max_bytes, - log_level=env_config.log_level, - delete_previous_logs=env_config.delete_previous_logs, - log_stdout=env_config.log_stdout, - log_format_string=env_config.log_formatter, - ) - - # Fund the checkpoint sender with some ETH. - if parsed_args.fund: - balance = FixedPoint(100).scaled_value - sender = PolicyAgent(Account().create("CHECKPOINT_BOT")) - set_anvil_account_balance(web3, sender.address, balance) - logging.info("Successfully funded the sender=%s.", sender.address) - else: - private_key = os.getenv("CHECKPOINT_BOT_KEY") - sender = PolicyAgent(Account().from_key(private_key)) - - # Get the Hyperdrive contract. - # TODO replace this with the hyperdrive interface - if parsed_args.pool_addr == "": - addresses = get_hyperdrive_addresses_from_artifacts(os.path.join(eth_config.artifacts_uri, "addresses.json")) - if parsed_args.pool not in addresses: - raise ValueError(f"Pool {parsed_args.pool} not recognized. Available options are {list(addresses.keys())}") - hyperdrive_contract_address = web3.to_checksum_address(addresses[parsed_args.pool]) - else: - hyperdrive_contract_address = web3.to_checksum_address(parsed_args.pool_addr) - - hyperdrive_contract: IHyperdriveContract = IHyperdriveContract.factory(w3=web3)(hyperdrive_contract_address) - - # Run the checkpoint bot. This bot will attempt to mint a new checkpoint - # every checkpoint after a waiting period. It will poll very infrequently - # to reduce the probability of needing to mint a checkpoint. - config = get_hyperdrive_pool_config(hyperdrive_contract) - checkpoint_duration = config.checkpoint_duration - - logging.info( - "Checkpoint Duration: %s; Block time: %s; Block timestamp interval: %s", - checkpoint_duration, - block_time, - block_timestamp_interval, - ) - - while True: - # Get the latest block time and check to see if a new checkpoint should - # be minted. This bot waits for a portion of the checkpoint to reduce - # the probability of needing a checkpoint. After the waiting period, - # the bot will attempt to mint a checkpoint. - latest_block = web3.eth.get_block("latest") - timestamp = latest_block.get("timestamp", None) - if timestamp is None: - raise AssertionError(f"{latest_block=} has no timestamp") - checkpoint_portion_elapsed = timestamp % checkpoint_duration - checkpoint_time = timestamp - timestamp % checkpoint_duration - enough_time_has_elapsed = checkpoint_portion_elapsed >= CHECKPOINT_WAITING_PERIOD * checkpoint_duration - checkpoint_doesnt_exist = not does_checkpoint_exist(hyperdrive_contract, checkpoint_time) - - logging.info( - "timestamp=%s checkpoint_portion_elapsed=%s checkpoint_time=%s " - "need_checkpoint=%s checkpoint_doesnt_exist=%s", - timestamp, - checkpoint_portion_elapsed, - checkpoint_time, - enough_time_has_elapsed, - checkpoint_doesnt_exist, - ) - - if enough_time_has_elapsed and checkpoint_doesnt_exist: - logging.info("Submitting a checkpoint for checkpointTime=%s...", checkpoint_time) - # TODO: We will run into issues with the gas price being too low - # with testnets and mainnet. When we get closer to production, we - # will need to make this more robust so that we retry this - # transaction if the transaction gets stuck. - try: - # 0 is the max iterations for distribute excess idle, where it will default to - # the default max iterations - fn_args = (checkpoint_time, 0) - receipt = smart_contract_transact( - web3, - hyperdrive_contract, - sender, - "checkpoint", - *fn_args, - ) - except Exception as e: # pylint: disable=broad-except - logging.warning("Checkpoint transaction failed with exception=%s, retrying", e) - # Catch all errors here and retry next iteration - # TODO adjust wait period - time.sleep(1) - continue - logging.info( - "Checkpoint successfully mined with receipt=%s", - receipt["transactionHash"].hex(), - ) - if parsed_args.fuzz: - # TODO: Add crash report - assert receipt["status"] == 1, "Checkpoint failed." - latest_block = web3.eth.get_block("latest") - timestamp = latest_block.get("timestamp", None) - if timestamp is None: - raise AssertionError(f"{latest_block=} has no timestamp") - checkpoint_portion_elapsed = timestamp % checkpoint_duration - checkpoint_time = timestamp - timestamp % checkpoint_duration - - enough_time_has_elapsed = checkpoint_portion_elapsed >= CHECKPOINT_WAITING_PERIOD * checkpoint_duration - assert not enough_time_has_elapsed, "We shouldn't need a checkpoint if one was just created." - - checkpoint_exists = does_checkpoint_exist(hyperdrive_contract, checkpoint_time) - assert checkpoint_exists, "Checkpoint should exist since it was just made." - - # Sleep for enough time that the block timestamp would have advanced - # far enough to consider minting a new checkpoint. - if checkpoint_portion_elapsed >= CHECKPOINT_WAITING_PERIOD * checkpoint_duration: - sleep_duration = checkpoint_duration * (1 + CHECKPOINT_WAITING_PERIOD) - checkpoint_portion_elapsed - else: - sleep_duration = checkpoint_duration * CHECKPOINT_WAITING_PERIOD - checkpoint_portion_elapsed - # Adjust sleep duration by the speedup factor - adjusted_sleep_duration = sleep_duration / (block_timestamp_interval / block_time) - logging.info( - "Current time is %s. Sleeping for %s seconds ...", - datetime.datetime.fromtimestamp(timestamp), - adjusted_sleep_duration, - ) - time.sleep(adjusted_sleep_duration) - - -class Args(NamedTuple): - """Command line arguments for the checkpoint bot.""" - - fuzz: bool - pool: str - pool_addr: str - rpc_uri: str - fund: bool - - -def namespace_to_args(namespace: argparse.Namespace) -> Args: - """Converts argprase.Namespace to Args. - - Arguments - --------- - namespace: argparse.Namespace - Object for storing arg attributes. - - Returns - ------- - Args - Formatted arguments - """ - return Args( - fuzz=namespace.fuzz, - pool=namespace.pool, - pool_addr=namespace.pool_addr, - rpc_uri=namespace.rpc_uri, - fund=namespace.fund, - ) - - -def parse_arguments(argv: Sequence[str] | None = None) -> Args: - """Parses input arguments. - - Arguments - --------- - argv: Sequence[str] - The argv values returned from argparser. - - Returns - ------- - Args - Formatted arguments - """ - parser = argparse.ArgumentParser(description="Runs a bot that creates checkpoints each checkpoint_duration.") - parser.add_argument( - "--fuzz", - action="store_true", - help="If set, then add an assertion step to verify that the checkpoint was successful.", - ) - - parser.add_argument( - "--pool", - type=str, - default="erc4626_hyperdrive", - help='The logical name of the pool to connect to. Options are "erc4626_hyperdrive" and "stethhyperdrive".', - ) - - parser.add_argument( - "--pool-addr", - type=str, - default="", - help="The address of the hyperdrive pool to connect to. Uses `--pool` if not provided.", - ) - - parser.add_argument( - "--rpc-uri", - type=str, - default="", - help="The RPC URI of the chain.", - ) - - parser.add_argument( - "--fund", - action="store_true", - help="If set, then fund the bot with some ETH. Otherwise, will look for env variable `CHECKPOINT_BOT_KEY`.", - ) - - # Use system arguments if none were passed - if argv is None: - argv = sys.argv - return namespace_to_args(parser.parse_args()) - - -# Run the checkpoint bot. -if __name__ == "__main__": - main() diff --git a/scripts/local_fuzz_bots.py b/scripts/local_fuzz_bots.py index 50129dd3a7..fd056c9a9d 100644 --- a/scripts/local_fuzz_bots.py +++ b/scripts/local_fuzz_bots.py @@ -8,7 +8,6 @@ from agent0 import LocalChain, LocalHyperdrive from agent0.hyperfuzz.system_fuzz import generate_fuzz_hyperdrive_config, run_fuzz_bots -from agent0.hyperlogs import setup_logging from agent0.hyperlogs.rollbar_utilities import initialize_rollbar @@ -17,9 +16,6 @@ def main() -> None: # TODO consolidate setup into single function log_to_rollbar = initialize_rollbar("localfuzzbots") - setup_logging( - log_stdout=True, - ) rng_seed = random.randint(0, 10000000) rng = np.random.default_rng(rng_seed) diff --git a/scripts/remote_fuzz_bot_invariant_checks.py b/scripts/remote_fuzz_bot_invariant_checks.py deleted file mode 100644 index ceb5359aa0..0000000000 --- a/scripts/remote_fuzz_bot_invariant_checks.py +++ /dev/null @@ -1,216 +0,0 @@ -"""Script for checking Hyperdrive invariants at each block on a remote chain. -""" - -from __future__ import annotations - -import argparse -import logging -import sys -import time -from typing import NamedTuple, Sequence - -from web3 import Web3 - -from agent0 import Hyperdrive -from agent0.core.base.config import EnvironmentConfig -from agent0.ethpy import build_eth_config -from agent0.ethpy.hyperdrive import HyperdriveReadInterface -from agent0.hyperfuzz.system_fuzz.invariant_checks import run_invariant_checks -from agent0.hyperlogs import setup_logging -from agent0.hyperlogs.rollbar_utilities import initialize_rollbar - - -def main(argv: Sequence[str] | None = None) -> None: - """Check Hyperdrive invariants each block. - - Arguments - --------- - argv: Sequence[str] - A sequence containing the uri to the database server and the test epsilon. - """ - - # Setup the experiment - parsed_args, interface = setup_fuzz(argv) - - # We use the logical name if we don't specify pool addr, otherwise we use the pool addr - if parsed_args.pool_addr == "": - rollbar_environment_name = "remote_fuzz_bot_invariant_check_" + parsed_args.pool - else: - rollbar_environment_name = "remote_fuzz_bot_invariant_check_" + parsed_args.pool_addr - - log_to_rollbar = initialize_rollbar(rollbar_environment_name) - - # Run the loop forever - last_executed_block_number = 0 # no matter what we will run the check the first time - while True: - latest_block = interface.get_block("latest") - latest_block_number = latest_block.get("number", None) - if latest_block_number is None: - raise AssertionError("Block has no number.") - if not latest_block_number > last_executed_block_number: - # take a nap - time.sleep(parsed_args.sleep_time) - continue - # Update block number - last_executed_block_number = latest_block_number - run_invariant_checks( - latest_block=latest_block, - interface=interface, - test_epsilon=parsed_args.test_epsilon, - raise_error_on_failure=False, - log_to_rollbar=log_to_rollbar, - ) - - -# TODO consolidate setup fuzz within hyperfuzz -def setup_fuzz(argv: Sequence[str] | None) -> tuple[Args, HyperdriveReadInterface]: - """Setup the fuzz config & interface. - - Arguments - --------- - argv: Sequence[str] - A sequnce containing the uri to the database server and the test epsilon. - - Returns - ------- - tuple[Args, HyperdriveReadInterface] - The parsed arguments and interface constructed from those arguments. - """ - parsed_args = parse_arguments(argv) - - eth_config = build_eth_config(parsed_args.eth_config_env_file) - # CLI arguments overwrite what's in the env file - if parsed_args.rpc_uri != "": - eth_config.rpc_uri = parsed_args.rpc_uri - - env_config = EnvironmentConfig( - delete_previous_logs=False, - halt_on_errors=False, - log_filename=".logging/invariant_checks.log", - log_level=logging.ERROR, - log_stdout=False, - global_random_seed=1234, - username="INVARIANT_CHECKS", - ) - # Setup logging - setup_logging( - log_filename=env_config.log_filename, - max_bytes=env_config.max_bytes, - log_level=env_config.log_level, - delete_previous_logs=env_config.delete_previous_logs, - log_stdout=env_config.log_stdout, - log_format_string=env_config.log_formatter, - ) - - # Setup hyperdrive interface - if parsed_args.pool_addr == "": - hyperdrive_addresses = Hyperdrive.get_hyperdrive_addresses_from_artifacts(eth_config.artifacts_uri) - if parsed_args.pool not in hyperdrive_addresses: - raise ValueError( - f"Pool {parsed_args.pool} not recognized. Available options are {list(hyperdrive_addresses.keys())}" - ) - hyperdrive_address = hyperdrive_addresses[parsed_args.pool] - else: - hyperdrive_address = Web3.to_checksum_address(parsed_args.pool_addr) - - interface = HyperdriveReadInterface(eth_config, hyperdrive_address=hyperdrive_address) - return parsed_args, interface - - -class Args(NamedTuple): - """Command line arguments for the invariant checker.""" - - test_epsilon: float - eth_config_env_file: str - sleep_time: int - pool: str - pool_addr: str - rpc_uri: str - - -def namespace_to_args(namespace: argparse.Namespace) -> Args: - """Converts argprase.Namespace to Args. - - Arguments - --------- - namespace: argparse.Namespace - Object for storing arg attributes. - - Returns - ------- - Args - Formatted arguments - """ - return Args( - test_epsilon=namespace.test_epsilon, - eth_config_env_file=namespace.eth_config_env_file, - sleep_time=namespace.sleep_time, - pool=namespace.pool, - pool_addr=namespace.pool_addr, - rpc_uri=namespace.rpc_uri, - ) - - -def parse_arguments(argv: Sequence[str] | None = None) -> Args: - """Parses input arguments. - - Arguments - --------- - argv: Sequence[str] - The argv values returned from argparser. - - Returns - ------- - Args - Formatted arguments - """ - parser = argparse.ArgumentParser(description="Runs a loop to check Hyperdrive invariants at each block.") - parser.add_argument( - "--test_epsilon", - type=float, - default=1e-4, - help="The allowed error for equality tests.", - ) - parser.add_argument( - "--eth_config_env_file", - type=str, - default="eth.env", - help="String pointing to eth config env file.", - ) - parser.add_argument( - "--sleep_time", - type=int, - default=5, - help="Sleep time between checks, in seconds.", - ) - - parser.add_argument( - "--pool", - type=str, - default="erc4626_hyperdrive", - help='The logical name of the pool to connect to. Options are "erc4626_hyperdrive" and "stethhyperdrive".', - ) - - parser.add_argument( - "--pool-addr", - type=str, - default="", - help="The address of the hyperdrive pool to connect to. Uses `--pool` if not provided.", - ) - - parser.add_argument( - "--rpc-uri", - type=str, - default="", - help="The RPC URI of the chain.", - ) - - # Use system arguments if none were passed - if argv is None: - argv = sys.argv - - return namespace_to_args(parser.parse_args()) - - -if __name__ == "__main__": - main() diff --git a/scripts/remote_fuzz_bots.py b/scripts/remote_fuzz_bots.py deleted file mode 100644 index fa024ef5de..0000000000 --- a/scripts/remote_fuzz_bots.py +++ /dev/null @@ -1,132 +0,0 @@ -"""Runs random bots against a remote chain for fuzz testing.""" - -from __future__ import annotations - -import argparse -import logging -import random -import sys -from typing import NamedTuple, Sequence - -from web3.types import RPCEndpoint - -from agent0 import Chain, Hyperdrive -from agent0.ethpy import build_eth_config -from agent0.hyperfuzz.system_fuzz import run_fuzz_bots -from agent0.hyperlogs import setup_logging -from agent0.hyperlogs.rollbar_utilities import initialize_rollbar - -# Crash behavior -STOP_CHAIN_ON_CRASH = False - - -def main(argv: Sequence[str] | None = None) -> None: - """Runs fuzz bots. - - Arguments - --------- - argv: Sequence[str] - The argv values returned from argparser. - """ - parsed_args = parse_arguments(argv) - - # TODO consolidate setup into single function - - # Get config and addresses - eth_config = build_eth_config() - hyperdrive_addresses = Hyperdrive.get_hyperdrive_addresses_from_artifacts(eth_config.artifacts_uri) - if parsed_args.pool not in hyperdrive_addresses: - raise ValueError( - f"Pool {parsed_args.pool} not recognized. Available options are {list(hyperdrive_addresses.keys())}" - ) - hyperdrive_address = hyperdrive_addresses[parsed_args.pool] - - log_to_rollbar = initialize_rollbar("remotefuzzbots_" + parsed_args.pool) - setup_logging( - log_stdout=True, - ) - - rng_seed = random.randint(0, 10000000) - - # Connect to the chain - chain = Chain(eth_config.rpc_uri) - - hyperdrive_config = Hyperdrive.Config( - preview_before_trade=True, - rng_seed=rng_seed, - log_to_rollbar=log_to_rollbar, - rollbar_log_prefix="fuzzbots", - crash_log_level=logging.CRITICAL, - crash_report_additional_info={"rng_seed": rng_seed}, - ) - hyperdrive_pool = Hyperdrive(chain, hyperdrive_address, hyperdrive_config) - - try: - run_fuzz_bots( - hyperdrive_pool, - check_invariance=False, - raise_error_on_crash=STOP_CHAIN_ON_CRASH, - log_to_rollbar=log_to_rollbar, - run_async=True, - ) - except Exception as exc: # pylint: disable=broad-exception-caught - if STOP_CHAIN_ON_CRASH: - hyperdrive_pool.interface.web3.provider.make_request( - method=RPCEndpoint("evm_setIntervalMining"), params=[0] - ) - # If `run_fuzz_bots` exits, it's because we're wanting it to exit on crash - # so we reraise the orig exception here - raise exc - - -class Args(NamedTuple): - """Command line arguments for the checkpoint bot.""" - - pool: str - - -def namespace_to_args(namespace: argparse.Namespace) -> Args: - """Converts argprase.Namespace to Args. - - Arguments - --------- - namespace: argparse.Namespace - Object for storing arg attributes. - - Returns - ------- - Args - Formatted arguments - """ - return Args(pool=namespace.pool) - - -def parse_arguments(argv: Sequence[str] | None = None) -> Args: - """Parses input arguments. - - Arguments - --------- - argv: Sequence[str] - The argv values returned from argparser. - - Returns - ------- - Args - Formatted arguments - """ - parser = argparse.ArgumentParser(description="Runs random bots.") - # TODO read this from the register or pass in pool address - parser.add_argument( - "--pool", - type=str, - default="erc4626_hyperdrive", - help='The logical name of the pool to connect to. Options are "erc4626_hyperdrive" and "stethhyperdrive".', - ) - # Use system arguments if none were passed - if argv is None: - argv = sys.argv - return namespace_to_args(parser.parse_args()) - - -if __name__ == "__main__": - main() diff --git a/scripts/run_acquire_data.py b/scripts/run_acquire_data.py index a815f025a0..4b446a553a 100644 --- a/scripts/run_acquire_data.py +++ b/scripts/run_acquire_data.py @@ -2,14 +2,24 @@ from __future__ import annotations +import os + from agent0.chainsync.exec import acquire_data +from agent0.ethpy.hyperdrive.addresses import get_hyperdrive_addresses_from_artifacts from agent0.hyperlogs import setup_logging if __name__ == "__main__": setup_logging(".logging/acquire_data.log", log_stdout=True) + # Get the RPC URI and pool address from environment var + rpc_uri = os.getenv("RPC_URI", "http://localhost:8545") + # TODO get this from the registry after refactor + artifacts_uri = os.getenv("ARTIFACTS_URI", "http://localhost:8080") + hyperdrive_addr = get_hyperdrive_addresses_from_artifacts(artifacts_uri)["erc4626_hyperdrive"] acquire_data( # This is the start block needed based on the devnet image, which corresponds # to the block that the contract was deployed. # TODO ideally would gather this from the deployer start_block=48, + rpc_uri=rpc_uri, + hyperdrive_address=hyperdrive_addr, ) diff --git a/scripts/run_data_analysis.py b/scripts/run_data_analysis.py index 4cb71a6d90..cd1df5b25b 100644 --- a/scripts/run_data_analysis.py +++ b/scripts/run_data_analysis.py @@ -2,14 +2,25 @@ from __future__ import annotations +import os + from agent0.chainsync.exec import data_analysis +from agent0.ethpy.hyperdrive.addresses import get_hyperdrive_addresses_from_artifacts from agent0.hyperlogs import setup_logging if __name__ == "__main__": setup_logging(".logging/data_analysis.log", log_stdout=True) + # Get the RPC URI and pool address from environment var + rpc_uri = os.getenv("RPC_URI", "http://localhost:8545") + # TODO get this from the registry after refactor + artifacts_uri = os.getenv("ARTIFACTS_URI", "http://localhost:8080") + hyperdrive_addr = get_hyperdrive_addresses_from_artifacts(artifacts_uri)["erc4626_hyperdrive"] + data_analysis( # This is the start block needed based on the devnet image, which corresponds # to the block that the contract was deployed. # TODO ideally would gather this from the deployer start_block=48, + rpc_uri=rpc_uri, + hyperdrive_address=hyperdrive_addr, ) diff --git a/scripts/run_unit_fuzz.py b/scripts/run_unit_fuzz.py index 5a3214333e..7ff4128973 100644 --- a/scripts/run_unit_fuzz.py +++ b/scripts/run_unit_fuzz.py @@ -36,7 +36,12 @@ def main(argv: Sequence[str] | None = None): while True: try: print("Running long short maturity test") - chain_config = LocalChain.Config(db_port=5434, chain_port=10001) + chain_config = LocalChain.Config( + db_port=5434, + chain_port=10001, + log_filename=".logging/fuzz_long_short_maturity_values.log", + log_to_stdout=False, + ) long_maturity_vals_epsilon = 1e-14 short_maturity_vals_epsilon = 1e-9 fuzz_long_short_maturity_values( @@ -51,7 +56,12 @@ def main(argv: Sequence[str] | None = None): try: print("Running path independence test") - chain_config = LocalChain.Config(db_port=5435, chain_port=10002) + chain_config = LocalChain.Config( + db_port=5435, + chain_port=10002, + log_filename=".logging/fuzz_path_independence.log", + log_to_stdout=False, + ) lp_share_price_epsilon = 1e-14 effective_share_reserves_epsilon = 1e-4 present_value_epsilon = 1e-4 @@ -69,7 +79,12 @@ def main(argv: Sequence[str] | None = None): try: print("Running fuzz profit test") - chain_config = LocalChain.Config(db_port=5436, chain_port=10003) + chain_config = LocalChain.Config( + db_port=5436, + chain_port=10003, + log_filename=".logging/fuzz_profit_check.log", + log_to_stdout=False, + ) fuzz_profit_check(chain_config) except FuzzAssertionException: pass @@ -78,7 +93,12 @@ def main(argv: Sequence[str] | None = None): try: print("Running fuzz present value test") - chain_config = LocalChain.Config(db_port=5437, chain_port=10004) + chain_config = LocalChain.Config( + db_port=5437, + chain_port=10004, + log_filename=".logging/fuzz_present_value.log", + log_to_stdout=False, + ) present_value_epsilon = 0.01 fuzz_present_value(test_epsilon=present_value_epsilon, chain_config=chain_config) except FuzzAssertionException: diff --git a/scripts/testnet_checkpoint_bots.py b/scripts/testnet_checkpoint_bots.py index 6d65f2b389..9621aa1737 100644 --- a/scripts/testnet_checkpoint_bots.py +++ b/scripts/testnet_checkpoint_bots.py @@ -19,8 +19,7 @@ from agent0.core.base import PolicyAgent from agent0.ethpy.base import smart_contract_transact from agent0.ethpy.hyperdrive import get_hyperdrive_pool_config -from agent0.hyperfuzz.system_fuzz.run_fuzz_bots import _async_runner -from agent0.hyperlogs import setup_logging +from agent0.hyperfuzz.system_fuzz.run_fuzz_bots import async_runner from agent0.hypertypes import IHyperdriveContract # Checkpoint bot has a lot going on @@ -200,11 +199,6 @@ def main(argv: Sequence[str] | None = None) -> None: parsed_args = parse_arguments(argv) - # Setup logging - setup_logging( - log_stdout=True, - ) - # Initialize chain = Chain(parsed_args.rpc_uri) @@ -232,7 +226,7 @@ def main(argv: Sequence[str] | None = None) -> None: # Run checkpoint bots # We set return_exceptions to False to crash immediately if a thread fails asyncio.run( - _async_runner( + async_runner( return_exceptions=False, funcs=partials, chain=chain, diff --git a/scripts/testnet_fuzz_bot_invariant_checks.py b/scripts/testnet_fuzz_bot_invariant_checks.py index c8b236b758..cdc3724d2b 100644 --- a/scripts/testnet_fuzz_bot_invariant_checks.py +++ b/scripts/testnet_fuzz_bot_invariant_checks.py @@ -19,7 +19,6 @@ from agent0 import Chain, Hyperdrive from agent0.hyperfuzz.system_fuzz.invariant_checks import run_invariant_checks -from agent0.hyperlogs import setup_logging from agent0.hyperlogs.rollbar_utilities import initialize_rollbar @@ -38,9 +37,6 @@ def main(argv: Sequence[str] | None = None) -> None: # We use the logical name if we don't specify pool addr, otherwise we use the pool addr rollbar_environment_name = "testnet_fuzz_bot_invariant_check" log_to_rollbar = initialize_rollbar(rollbar_environment_name) - setup_logging( - log_stdout=True, - ) # We calculate how many blocks we should wait before checking for a new pool pool_check_num_blocks = parsed_args.pool_check_sleep_time // 12 diff --git a/src/agent0/chainsync/exec/acquire_data.py b/src/agent0/chainsync/exec/acquire_data.py index 8ade771da9..d68f133f6d 100644 --- a/src/agent0/chainsync/exec/acquire_data.py +++ b/src/agent0/chainsync/exec/acquire_data.py @@ -16,7 +16,6 @@ get_latest_block_number_from_pool_info_table, init_data_chain_to_db, ) -from agent0.ethpy import EthConfig from agent0.ethpy.hyperdrive import HyperdriveReadInterface _SLEEP_AMOUNT = 1 @@ -25,14 +24,15 @@ # TODO cleanup # pylint: disable=too-many-arguments # pylint: disable=too-many-locals +# pylint: disable=too-many-branches def acquire_data( start_block: int = 0, lookback_block_limit: int = 1000, interface: HyperdriveReadInterface | None = None, - eth_config: EthConfig | None = None, + rpc_uri: str | None = None, + hyperdrive_address: ChecksumAddress | None = None, db_session: Session | None = None, postgres_config: PostgresConfig | None = None, - hyperdrive_address: ChecksumAddress | None = None, exit_on_catch_up: bool = False, exit_callback_fn: Callable[[], bool] | None = None, suppress_logs: bool = False, @@ -47,18 +47,18 @@ def acquire_data( The maximum number of blocks to look back when filling in missing data interface: HyperdriveReadInterface | None, optional An initialized HyperdriveReadInterface object. If not set, will initialize one based on - eth_config and hyperdrive_address. - eth_config: EthConfig | None - Configuration for URIs to the rpc and artifacts. If not set, will look for addresses - in eth.env. + rpc_uri and hyperdrive_address. + rpc_uri: str, optional + The URI for the web3 provider to initialize the interface with. Not used if an interface + is provided. + hyperdrive_address: ChecksumAddress | None, optional + The address of the hyperdrive contract to initialize the interface with. Not used if + an interface is provided. db_session: Session | None Session object for connecting to db. If None, will initialize a new session based on postgres_config. postgres_config: PostgresConfig | None = None, PostgresConfig for connecting to db. If none, will set from postgres.env. - hyperdrive_address: ChecksumAddress | None, optional - The address of the hyperdrive contract. - If not set, will use the erc4626_hyperdrive contract from `eth_config.artifacts_uri`. exit_on_catch_up: bool, optional If True, will exit after catching up to current block. Defaults to False. exit_callback_fn: Callable[[], bool] | None, optional @@ -72,7 +72,9 @@ def acquire_data( ## Initialization if interface is None: - interface = HyperdriveReadInterface(eth_config, hyperdrive_address) + if hyperdrive_address is None or rpc_uri is None: + raise ValueError("hyperdrive_address and rpc_uri must be provided if not providing interface") + interface = HyperdriveReadInterface(hyperdrive_address, rpc_uri) # postgres session db_session_init = False diff --git a/src/agent0/chainsync/exec/data_analysis.py b/src/agent0/chainsync/exec/data_analysis.py index 945e1e0cec..bc712b05b6 100644 --- a/src/agent0/chainsync/exec/data_analysis.py +++ b/src/agent0/chainsync/exec/data_analysis.py @@ -18,7 +18,6 @@ get_latest_block_number_from_table, get_pool_config, ) -from agent0.ethpy import EthConfig from agent0.ethpy.hyperdrive import HyperdriveReadInterface _SLEEP_AMOUNT = 1 @@ -31,10 +30,10 @@ def data_analysis( start_block: int = 0, interface: HyperdriveReadInterface | None = None, - eth_config: EthConfig | None = None, + rpc_uri: str | None = None, + hyperdrive_address: ChecksumAddress | None = None, db_session: Session | None = None, postgres_config: PostgresConfig | None = None, - hyperdrive_address: ChecksumAddress | None = None, exit_on_catch_up: bool = False, exit_callback_fn: Callable[[], bool] | None = None, suppress_logs: bool = False, @@ -48,18 +47,18 @@ def data_analysis( The starting block to filter the query on interface: HyperdriveReadInterface | None, optional An initialized HyperdriveReadInterface object. If not set, will initialize one based on - eth_config and hyperdrive_address. - eth_config: EthConfig | None - Configuration for URIs to the rpc and artifacts. If not set, will look for addresses - in eth.env. + rpc_uri and hyperdrive_address. + rpc_uri: str, optional + The URI for the web3 provider to initialize the interface with. Not used if an interface + is provided. + hyperdrive_address: ChecksumAddress | None, optional + The address of the hyperdrive contract to initialize the interface with. Not used if + an interface is provided. db_session: Session | None Session object for connecting to db. If None, will initialize a new session based on postgres.env. postgres_config: PostgresConfig | None = None, PostgresConfig for connecting to db. If none, will set from postgres.env. - hyperdrive_address: ChecksumAddress | None, optional - The address of the hyperdrive contract. - If not set, will use the erc4626_hyperdrive contract from `eth_config.artifacts_uri`. exit_on_catch_up: bool If True, will exit after catching up to current block exit_callback_fn: Callable[[], bool] | None, optional @@ -74,9 +73,10 @@ def data_analysis( # TODO implement logger instead of global logging to suppress based on module name. ## Initialization - # create hyperdrive interface if interface is None: - interface = HyperdriveReadInterface(eth_config, hyperdrive_address) + if hyperdrive_address is None or rpc_uri is None: + raise ValueError("hyperdrive_address and rpc_uri must be provided if not providing interface") + interface = HyperdriveReadInterface(hyperdrive_address, rpc_uri) # postgres session db_session_init = False diff --git a/src/agent0/core/__init__.py b/src/agent0/core/__init__.py index c5213a38aa..97a17ff1ff 100644 --- a/src/agent0/core/__init__.py +++ b/src/agent0/core/__init__.py @@ -1,8 +1 @@ -"""Account key config and various helper functions""" - -from .accounts_config import ( - AccountKeyConfig, - build_account_config_from_env, - build_account_key_config_from_agent_config, - initialize_accounts, -) +"""Core modules for agent0.""" diff --git a/src/agent0/core/accounts_config.py b/src/agent0/core/accounts_config.py deleted file mode 100644 index 313dc39438..0000000000 --- a/src/agent0/core/accounts_config.py +++ /dev/null @@ -1,232 +0,0 @@ -"""Defines the accounts configuration from env vars.""" - -from __future__ import annotations - -import json -import logging -import os -import sys -from dataclasses import dataclass - -import numpy as np -from dotenv import load_dotenv -from numpy.random._generator import Generator - -from .base.config import AgentConfig, Budget -from .base.make_key import make_private_key - - -@dataclass -class AccountKeyConfig: - """The account config dataclass.""" - - # default values for local contracts - # Matching environment variables to search for - # pylint: disable=invalid-name - USER_KEY: str | None - """The user's private key.""" - AGENT_KEYS: list[str] - """A list of agent private keys.""" - AGENT_ETH_BUDGETS: list[int] - """A list of agent eth budgets.""" - AGENT_BASE_BUDGETS: list[int] - """A list of agent base budgets.""" - - def to_env_str(self) -> str: - """Convert the configuration dataclass to a string, ready to be written as an env file - - Returns - ------- - str - The env string, ready to be written as an env file - """ - env_str = "" - if self.USER_KEY is None: - env_str += "USER_KEY=\n" - else: - env_str += "USER_KEY='" + self.USER_KEY + "'" + "\n" - env_str += "AGENT_KEYS='" + json.dumps(self.AGENT_KEYS) + "'" + "\n" - env_str += "AGENT_BASE_BUDGETS='" + json.dumps(self.AGENT_BASE_BUDGETS) + "'" + "\n" - env_str += "AGENT_ETH_BUDGETS='" + json.dumps(self.AGENT_ETH_BUDGETS) + "'" + "\n" - return env_str - - -def initialize_accounts( - agent_config: list[AgentConfig], - env_file: str | None = None, - rng: Generator | None = None, -) -> AccountKeyConfig: - """Build or load an accounts environment file. - If it doesn't exist, create it based on agent_config. - (if develop is off, print instructions on adding in user private key and running script to fund agents). - If it does exist, read it in and use it. - - Arguments - --------- - agent_config: list[AgentConfig] - The list of agent configs that define policies and arguments. - env_file: str | None - The path to the env file to write/load from. Defaults to `accounts.env`. - rng: Generator | None, optional - The random number generator to pass for each bot. - If not provided, then one will be constructed from the system seed. - - Returns - ------- - AccountKeyConfig - The account config object linked to the env file. - """ - # Create rng if it is not provided - if rng is None: - rng = np.random.default_rng() - - # See if develop flag is set - develop_env = os.environ.get("DEVELOP") - develop = (develop_env is not None) and (develop_env.lower() == "true") - - # Default location - if env_file is None: - env_file = "account.env" - - # If we're in develop mode, we don't use the env file at all - if develop: - account_key_config = build_account_key_config_from_agent_config(agent_config, rng) - # If we're not in develop mode and the env file doesn't exist - # we create the env file keeping track of keys and budgets - elif not os.path.exists(env_file): - logging.info("Creating %s", env_file) - # Create AccountKeyConfig from agent config - account_key_config = build_account_key_config_from_agent_config(agent_config, rng) - # Create file - with open(env_file, "w", encoding="UTF-8") as file: - file.write(account_key_config.to_env_str()) - print( - f"Account key config written {env_file}. " - "Run the following command to fund the accounts, then rerun this script." - ) - # Different commands depending on if default env file is used - command_str = "python scripts/fund_agents_from_user_key.py -u " - if env_file != "account.env": - command_str += f" -f {env_file}" - print(command_str) - # Clean exit - sys.exit(0) - # If we're not in develop mode and the env file does exist - # we load the env file key - else: - logging.info("Loading %s", env_file) - # Ensure account_config matches up with env_file - account_key_config = build_account_config_from_env(env_file) - num_total_agents = sum(agent.number_of_agents for agent in agent_config) - if num_total_agents != len(account_key_config.AGENT_KEYS): - raise ValueError("Number of agents in agent config does not match number of agents in env file") - - return account_key_config - - -def build_account_key_config_from_agent_config( - agent_configs: list[AgentConfig], rng: Generator | None = None, user_key: str | None = None -) -> AccountKeyConfig: - """Build an Account Config from a provided agent config. - - Arguments - --------- - agent_configs: list[AgentConfig] - The list of agent configs that define policies and arguments. - rng: Generator | None, optional - The random number generator to pass for each bot. - If not provided, then one will be constructed from the system seed. - user_key: str - The provided user key to use - - Returns - ------- - AccountKeyConfig - The account config object linked to the env file. - """ - # Create rng if it is not provided - if rng is None: - rng = np.random.default_rng() - agent_private_keys = [] - agent_base_budgets = [] - agent_eth_budgets = [] - for agent_info in agent_configs: - for _ in range(agent_info.number_of_agents): - agent_private_keys.append(make_private_key()) - - if isinstance(agent_info.eth_budget_wei, Budget): - agent_eth_budgets.append(agent_info.eth_budget_wei.sample_budget(rng).scaled_value) - elif isinstance(agent_info.eth_budget_wei, int): - agent_eth_budgets.append(agent_info.eth_budget_wei) - else: - raise ValueError(f"Unknown eth_budget_wei type: {type(agent_info.eth_budget_wei)}") - - if isinstance(agent_info.base_budget_wei, Budget): - agent_base_budgets.append(agent_info.base_budget_wei.sample_budget(rng).scaled_value) - elif isinstance(agent_info.base_budget_wei, int): - agent_base_budgets.append(agent_info.base_budget_wei) - else: - raise ValueError(f"Unknown base_budget_wei type: {type(agent_info.base_budget_wei)}") - - return AccountKeyConfig( - USER_KEY=user_key, - AGENT_KEYS=agent_private_keys, - AGENT_ETH_BUDGETS=agent_eth_budgets, - AGENT_BASE_BUDGETS=agent_base_budgets, - ) - - -def build_account_config_from_env(env_file: str | None = None, user_key: str | None = None) -> AccountKeyConfig: - """Build an Account Config from environmental variables. - - Arguments - --------- - env_file: str | None - The path to the env file to load from. Defaults to `accounts.env`. - user_key: str - The provided user key to use - - Returns - ------- - AccountConfig - Config settings required to connect to the eth node - """ - # Default location - if env_file is None: - env_file = "account.env" - - # Look for and load local config if it exists - load_dotenv(env_file, override=True) - - if user_key is None: - # USER PRIVATE KEY - user_key = os.environ.get("USER_KEY") - if user_key is None: - raise ValueError("USER_KEY environment variable must be set") - - # LIST OF AGENT PRIVATE KEYS - # NOTE: The env var should follow the JSON specification: https://www.json.org/json-en.html - # for example, `export AGENT_KEYS='["foo", "bar"]'` - key_string = os.environ.get("AGENT_KEYS") - if key_string is None: - raise ValueError("AGENT_KEYS environment variable must be set") - agent_keys = json.loads(key_string) - # AGENT ETHEREUM FUNDING AMOUNTS - eth_budget_string = os.environ.get("AGENT_ETH_BUDGETS") - if eth_budget_string is None: - raise ValueError("AGENT_ETH_BUDGETS environment variable must be set") - agent_eth_budgets = [int(budget) for budget in json.loads(eth_budget_string)] - # AGENT BASE FUNDING AMOUNTS - base_budget_string = os.environ.get("AGENT_BASE_BUDGETS") - if base_budget_string is None: - raise ValueError("AGENT_BASE_BUDGETS environment variable must be set") - agent_base_budgets = [int(budget) for budget in json.loads(base_budget_string)] - if len(agent_keys) != len(agent_eth_budgets) or len(agent_keys) != len(agent_base_budgets): - raise AssertionError(f"{len(agent_keys)=} must equal {len(agent_eth_budgets)=} and {len(agent_base_budgets)=}") - - return AccountKeyConfig( - USER_KEY=user_key, - AGENT_KEYS=agent_keys, - AGENT_ETH_BUDGETS=agent_eth_budgets, - AGENT_BASE_BUDGETS=agent_base_budgets, - ) diff --git a/src/agent0/core/base/config/__init__.py b/src/agent0/core/base/config/__init__.py deleted file mode 100644 index 76a907f0c2..0000000000 --- a/src/agent0/core/base/config/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Configuration for running agents.""" - -from .agent_config import AgentConfig -from .budget import Budget -from .environment_config import DEFAULT_USERNAME, EnvironmentConfig diff --git a/src/agent0/core/base/config/agent_config.py b/src/agent0/core/base/config/agent_config.py deleted file mode 100644 index c1a0de66e1..0000000000 --- a/src/agent0/core/base/config/agent_config.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Information for creating an agent""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import Type - -from agent0.core.base.policies import BasePolicy - -from .budget import Budget - - -@dataclass -class AgentConfig: - """Information about an agent.""" - - # lots of configs! - # pylint: disable=too-many-instance-attributes - - policy: Type[BasePolicy] - """The agent's policy; should match the class name.""" - policy_config: BasePolicy.Config - """The policy's config object for custom policy configuration.""" - base_budget_wei: Budget | int - """The base token budget for the agent in units of wei.""" - eth_budget_wei: Budget | int - """The ethereum budget for the agent in units of wei.""" - number_of_agents: int = 1 - """The number of agents of this type to spin up.""" - private_keys: list[str] | None = None - """list of strings, where each key contains a private key of one of the agents.""" - - def __post_init__(self): - if self.private_keys is not None and len(self.private_keys) != self.number_of_agents: - raise ValueError( - f"if private_keys is set then {len(self.private_keys)=} must equal {self.number_of_agents=}" - ) diff --git a/src/agent0/core/base/config/environment_config.py b/src/agent0/core/base/config/environment_config.py deleted file mode 100644 index 799802971a..0000000000 --- a/src/agent0/core/base/config/environment_config.py +++ /dev/null @@ -1,116 +0,0 @@ -"""State object for setting environment configuration.""" - -from __future__ import annotations - -import json -from dataclasses import dataclass - -from agent0.core.base.types import Freezable -from agent0.hyperlogs import DEFAULT_LOG_LEVEL, DEFAULT_LOG_MAXBYTES, ExtendedJSONEncoder - -DEFAULT_USERNAME = "changeme" - - -@dataclass -class EnvironmentConfig(Freezable): - """Parameters that can be set either locally or passed from docker.""" - - # lots of configs! - # pylint: disable=too-many-instance-attributes - - username: str = DEFAULT_USERNAME - """Logical username for who is running agents.""" - halt_on_errors: bool = False - """If true, stop executing when trade errors occur.""" - halt_on_slippage: bool = False - """If halt_on_errors is True, halt_on_slippage controls if we halt when slippage happens""" - crash_report_to_file: bool = False - """ - If true, will write crash report under .crash_report directory - including the anvil crash state. - Since crash reports are timestamped, we set this default to false - to avoid using lots of disk space - """ - crash_report_file_prefix: str = "" - """The string prefix to prepend to crash reports.""" - log_filename: str = ".logging/agent0_logs.log" - """Optional output filename for logging.""" - log_level: int = DEFAULT_LOG_LEVEL # INFO - """log level; should be in [logging.DEBUG, logging.INFO, logging.WARNING].""" - delete_previous_logs: bool = False - """Delete_previous_logs; if True, delete existing logs at the start of the run.""" - log_stdout: bool = False - """Log log_file_and_stdout; if True, save to file and write to stdout, else just save to file.""" - log_to_rollbar: bool = False - """If True, enables rollbar logging.""" - log_formatter: str = "\n%(asctime)s: %(levelname)s: %(module)s.%(funcName)s:\n%(message)s" - """ - log_formatter; specifies the format in which the logger saves the logs - see https://docs.python.org/3/library/logging.html#logrecord-attributes for which attributes can be used - """ - max_bytes: int = DEFAULT_LOG_MAXBYTES # int(2e6) or 2MB - """maximum log file output size, in bytes.""" - global_random_seed: int | None = None - """int to be used for the random seed.""" - read_retry_count: int | None = None - """ - Number of times to retry for read smart contract calls. - Defaults to what's being used in ethpy, which is 5. - """ - write_retry_count: int | None = None - """ - Number of times to retry for write smart contract calls. - Defaults to what's being used in ethpy, which is 1. - """ - randomize_liquidation: bool = False - """If true, will randomize liquidation trades when liquidating.""" - - def __post_init__(self) -> None: - """Disallow adding new attributes.""" - self.no_new_attribs = True - - def __getitem__(self, attrib) -> None: - """Get the value of the attribute.""" - return getattr(self, attrib) - - def __setitem__(self, attrib, value) -> None: - """Set the value of the attribute.""" - self.__setattr__(attrib, value) - - def __str__(self) -> str: - """Return a string representation of the object.""" - # cls arg tells json how to handle numpy objects and nested dataclasses - return json.dumps(self.__dict__, sort_keys=True, indent=2, cls=ExtendedJSONEncoder) - - def copy(self) -> EnvironmentConfig: - """Return a new copy of self. - - Returns - ------- - EnvironmentConfig - A copy of the environment config. - """ - return EnvironmentConfig(**{key: value for key, value in self.__dict__.items() if key not in ["rng"]}) - - def load_from_json(self, json_file_location: str) -> None: - """Load configuration settings from a JSON file and update self. - - Arguments - --------- - json_file_location: str - The path to the json file to load from. - """ - with open(json_file_location, mode="r", encoding="UTF-8") as file: - json_config = json.load(file) - self.__dict__.update(**json_config) - - def save_as_json(self, json_file_location: str) -> None: - """Save configuration settings in JSON format. - - Arguments - --------- - json_file_location: str - The path for the output file. - """ - with open(json_file_location, mode="w", encoding="UTF-8") as file: - json.dump(self.__dict__, file, sort_keys=True, indent=2, cls=ExtendedJSONEncoder) diff --git a/src/agent0/core/hyperdrive/interactive/chain.py b/src/agent0/core/hyperdrive/interactive/chain.py index 65e78bc893..95ee47a96f 100644 --- a/src/agent0/core/hyperdrive/interactive/chain.py +++ b/src/agent0/core/hyperdrive/interactive/chain.py @@ -1,16 +1,42 @@ """The chain objects that encapsulates a chain.""" +from __future__ import annotations + import contextlib +from dataclasses import dataclass from web3.types import BlockData, Timestamp from agent0.ethpy.base import initialize_web3_with_http_provider +from agent0.hyperlogs import setup_logging class Chain: """A class that represents a ethereum node.""" - def __init__(self, rpc_uri: str): + # Lots of config + # pylint: disable=too-many-instance-attributes + @dataclass(kw_only=True) + class Config: + """The configuration for the chain object.""" + + # Logging parameters + log_filename: str | None = None + """Path and name of the log file. Won't log to file if None. Defaults to None.""" + log_max_bytes: int | None = None + """Maximum size of the log file in bytes. Defaults to hyperlogs.DEFAULT_LOG_MAXBYTES.""" + log_level: int | None = None + """Log level to track. Defaults to hyperlogs.DEFAULT_LOG_LEVEL.""" + delete_previous_logs: bool = False + """Whether to delete previous log file if it exists. Defaults to False.""" + log_to_stdout: bool = True + """Whether to log to standard output. Defaults to True.""" + log_format_string: str | None = None + """Log formatter object. Defaults to None.""" + keep_previous_handlers: bool = False + """Whether to keep previous handlers. Defaults to False.""" + + def __init__(self, rpc_uri: str, config: Config | None = None): """Initialize the Chain class that connects to an existing chain. Also launches a postgres docker container for gathering data. @@ -18,8 +44,22 @@ def __init__(self, rpc_uri: str): --------- rpc_uri: str The uri for the chain to connect to, e.g., `http://localhost:8545`. + + config: Chain.Config + The chain configuration. """ - # TODO setup logging here + if config is None: + config = self.Config() + + setup_logging( + log_filename=config.log_filename, + max_bytes=config.log_max_bytes, + log_level=config.log_level, + delete_previous_logs=config.delete_previous_logs, + log_stdout=config.log_to_stdout, + log_format_string=config.log_format_string, + keep_previous_handlers=config.keep_previous_handlers, + ) self.rpc_uri = rpc_uri # Initialize web3 here for rpc calls diff --git a/src/agent0/core/hyperdrive/interactive/exec/execute_agent_trades.py b/src/agent0/core/hyperdrive/interactive/exec/execute_agent_trades.py index 828882b969..f4efa8c47d 100644 --- a/src/agent0/core/hyperdrive/interactive/exec/execute_agent_trades.py +++ b/src/agent0/core/hyperdrive/interactive/exec/execute_agent_trades.py @@ -34,9 +34,11 @@ from agent0.core.hyperdrive import HyperdrivePolicyAgent +# pylint: disable=too-many-arguments async def async_execute_agent_trades( interface: HyperdriveReadWriteInterface, agent: HyperdrivePolicyAgent, + preview_before_trade: bool, liquidate: bool, randomize_liquidation: bool = False, interactive_mode: bool = False, @@ -55,6 +57,8 @@ async def async_execute_agent_trades( The Hyperdrive API interface object. agent: HyperdrivePolicyAgent The HyperdrivePolicyAgent that is conducting the trade. + preview_before_trade: bool + Whether or not to preview the trade before it is executed. liquidate: bool If set, will ignore all policy settings and liquidate all open positions. randomize_liquidation: bool, optional @@ -99,7 +103,13 @@ async def async_execute_agent_trades( wallet_deltas_or_exception: list[tuple[HyperdriveWalletDeltas, ReceiptBreakdown] | BaseException] = ( await asyncio.gather( *[ - _async_match_contract_call_to_trade(agent, interface, trade_object, nonce=Nonce(base_nonce + i)) + _async_match_contract_call_to_trade( + agent, + interface, + trade_object, + nonce=Nonce(base_nonce + i), + preview_before_trade=preview_before_trade, + ) for i, trade_object in enumerate(trades) ], # Instead of throwing exception, return the exception to the caller here @@ -132,6 +142,7 @@ async def async_execute_single_trade( agent: HyperdrivePolicyAgent, trade_object: Trade[HyperdriveMarketAction], execute_policy_post_action: bool, + preview_before_trade: bool, ) -> TradeResult: """Executes a single trade made by the agent. @@ -149,6 +160,8 @@ async def async_execute_single_trade( The trade to execute. execute_policy_post_action: bool Whether or not to execute the post_action of the policy after the trade. + preview_before_trade: bool + Whether or not to preview the trade before it is executed. Returns ------- @@ -161,7 +174,13 @@ async def async_execute_single_trade( nonce = retry_call(DEFAULT_READ_RETRY_COUNT, None, interface.web3.eth.get_transaction_count, agent.checksum_address) try: - wallet_delta_or_exception = await _async_match_contract_call_to_trade(agent, interface, trade_object, nonce) + wallet_delta_or_exception = await _async_match_contract_call_to_trade( + agent, + interface, + trade_object, + nonce, + preview_before_trade, + ) except Exception as e: # pylint: disable=broad-except wallet_delta_or_exception = e @@ -259,6 +278,7 @@ async def _async_match_contract_call_to_trade( interface: HyperdriveReadWriteInterface, trade_envelope: Trade[HyperdriveMarketAction], nonce: Nonce, + preview_before_trade, ) -> tuple[HyperdriveWalletDeltas, ReceiptBreakdown]: """Match statement that executes the smart contract trade based on the provided type. @@ -290,6 +310,7 @@ async def _async_match_contract_call_to_trade( slippage_tolerance=trade.slippage_tolerance, gas_limit=trade.gas_limit, nonce=nonce, + preview_before_trade=preview_before_trade, ) wallet_deltas = HyperdriveWalletDeltas( balance=Quantity( @@ -313,6 +334,7 @@ async def _async_match_contract_call_to_trade( slippage_tolerance=trade.slippage_tolerance, gas_limit=trade.gas_limit, nonce=nonce, + preview_before_trade=preview_before_trade, ) wallet_deltas = HyperdriveWalletDeltas( balance=Quantity( @@ -333,6 +355,7 @@ async def _async_match_contract_call_to_trade( slippage_tolerance=trade.slippage_tolerance, gas_limit=trade.gas_limit, nonce=nonce, + preview_before_trade=preview_before_trade, ) wallet_deltas = HyperdriveWalletDeltas( balance=Quantity( @@ -356,6 +379,7 @@ async def _async_match_contract_call_to_trade( slippage_tolerance=trade.slippage_tolerance, gas_limit=trade.gas_limit, nonce=nonce, + preview_before_trade=preview_before_trade, ) wallet_deltas = HyperdriveWalletDeltas( balance=Quantity( @@ -382,6 +406,7 @@ async def _async_match_contract_call_to_trade( slippage_tolerance=None, gas_limit=trade.gas_limit, nonce=nonce, + preview_before_trade=preview_before_trade, ) wallet_deltas = HyperdriveWalletDeltas( balance=Quantity( @@ -393,7 +418,11 @@ async def _async_match_contract_call_to_trade( case HyperdriveActionType.REMOVE_LIQUIDITY: trade_result = await interface.async_remove_liquidity( - agent, trade.trade_amount, gas_limit=trade.gas_limit, nonce=nonce + agent, + trade.trade_amount, + gas_limit=trade.gas_limit, + nonce=nonce, + preview_before_trade=preview_before_trade, ) wallet_deltas = HyperdriveWalletDeltas( balance=Quantity( @@ -406,7 +435,11 @@ async def _async_match_contract_call_to_trade( case HyperdriveActionType.REDEEM_WITHDRAW_SHARE: trade_result = await interface.async_redeem_withdraw_shares( - agent, trade.trade_amount, gas_limit=trade.gas_limit, nonce=nonce + agent, + trade.trade_amount, + gas_limit=trade.gas_limit, + nonce=nonce, + preview_before_trade=preview_before_trade, ) wallet_deltas = HyperdriveWalletDeltas( balance=Quantity( diff --git a/src/agent0/core/hyperdrive/interactive/hyperdrive.py b/src/agent0/core/hyperdrive/interactive/hyperdrive.py index 65228176e3..f79b7dd10e 100644 --- a/src/agent0/core/hyperdrive/interactive/hyperdrive.py +++ b/src/agent0/core/hyperdrive/interactive/hyperdrive.py @@ -4,7 +4,6 @@ import asyncio import logging -import os from dataclasses import dataclass from typing import Any, Literal, Type, overload @@ -31,7 +30,6 @@ from agent0.core.hyperdrive.crash_report import log_hyperdrive_crash_report from agent0.core.hyperdrive.policies import HyperdriveBasePolicy from agent0.core.test_utils import assert_never -from agent0.ethpy import EthConfig from agent0.ethpy.base import set_anvil_account_balance, smart_contract_transact from agent0.ethpy.hyperdrive import ( HyperdriveReadWriteInterface, @@ -173,23 +171,9 @@ def __init__( else: self.config = config - # Define agent0 configs with this setup - # TODO use hypertypes abis here - # https://github.com/delvtech/agent0/issues/1125 - full_path = os.path.realpath(__file__) - current_file_dir, _ = os.path.split(full_path) - abi_dir = os.path.join(current_file_dir, "..", "..", "..", "..", "..", "packages", "hyperdrive", "src", "abis") - - self.eth_config = EthConfig( - artifacts_uri="not_used", - rpc_uri=chain.rpc_uri, - abi_dir=abi_dir, - preview_before_trade=self.config.preview_before_trade, - ) - self.interface = HyperdriveReadWriteInterface( - self.eth_config, hyperdrive_address, + rpc_uri=chain.rpc_uri, web3=chain._web3, txn_receipt_timeout=self.config.txn_receipt_timeout, ) @@ -302,7 +286,11 @@ def _open_long(self, agent: HyperdrivePolicyAgent, base: FixedPoint) -> OpenLong # TODO expose async here to the caller eventually trade_result: TradeResult = asyncio.run( async_execute_single_trade( - self.interface, agent, trade_object, self.config.always_execute_policy_post_action + self.interface, + agent, + trade_object, + self.config.always_execute_policy_post_action, + self.config.preview_before_trade, ) ) tx_receipt = self._handle_trade_result(trade_result, always_throw_exception=True) @@ -314,7 +302,11 @@ def _close_long(self, agent: HyperdrivePolicyAgent, maturity_time: int, bonds: F # TODO expose async here to the caller eventually trade_result: TradeResult = asyncio.run( async_execute_single_trade( - self.interface, agent, trade_object, self.config.always_execute_policy_post_action + self.interface, + agent, + trade_object, + self.config.always_execute_policy_post_action, + self.config.preview_before_trade, ) ) tx_receipt = self._handle_trade_result(trade_result, always_throw_exception=True) @@ -325,7 +317,11 @@ def _open_short(self, agent: HyperdrivePolicyAgent, bonds: FixedPoint) -> OpenSh # TODO expose async here to the caller eventually trade_result: TradeResult = asyncio.run( async_execute_single_trade( - self.interface, agent, trade_object, self.config.always_execute_policy_post_action + self.interface, + agent, + trade_object, + self.config.always_execute_policy_post_action, + self.config.preview_before_trade, ) ) tx_receipt = self._handle_trade_result(trade_result, always_throw_exception=True) @@ -336,7 +332,11 @@ def _close_short(self, agent: HyperdrivePolicyAgent, maturity_time: int, bonds: # TODO expose async here to the caller eventually trade_result: TradeResult = asyncio.run( async_execute_single_trade( - self.interface, agent, trade_object, self.config.always_execute_policy_post_action + self.interface, + agent, + trade_object, + self.config.always_execute_policy_post_action, + self.config.preview_before_trade, ) ) tx_receipt = self._handle_trade_result(trade_result, always_throw_exception=True) @@ -347,7 +347,11 @@ def _add_liquidity(self, agent: HyperdrivePolicyAgent, base: FixedPoint) -> AddL # TODO expose async here to the caller eventually trade_result: TradeResult = asyncio.run( async_execute_single_trade( - self.interface, agent, trade_object, self.config.always_execute_policy_post_action + self.interface, + agent, + trade_object, + self.config.always_execute_policy_post_action, + self.config.preview_before_trade, ) ) tx_receipt = self._handle_trade_result(trade_result, always_throw_exception=True) @@ -358,7 +362,11 @@ def _remove_liquidity(self, agent: HyperdrivePolicyAgent, shares: FixedPoint) -> # TODO expose async here to the caller eventually trade_result: TradeResult = asyncio.run( async_execute_single_trade( - self.interface, agent, trade_object, self.config.always_execute_policy_post_action + self.interface, + agent, + trade_object, + self.config.always_execute_policy_post_action, + self.config.preview_before_trade, ) ) tx_receipt = self._handle_trade_result(trade_result, always_throw_exception=True) @@ -369,7 +377,11 @@ def _redeem_withdraw_share(self, agent: HyperdrivePolicyAgent, shares: FixedPoin # TODO expose async here to the caller eventually trade_results: TradeResult = asyncio.run( async_execute_single_trade( - self.interface, agent, trade_object, self.config.always_execute_policy_post_action + self.interface, + agent, + trade_object, + self.config.always_execute_policy_post_action, + self.config.preview_before_trade, ) ) tx_receipt = self._handle_trade_result(trade_results, always_throw_exception=True) @@ -385,7 +397,13 @@ def _execute_policy_action( raise ValueError("Must pass in a policy in the constructor to execute policy action.") trade_results: list[TradeResult] = asyncio.run( - async_execute_agent_trades(self.interface, agent, liquidate=False, interactive_mode=True) + async_execute_agent_trades( + self.interface, + agent, + preview_before_trade=self.config.preview_before_trade, + liquidate=False, + interactive_mode=True, + ) ) out_events = [] # The underlying policy can execute multiple actions in one step @@ -405,6 +423,7 @@ def _liquidate( async_execute_agent_trades( self.interface, agent, + preview_before_trade=self.config.preview_before_trade, liquidate=True, randomize_liquidation=randomize, interactive_mode=True, diff --git a/src/agent0/core/hyperdrive/interactive/local_chain.py b/src/agent0/core/hyperdrive/interactive/local_chain.py index c3dc085447..dc965d132c 100644 --- a/src/agent0/core/hyperdrive/interactive/local_chain.py +++ b/src/agent0/core/hyperdrive/interactive/local_chain.py @@ -39,8 +39,8 @@ class LocalChain(Chain): # However, `load_state` is just a function stub that needs to throw `NotImplementedError` if called. # pylint: disable=abstract-method - @dataclass - class Config: + @dataclass(kw_only=True) + class Config(Chain.Config): """The configuration for the local chain object.""" block_time: int | None = None @@ -110,7 +110,7 @@ def __init__(self, config: Config | None = None, fork_uri: str | None = None, fo if fork_uri is not None: time.sleep(2) - super().__init__(f"http://127.0.0.1:{str(config.chain_port)}") + super().__init__(f"http://127.0.0.1:{str(config.chain_port)}", config) # Remove protocol and replace . and : with dashes self.chain_id = self.rpc_uri.replace("http://", "").replace("https://", "").replace(".", "-").replace(":", "-") diff --git a/src/agent0/core/hyperdrive/interactive/local_hyperdrive.py b/src/agent0/core/hyperdrive/interactive/local_hyperdrive.py index 92385e8ff8..e6ff710faa 100644 --- a/src/agent0/core/hyperdrive/interactive/local_hyperdrive.py +++ b/src/agent0/core/hyperdrive/interactive/local_hyperdrive.py @@ -315,9 +315,9 @@ def _launch_data_pipeline(self, start_block: int | None = None): kwargs={ "start_block": start_block, "lookback_block_limit": 10000, - "eth_config": self.eth_config, - "postgres_config": self.postgres_config, + "rpc_uri": self.chain.rpc_uri, "hyperdrive_address": self.interface.hyperdrive_address, + "postgres_config": self.postgres_config, "exit_on_catch_up": False, "exit_callback_fn": lambda: self._stop_threads, "suppress_logs": True, @@ -327,9 +327,9 @@ def _launch_data_pipeline(self, start_block: int | None = None): target=data_analysis, kwargs={ "start_block": start_block, - "eth_config": self.eth_config, - "postgres_config": self.postgres_config, + "rpc_uri": self.chain.rpc_uri, "hyperdrive_address": self.interface.hyperdrive_address, + "postgres_config": self.postgres_config, "exit_on_catch_up": False, "exit_callback_fn": lambda: self._stop_threads, "suppress_logs": True, diff --git a/src/agent0/core/hyperdrive/policies/random_test.py b/src/agent0/core/hyperdrive/policies/random_test.py index 46f6156d1a..f7cb4d4a79 100644 --- a/src/agent0/core/hyperdrive/policies/random_test.py +++ b/src/agent0/core/hyperdrive/policies/random_test.py @@ -22,7 +22,7 @@ @pytest.mark.anvil def test_random_policy(fast_chain_fixture: LocalChain): initial_pool_config = LocalHyperdrive.Config( - initial_liquidity=FixedPoint(1_000), + initial_liquidity=FixedPoint(1_000_000), initial_fixed_apr=FixedPoint("0.05"), position_duration=60 * 60 * 24 * 7, # 1 week checkpoint_duration=60 * 60 * 24, # 1 day @@ -30,7 +30,7 @@ def test_random_policy(fast_chain_fixture: LocalChain): interactive_hyperdrive = LocalHyperdrive(fast_chain_fixture, initial_pool_config) random_agent = interactive_hyperdrive.init_agent( - base=FixedPoint(1_000_000), + base=FixedPoint(100_000), eth=FixedPoint(100), name="alice", policy=PolicyZoo.random, @@ -46,7 +46,7 @@ def test_random_policy(fast_chain_fixture: LocalChain): @pytest.mark.anvil def test_random_policy_trades(fast_chain_fixture: LocalChain): initial_pool_config = LocalHyperdrive.Config( - initial_liquidity=FixedPoint(1_000), + initial_liquidity=FixedPoint(1_000_000), initial_fixed_apr=FixedPoint("0.05"), position_duration=60 * 60 * 24 * 7, # 1 week checkpoint_duration=60 * 60 * 24, # 1 day @@ -54,7 +54,7 @@ def test_random_policy_trades(fast_chain_fixture: LocalChain): interactive_hyperdrive = LocalHyperdrive(fast_chain_fixture, initial_pool_config) random_agent = interactive_hyperdrive.init_agent( - base=FixedPoint(1_000_000), + base=FixedPoint(100_000), eth=FixedPoint(100), name="alice", policy=PolicyZoo.random, diff --git a/src/agent0/ethpy/__init__.py b/src/agent0/ethpy/__init__.py index d03ed6bafd..fe88d9a132 100644 --- a/src/agent0/ethpy/__init__.py +++ b/src/agent0/ethpy/__init__.py @@ -1,3 +1 @@ -"""Loads config""" - -from .eth_config import EthConfig, build_eth_config +"""Ethpy is a library for interacting with a hyperdrive contract on a blockchain.""" diff --git a/src/agent0/ethpy/eth_config.py b/src/agent0/ethpy/eth_config.py deleted file mode 100644 index a68513e643..0000000000 --- a/src/agent0/ethpy/eth_config.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Defines the eth chain connection configuration from env vars.""" - -from __future__ import annotations - -import os -from dataclasses import dataclass - -from dotenv import load_dotenv -from eth_typing import URI - - -@dataclass -class EthConfig: - """The configuration dataclass for postgres connections.""" - - artifacts_uri: URI | str = URI("http://localhost:8080") - """The uri of the artifacts server from which we get addresses.""" - rpc_uri: URI | str = URI("http://localhost:8545") - """The uri to the ethereum node.""" - database_api_uri: URI | str | None = URI("http://localhost:5002") - """The uri to the database server. If set to None, we don't use the database.""" - abi_dir: str = "./packages/hyperdrive/src/abis" - """The path to the abi directory.""" - preview_before_trade: bool = False - """Whether to preview the trade before submitting it. Defaults to False.""" - - def __post_init__(self): - if isinstance(self.artifacts_uri, str): - self.artifacts_uri = URI(self.artifacts_uri) - if isinstance(self.rpc_uri, str): - self.rpc_uri = URI(self.rpc_uri) - if isinstance(self.database_api_uri, str): - self.database_api_uri = URI(self.database_api_uri) - - -def build_eth_config(dotenv_file: str = "eth.env") -> EthConfig: - """Build an eth config that looks for environmental variables. - If env var exists, use that, otherwise, default. - - Arguments - --------- - dotenv_file: str, optional - The path location of the dotenv file to load from. - Defaults to "eth.env". - - Returns - ------- - EthConfig - Config settings required to connect to the eth node - """ - # Look for and load local config if it exists - if os.path.exists(dotenv_file): - load_dotenv(dotenv_file) - - artifacts_uri = os.getenv("ARTIFACTS_URI") - rpc_uri = os.getenv("RPC_URI") - database_api_uri = os.getenv("DATABASE_API_URI") - abi_dir = os.getenv("ABI_DIR") - preview_before_trade = os.getenv("PREVIEW_BEFORE_TRADE") - - arg_dict = {} - if artifacts_uri is not None: - arg_dict["artifacts_uri"] = artifacts_uri - if rpc_uri is not None: - arg_dict["rpc_uri"] = rpc_uri - if database_api_uri is not None: - arg_dict["database_api_uri"] = database_api_uri - if abi_dir is not None: - arg_dict["abi_dir"] = abi_dir - if preview_before_trade is not None: - arg_dict["preview_before_trade"] = preview_before_trade - return EthConfig(**arg_dict) diff --git a/src/agent0/ethpy/eth_config_test.py b/src/agent0/ethpy/eth_config_test.py deleted file mode 100644 index 29b308bdbc..0000000000 --- a/src/agent0/ethpy/eth_config_test.py +++ /dev/null @@ -1,120 +0,0 @@ -"""Test for the eth_config.py class and functions.""" - -from __future__ import annotations - -import os - -from eth_typing import URI - -from .eth_config import build_eth_config - - -class TestBuildEthConfig: - """Tests for eth_config.py::build_eth_config().""" - - def test_build_eth_config_from_env(self): - """Test building the eth config from the environment.""" - # Get the current environment variables before changing them - rpc_uri = os.environ["RPC_URI"] if "RPC_URI" in os.environ else None - artifacts_uri = os.environ["ARTIFACTS_URI"] if "ARTIFACTS_URI" in os.environ else None - database_api_uri = os.environ["DATABASE_API_URI"] if "DATABASE_API_URI" in os.environ else None - abi_dir = os.environ["ABI_DIR"] if "ABI_DIR" in os.environ else None - - # Set the environment varialbes to known values - os.environ["RPC_URI"] = "http://localhost:9999" - os.environ["ARTIFACTS_URI"] = "http://localhost:9999" - os.environ["DATABASE_API_URI"] = "http://localhost:9999" - os.environ["ABI_DIR"] = "./packages/hyperdrive/src/abis/test" - - # Get the eth config, which should construct an EthConfig from the environment variables - eth_config = build_eth_config("") - - # Check values - assert eth_config.rpc_uri == URI("http://localhost:9999") - assert eth_config.artifacts_uri == URI("http://localhost:9999") - assert eth_config.database_api_uri == URI("http://localhost:9999") - assert eth_config.abi_dir == "./packages/hyperdrive/src/abis/test" - - # Reset the Environment variables if they were set before the test - if rpc_uri is not None: - os.environ["RPC_URI"] = rpc_uri - if artifacts_uri is not None: - os.environ["ARTIFACTS_URI"] = artifacts_uri - if database_api_uri is not None: - os.environ["DATABASE_API_URI"] = database_api_uri - if abi_dir is not None: - os.environ["ABI_DIR"] = abi_dir - - def test_build_eth_config_from_env_file(self, tmp_path): - """Test building the eth config from an environment file. - - Arguments - --------- - tmp_path: pathlib.Path - Pytest fixture for a temporary path; it is deleted after the test finishes. - - """ - # Create a temporary dotenv file - dotenv_file = tmp_path / "eth.env" - dotenv_file.write_text( - """ - # Local connection - RPC_URI="http://localhost:9999" - ARTIFACTS_URI="http://localhost:9999" - DATABASE_API_URI="http://localhost:9999" - ABI_DIR="./packages/hyperdrive/src/abis/test" - """ - ) - - # Get the eth config, which should construct an EthConfig from the dotenv file - eth_config = build_eth_config(str(dotenv_file)) - - # Check values - assert eth_config.rpc_uri == URI("http://localhost:9999") - assert eth_config.artifacts_uri == URI("http://localhost:9999") - assert eth_config.database_api_uri == URI("http://localhost:9999") - assert eth_config.abi_dir == "./packages/hyperdrive/src/abis/test" - - def test_build_eth_config_defaults(self): - """Test building the eth config from an environment file.""" - # Get the current environment variables before changing them - # Then delete all of the variables - if "RPC_URI" in os.environ: - rpc_uri = os.environ["RPC_URI"] - del os.environ["RPC_URI"] - else: - rpc_uri = None - if "ARTIFACTS_URI" in os.environ: - artifacts_uri = os.environ["ARTIFACTS_URI"] - del os.environ["ARTIFACTS_URI"] - else: - artifacts_uri = None - if "DATABASE_API_URI" in os.environ: - database_api_uri = os.environ["DATABASE_API_URI"] - del os.environ["DATABASE_API_URI"] - else: - database_api_uri = None - if "ABI_DIR" in os.environ: - abi_dir = os.environ["ABI_DIR"] - del os.environ["ABI_DIR"] - else: - abi_dir = None - - # Get the eth config, which should construct an EthConfig with default values - eth_config = build_eth_config("") - - # Check values - assert eth_config.artifacts_uri == URI("http://localhost:8080") - assert eth_config.rpc_uri == URI("http://localhost:8545") - assert eth_config.database_api_uri == URI("http://localhost:5002") - assert eth_config.abi_dir == "./packages/hyperdrive/src/abis" - - # Reset the Environment variables if they were set before the test - if rpc_uri is not None: - os.environ["RPC_URI"] = rpc_uri - if artifacts_uri is not None: - os.environ["ARTIFACTS_URI"] = artifacts_uri - if database_api_uri is not None: - os.environ["DATABASE_API_URI"] = database_api_uri - if abi_dir is not None: - os.environ["ABI_DIR"] = abi_dir diff --git a/src/agent0/ethpy/hyperdrive/interface/_contract_calls.py b/src/agent0/ethpy/hyperdrive/interface/_contract_calls.py index 2532ba1d5d..654a0d3ef3 100644 --- a/src/agent0/ethpy/hyperdrive/interface/_contract_calls.py +++ b/src/agent0/ethpy/hyperdrive/interface/_contract_calls.py @@ -654,6 +654,7 @@ async def _async_redeem_withdraw_shares( txn_options_base_fee_multiple: float | None = None, txn_options_priority_fee_multiple: float | None = None, nonce: Nonce | None = None, + preview_before_trade: bool = False, ) -> ReceiptBreakdown: """See API for documentation.""" # for now, assume an underlying vault share price of at least 1, should be higher by a bit @@ -679,20 +680,20 @@ async def _async_redeem_withdraw_shares( # before calling smart contract transact # Since current_pool_state.block_number is a property, we want to get the static block here current_block = interface.current_pool_state.block_number - preview_result = smart_contract_preview_transaction( - interface.hyperdrive_contract, - agent_checksum_address, - "redeemWithdrawalShares", - *fn_args, - block_number=current_block, - read_retry_count=interface.read_retry_count, - ) - # Here, a preview call of redeem withdrawal shares will still be successful without logs if - # the amount of shares to redeem is larger than what's in the wallet. We want to catch this error - # here with a useful error message, so we check that explicitly here - - if preview_result["withdrawalSharesRedeemed"] == 0 and trade_amount > 0: - raise ValueError("Preview call for redeem withdrawal shares returned 0 for non-zero input trade amount") + if preview_before_trade is True: + preview_result = smart_contract_preview_transaction( + interface.hyperdrive_contract, + agent_checksum_address, + "redeemWithdrawalShares", + *fn_args, + block_number=current_block, + read_retry_count=interface.read_retry_count, + ) + # Here, a preview call of redeem withdrawal shares will still be successful without logs if + # the amount of shares to redeem is larger than what's in the wallet. We want to catch this error + # here with a useful error message, so we check that explicitly here + if preview_result["withdrawalSharesRedeemed"] == 0 and trade_amount > 0: + raise ValueError("Preview call for redeem withdrawal shares returned 0 for non-zero input trade amount") try: tx_receipt = await async_smart_contract_transact( diff --git a/src/agent0/ethpy/hyperdrive/interface/read_interface.py b/src/agent0/ethpy/hyperdrive/interface/read_interface.py index edf4646f21..68c16a9ea0 100644 --- a/src/agent0/ethpy/hyperdrive/interface/read_interface.py +++ b/src/agent0/ethpy/hyperdrive/interface/read_interface.py @@ -4,7 +4,6 @@ import copy import logging -import os from typing import TYPE_CHECKING, cast from eth_account import Account @@ -12,9 +11,7 @@ from web3.exceptions import BadFunctionCallOutput, ContractLogicError from web3.types import BlockData, BlockIdentifier, Timestamp -from agent0.ethpy import build_eth_config from agent0.ethpy.base import initialize_web3_with_http_provider -from agent0.ethpy.hyperdrive.addresses import get_hyperdrive_addresses_from_artifacts from agent0.ethpy.hyperdrive.deploy import DeployedHyperdrivePool from agent0.ethpy.hyperdrive.state import PoolState from agent0.ethpy.hyperdrive.transactions import ( @@ -77,8 +74,6 @@ from eth_typing import BlockNumber, ChecksumAddress from web3 import Web3 - from agent0.ethpy import EthConfig - class HyperdriveReadInterface: """Read-only end-point API for interfacing with a deployed Hyperdrive pool.""" @@ -87,8 +82,8 @@ class HyperdriveReadInterface: def __init__( self, - eth_config: EthConfig | None = None, - hyperdrive_address: ChecksumAddress | None = None, + hyperdrive_address: ChecksumAddress, + rpc_uri: str | None = None, web3: Web3 | None = None, read_retry_count: int | None = None, txn_receipt_timeout: float | None = None, @@ -99,32 +94,27 @@ def __init__( Arguments --------- - eth_config: EthConfig, optional - Configuration dataclass for the ethereum environment. - If given, then it is constructed from environment variables. - hyperdrive_address: ChecksumAddress | None, optional + hyperdrive_address: ChecksumAddress This is a contract address for a deployed hyperdrive. - If given, then the `eth_config.artifacts_uri` variable is not used, and this address is used instead. - If not given, then we use the erc4626_hyperdrive contract from `eth_config.artifacts_uri`. + rpc_uri: str, optional + The URI to initialize the web3 provider. Not used if web3 is provided. web3: Web3, optional web3 provider object, optional - If given, a web3 object is constructed using the `eth_config.rpc_uri` as the http provider. + If not given, a web3 object is constructed using the `rpc_uri` as the http provider. read_retry_count: int | None, optional The number of times to retry the read call if it fails. Defaults to 5. txn_receipt_timeout: float | None, optional The timeout for waiting for a transaction receipt in seconds. Defaults to 120. """ # Handle defaults for config and addresses. - self.eth_config: EthConfig = build_eth_config() if eth_config is None else eth_config - if hyperdrive_address is None: - hyperdrive_address = get_hyperdrive_addresses_from_artifacts( - os.path.join(self.eth_config.artifacts_uri, "addresses.json") - )["erc4626_hyperdrive"] - self.hyperdrive_address = hyperdrive_address + # Setup provider for communicating with the chain. + if web3 is None and rpc_uri is None: + raise ValueError("Must provide either `web3` or `rpc_uri`") if web3 is None: - web3 = initialize_web3_with_http_provider(self.eth_config.rpc_uri, reset_provider=False) + assert rpc_uri is not None + web3 = initialize_web3_with_http_provider(rpc_uri, reset_provider=False) self.web3 = web3 # Setup Hyperdrive contract diff --git a/src/agent0/ethpy/hyperdrive/interface/read_write_interface.py b/src/agent0/ethpy/hyperdrive/interface/read_write_interface.py index 22f4940991..4db2afa44d 100644 --- a/src/agent0/ethpy/hyperdrive/interface/read_write_interface.py +++ b/src/agent0/ethpy/hyperdrive/interface/read_write_interface.py @@ -31,7 +31,6 @@ from web3 import Web3 from web3.types import Nonce - from agent0.ethpy import EthConfig from agent0.ethpy.hyperdrive.receipt_breakdown import ReceiptBreakdown @@ -40,8 +39,8 @@ class HyperdriveReadWriteInterface(HyperdriveReadInterface): def __init__( self, - eth_config: EthConfig | None = None, - hyperdrive_address: ChecksumAddress | None = None, + hyperdrive_address: ChecksumAddress, + rpc_uri: str | None = None, web3: Web3 | None = None, read_retry_count: int | None = None, write_retry_count: int | None = None, @@ -51,16 +50,13 @@ def __init__( Arguments --------- - eth_config: EthConfig, optional - Configuration dataclass for the ethereum environment. - If given, then it is constructed from environment variables. - hyperdrive_address: ChecksumAddress | None, optional + hyperdrive_address: ChecksumAddress This is a contract address for a deployed hyperdrive. - If given, then the `eth_config.artifacts_uri` variable is not used, and this address is used instead. - If not given, then we use the erc4626_hyperdrive contract from `eth_config.artifacts_uri`. + rpc_uri: str, optional + The URI to initialize the web3 provider. Not used if web3 is provided. web3: Web3, optional web3 provider object, optional - If given, a web3 object is constructed using the `eth_config.rpc_uri` as the http provider. + If not given, a web3 object is constructed using the `rpc_uri` as the http provider. read_retry_count: int | None, optional The number of times to retry the read call if it fails. Defaults to 5. write_retry_count: int | None, optional @@ -69,8 +65,8 @@ def __init__( The timeout for waiting for a transaction receipt in seconds. Defaults to 120. """ super().__init__( - eth_config=eth_config, hyperdrive_address=hyperdrive_address, + rpc_uri=rpc_uri, web3=web3, read_retry_count=read_retry_count, txn_receipt_timeout=txn_receipt_timeout, @@ -86,7 +82,6 @@ def get_read_interface(self) -> HyperdriveReadInterface: This instantiated object, but as a ReadInterface. """ return HyperdriveReadInterface( - eth_config=self.eth_config, hyperdrive_address=self.hyperdrive_address, web3=self.web3, read_retry_count=self.read_retry_count, @@ -144,6 +139,7 @@ async def async_open_long( txn_options_base_fee_multiple: float | None = None, txn_options_priority_fee_multiple: float | None = None, nonce: Nonce | None = None, + preview_before_trade: bool = False, ) -> ReceiptBreakdown: """Contract call to open a long position. @@ -166,6 +162,8 @@ async def async_open_long( The multiple applied to the priority fee for the transaction. Defaults to 1. nonce: Nonce, optional An optional explicit nonce to set with the transaction. + preview_before_trade: bool, optional + Whether to preview the trade before executing. Defaults to False. Returns ------- @@ -181,7 +179,7 @@ async def async_open_long( txn_options_base_fee_multiple=txn_options_base_fee_multiple, txn_options_priority_fee_multiple=txn_options_priority_fee_multiple, nonce=nonce, - preview_before_trade=self.eth_config.preview_before_trade, + preview_before_trade=preview_before_trade, ) # We do not control the number of arguments; this is set by hyperdrive-rs @@ -196,6 +194,7 @@ async def async_close_long( txn_options_base_fee_multiple: float | None = None, txn_options_priority_fee_multiple: float | None = None, nonce: Nonce | None = None, + preview_before_trade: bool = False, ) -> ReceiptBreakdown: """Contract call to close a long position. @@ -220,6 +219,8 @@ async def async_close_long( The multiple applied to the priority fee for the transaction. Defaults to 1. nonce: Nonce, optional An optional explicit nonce to set with the transaction. + preview_before_trade: bool, optional + Whether to preview the trade before executing. Defaults to False. Returns ------- @@ -236,7 +237,7 @@ async def async_close_long( txn_options_base_fee_multiple=txn_options_base_fee_multiple, txn_options_priority_fee_multiple=txn_options_priority_fee_multiple, nonce=nonce, - preview_before_trade=self.eth_config.preview_before_trade, + preview_before_trade=preview_before_trade, ) async def async_open_short( @@ -248,6 +249,7 @@ async def async_open_short( txn_options_base_fee_multiple: float | None = None, txn_options_priority_fee_multiple: float | None = None, nonce: Nonce | None = None, + preview_before_trade: bool = False, ) -> ReceiptBreakdown: """Contract call to open a short position. @@ -270,6 +272,8 @@ async def async_open_short( The multiple applied to the priority fee for the transaction. Defaults to 1. nonce: Nonce, optional An explicit nonce to set with the transaction. + preview_before_trade: bool, optional + Whether to preview the trade before executing. Defaults to False. Returns ------- @@ -285,7 +289,7 @@ async def async_open_short( txn_options_base_fee_multiple=txn_options_base_fee_multiple, txn_options_priority_fee_multiple=txn_options_priority_fee_multiple, nonce=nonce, - preview_before_trade=self.eth_config.preview_before_trade, + preview_before_trade=preview_before_trade, ) # We do not control the number of arguments; this is set by hyperdrive-rs @@ -300,6 +304,7 @@ async def async_close_short( txn_options_base_fee_multiple: float | None = None, txn_options_priority_fee_multiple: float | None = None, nonce: Nonce | None = None, + preview_before_trade: bool = False, ) -> ReceiptBreakdown: """Contract call to close a short position. @@ -324,6 +329,8 @@ async def async_close_short( The multiple applied to the priority fee for the transaction. Defaults to 1. nonce: Nonce | None, optional An explicit nonce to set with the transaction. + preview_before_trade: bool, optional + Whether to preview the trade before executing. Defaults to False. Returns ------- @@ -340,7 +347,7 @@ async def async_close_short( txn_options_base_fee_multiple=txn_options_base_fee_multiple, txn_options_priority_fee_multiple=txn_options_priority_fee_multiple, nonce=nonce, - preview_before_trade=self.eth_config.preview_before_trade, + preview_before_trade=preview_before_trade, ) # We do not control the number of arguments; this is set by hyperdrive-rs @@ -356,6 +363,7 @@ async def async_add_liquidity( txn_options_base_fee_multiple: float | None = None, txn_options_priority_fee_multiple: float | None = None, nonce: Nonce | None = None, + preview_before_trade: bool = False, ) -> ReceiptBreakdown: """Contract call to add liquidity to the Hyperdrive pool. @@ -382,6 +390,8 @@ async def async_add_liquidity( The multiple applied to the priority fee for the transaction. Defaults to 1. nonce: Nonce | None, optional An explicit nonce to set with the transaction. + preview_before_trade: bool, optional + Whether to preview the trade before executing. Defaults to False. Returns ------- @@ -399,7 +409,7 @@ async def async_add_liquidity( txn_options_base_fee_multiple=txn_options_base_fee_multiple, txn_options_priority_fee_multiple=txn_options_priority_fee_multiple, nonce=nonce, - preview_before_trade=self.eth_config.preview_before_trade, + preview_before_trade=preview_before_trade, ) async def async_remove_liquidity( @@ -410,6 +420,7 @@ async def async_remove_liquidity( txn_options_base_fee_multiple: float | None = None, txn_options_priority_fee_multiple: float | None = None, nonce: Nonce | None = None, + preview_before_trade: bool = False, ) -> ReceiptBreakdown: """Contract call to remove liquidity from the Hyperdrive pool. @@ -428,6 +439,8 @@ async def async_remove_liquidity( The multiple applied to the priority fee for the transaction. Defaults to 1. nonce: Nonce | None, optional An explicit nonce to set with the transaction. + preview_before_trade: bool, optional + Whether to preview the trade before executing. Defaults to False. Returns ------- @@ -442,7 +455,7 @@ async def async_remove_liquidity( txn_options_base_fee_multiple=txn_options_base_fee_multiple, txn_options_priority_fee_multiple=txn_options_priority_fee_multiple, nonce=nonce, - preview_before_trade=self.eth_config.preview_before_trade, + preview_before_trade=preview_before_trade, ) async def async_redeem_withdraw_shares( @@ -453,6 +466,7 @@ async def async_redeem_withdraw_shares( txn_options_base_fee_multiple: float | None = None, txn_options_priority_fee_multiple: float | None = None, nonce: Nonce | None = None, + preview_before_trade: bool = False, ) -> ReceiptBreakdown: """Contract call to redeem withdraw shares from Hyperdrive pool. @@ -479,6 +493,8 @@ async def async_redeem_withdraw_shares( The multiple applied to the priority fee for the transaction. Defaults to 1. nonce: Nonce | None, optional An explicit nonce to set with the transaction. + preview_before_trade: bool, optional + Whether to preview the trade before executing. Defaults to False. Returns ------- @@ -493,4 +509,5 @@ async def async_redeem_withdraw_shares( txn_options_base_fee_multiple=txn_options_base_fee_multiple, txn_options_priority_fee_multiple=txn_options_priority_fee_multiple, nonce=nonce, + preview_before_trade=preview_before_trade, ) diff --git a/src/agent0/hyperfuzz/system_fuzz/run_fuzz_bots.py b/src/agent0/hyperfuzz/system_fuzz/run_fuzz_bots.py index 5f84d5319e..af52ce55fe 100644 --- a/src/agent0/hyperfuzz/system_fuzz/run_fuzz_bots.py +++ b/src/agent0/hyperfuzz/system_fuzz/run_fuzz_bots.py @@ -112,7 +112,7 @@ def generate_fuzz_hyperdrive_config(rng: Generator, log_to_rollbar: bool, rng_se # TODO move this to somewhere that's more general -async def _async_runner( +async def async_runner( return_exceptions: bool, funcs: list[Callable[P, R]], *args: P.args, @@ -271,7 +271,7 @@ def run_fuzz_bots( logging.info("Funding bots...") if run_async: asyncio.run( - _async_runner( + async_runner( return_exceptions=True, funcs=[agent.add_funds for agent in agents], base=base_budget_per_bot, @@ -284,7 +284,7 @@ def run_fuzz_bots( logging.info("Setting max approval...") if run_async: asyncio.run( - _async_runner( + async_runner( return_exceptions=True, funcs=[agent.set_max_approval for agent in agents], ) @@ -304,7 +304,7 @@ def run_fuzz_bots( try: if run_async: trades = asyncio.run( - _async_runner( + async_runner( return_exceptions=True, funcs=[agent.execute_policy_action for agent in agents], ) @@ -342,7 +342,7 @@ def run_fuzz_bots( logging.info("Refunding agents...") if run_async: asyncio.run( - _async_runner( + async_runner( return_exceptions=True, funcs=[agent.add_funds for agent in agents], base=base_budget_per_bot, diff --git a/src/agent0/hyperfuzz/unit_fuzz/fuzz_long_short_maturity_values.py b/src/agent0/hyperfuzz/unit_fuzz/fuzz_long_short_maturity_values.py index 4c4697794f..bf71ec7ae7 100644 --- a/src/agent0/hyperfuzz/unit_fuzz/fuzz_long_short_maturity_values.py +++ b/src/agent0/hyperfuzz/unit_fuzz/fuzz_long_short_maturity_values.py @@ -57,7 +57,6 @@ def fuzz_long_short_maturity_values( long_maturity_vals_epsilon: float, short_maturity_vals_epsilon: float, chain_config: LocalChain.Config | None = None, - log_to_stdout: bool = False, ): """Does fuzzy invariant checks on closing longs and shorts past maturity. @@ -71,20 +70,14 @@ def fuzz_long_short_maturity_values( The allowed error for maturity values equality tests for shorts. chain_config: LocalChain.Config, optional Configuration options for the local chain. - log_to_stdout: bool, optional - If True, log to stdout in addition to a file. - Defaults to False. """ - log_filename = ".logging/fuzz_long_short_maturity_values.log" # Parameters for local chain initialization, defines defaults in constructor # set a large block time so i can manually control when it ticks # TODO: set block time really high after contracts deployed: # chain_config = LocalChain.Config(block_time=1_000_000) chain, random_seed, rng, interactive_hyperdrive = setup_fuzz( - log_filename, chain_config, - log_to_stdout, fuzz_test_name="fuzz_long_short_maturity_values", ) signer = interactive_hyperdrive.init_agent(eth=FixedPoint(100)) @@ -195,7 +188,6 @@ class Args(NamedTuple): long_maturity_vals_epsilon: float short_maturity_vals_epsilon: float chain_config: LocalChain.Config - log_to_stdout: bool def namespace_to_args(namespace: argparse.Namespace) -> Args: @@ -216,8 +208,7 @@ def namespace_to_args(namespace: argparse.Namespace) -> Args: num_trades=namespace.num_trades, long_maturity_vals_epsilon=namespace.long_maturity_vals_epsilon, short_maturity_vals_epsilon=namespace.short_maturity_vals_epsilon, - chain_config=LocalChain.Config(chain_port=namespace.chain_port), - log_to_stdout=namespace.log_to_stdout, + chain_config=LocalChain.Config(chain_port=namespace.chain_port, log_to_stdout=namespace.log_to_stdout), ) diff --git a/src/agent0/hyperfuzz/unit_fuzz/fuzz_path_independence.py b/src/agent0/hyperfuzz/unit_fuzz/fuzz_path_independence.py index aa6a530fec..230bbd4829 100644 --- a/src/agent0/hyperfuzz/unit_fuzz/fuzz_path_independence.py +++ b/src/agent0/hyperfuzz/unit_fuzz/fuzz_path_independence.py @@ -72,7 +72,6 @@ def fuzz_path_independence( effective_share_reserves_epsilon: float, present_value_epsilon: float, chain_config: LocalChain.Config, - log_to_stdout: bool = False, ): """Does fuzzy invariant checks for opening and closing longs and shorts. @@ -90,9 +89,6 @@ def fuzz_path_independence( The allowed error for present value equality tests. chain_config: LocalChain.Config, optional Configuration options for the local chain. - log_to_stdout: bool, optional - If True, log to stdout in addition to a file. - Defaults to False. """ # pylint: disable=too-many-statements # pylint: disable=too-many-arguments @@ -102,11 +98,8 @@ def fuzz_path_independence( if perm(num_trades) < 2 * num_paths_checked: raise AssertionError("Need more trades to check {num_paths_checked} paths.") - log_filename = ".logging/fuzz_path_independence.log" chain, random_seed, rng, interactive_hyperdrive = setup_fuzz( - log_filename, chain_config, - log_to_stdout, # Trade crashes in this file have expected failures, hence we log interactive # hyperdrive crashes as info instead of critical. crash_log_level=logging.INFO, @@ -300,7 +293,6 @@ class Args(NamedTuple): effective_share_reserves_epsilon: float present_value_epsilon: float chain_config: LocalChain.Config - log_to_stdout: bool def namespace_to_args(namespace: argparse.Namespace) -> Args: @@ -322,8 +314,7 @@ def namespace_to_args(namespace: argparse.Namespace) -> Args: lp_share_price_epsilon=namespace.lp_share_price_epsilon, effective_share_reserves_epsilon=namespace.effective_share_reserves_epsilon, present_value_epsilon=namespace.present_value_epsilon, - chain_config=LocalChain.Config(chain_port=namespace.chain_port), - log_to_stdout=namespace.log_to_stdout, + chain_config=LocalChain.Config(chain_port=namespace.chain_port, log_to_stdout=namespace.log_to_stdout), ) diff --git a/src/agent0/hyperfuzz/unit_fuzz/fuzz_present_value.py b/src/agent0/hyperfuzz/unit_fuzz/fuzz_present_value.py index 8312f05bd3..de23dc5d1b 100644 --- a/src/agent0/hyperfuzz/unit_fuzz/fuzz_present_value.py +++ b/src/agent0/hyperfuzz/unit_fuzz/fuzz_present_value.py @@ -55,7 +55,6 @@ def main(argv: Sequence[str] | None = None): def fuzz_present_value( test_epsilon: float, chain_config: LocalChain.Config, - log_to_stdout: bool = False, ): """Does fuzzy invariant checks for opening and closing longs and shorts. @@ -65,15 +64,9 @@ def fuzz_present_value( The allowed error for present value equality tests. chain_config: LocalChain.Config, optional Configuration options for the local chain. - log_to_stdout: bool, optional - If True, log to stdout in addition to a file. - Defaults to False. """ - log_filename = ".logging/fuzz_present_value.log" chain, random_seed, rng, interactive_hyperdrive = setup_fuzz( - log_filename, chain_config, - log_to_stdout, curve_fee=FixedPoint(0), flat_fee=FixedPoint(0), governance_lp_fee=FixedPoint(0), @@ -203,7 +196,6 @@ class Args(NamedTuple): test_epsilon: float chain_config: LocalChain.Config - log_to_stdout: bool def namespace_to_args(namespace: argparse.Namespace) -> Args: @@ -221,8 +213,7 @@ def namespace_to_args(namespace: argparse.Namespace) -> Args: """ return Args( test_epsilon=namespace.test_epsilon, - chain_config=LocalChain.Config(chain_port=namespace.chain_port), - log_to_stdout=namespace.log_to_stdout, + chain_config=LocalChain.Config(chain_port=namespace.chain_port, log_to_stdout=namespace.log_to_stdout), ) diff --git a/src/agent0/hyperfuzz/unit_fuzz/fuzz_profit_check.py b/src/agent0/hyperfuzz/unit_fuzz/fuzz_profit_check.py index ccb572b7ed..e5b6b3cbe1 100644 --- a/src/agent0/hyperfuzz/unit_fuzz/fuzz_profit_check.py +++ b/src/agent0/hyperfuzz/unit_fuzz/fuzz_profit_check.py @@ -53,25 +53,19 @@ def main(argv: Sequence[str] | None = None): fuzz_profit_check(*parsed_args) -def fuzz_profit_check(chain_config: LocalChain.Config | None = None, log_to_stdout: bool = False): +def fuzz_profit_check(chain_config: LocalChain.Config | None = None): """Fuzzes invariant checks for profit from long and short positions. Parameters ---------- chain_config: LocalChain.Config, optional Configuration options for the local chain. - log_to_stdout: bool, optional - If True, log to stdout in addition to a file. - Defaults to False. """ # pylint: disable=too-many-statements # Setup the environment - log_filename = ".logging/fuzz_profit_check.log" chain, random_seed, rng, interactive_hyperdrive = setup_fuzz( - log_filename, chain_config, - log_to_stdout, fuzz_test_name="fuzz_profit_check", flat_fee=FixedPoint(0), curve_fee=FixedPoint(0.001), # 0.1% @@ -224,7 +218,6 @@ class Args(NamedTuple): """Command line arguments for the invariant checker.""" chain_config: LocalChain.Config - log_to_stdout: bool def namespace_to_args(namespace: argparse.Namespace) -> Args: @@ -241,8 +234,7 @@ def namespace_to_args(namespace: argparse.Namespace) -> Args: Formatted arguments """ return Args( - chain_config=LocalChain.Config(chain_port=namespace.chain_port), - log_to_stdout=namespace.log_to_stdout, + chain_config=LocalChain.Config(chain_port=namespace.chain_port, log_to_stdout=namespace.log_to_stdout), ) diff --git a/src/agent0/hyperfuzz/unit_fuzz/helpers/setup_fuzz.py b/src/agent0/hyperfuzz/unit_fuzz/helpers/setup_fuzz.py index 75d5974f7d..2bdab170de 100644 --- a/src/agent0/hyperfuzz/unit_fuzz/helpers/setup_fuzz.py +++ b/src/agent0/hyperfuzz/unit_fuzz/helpers/setup_fuzz.py @@ -9,13 +9,10 @@ from numpy.random._generator import Generator from agent0.core.hyperdrive.interactive import LocalChain, LocalHyperdrive -from agent0.hyperlogs import setup_logging def setup_fuzz( - log_filename: str, chain_config: LocalChain.Config | None = None, - log_to_stdout: bool = False, log_to_rollbar: bool = True, crash_log_level: int | None = None, fuzz_test_name: str | None = None, @@ -29,14 +26,8 @@ def setup_fuzz( Arguments --------- - log_filename: str - Output location for the logging file, - which will include state information if the test fails. chain_config: LocalChain.Config, optional Configuration options for the local chain. - log_to_stdout: bool, optional - If True, log to stdout in addition to a file. - Defaults to False. log_to_rollbar: bool, optional If True, log errors rollbar. Defaults to True. crash_log_level: int | None, optional @@ -70,11 +61,6 @@ def setup_fuzz( """ # pylint: disable=too-many-arguments # pylint: disable=too-many-locals - setup_logging( - log_filename=log_filename, - delete_previous_logs=False, - log_stdout=log_to_stdout, - ) # Setup local chain config = chain_config if chain_config else LocalChain.Config() diff --git a/src/agent0/test_fixtures/hyperdrive_fixture.py b/src/agent0/test_fixtures/hyperdrive_fixture.py index b3dd769334..b6eb0de96d 100644 --- a/src/agent0/test_fixtures/hyperdrive_fixture.py +++ b/src/agent0/test_fixtures/hyperdrive_fixture.py @@ -35,6 +35,8 @@ def launch_hyperdrive(in_chain: LocalChain) -> LocalHyperdrive: # instead of changing the default here position_duration=60 * 60 * 24 * 365, # 1 year flat_fee=FixedPoint("0.0005"), + # Always preview before trade in tests + preview_before_trade=True, ) return LocalHyperdrive(in_chain, config) diff --git a/tests/pipeline_with_random_test.py b/tests/pipeline_with_random_test.py index 702b1464d9..fc165ee619 100644 --- a/tests/pipeline_with_random_test.py +++ b/tests/pipeline_with_random_test.py @@ -23,12 +23,15 @@ def test_pipeline_with_random_policy( All arguments are fixtures. """ agent = fast_hyperdrive_fixture.init_agent( - base=FixedPoint(10_000_000), + base=FixedPoint(1_000_000), eth=FixedPoint(100), policy=PolicyZoo.random, policy_config=PolicyZoo.random.Config(slippage_tolerance=None), ) + # Add liquidity to avoid insufficient liquidity error + agent.add_liquidity(FixedPoint(500_000)) + # Do a handful of trades for _ in range(3): agent.execute_policy_action()