From c8b925de7c219c74d000f96dfe2b9c943eeffe1d Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 12:25:39 -0700 Subject: [PATCH 01/16] Using conftest.py to avoid importing dependent test fixtures --- conftest.py | 25 +++++++++++++------- lib/ethpy/ethpy/test_fixtures/local_chain.py | 1 + requirements-dev.txt | 5 +++- tests/system_test.py | 12 ++++------ 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/conftest.py b/conftest.py index 8abc9633f2..e2b65fb340 100644 --- a/conftest.py +++ b/conftest.py @@ -1,3 +1,13 @@ +# Ignore docstrings for this file +# pylint: disable=missing-docstring + + +import os + +import pytest +from chainsync.test_fixtures import database_engine, db_session, dummy_session, psql_docker +from ethpy.test_fixtures import local_chain, local_hyperdrive_chain + # Hack to allow for vscode debugger to throw exception immediately # instead of allowing pytest to catch the exception and report # Based on https://stackoverflow.com/questions/62419998/how-can-i-get-pytest-to-not-catch-exceptions/62563106#62563106 @@ -15,15 +25,6 @@ # "_PYTEST_RAISE": "1" # }, # }, - -# Ignore docstrings for this file -# pylint: disable=missing-docstring - - -import os - -import pytest - if os.getenv("_PYTEST_RAISE", "0") != "0": @pytest.hookimpl(tryfirst=True) @@ -33,3 +34,9 @@ def pytest_exception_interact(call): @pytest.hookimpl(tryfirst=True) def pytest_internalerror(excinfo): raise excinfo.value + + +# Importing all fixtures here and defining here +# This allows for users of fixtures to not have to import all dependency fixtures when running +# TODO this means pytest can only be ran from this directory +__all__ = ["database_engine", "db_session", "dummy_session", "psql_docker", "local_chain", "local_hyperdrive_chain"] diff --git a/lib/ethpy/ethpy/test_fixtures/local_chain.py b/lib/ethpy/ethpy/test_fixtures/local_chain.py index 510ea8fbe6..73b6ea0560 100644 --- a/lib/ethpy/ethpy/test_fixtures/local_chain.py +++ b/lib/ethpy/ethpy/test_fixtures/local_chain.py @@ -74,6 +74,7 @@ def local_hyperdrive_chain(local_chain: str) -> dict: hyperdrive_addr = deploy_and_initialize_hyperdrive(web3, base_token_contract, factory_contract, account) return { + "rpc_url": local_chain, "web3": web3, "deploy_account": account, "hyperdrive_contract_addresses": HyperdriveAddresses( diff --git a/requirements-dev.txt b/requirements-dev.txt index cda1a8eccd..fabab4d06f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -19,4 +19,7 @@ sphinx-rtd-theme>=1.2.2 sphinxcontrib-napoleon>=0.7 tomli>=2.0.1 # urllib3 v2 requires OpenSSL v1.1.1+, but Vercel uses v1.0.2 -urllib3<2.0 \ No newline at end of file +urllib3<2.0 +# Used for tests of db interface +docker +pytest-postgresql \ No newline at end of file diff --git a/tests/system_test.py b/tests/system_test.py index 08a35358ee..70abd160cd 100644 --- a/tests/system_test.py +++ b/tests/system_test.py @@ -28,7 +28,7 @@ cycle_trade_policy, ) from chainsync.test_fixtures import db_session # pylint: disable=unused-import, ungrouped-imports -from ethpy.test_fixtures import local_chain, local_hyperdrive_chain # pylint: disable=unused-import, ungrouped-imports +from ethpy.test_fixtures import local_hyperdrive_chain # pylint: disable=unused-import, ungrouped-imports # fixture arguments in test function have to be the same as the fixture name # pylint: disable=redefined-outer-name @@ -37,11 +37,9 @@ class TestLocalChain: """Tests bringing up local chain""" - # This is using 2 fixtures. Since hyperdrive_contract_address depends on local_chain, we need both here - # This is due to adding test fixtures through imports - def test_hyperdrive_init_and_deploy(self, local_chain: str, local_hyperdrive_chain: dict): + def test_hyperdrive_init_and_deploy(self, local_hyperdrive_chain: dict): """Create and entry""" - print(local_chain) + print(local_hyperdrive_chain["rpc_url"]) print(local_hyperdrive_chain) @@ -60,7 +58,6 @@ class TestBotToDb: # pylint: disable=too-many-locals, too-many-statements def test_bot_to_db( self, - local_chain: str, local_hyperdrive_chain: dict, cycle_trade_policy: Type[BasePolicy], db_session: Session, @@ -69,6 +66,7 @@ def test_bot_to_db( All arguments are fixtures. """ # Get hyperdrive chain info + rpc_url: str = local_hyperdrive_chain["rpc_url"] deploy_account: LocalAccount = local_hyperdrive_chain["deploy_account"] hyperdrive_contract_addresses: HyperdriveAddresses = local_hyperdrive_chain["hyperdrive_contract_addresses"] @@ -102,7 +100,7 @@ def test_bot_to_db( eth_config = EthConfig( # Artifacts_url isn't used here, as we explicitly set addresses and passed to run_bots ARTIFACTS_URL="not_used", - RPC_URL=local_chain, + RPC_URL=rpc_url, # Using default abi dir ) From aebcdc1116ab5b9c84c4a2022a5d1b03f24f932a Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 12:42:52 -0700 Subject: [PATCH 02/16] Adding test fixture to bring up local postgres container --- lib/chainsync/chainsync/db/base/interface.py | 7 +- .../chainsync/test_fixtures/__init__.py | 2 +- .../chainsync/test_fixtures/db_session.py | 67 +++++++++++++++++-- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/lib/chainsync/chainsync/db/base/interface.py b/lib/chainsync/chainsync/db/base/interface.py index 21a349c410..c4c6b03876 100644 --- a/lib/chainsync/chainsync/db/base/interface.py +++ b/lib/chainsync/chainsync/db/base/interface.py @@ -8,7 +8,7 @@ import pandas as pd import sqlalchemy -from chainsync import build_postgres_config +from chainsync import PostgresConfig, build_postgres_config from sqlalchemy import URL, Column, Engine, MetaData, String, Table, create_engine, exc, func, inspect from sqlalchemy.exc import OperationalError from sqlalchemy.ext.declarative import declared_attr @@ -56,7 +56,7 @@ def drop_table(session: Session, table_name: str) -> None: table.drop(checkfirst=True, bind=bind) -def initialize_engine() -> Engine: +def initialize_engine(postgres_config: PostgresConfig | None = None) -> Engine: """Initializes the postgres engine from config Returns @@ -64,7 +64,8 @@ def initialize_engine() -> Engine: Engine The initialized engine object connected to postgres """ - postgres_config = build_postgres_config() + if postgres_config is None: + postgres_config = build_postgres_config() url_object = URL.create( drivername="postgresql", diff --git a/lib/chainsync/chainsync/test_fixtures/__init__.py b/lib/chainsync/chainsync/test_fixtures/__init__.py index b205227222..a490ab4d77 100644 --- a/lib/chainsync/chainsync/test_fixtures/__init__.py +++ b/lib/chainsync/chainsync/test_fixtures/__init__.py @@ -1,3 +1,3 @@ """Test fixtures for chainsync""" -from .db_session import db_session +from .db_session import database_engine, db_session, psql_docker from .dummy_session import dummy_session diff --git a/lib/chainsync/chainsync/test_fixtures/db_session.py b/lib/chainsync/chainsync/test_fixtures/db_session.py index 51d4ef3456..543faf35ae 100644 --- a/lib/chainsync/chainsync/test_fixtures/db_session.py +++ b/lib/chainsync/chainsync/test_fixtures/db_session.py @@ -1,20 +1,73 @@ """Pytest fixture that creates an in memory db session and creates the base db schema""" +import time from typing import Any, Generator +import docker import pytest -from chainsync.db.base import Base -from sqlalchemy import create_engine +from chainsync import PostgresConfig +from chainsync.db.base import Base, initialize_engine +from pytest_postgresql.janitor import DatabaseJanitor from sqlalchemy.orm import Session, sessionmaker +@pytest.fixture(scope="session") +def psql_docker() -> Generator[PostgresConfig, Any, Any]: + client = docker.from_env() + + # Using these config for tests + postgres_config = PostgresConfig( + POSTGRES_USER="admin", + POSTGRES_PASSWORD="password", + POSTGRES_DB="postgres_db_test", + POSTGRES_HOST="localhost", + POSTGRES_PORT=5555, + ) + + container = client.containers.run( + image="postgres:12", + auto_remove=True, + environment=dict( + POSTGRES_USER=postgres_config.POSTGRES_USER, + POSTGRES_PASSWORD=postgres_config.POSTGRES_PASSWORD, + ), + name="test_postgres", + ports={"5432/tcp": ("127.0.0.1", postgres_config.POSTGRES_PORT)}, + detach=True, + remove=True, + ) + + # Wait for the container to start + time.sleep(5) + + yield postgres_config + + container.stop() + + +@pytest.fixture(scope="session") +def database_engine(psql_docker): + # Using default postgres info + # Renaming variable to match what it actually is, i.e., the postgres config + postgres_config = psql_docker + with DatabaseJanitor( + user=postgres_config.POSTGRES_USER, + host="localhost", + port=postgres_config.POSTGRES_PORT, + dbname=postgres_config.POSTGRES_DB, + version=12, + password=postgres_config.POSTGRES_PASSWORD, + ): + engine = initialize_engine(postgres_config) + yield engine + + @pytest.fixture(scope="function") -def db_session() -> Generator[Session, Any, Any]: +def db_session(database_engine) -> Generator[Session, Any, Any]: """Initializes the in memory db session and creates the db schema""" - engine = create_engine("sqlite:///:memory:") # in-memory SQLite database for testing - session = sessionmaker(bind=engine) + session = sessionmaker(bind=database_engine) - Base.metadata.create_all(engine) # create tables + Base.metadata.create_all(database_engine) # create tables db_session_ = session() yield db_session_ db_session_.close() - Base.metadata.drop_all(engine) # drop tables + Base.metadata.drop_all(database_engine) # drop tables From cdf81966493ce7d5b79411a11a62f471c2454c03 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 12:45:04 -0700 Subject: [PATCH 03/16] Removing foregn key constraint in schema --- lib/chainsync/chainsync/db/hyperdrive/schema.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/chainsync/chainsync/db/hyperdrive/schema.py b/lib/chainsync/chainsync/db/hyperdrive/schema.py index b2bd0a8df6..64d66c048d 100644 --- a/lib/chainsync/chainsync/db/hyperdrive/schema.py +++ b/lib/chainsync/chainsync/db/hyperdrive/schema.py @@ -5,7 +5,7 @@ from typing import Union from chainsync.db.base import Base -from sqlalchemy import BigInteger, Boolean, DateTime, ForeignKey, Integer, Numeric, String +from sqlalchemy import BigInteger, Boolean, DateTime, Integer, Numeric, String from sqlalchemy.orm import Mapped, mapped_column # pylint: disable=invalid-name @@ -91,7 +91,7 @@ class WalletInfo(Base): BigInteger().with_variant(Integer, "sqlite"), primary_key=True, init=False, autoincrement=True ) - blockNumber: Mapped[int] = mapped_column(BigInteger, ForeignKey("poolinfo.blockNumber"), index=True) + blockNumber: Mapped[int] = mapped_column(BigInteger, index=True) walletAddress: Mapped[Union[str, None]] = mapped_column(String, index=True, default=None) # baseTokenType can be BASE, LONG, SHORT, LP, or WITHDRAWAL_SHARE baseTokenType: Mapped[Union[str, None]] = mapped_column(String, index=True, default=None) @@ -116,8 +116,8 @@ class WalletDelta(Base): id: Mapped[int] = mapped_column( BigInteger().with_variant(Integer, "sqlite"), primary_key=True, init=False, autoincrement=True ) - transactionHash: Mapped[str] = mapped_column(String, ForeignKey("transactions.transactionHash"), index=True) - blockNumber: Mapped[int] = mapped_column(BigInteger, ForeignKey("poolinfo.blockNumber"), index=True) + transactionHash: Mapped[str] = mapped_column(String, index=True) + blockNumber: Mapped[int] = mapped_column(BigInteger, index=True) walletAddress: Mapped[Union[str, None]] = mapped_column(String, index=True, default=None) # baseTokenType can be BASE, LONG, SHORT, LP, or WITHDRAWAL_SHARE baseTokenType: Mapped[Union[str, None]] = mapped_column(String, index=True, default=None) @@ -146,7 +146,7 @@ class HyperdriveTransaction(Base): transactionHash: Mapped[str] = mapped_column(String, index=True, unique=True) #### Fields from base transactions #### - blockNumber: Mapped[int] = mapped_column(BigInteger, ForeignKey("poolinfo.blockNumber"), index=True) + blockNumber: Mapped[int] = mapped_column(BigInteger, index=True) transactionIndex: Mapped[Union[int, None]] = mapped_column(Integer, default=None) nonce: Mapped[Union[int, None]] = mapped_column(Integer, default=None) # Transaction receipt to/from From e77066b7c0c50b2a635515a0a6388468de44b039 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 13:25:51 -0700 Subject: [PATCH 04/16] Fixing a rounding error in system test bot --- conftest.py | 11 ++++++- .../test_fixtures/cycle_trade_policy.py | 8 ++--- .../chainsync/db/hyperdrive/interface_test.py | 8 +---- tests/system_test.py | 29 +++++++------------ 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/conftest.py b/conftest.py index e2b65fb340..206634681b 100644 --- a/conftest.py +++ b/conftest.py @@ -5,6 +5,7 @@ import os import pytest +from agent0.test_fixtures import cycle_trade_policy from chainsync.test_fixtures import database_engine, db_session, dummy_session, psql_docker from ethpy.test_fixtures import local_chain, local_hyperdrive_chain @@ -39,4 +40,12 @@ def pytest_internalerror(excinfo): # Importing all fixtures here and defining here # This allows for users of fixtures to not have to import all dependency fixtures when running # TODO this means pytest can only be ran from this directory -__all__ = ["database_engine", "db_session", "dummy_session", "psql_docker", "local_chain", "local_hyperdrive_chain"] +__all__ = [ + "database_engine", + "db_session", + "dummy_session", + "psql_docker", + "local_chain", + "local_hyperdrive_chain", + "cycle_trade_policy", +] diff --git a/lib/agent0/agent0/test_fixtures/cycle_trade_policy.py b/lib/agent0/agent0/test_fixtures/cycle_trade_policy.py index 2efa71893e..ffe02d8895 100644 --- a/lib/agent0/agent0/test_fixtures/cycle_trade_policy.py +++ b/lib/agent0/agent0/test_fixtures/cycle_trade_policy.py @@ -44,7 +44,7 @@ def action(self, market: HyperdriveMarketState, wallet: HyperdriveWallet) -> lis market_type=MarketType.HYPERDRIVE, market_action=HyperdriveMarketAction( action_type=HyperdriveActionType.ADD_LIQUIDITY, - trade_amount=FixedPoint(scaled_value=int(11111e18)), + trade_amount=FixedPoint(11111), wallet=wallet, ), ) @@ -56,7 +56,7 @@ def action(self, market: HyperdriveMarketState, wallet: HyperdriveWallet) -> lis market_type=MarketType.HYPERDRIVE, market_action=HyperdriveMarketAction( action_type=HyperdriveActionType.OPEN_LONG, - trade_amount=FixedPoint(scaled_value=int(22222e18)), + trade_amount=FixedPoint(22222), wallet=wallet, ), ) @@ -68,7 +68,7 @@ def action(self, market: HyperdriveMarketState, wallet: HyperdriveWallet) -> lis market_type=MarketType.HYPERDRIVE, market_action=HyperdriveMarketAction( action_type=HyperdriveActionType.OPEN_SHORT, - trade_amount=FixedPoint(scaled_value=int(33333e18)), + trade_amount=FixedPoint(33333), wallet=wallet, ), ) @@ -138,7 +138,7 @@ def action(self, market: HyperdriveMarketState, wallet: HyperdriveWallet) -> lis market_type=MarketType.HYPERDRIVE, market_action=HyperdriveMarketAction( action_type=HyperdriveActionType.OPEN_LONG, - trade_amount=FixedPoint(scaled_value=int(1e18)), + trade_amount=FixedPoint(1), wallet=wallet, ), ) diff --git a/lib/chainsync/chainsync/db/hyperdrive/interface_test.py b/lib/chainsync/chainsync/db/hyperdrive/interface_test.py index 6c03599a06..2e7afa71e4 100644 --- a/lib/chainsync/chainsync/db/hyperdrive/interface_test.py +++ b/lib/chainsync/chainsync/db/hyperdrive/interface_test.py @@ -151,10 +151,6 @@ def test_get_pool_config(self, db_session): pool_config_df_1 = get_pool_config(db_session) assert len(pool_config_df_1) == 1 - # TODO In testing, we use sqlite, which does not implement the fixed point Numeric type - # Internally, they store Numeric types as floats, hence we see rounding errors in testing - # This does not happen in postgres, where these values match exactly. - # https://github.com/delvtech/elf-simulations/issues/836 np.testing.assert_array_equal(pool_config_df_1["initialSharePrice"], np.array([3.2])) pool_config_2 = PoolConfig(contractAddress="1", initialSharePrice=Decimal("3.4")) @@ -185,9 +181,7 @@ def test_pool_config_verify(self, db_session): assert pool_config_df_1.loc[0, "initialSharePrice"] == 3.2 # Nothing should happen if we give the same pool_config - # TODO Below is a hack due to sqlite not having numerics - # We explicitly print 18 spots after floating point to match rounding error in sqlite - pool_config_2 = PoolConfig(contractAddress="0", initialSharePrice=Decimal(f"{3.2:.18f}")) + pool_config_2 = PoolConfig(contractAddress="0", initialSharePrice=Decimal("3.2")) add_pool_config(pool_config_2, db_session) pool_config_df_2 = get_pool_config(db_session) assert len(pool_config_df_2) == 1 diff --git a/tests/system_test.py b/tests/system_test.py index 70abd160cd..7583f60f00 100644 --- a/tests/system_test.py +++ b/tests/system_test.py @@ -47,10 +47,6 @@ def _to_unscaled_decimal(scaled_value: int) -> Decimal: return Decimal(str(FixedPoint(scaled_value=scaled_value))) -def _decimal_almost_equal(a_val: Decimal, b_val: Decimal) -> bool: - return abs(a_val - b_val) < 1e-12 - - class TestBotToDb: """Tests pipeline from bots making trades to viewing the trades in the db""" @@ -86,9 +82,9 @@ def test_bot_to_db( AgentConfig( policy=cycle_trade_policy, number_of_agents=1, - slippage_tolerance=FixedPoint(0.0001), - base_budget_wei=int(1_000_000e18), # 1 million base - eth_budget_wei=int(100e18), # 100 base + slippage_tolerance=FixedPoint("0.0001"), + base_budget_wei=FixedPoint("1_000_000").scaled_value, # 1 million base + eth_budget_wei=FixedPoint("100").scaled_value, # 100 base init_kwargs={}, ), ] @@ -135,7 +131,7 @@ def test_bot_to_db( # TODO these expected values are defined in lib/ethpy/ethpy/test_fixtures/deploy_hyperdrive.py # Eventually, we want to parameterize these values to pass into deploying hyperdrive - expected_timestretch_fp = FixedPoint(scaled_value=_calculateTimeStretch(int(0.05e18))) + expected_timestretch_fp = FixedPoint(scaled_value=_calculateTimeStretch(FixedPoint("0.05").scaled_value)) # TODO this is actually inv of solidity time stretch, fix expected_timestretch = _to_unscaled_decimal((1 / expected_timestretch_fp).scaled_value) expected_inv_timestretch = _to_unscaled_decimal(expected_timestretch_fp.scaled_value) @@ -143,17 +139,17 @@ def test_bot_to_db( expected_pool_config = { "contractAddress": hyperdrive_contract_addresses.mock_hyperdrive, "baseToken": hyperdrive_contract_addresses.base_token, - "initialSharePrice": _to_unscaled_decimal(int(1e18)), - "minimumShareReserves": _to_unscaled_decimal(int(10e18)), + "initialSharePrice": _to_unscaled_decimal(FixedPoint("1").scaled_value), + "minimumShareReserves": _to_unscaled_decimal(FixedPoint("10").scaled_value), "positionDuration": 604800, # 1 week "checkpointDuration": 3600, # 1 hour # TODO this is actually inv of solidity time stretch, fix "timeStretch": expected_timestretch, "governance": deploy_account.address, "feeCollector": deploy_account.address, - "curveFee": _to_unscaled_decimal(int(0.1e18)), # 10% - "flatFee": _to_unscaled_decimal(int(0.0005e18)), # 0.05% - "governanceFee": _to_unscaled_decimal(int(0.15e18)), # 15% + "curveFee": _to_unscaled_decimal(FixedPoint("0.1").scaled_value), # 10% + "flatFee": _to_unscaled_decimal(FixedPoint("0.0005").scaled_value), # 0.05% + "governanceFee": _to_unscaled_decimal(FixedPoint("0.15").scaled_value), # 15% "oracleSize": _to_unscaled_decimal(10), "updateGap": 3600, # TODO don't know where this is getting set "invTimeStretch": expected_inv_timestretch, @@ -171,13 +167,8 @@ def test_bot_to_db( # Value comparison for key, expected_value in expected_pool_config.items(): - # TODO In testing, we use sqlite, which does not implement the fixed point Numeric type - # Internally, they store Numeric types as floats, hence we see rounding errors in testing - # This does not happen in postgres, where these values match exactly. - # https://github.com/delvtech/elf-simulations/issues/836 - if isinstance(expected_value, Decimal): - assert_val = _decimal_almost_equal(db_pool_config[key], expected_value) + assert_val = db_pool_config[key] == expected_value else: assert_val = db_pool_config[key] == expected_value From dce07b1624c1f12548b38cffb2b8b9ec931c9c6a Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 13:51:07 -0700 Subject: [PATCH 05/16] No floats in fixedpoint constructor --- README.md | 4 +-- lib/agent0/agent0/base/config/agent_config.py | 2 +- lib/agent0/agent0/base/config/budget.py | 8 ++--- .../hyperdrive/exec/execute_agent_trades.py | 2 +- lib/agent0/bin/checkpoint_bot.py | 3 +- lib/agent0/examples/example_agent.py | 10 +++---- lib/agent0/examples/hyperdrive_agents.py | 28 ++++++++--------- lib/ethpy/ethpy/hyperdrive/interface.py | 2 +- .../ethpy/test_fixtures/deploy_hyperdrive.py | 30 +++++++++---------- 9 files changed, 44 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index cef1fe55f2..b98627609f 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,9 @@ Due to a lack of known precision, operations against Python floats are not allow However, operations against `int` are allowed. In this case, the `int` _argument_ is assumed to be "unscaled", i.e. if you write `int(8) * FixedPoint(8)` we will scale up the first variable return a `FixedPoint` number that represents the float `64.0` in 18-decimal FixedPoint format. So in this example the internal representation of that operation would be `64*10**18`. -If you cast FixedPoint numbers to ints or floats you will get "unscaled" representation, e.g. `float(FixedPoint(8.0)) == 8.0` and `int(FixedPoint(8.528)) == 8`. +If you cast FixedPoint numbers to ints or floats you will get "unscaled" representation, e.g. `float(FixedPoint("8.0")) == 8.0` and `int(FixedPoint("8.528")) == 8`. -If you want the integer scaled representation, which can be useful for communicating with Solidity contracts, you must ask for it explicitly, e.g. `FixedPoint(8.52).scaled_value == 8520000000000000000`. +If you want the integer scaled representation, which can be useful for communicating with Solidity contracts, you must ask for it explicitly, e.g. `FixedPoint("8.52").scaled_value == 8520000000000000000`. Conversely, if you want to initialize a FixedPoint variable using the scaled integer representation, then you need to instantiate the variable using the `scaled_value` argument, e.g. `FixedPoint(scaled_value=8)`. In that example, the internal representation is `8`, so casting it to a float would produce a small value: `float(FixedPoint(scaled_value=8)) == 8e-18`. diff --git a/lib/agent0/agent0/base/config/agent_config.py b/lib/agent0/agent0/base/config/agent_config.py index 67e36f1f14..20f6b8e674 100644 --- a/lib/agent0/agent0/base/config/agent_config.py +++ b/lib/agent0/agent0/base/config/agent_config.py @@ -39,7 +39,7 @@ class AgentConfig: name: str = "BoringBotty" base_budget_wei: Budget | int = Budget() eth_budget_wei: Budget | int = Budget(min_wei=0, max_wei=0) - slippage_tolerance: FixedPoint = FixedPoint(0.0001) # default to 0.01% + slippage_tolerance: FixedPoint = FixedPoint("0.0001") # default to 0.01% number_of_agents: int = 1 private_keys: list[str] | None = None init_kwargs: dict = field(default_factory=dict) diff --git a/lib/agent0/agent0/base/config/budget.py b/lib/agent0/agent0/base/config/budget.py index b307a5ec49..eac1778e77 100644 --- a/lib/agent0/agent0/base/config/budget.py +++ b/lib/agent0/agent0/base/config/budget.py @@ -16,10 +16,10 @@ class Budget: Wei in the variables below refers to the smallest unit of base, not to ETH. """ - mean_wei: int = int(5_000 * 1e18) - std_wei: int = int(2_000 * 1e18) - min_wei: int = int(1_000 * 1e18) - max_wei: int = int(10_000 * 1e18) + mean_wei: int = FixedPoint(5_000).scaled_value + std_wei: int = FixedPoint(2_000).scaled_value + min_wei: int = FixedPoint(1_000).scaled_value + max_wei: int = FixedPoint(10_000).scaled_value def sample_budget(self, rng: NumpyGenerator) -> FixedPoint: """Return a sample from a clipped normal distribution. diff --git a/lib/agent0/agent0/hyperdrive/exec/execute_agent_trades.py b/lib/agent0/agent0/hyperdrive/exec/execute_agent_trades.py index 5517f6b506..ce1dae0c0a 100644 --- a/lib/agent0/agent0/hyperdrive/exec/execute_agent_trades.py +++ b/lib/agent0/agent0/hyperdrive/exec/execute_agent_trades.py @@ -229,7 +229,7 @@ async def async_match_contract_call_to_trade( # TODO: The following variables are hard coded for now, but should be specified in the trade spec min_apr = int(1) - max_apr = int(1e18) + max_apr = FixedPoint(1).scaled_value as_underlying = True match trade.action_type: case HyperdriveActionType.INITIALIZE_MARKET: diff --git a/lib/agent0/bin/checkpoint_bot.py b/lib/agent0/bin/checkpoint_bot.py index 206f22d74c..ff3c261da0 100644 --- a/lib/agent0/bin/checkpoint_bot.py +++ b/lib/agent0/bin/checkpoint_bot.py @@ -20,6 +20,7 @@ smart_contract_transact, ) from ethpy.hyperdrive import fetch_hyperdrive_address_from_url, get_hyperdrive_config +from fixedpointmath import FixedPoint from web3.contract.contract import Contract # The portion of the checkpoint that the bot will wait before attempting to @@ -69,7 +70,7 @@ def main() -> None: ) # Fund the checkpoint sender with some ETH. - balance = int(100e18) + balance = FixedPoint(100).scaled_value sender = EthAgent(Account().create("CHECKPOINT_BOT")) set_anvil_account_balance(web3, sender.address, balance) logging.info("Successfully funded the sender=%s.", sender.address) diff --git a/lib/agent0/examples/example_agent.py b/lib/agent0/examples/example_agent.py index 9987b561ee..316de69428 100644 --- a/lib/agent0/examples/example_agent.py +++ b/lib/agent0/examples/example_agent.py @@ -37,7 +37,7 @@ def __init__( rng: NumpyGenerator | None = None, slippage_tolerance: FixedPoint | None = None, # Add additional parameters for custom policy here - static_trade_amount_wei: int = int(100e18), # 100 base + static_trade_amount_wei: int = FixedPoint(100).scaled_value, # 100 base ): self.static_trade_amount_wei = static_trade_amount_wei # We want to do a sequence of trades one at a time, so we keep an internal counter based on @@ -174,10 +174,10 @@ def action(self, market: HyperdriveMarketState, wallet: HyperdriveWallet) -> lis AgentConfig( policy=CycleTradesPolicy, number_of_agents=1, - slippage_tolerance=FixedPoint(0.0001), - base_budget_wei=int(10_000e18), # 10k base - eth_budget_wei=int(10e18), # 10 base - init_kwargs={"static_trade_amount_wei": int(100e18)}, # 100 base static trades + slippage_tolerance=FixedPoint("0.0001"), + base_budget_wei=FixedPoint(10_000).scaled_value, # 10k base + eth_budget_wei=FixedPoint(10).scaled_value, # 10 base + init_kwargs={"static_trade_amount_wei": FixedPoint(100).scaled_value)}, # 100 base static trades ), ] diff --git a/lib/agent0/examples/hyperdrive_agents.py b/lib/agent0/examples/hyperdrive_agents.py index 6b4de7fa4b..a1fa64ea69 100644 --- a/lib/agent0/examples/hyperdrive_agents.py +++ b/lib/agent0/examples/hyperdrive_agents.py @@ -32,35 +32,35 @@ AgentConfig( policy=Policies.random_agent, number_of_agents=3, - slippage_tolerance=FixedPoint(0.0001), + slippage_tolerance=FixedPoint("0.0001"), base_budget_wei=Budget( - mean_wei=int(5_000e18), # 5k base - std_wei=int(1_000e18), # 1k base + mean_wei=FixedPoint(5_000).scaled_value, # 5k base + std_wei=FixedPoint(1_000).scaled_value, # 1k base min_wei=1, # 1 WEI base - max_wei=int(100_000e18), # 100k base + max_wei=FixedPoint(100_000).scaled_value, # 100k base ), - eth_budget_wei=Budget(min_wei=int(1e18), max_wei=int(1e18)), - init_kwargs={"trade_chance": FixedPoint(0.8)}, + eth_budget_wei=Budget(min_wei=FixedPoint(1).scaled_value, max_wei=FixedPoint(1).scaled_value), + init_kwargs={"trade_chance": FixedPoint("0.8")}, ), AgentConfig( policy=Policies.long_louie, number_of_agents=0, # Fixed budgets - base_budget_wei=int(5_000e18), # 5k base - eth_budget_wei=int(1e18), # 1 base - init_kwargs={"trade_chance": FixedPoint(0.8), "risk_threshold": FixedPoint(0.9)}, + base_budget_wei=FixedPoint(5_000).scaled_value, # 5k base + eth_budget_wei=FixedPoint(1).scaled_value, # 1 base + init_kwargs={"trade_chance": FixedPoint("0.8"), "risk_threshold": FixedPoint("0.9")}, ), AgentConfig( policy=Policies.short_sally, number_of_agents=0, base_budget_wei=Budget( - mean_wei=int(5_000e18), # 5k base - std_wei=int(1_000e18), # 1k base + mean_wei=FixedPoint(5_000).scaled_value, # 5k base + std_wei=FixedPoint(1_000).scaled_value, # 1k base min_wei=1, # 1 WEI base - max_wei=int(100_000e18), # 100k base + max_wei=FixedPoint(100_000).scaled_value, # 100k base ), - eth_budget_wei=Budget(min_wei=int(1e18), max_wei=int(1e18)), - init_kwargs={"trade_chance": FixedPoint(0.8), "risk_threshold": FixedPoint(0.8)}, + eth_budget_wei=Budget(min_wei=FixedPoint(1).scaled_value, max_wei=FixedPoint(1).scaled_value), + init_kwargs={"trade_chance": FixedPoint("0.8"), "risk_threshold": FixedPoint("0.8")}, ), ] diff --git a/lib/ethpy/ethpy/hyperdrive/interface.py b/lib/ethpy/ethpy/hyperdrive/interface.py index b98f6dae97..1b1de82de2 100644 --- a/lib/ethpy/ethpy/hyperdrive/interface.py +++ b/lib/ethpy/ethpy/hyperdrive/interface.py @@ -159,7 +159,7 @@ def get_hyperdrive_market(web3: Web3, hyperdrive_contract: Contract) -> Hyperdri total_supply_longs={FixedPoint(0): FixedPoint(pool_info["longsOutstanding"])}, total_supply_shorts={FixedPoint(0): FixedPoint(pool_info["shortsOutstanding"])}, total_supply_withdraw_shares=FixedPoint(pool_info["totalSupplyWithdrawalShares"]), - variable_apr=FixedPoint(0.01), # TODO: insert real value + variable_apr=FixedPoint("0.01"), # TODO: insert real value withdraw_shares_ready_to_withdraw=FixedPoint(pool_info["withdrawalSharesReadyToWithdraw"]), withdraw_capital=FixedPoint(0), withdraw_interest=FixedPoint(0), diff --git a/lib/ethpy/ethpy/test_fixtures/deploy_hyperdrive.py b/lib/ethpy/ethpy/test_fixtures/deploy_hyperdrive.py index 571302dc6a..427f5f0732 100644 --- a/lib/ethpy/ethpy/test_fixtures/deploy_hyperdrive.py +++ b/lib/ethpy/ethpy/test_fixtures/deploy_hyperdrive.py @@ -34,10 +34,8 @@ def _calculateTimeStretch(apr: int) -> int: # pylint: disable=invalid-name The scaled output time stretch """ fp_apr = FixedPoint(scaled_value=apr) - time_stretch = FixedPoint(scaled_value=int(5.24592e18)) / ( - FixedPoint(scaled_value=int(0.04665e18)) * (fp_apr * 100) - ) - return (FixedPoint(scaled_value=int(1e18)) / time_stretch).scaled_value + time_stretch = FixedPoint("5.24592") / (FixedPoint("0.04665") * (fp_apr * 100)) + return (FixedPoint(1) / time_stretch).scaled_value def initialize_deploy_account(web3: Web3) -> LocalAccount: @@ -81,13 +79,13 @@ def deploy_hyperdrive_factory(rpc_url: str, deploy_account: LocalAccount) -> tup # TODO parameterize these parameters # pylint: disable=too-many-locals # Initial factory settings - initial_variable_rate = int(0.05e18) - curve_fee = int(0.1e18) # 10% - flat_fee = int(0.0005e18) # 0.05% - governance_fee = int(0.15e18) # 15% - max_curve_fee = int(0.3e18) # 30% - max_flat_fee = int(0.0015e18) # 0.15% - max_governance_fee = int(0.30e18) # 30% + initial_variable_rate = FixedPoint("0.05").scaled_value + curve_fee = FixedPoint("0.1").scaled_value # 10% + flat_fee = FixedPoint("0.0005").scaled_value # 0.05% + governance_fee = FixedPoint("0.15").scaled_value # 15% + max_curve_fee = FixedPoint("0.3").scaled_value # 30% + max_flat_fee = FixedPoint("0.0015").scaled_value # 0.15% + max_governance_fee = FixedPoint("0.30").scaled_value # 30% # Configuration settings abi_folder = "packages/hyperdrive/src/abis/" @@ -182,15 +180,15 @@ def deploy_and_initialize_hyperdrive( # TODO parameterize these parameters # pylint: disable=too-many-locals # Initial hyperdrive settings - initial_contribution = int(100_000_000e18) - initial_share_price = int(1e18) - minimum_share_reserves = int(10e18) + initial_contribution = FixedPoint(100_000_000).scaled_value + initial_share_price = FixedPoint(1).scaled_value + minimum_share_reserves = FixedPoint(10).scaled_value position_duration = 604800 # 1 week checkpoint_duration = 3600 # 1 hour - time_stretch = _calculateTimeStretch(int(0.05e18)) + time_stretch = _calculateTimeStretch(FixedPoint("0.05").scaled_value) oracle_size = 10 update_gap = 3600 # 1 hour - initial_fixed_rate = int(0.05e18) + initial_fixed_rate = FixedPoint("0.05").scaled_value deploy_account_addr = Web3.to_checksum_address(deploy_account.address) From d09f0bb4602ffcc598066a30ddc66bd28c983075 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 13:54:23 -0700 Subject: [PATCH 06/16] No more sqlite --- lib/chainsync/chainsync/db/base/schema_test.py | 3 --- .../chainsync/db/hyperdrive/schema.py | 18 +++--------------- .../chainsync/db/hyperdrive/schema_test.py | 12 ------------ 3 files changed, 3 insertions(+), 30 deletions(-) diff --git a/lib/chainsync/chainsync/db/base/schema_test.py b/lib/chainsync/chainsync/db/base/schema_test.py index 3f312dd060..41b4aa6d3f 100644 --- a/lib/chainsync/chainsync/db/base/schema_test.py +++ b/lib/chainsync/chainsync/db/base/schema_test.py @@ -13,9 +13,6 @@ class TestUserMapTable: def test_create_user_map(self, db_session): """Create and entry""" - # Note: this test is using in-memory sqlite, which doesn't seem to support - # autoincrement ids without init, whereas postgres does this with no issues - # Hence, we explicitly add id here user_map = UserMap(address="1", username="a") db_session.add(user_map) db_session.commit() diff --git a/lib/chainsync/chainsync/db/hyperdrive/schema.py b/lib/chainsync/chainsync/db/hyperdrive/schema.py index 64d66c048d..41143b3487 100644 --- a/lib/chainsync/chainsync/db/hyperdrive/schema.py +++ b/lib/chainsync/chainsync/db/hyperdrive/schema.py @@ -85,11 +85,7 @@ class WalletInfo(Base): __tablename__ = "walletinfo" # Default table primary key - # Note that we use postgres in production and sqlite in testing, but sqlite has issues with - # autoincrement with BigIntegers. Hence, we use the Integer variant when using sqlite in tests - id: Mapped[int] = mapped_column( - BigInteger().with_variant(Integer, "sqlite"), primary_key=True, init=False, autoincrement=True - ) + id: Mapped[int] = mapped_column(BigInteger(), primary_key=True, init=False, autoincrement=True) blockNumber: Mapped[int] = mapped_column(BigInteger, index=True) walletAddress: Mapped[Union[str, None]] = mapped_column(String, index=True, default=None) @@ -111,11 +107,7 @@ class WalletDelta(Base): __tablename__ = "walletdelta" # Default table primary key - # Note that we use postgres in production and sqlite in testing, but sqlite has issues with - # autoincrement with BigIntegers. Hence, we use the Integer variant when using sqlite in tests - id: Mapped[int] = mapped_column( - BigInteger().with_variant(Integer, "sqlite"), primary_key=True, init=False, autoincrement=True - ) + id: Mapped[int] = mapped_column(BigInteger(), primary_key=True, init=False, autoincrement=True) transactionHash: Mapped[str] = mapped_column(String, index=True) blockNumber: Mapped[int] = mapped_column(BigInteger, index=True) walletAddress: Mapped[Union[str, None]] = mapped_column(String, index=True, default=None) @@ -138,11 +130,7 @@ class HyperdriveTransaction(Base): __tablename__ = "transactions" # Default table primary key - # Note that we use postgres in production and sqlite in testing, but sqlite has issues with - # autoincrement with BigIntegers. Hence, we use the Integer variant when using sqlite in tests - id: Mapped[int] = mapped_column( - BigInteger().with_variant(Integer, "sqlite"), primary_key=True, init=False, autoincrement=True - ) + id: Mapped[int] = mapped_column(BigInteger(), primary_key=True, init=False, autoincrement=True) transactionHash: Mapped[str] = mapped_column(String, index=True, unique=True) #### Fields from base transactions #### diff --git a/lib/chainsync/chainsync/db/hyperdrive/schema_test.py b/lib/chainsync/chainsync/db/hyperdrive/schema_test.py index 77de855807..40f35d5ebb 100644 --- a/lib/chainsync/chainsync/db/hyperdrive/schema_test.py +++ b/lib/chainsync/chainsync/db/hyperdrive/schema_test.py @@ -16,9 +16,6 @@ class TestTransactionTable: def test_create_transaction(self, db_session): """Create and entry""" - # Note: this test is using in-memory sqlite, which doesn't seem to support - # autoincrement ids without init, whereas postgres does this with no issues - # Hence, we explicitly add id here transaction = HyperdriveTransaction(blockNumber=1, transactionHash="a", event_value=Decimal("3.2")) db_session.add(transaction) db_session.commit() @@ -59,9 +56,6 @@ class TestCheckpointTable: def test_create_checkpoint(self, db_session): """Create and entry""" - # Note: this test is using in-memory sqlite, which doesn't seem to support - # autoincrement ids without init, whereas postgres does this with no issues - # Hence, we explicitly add id here timestamp = datetime.now() checkpoint = CheckpointInfo(blockNumber=1, timestamp=timestamp) db_session.add(checkpoint) @@ -172,9 +166,6 @@ class TestWalletDeltaTable: def test_create_wallet_delta(self, db_session): """Create and entry""" - # Note: this test is using in-memory sqlite, which doesn't seem to support - # autoincrement ids without init, whereas postgres does this with no issues - # Hence, we explicitly add id here wallet_delta = WalletDelta(blockNumber=1, transactionHash="a", delta=Decimal("3.2")) db_session.add(wallet_delta) db_session.commit() @@ -211,9 +202,6 @@ class TestWalletInfoTable: def test_create_wallet_info(self, db_session): """Create and entry""" - # Note: this test is using in-memory sqlite, which doesn't seem to support - # autoincrement ids without init, whereas postgres does this with no issues - # Hence, we explicitly add id here wallet_info = WalletInfo(blockNumber=1, tokenValue=Decimal("3.2")) db_session.add(wallet_info) db_session.commit() From 6045413d2df1c7587f45245665b1afe9ee3ffba6 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 13:57:07 -0700 Subject: [PATCH 07/16] Adding important note for pytest debugger + postgres test fixture --- conftest.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/conftest.py b/conftest.py index 206634681b..6ee4a2acba 100644 --- a/conftest.py +++ b/conftest.py @@ -13,6 +13,12 @@ # instead of allowing pytest to catch the exception and report # Based on https://stackoverflow.com/questions/62419998/how-can-i-get-pytest-to-not-catch-exceptions/62563106#62563106 +# IMPORTANT NOTE!!!!! +# If you end up using this debugging method, this will catch exceptions before teardown of fixtures +# This means that the local postgres fixture (which launches a docker container) will not automatically +# be cleaned up if you, e.g., use the debugger and a db test fails. Make sure to manually clean up. +# TODO maybe automatically close the container on catch here + # Use this in conjunction with the following launch.json configuration: # { # "name": "Debug Current Test", From 9aa9d183375e7bd6cec764e7b52bb5373b8e22cb Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 13:59:12 -0700 Subject: [PATCH 08/16] lint --- lib/agent0/examples/example_agent.py | 2 +- lib/chainsync/chainsync/test_fixtures/db_session.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/agent0/examples/example_agent.py b/lib/agent0/examples/example_agent.py index 316de69428..17b6f5b299 100644 --- a/lib/agent0/examples/example_agent.py +++ b/lib/agent0/examples/example_agent.py @@ -177,7 +177,7 @@ def action(self, market: HyperdriveMarketState, wallet: HyperdriveWallet) -> lis slippage_tolerance=FixedPoint("0.0001"), base_budget_wei=FixedPoint(10_000).scaled_value, # 10k base eth_budget_wei=FixedPoint(10).scaled_value, # 10 base - init_kwargs={"static_trade_amount_wei": FixedPoint(100).scaled_value)}, # 100 base static trades + init_kwargs={"static_trade_amount_wei": FixedPoint(100).scaled_value}, # 100 base static trades ), ] diff --git a/lib/chainsync/chainsync/test_fixtures/db_session.py b/lib/chainsync/chainsync/test_fixtures/db_session.py index 543faf35ae..52ed4f32a3 100644 --- a/lib/chainsync/chainsync/test_fixtures/db_session.py +++ b/lib/chainsync/chainsync/test_fixtures/db_session.py @@ -41,7 +41,8 @@ def psql_docker() -> Generator[PostgresConfig, Any, Any]: yield postgres_config - container.stop() + # Docker doesn't play nice with types + container.stop() # type:ignore @pytest.fixture(scope="session") From 85ba99ca296d8360655cd5cfd1c7c01489057b36 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 14:05:26 -0700 Subject: [PATCH 09/16] pylint --- lib/chainsync/chainsync/test_fixtures/db_session.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/chainsync/chainsync/test_fixtures/db_session.py b/lib/chainsync/chainsync/test_fixtures/db_session.py index 52ed4f32a3..d34960678d 100644 --- a/lib/chainsync/chainsync/test_fixtures/db_session.py +++ b/lib/chainsync/chainsync/test_fixtures/db_session.py @@ -9,9 +9,13 @@ from pytest_postgresql.janitor import DatabaseJanitor from sqlalchemy.orm import Session, sessionmaker +# fixture arguments in test function have to be the same as the fixture name +# pylint: disable=redefined-outer-name + @pytest.fixture(scope="session") def psql_docker() -> Generator[PostgresConfig, Any, Any]: + """Test fixture for running postgres in docker""" client = docker.from_env() # Using these config for tests @@ -47,6 +51,7 @@ def psql_docker() -> Generator[PostgresConfig, Any, Any]: @pytest.fixture(scope="session") def database_engine(psql_docker): + """Test fixture creating psql engine on local postgres container""" # Using default postgres info # Renaming variable to match what it actually is, i.e., the postgres config postgres_config = psql_docker From 77d4353eb22fc969459ce8eca07082c2506cb439 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 14:14:44 -0700 Subject: [PATCH 10/16] more lint --- lib/chainsync/chainsync/test_fixtures/db_session.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/chainsync/chainsync/test_fixtures/db_session.py b/lib/chainsync/chainsync/test_fixtures/db_session.py index d34960678d..b036144680 100644 --- a/lib/chainsync/chainsync/test_fixtures/db_session.py +++ b/lib/chainsync/chainsync/test_fixtures/db_session.py @@ -30,10 +30,10 @@ def psql_docker() -> Generator[PostgresConfig, Any, Any]: container = client.containers.run( image="postgres:12", auto_remove=True, - environment=dict( - POSTGRES_USER=postgres_config.POSTGRES_USER, - POSTGRES_PASSWORD=postgres_config.POSTGRES_PASSWORD, - ), + environment={ + "POSTGRES_USER": postgres_config.POSTGRES_USER, + "POSTGRES_PASSWORD": postgres_config.POSTGRES_PASSWORD, + }, name="test_postgres", ports={"5432/tcp": ("127.0.0.1", postgres_config.POSTGRES_PORT)}, detach=True, From 16d55a999870959992591f38dbae6fb396162576 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 15:13:45 -0700 Subject: [PATCH 11/16] No more fixture imports in favor of conftest.py --- .../chainsync/db/hyperdrive/interface_test.py | 7 +------ .../chainsync/db/hyperdrive/schema_test.py | 6 +----- tests/system_test.py | 16 ++-------------- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/lib/chainsync/chainsync/db/hyperdrive/interface_test.py b/lib/chainsync/chainsync/db/hyperdrive/interface_test.py index 2e7afa71e4..58f26414fe 100644 --- a/lib/chainsync/chainsync/db/hyperdrive/interface_test.py +++ b/lib/chainsync/chainsync/db/hyperdrive/interface_test.py @@ -6,9 +6,6 @@ import pytest from chainsync.db.base import get_latest_block_number_from_table -# Ignoring unused import warning, fixtures are used through variable name -from chainsync.test_fixtures import db_session # pylint: disable=unused-import - from .interface import ( add_checkpoint_infos, add_pool_config, @@ -29,10 +26,8 @@ ) from .schema import CheckpointInfo, HyperdriveTransaction, PoolConfig, PoolInfo, WalletDelta, WalletInfo -# fixture arguments in test function have to be the same as the fixture name -# pylint: disable=redefined-outer-name - +# These tests are using fixtures defined in conftest.py class TestTransactionInterface: """Testing postgres interface for transaction table""" diff --git a/lib/chainsync/chainsync/db/hyperdrive/schema_test.py b/lib/chainsync/chainsync/db/hyperdrive/schema_test.py index 40f35d5ebb..0f434b86da 100644 --- a/lib/chainsync/chainsync/db/hyperdrive/schema_test.py +++ b/lib/chainsync/chainsync/db/hyperdrive/schema_test.py @@ -2,13 +2,9 @@ from datetime import datetime from decimal import Decimal -# Ignoring unused import warning, fixtures are used through variable name -from chainsync.test_fixtures import db_session # pylint: disable=unused-import - from .schema import CheckpointInfo, HyperdriveTransaction, PoolConfig, PoolInfo, WalletDelta, WalletInfo -# fixture arguments in test function have to be the same as the fixture name -# pylint: disable=redefined-outer-name +# These tests are using fixtures defined in conftest.py class TestTransactionTable: diff --git a/tests/system_test.py b/tests/system_test.py index 7583f60f00..d388ae707b 100644 --- a/tests/system_test.py +++ b/tests/system_test.py @@ -9,6 +9,7 @@ from agent0.base.config import AgentConfig, EnvironmentConfig from agent0.base.policies import BasePolicy from agent0.hyperdrive.exec import run_agents +from agent0.test_fixtures import AgentDoneException from chainsync.db.hyperdrive.interface import get_pool_config, get_pool_info, get_transactions, get_wallet_deltas from chainsync.exec import acquire_data from eth_account.signers.local import LocalAccount @@ -18,20 +19,7 @@ from fixedpointmath import FixedPoint from sqlalchemy.orm import Session -# This pass is to prevent auto reordering imports from reordering the imports below -pass # pylint: disable=unnecessary-pass - -# Test fixture imports -# Ignoring unused import warning, fixtures are used through variable name -from agent0.test_fixtures import ( # pylint: disable=unused-import, ungrouped-imports - AgentDoneException, - cycle_trade_policy, -) -from chainsync.test_fixtures import db_session # pylint: disable=unused-import, ungrouped-imports -from ethpy.test_fixtures import local_hyperdrive_chain # pylint: disable=unused-import, ungrouped-imports - -# fixture arguments in test function have to be the same as the fixture name -# pylint: disable=redefined-outer-name +# These tests are using fixtures defined in conftest.py class TestLocalChain: From 24ff5dd67b5ee2e2c02895d8d16136fa4a77c42a Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 15:24:10 -0700 Subject: [PATCH 12/16] Removing unnecessary conversions in system test --- tests/system_test.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/system_test.py b/tests/system_test.py index d388ae707b..d4c7a9d04d 100644 --- a/tests/system_test.py +++ b/tests/system_test.py @@ -31,8 +31,8 @@ def test_hyperdrive_init_and_deploy(self, local_hyperdrive_chain: dict): print(local_hyperdrive_chain) -def _to_unscaled_decimal(scaled_value: int) -> Decimal: - return Decimal(str(FixedPoint(scaled_value=scaled_value))) +def _to_unscaled_decimal(fp_val: FixedPoint) -> Decimal: + return Decimal(str(fp_val)) class TestBotToDb: @@ -121,24 +121,24 @@ def test_bot_to_db( # Eventually, we want to parameterize these values to pass into deploying hyperdrive expected_timestretch_fp = FixedPoint(scaled_value=_calculateTimeStretch(FixedPoint("0.05").scaled_value)) # TODO this is actually inv of solidity time stretch, fix - expected_timestretch = _to_unscaled_decimal((1 / expected_timestretch_fp).scaled_value) - expected_inv_timestretch = _to_unscaled_decimal(expected_timestretch_fp.scaled_value) + expected_timestretch = _to_unscaled_decimal((1 / expected_timestretch_fp)) + expected_inv_timestretch = _to_unscaled_decimal(expected_timestretch_fp) expected_pool_config = { "contractAddress": hyperdrive_contract_addresses.mock_hyperdrive, "baseToken": hyperdrive_contract_addresses.base_token, - "initialSharePrice": _to_unscaled_decimal(FixedPoint("1").scaled_value), - "minimumShareReserves": _to_unscaled_decimal(FixedPoint("10").scaled_value), + "initialSharePrice": _to_unscaled_decimal(FixedPoint("1")), + "minimumShareReserves": _to_unscaled_decimal(FixedPoint("10")), "positionDuration": 604800, # 1 week "checkpointDuration": 3600, # 1 hour # TODO this is actually inv of solidity time stretch, fix "timeStretch": expected_timestretch, "governance": deploy_account.address, "feeCollector": deploy_account.address, - "curveFee": _to_unscaled_decimal(FixedPoint("0.1").scaled_value), # 10% - "flatFee": _to_unscaled_decimal(FixedPoint("0.0005").scaled_value), # 0.05% - "governanceFee": _to_unscaled_decimal(FixedPoint("0.15").scaled_value), # 15% - "oracleSize": _to_unscaled_decimal(10), + "curveFee": _to_unscaled_decimal(FixedPoint("0.1")), # 10% + "flatFee": _to_unscaled_decimal(FixedPoint("0.0005")), # 0.05% + "governanceFee": _to_unscaled_decimal(FixedPoint("0.15")), # 15% + "oracleSize": _to_unscaled_decimal(FixedPoint("10")), "updateGap": 3600, # TODO don't know where this is getting set "invTimeStretch": expected_inv_timestretch, } From 437e05f5e1fbe82d83a622b2fa79ceded62ba574 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 15:26:15 -0700 Subject: [PATCH 13/16] Using iterator type instead of generator for yielding --- lib/chainsync/chainsync/test_fixtures/db_session.py | 6 +++--- lib/chainsync/chainsync/test_fixtures/dummy_session.py | 4 ++-- lib/ethpy/ethpy/test_fixtures/local_chain.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/chainsync/chainsync/test_fixtures/db_session.py b/lib/chainsync/chainsync/test_fixtures/db_session.py index b036144680..9d89c4cce2 100644 --- a/lib/chainsync/chainsync/test_fixtures/db_session.py +++ b/lib/chainsync/chainsync/test_fixtures/db_session.py @@ -1,6 +1,6 @@ """Pytest fixture that creates an in memory db session and creates the base db schema""" import time -from typing import Any, Generator +from typing import Any, Iterator import docker import pytest @@ -14,7 +14,7 @@ @pytest.fixture(scope="session") -def psql_docker() -> Generator[PostgresConfig, Any, Any]: +def psql_docker() -> Iterator[PostgresConfig]: """Test fixture for running postgres in docker""" client = docker.from_env() @@ -68,7 +68,7 @@ def database_engine(psql_docker): @pytest.fixture(scope="function") -def db_session(database_engine) -> Generator[Session, Any, Any]: +def db_session(database_engine) -> Iterator[Session]: """Initializes the in memory db session and creates the db schema""" session = sessionmaker(bind=database_engine) diff --git a/lib/chainsync/chainsync/test_fixtures/dummy_session.py b/lib/chainsync/chainsync/test_fixtures/dummy_session.py index 376aaed6ba..e02ab39c6f 100644 --- a/lib/chainsync/chainsync/test_fixtures/dummy_session.py +++ b/lib/chainsync/chainsync/test_fixtures/dummy_session.py @@ -1,5 +1,5 @@ """Pytest fixture that creates an in memory db session and creates dummy db schemas""" -from typing import Any, Generator +from typing import Any, Iterator import pytest from sqlalchemy import String, create_engine @@ -27,7 +27,7 @@ class DropMe(DummyBase): @pytest.fixture(scope="function") -def dummy_session() -> Generator[Session, Any, Any]: +def dummy_session() -> Iterator[Session]: """Dummy session fixture for tests""" engine = create_engine("sqlite:///:memory:") # in-memory SQLite database for testing session = sessionmaker(bind=engine) diff --git a/lib/ethpy/ethpy/test_fixtures/local_chain.py b/lib/ethpy/ethpy/test_fixtures/local_chain.py index 73b6ea0560..364c443062 100644 --- a/lib/ethpy/ethpy/test_fixtures/local_chain.py +++ b/lib/ethpy/ethpy/test_fixtures/local_chain.py @@ -1,7 +1,7 @@ """Test fixture for deploying local anvil chain and initializing hyperdrive""" import subprocess import time -from typing import Any, Generator +from typing import Any, Iterator import pytest from ethpy.base import initialize_web3_with_http_provider @@ -15,12 +15,12 @@ @pytest.fixture(scope="function") -def local_chain() -> Generator[str, Any, Any]: +def local_chain() -> Iterator[str]: """Launches a local anvil chain for testing. Kills the anvil chain after. Returns ------- - Generator[str, Any, Any] + Iterator[str] Yields the local anvil chain url """ anvil_port = 9999 From 938698f97c4553b6f6632ccbe0f4292b2bf48c64 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 15:30:42 -0700 Subject: [PATCH 14/16] Updating readme --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index b98627609f..7644124a38 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,13 @@ In that example, the internal representation is `8`, so casting it to a float wo To understand more, we recommend that you study the fixed point tests and source implementation in `elfpy/math/`. +Warning! Using floating point as a constructor to FixedPoint can cause loss of precision. For example, +``` +>>> FixedPoint(1e18) +FixedPoint("1000000000000000042.420637374017961984") +``` +Allowing floating point in the constructor of FixedPoint will be removed in a future version of `fixedpointmath`. + ## Modifying configuration for agent deployment Follow `lib/agent0/README.md` for agent deployment. From 69ac6bdf85325ddc632cca71aeb7726e1614a4ee Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 15:40:29 -0700 Subject: [PATCH 15/16] lint --- lib/chainsync/chainsync/test_fixtures/db_session.py | 2 +- lib/chainsync/chainsync/test_fixtures/dummy_session.py | 2 +- lib/ethpy/ethpy/test_fixtures/local_chain.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/chainsync/chainsync/test_fixtures/db_session.py b/lib/chainsync/chainsync/test_fixtures/db_session.py index 9d89c4cce2..72f624267b 100644 --- a/lib/chainsync/chainsync/test_fixtures/db_session.py +++ b/lib/chainsync/chainsync/test_fixtures/db_session.py @@ -1,6 +1,6 @@ """Pytest fixture that creates an in memory db session and creates the base db schema""" import time -from typing import Any, Iterator +from typing import Iterator import docker import pytest diff --git a/lib/chainsync/chainsync/test_fixtures/dummy_session.py b/lib/chainsync/chainsync/test_fixtures/dummy_session.py index e02ab39c6f..0e57c90fda 100644 --- a/lib/chainsync/chainsync/test_fixtures/dummy_session.py +++ b/lib/chainsync/chainsync/test_fixtures/dummy_session.py @@ -1,5 +1,5 @@ """Pytest fixture that creates an in memory db session and creates dummy db schemas""" -from typing import Any, Iterator +from typing import Iterator import pytest from sqlalchemy import String, create_engine diff --git a/lib/ethpy/ethpy/test_fixtures/local_chain.py b/lib/ethpy/ethpy/test_fixtures/local_chain.py index 364c443062..f1ee52e245 100644 --- a/lib/ethpy/ethpy/test_fixtures/local_chain.py +++ b/lib/ethpy/ethpy/test_fixtures/local_chain.py @@ -1,7 +1,7 @@ """Test fixture for deploying local anvil chain and initializing hyperdrive""" import subprocess import time -from typing import Any, Iterator +from typing import Iterator import pytest from ethpy.base import initialize_web3_with_http_provider From cfb632b46d1de5f5a0072a2ca9c4b521cac67aaf Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Mon, 21 Aug 2023 15:44:19 -0700 Subject: [PATCH 16/16] Oracle size is an integer, not fixedpoint --- lib/chainsync/chainsync/db/hyperdrive/schema.py | 2 +- lib/ethpy/ethpy/hyperdrive/interface.py | 2 +- tests/system_test.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/chainsync/chainsync/db/hyperdrive/schema.py b/lib/chainsync/chainsync/db/hyperdrive/schema.py index 41143b3487..50fae2fe4f 100644 --- a/lib/chainsync/chainsync/db/hyperdrive/schema.py +++ b/lib/chainsync/chainsync/db/hyperdrive/schema.py @@ -35,7 +35,7 @@ class PoolConfig(Base): curveFee: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None) flatFee: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None) governanceFee: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None) - oracleSize: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None) + oracleSize: Mapped[Union[int, None]] = mapped_column(Integer, default=None) updateGap: Mapped[Union[int, None]] = mapped_column(Integer, default=None) invTimeStretch: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None) updateGap: Mapped[Union[int, None]] = mapped_column(Integer, default=None) diff --git a/lib/ethpy/ethpy/hyperdrive/interface.py b/lib/ethpy/ethpy/hyperdrive/interface.py index 1b1de82de2..6a7a787bdd 100644 --- a/lib/ethpy/ethpy/hyperdrive/interface.py +++ b/lib/ethpy/ethpy/hyperdrive/interface.py @@ -124,7 +124,7 @@ def get_hyperdrive_config(hyperdrive_contract: Contract) -> dict[str, Any]: pool_config["curveFee"] = FixedPoint(scaled_value=curve_fee) pool_config["flatFee"] = FixedPoint(scaled_value=flat_fee) pool_config["governanceFee"] = FixedPoint(scaled_value=governance_fee) - pool_config["oracleSize"] = FixedPoint(scaled_value=hyperdrive_config["oracleSize"]) + pool_config["oracleSize"] = hyperdrive_config["oracleSize"] pool_config["updateGap"] = hyperdrive_config["updateGap"] return pool_config diff --git a/tests/system_test.py b/tests/system_test.py index d4c7a9d04d..d1cbec2ce0 100644 --- a/tests/system_test.py +++ b/tests/system_test.py @@ -138,7 +138,7 @@ def test_bot_to_db( "curveFee": _to_unscaled_decimal(FixedPoint("0.1")), # 10% "flatFee": _to_unscaled_decimal(FixedPoint("0.0005")), # 0.05% "governanceFee": _to_unscaled_decimal(FixedPoint("0.15")), # 15% - "oracleSize": _to_unscaled_decimal(FixedPoint("10")), + "oracleSize": 10, "updateGap": 3600, # TODO don't know where this is getting set "invTimeStretch": expected_inv_timestretch, }