From 75e931027b908c467f57cad8e47ced6012d6d636 Mon Sep 17 00:00:00 2001 From: Evan Griffiths <56087052+evangriffiths@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:01:16 +0100 Subject: [PATCH 1/9] Add utc timezones to OmenBet timestamps (#440) --- .../markets/omen/data_models.py | 5 +++-- .../tools/langfuse_client_utils.py | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/prediction_market_agent_tooling/markets/omen/data_models.py b/prediction_market_agent_tooling/markets/omen/data_models.py index 1c127417..db61b3dd 100644 --- a/prediction_market_agent_tooling/markets/omen/data_models.py +++ b/prediction_market_agent_tooling/markets/omen/data_models.py @@ -1,6 +1,7 @@ import typing as t from datetime import datetime +import pytz from pydantic import BaseModel from web3 import Web3 @@ -218,7 +219,7 @@ def openingTimestamp(self) -> int: @property def opening_datetime(self) -> datetime: - return datetime.fromtimestamp(self.openingTimestamp) + return datetime.fromtimestamp(self.openingTimestamp, tz=pytz.UTC) @property def close_time(self) -> datetime: @@ -400,7 +401,7 @@ class OmenBet(BaseModel): @property def creation_datetime(self) -> datetime: - return datetime.fromtimestamp(self.creationTimestamp) + return datetime.fromtimestamp(self.creationTimestamp, tz=pytz.UTC) @property def boolean_outcome(self) -> bool: diff --git a/prediction_market_agent_tooling/tools/langfuse_client_utils.py b/prediction_market_agent_tooling/tools/langfuse_client_utils.py index 8e31533b..ba7f3865 100644 --- a/prediction_market_agent_tooling/tools/langfuse_client_utils.py +++ b/prediction_market_agent_tooling/tools/langfuse_client_utils.py @@ -6,6 +6,7 @@ from langfuse.client import TraceWithDetails from pydantic import BaseModel +from prediction_market_agent_tooling.loggers import logger from prediction_market_agent_tooling.markets.data_models import ( PlacedTrade, ProbabilisticAnswer, @@ -146,9 +147,21 @@ def get_trace_for_bet( else: # In-case there are multiple traces for the same market, get the closest # trace to the bet + bet_timestamp = add_utc_timezone_validator(bet.created_time) closest_trace_index = get_closest_datetime_from_list( - add_utc_timezone_validator(bet.created_time), + bet_timestamp, [t.timestamp for t in traces_for_bet], ) + # Sanity check: Let's say the upper bound for time between + # `agent.process_market` being called and the bet being placed is 20 + # minutes + candidate_trace = traces_for_bet[closest_trace_index] + if abs(candidate_trace.timestamp - bet_timestamp).total_seconds() > 1200: + logger.info( + f"Closest trace to bet has timestamp {candidate_trace.timestamp}, " + f"but bet was created at {bet_timestamp}. Not matching" + ) + return None + return traces_for_bet[closest_trace_index] From 247e7b25b255ca2a614286b4fe1b1ce8613312af Mon Sep 17 00:00:00 2001 From: Peter Jung Date: Thu, 26 Sep 2024 16:36:51 +0200 Subject: [PATCH 2/9] Integrate Tenderly to local_web3 (#436) --- prediction_market_agent_tooling/config.py | 1 + .../conftest.py | 44 ++++++++++++++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/prediction_market_agent_tooling/config.py b/prediction_market_agent_tooling/config.py index 312acf22..c124625b 100644 --- a/prediction_market_agent_tooling/config.py +++ b/prediction_market_agent_tooling/config.py @@ -35,6 +35,7 @@ class APIKeys(BaseSettings): SAFE_ADDRESS: t.Optional[ChecksumAddress] = None OPENAI_API_KEY: t.Optional[SecretStr] = None GRAPH_API_KEY: t.Optional[SecretStr] = None + TENDERLY_FORK_RPC: t.Optional[str] = None GOOGLE_SEARCH_API_KEY: t.Optional[SecretStr] = None GOOGLE_SEARCH_ENGINE_ID: t.Optional[SecretStr] = None diff --git a/tests_integration_with_local_chain/conftest.py b/tests_integration_with_local_chain/conftest.py index 55fed91c..1bb9d8ec 100644 --- a/tests_integration_with_local_chain/conftest.py +++ b/tests_integration_with_local_chain/conftest.py @@ -1,6 +1,7 @@ import typing as t import pytest +import requests from ape.managers import ChainManager from ape_test import TestAccount from dotenv import load_dotenv @@ -8,7 +9,13 @@ from web3 import Web3 from prediction_market_agent_tooling.config import APIKeys -from prediction_market_agent_tooling.gtypes import private_key_type +from prediction_market_agent_tooling.gtypes import ( + HexAddress, + private_key_type, + xDai, + xdai_type, +) +from prediction_market_agent_tooling.tools.web3_utils import xdai_to_wei @pytest.fixture(autouse=True, scope="session") @@ -17,13 +24,26 @@ def load_env() -> None: @pytest.fixture(scope="session") -def local_web3(load_env: None, chain: ChainManager) -> t.Generator[Web3, None, None]: +def local_web3( + load_env: None, chain: ChainManager, accounts: list[TestAccount] +) -> t.Generator[Web3, None, None]: print("entering fixture local_web3") - with chain.network_manager.parse_network_choice( - "gnosis:mainnet_fork:foundry" - ) as provider: - w3 = Web3(Web3.HTTPProvider(provider.http_uri)) + + if (tenderly_fork_rpc := APIKeys().TENDERLY_FORK_RPC) is not None: + print("using tenderly rpc") + w3 = Web3(Web3.HTTPProvider(tenderly_fork_rpc)) + print("funding test accounts on tenderly") + fund_account_on_tenderly( + tenderly_fork_rpc, [a.address for a in accounts], xdai_type(1000) + ) yield w3 + else: + print("using foundry") + with chain.network_manager.parse_network_choice( + "gnosis:mainnet_fork:foundry" + ) as provider: + w3 = Web3(Web3.HTTPProvider(provider.http_uri)) + yield w3 print("exiting fixture local_web3") @@ -41,3 +61,15 @@ def test_keys(accounts: list[TestAccount]) -> APIKeys: return APIKeys( BET_FROM_PRIVATE_KEY=private_key_type(account.private_key), SAFE_ADDRESS=None ) + + +def fund_account_on_tenderly( + fork_rpc: str, addresses: list[HexAddress], balance: xDai +) -> None: + payload = { + "jsonrpc": "2.0", + "method": "tenderly_setBalance", + "params": [addresses, f"0x{xdai_to_wei(balance):X}"], + } + response = requests.post(fork_rpc, json=payload) + response.raise_for_status() From 0312c5520924503d691c8437cea1c9a63621555b Mon Sep 17 00:00:00 2001 From: Peter Jung Date: Mon, 30 Sep 2024 09:01:17 +0200 Subject: [PATCH 3/9] Use `mint_new_block` --- prediction_market_agent_tooling/tools/web3_utils.py | 10 ++++++++++ .../markets/omen/test_local_chain.py | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/prediction_market_agent_tooling/tools/web3_utils.py b/prediction_market_agent_tooling/tools/web3_utils.py index edbdd62e..2bf483cd 100644 --- a/prediction_market_agent_tooling/tools/web3_utils.py +++ b/prediction_market_agent_tooling/tools/web3_utils.py @@ -12,6 +12,7 @@ from web3.constants import HASH_ZERO from web3.types import AccessList, AccessListEntry, Nonce, TxParams, TxReceipt, Wei +from prediction_market_agent_tooling.config import APIKeys from prediction_market_agent_tooling.gtypes import ( ABI, ChecksumAddress, @@ -24,6 +25,7 @@ xdai_type, ) from prediction_market_agent_tooling.loggers import logger +from prediction_market_agent_tooling.tools.contract import DebuggingContract ONE_NONCE = Nonce(1) ONE_XDAI = xdai_type(1) @@ -335,3 +337,11 @@ def byte32_to_ipfscidv0(hex: HexBytes) -> IPFSCIDVersion0: """ completed_binary_str = b"\x12 " + hex return IPFSCIDVersion0(base58.b58encode(completed_binary_str).decode("utf-8")) + + +def mint_new_block(keys: APIKeys, web3: Web3) -> None: + """ + Mints a new block on the web3's blockchain. + Useful for tests that debends on chain's timestamp, this will update it. + """ + DebuggingContract().inc(keys, web3) diff --git a/tests_integration_with_local_chain/markets/omen/test_local_chain.py b/tests_integration_with_local_chain/markets/omen/test_local_chain.py index 9c674c67..2ed13362 100644 --- a/tests_integration_with_local_chain/markets/omen/test_local_chain.py +++ b/tests_integration_with_local_chain/markets/omen/test_local_chain.py @@ -16,6 +16,7 @@ from prediction_market_agent_tooling.tools.contract import DebuggingContract from prediction_market_agent_tooling.tools.utils import utcnow from prediction_market_agent_tooling.tools.web3_utils import ( + mint_new_block, send_xdai_to, wei_to_xdai, xdai_to_wei, @@ -102,7 +103,7 @@ def test_fresh_account_has_less_than_minimum_required_balance( def test_now(local_web3: Web3, test_keys: APIKeys) -> None: # we need to mint a new block to update timestamp - DebuggingContract().inc(test_keys, local_web3) + mint_new_block(test_keys, local_web3) allowed_difference = 10 # seconds chain_timestamp = DebuggingContract().getNow(local_web3) utc_timestamp = int(utcnow().timestamp()) @@ -124,7 +125,7 @@ def test_now_failed(local_web3: Web3, test_keys: APIKeys) -> None: def test_now_datetime(local_web3: Web3, test_keys: APIKeys) -> None: # we need to mint a new block to update timestamp - DebuggingContract().inc(test_keys, local_web3) + mint_new_block(test_keys, local_web3) allowed_difference = 10 # seconds chain_datetime = DebuggingContract().get_now(local_web3) utc_datetime = utcnow() From 75d1f1ee8d5bff29661c7710273cabd1c5c6824d Mon Sep 17 00:00:00 2001 From: Peter Jung Date: Mon, 30 Sep 2024 09:03:50 +0200 Subject: [PATCH 4/9] lower the difference --- .../markets/omen/test_local_chain.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests_integration_with_local_chain/markets/omen/test_local_chain.py b/tests_integration_with_local_chain/markets/omen/test_local_chain.py index 2ed13362..5acba8fe 100644 --- a/tests_integration_with_local_chain/markets/omen/test_local_chain.py +++ b/tests_integration_with_local_chain/markets/omen/test_local_chain.py @@ -104,7 +104,7 @@ def test_fresh_account_has_less_than_minimum_required_balance( def test_now(local_web3: Web3, test_keys: APIKeys) -> None: # we need to mint a new block to update timestamp mint_new_block(test_keys, local_web3) - allowed_difference = 10 # seconds + allowed_difference = 5 # seconds chain_timestamp = DebuggingContract().getNow(local_web3) utc_timestamp = int(utcnow().timestamp()) assert ( @@ -115,7 +115,7 @@ def test_now(local_web3: Web3, test_keys: APIKeys) -> None: def test_now_failed(local_web3: Web3, test_keys: APIKeys) -> None: # Sleep a little to let the local chain go out of sync without updating the block time.sleep(5) - allowed_difference = 10 # seconds + allowed_difference = 5 # seconds chain_timestamp = DebuggingContract().getNow(local_web3) utc_timestamp = int(utcnow().timestamp()) assert ( @@ -126,7 +126,7 @@ def test_now_failed(local_web3: Web3, test_keys: APIKeys) -> None: def test_now_datetime(local_web3: Web3, test_keys: APIKeys) -> None: # we need to mint a new block to update timestamp mint_new_block(test_keys, local_web3) - allowed_difference = 10 # seconds + allowed_difference = 5 # seconds chain_datetime = DebuggingContract().get_now(local_web3) utc_datetime = utcnow() actual_difference = abs(chain_datetime - utc_datetime) From e8694219197ee2c1f941a803f7a2dd8dbaaf3ca3 Mon Sep 17 00:00:00 2001 From: Peter Jung Date: Mon, 30 Sep 2024 09:13:44 +0200 Subject: [PATCH 5/9] fix circular import --- prediction_market_agent_tooling/tools/web3_utils.py | 10 ---------- tests/utils.py | 13 +++++++++++++ .../markets/omen/test_local_chain.py | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/prediction_market_agent_tooling/tools/web3_utils.py b/prediction_market_agent_tooling/tools/web3_utils.py index 2bf483cd..edbdd62e 100644 --- a/prediction_market_agent_tooling/tools/web3_utils.py +++ b/prediction_market_agent_tooling/tools/web3_utils.py @@ -12,7 +12,6 @@ from web3.constants import HASH_ZERO from web3.types import AccessList, AccessListEntry, Nonce, TxParams, TxReceipt, Wei -from prediction_market_agent_tooling.config import APIKeys from prediction_market_agent_tooling.gtypes import ( ABI, ChecksumAddress, @@ -25,7 +24,6 @@ xdai_type, ) from prediction_market_agent_tooling.loggers import logger -from prediction_market_agent_tooling.tools.contract import DebuggingContract ONE_NONCE = Nonce(1) ONE_XDAI = xdai_type(1) @@ -337,11 +335,3 @@ def byte32_to_ipfscidv0(hex: HexBytes) -> IPFSCIDVersion0: """ completed_binary_str = b"\x12 " + hex return IPFSCIDVersion0(base58.b58encode(completed_binary_str).decode("utf-8")) - - -def mint_new_block(keys: APIKeys, web3: Web3) -> None: - """ - Mints a new block on the web3's blockchain. - Useful for tests that debends on chain's timestamp, this will update it. - """ - DebuggingContract().inc(keys, web3) diff --git a/tests/utils.py b/tests/utils.py index f2cf1cdc..b7ed8589 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,3 +1,16 @@ import os +from web3 import Web3 + +from prediction_market_agent_tooling.config import APIKeys +from prediction_market_agent_tooling.tools.contract import DebuggingContract + RUN_PAID_TESTS = os.environ.get("RUN_PAID_TESTS", "0") == "1" + + +def mint_new_block(keys: APIKeys, web3: Web3) -> None: + """ + Mints a new block on the web3's blockchain. + Useful for tests that debends on chain's timestamp, this will update it. + """ + DebuggingContract().inc(keys, web3) diff --git a/tests_integration_with_local_chain/markets/omen/test_local_chain.py b/tests_integration_with_local_chain/markets/omen/test_local_chain.py index 5acba8fe..f984a72f 100644 --- a/tests_integration_with_local_chain/markets/omen/test_local_chain.py +++ b/tests_integration_with_local_chain/markets/omen/test_local_chain.py @@ -16,11 +16,11 @@ from prediction_market_agent_tooling.tools.contract import DebuggingContract from prediction_market_agent_tooling.tools.utils import utcnow from prediction_market_agent_tooling.tools.web3_utils import ( - mint_new_block, send_xdai_to, wei_to_xdai, xdai_to_wei, ) +from tests.utils import mint_new_block def test_connect_local_chain(local_web3: Web3) -> None: From 4930a23cb176adab68e55f13e1010850b7eb290f Mon Sep 17 00:00:00 2001 From: Peter Jung Date: Mon, 30 Sep 2024 09:21:57 +0200 Subject: [PATCH 6/9] fix another test --- tests_integration_with_local_chain/markets/omen/test_omen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_integration_with_local_chain/markets/omen/test_omen.py b/tests_integration_with_local_chain/markets/omen/test_omen.py index b8719625..a7189959 100644 --- a/tests_integration_with_local_chain/markets/omen/test_omen.py +++ b/tests_integration_with_local_chain/markets/omen/test_omen.py @@ -382,7 +382,7 @@ def test_place_bet_with_autodeposit( # Check that we have xdai funds, but no wxdai funds initial_balances = get_balances(address=test_keys.bet_from_address, web3=local_web3) - assert initial_balances.wxdai == xdai_type(0) + assert np.isclose(initial_balances.wxdai, xdai_type(0)) assert initial_balances.xdai > xdai_type(0) # Try to place a bet with 90% of the xDai funds From ec70b3523eced5cbe080a3aab598a8be5ccdaf3a Mon Sep 17 00:00:00 2001 From: Peter Jung Date: Mon, 30 Sep 2024 12:15:34 +0200 Subject: [PATCH 7/9] be less strict --- .../markets/omen/test_local_chain.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests_integration_with_local_chain/markets/omen/test_local_chain.py b/tests_integration_with_local_chain/markets/omen/test_local_chain.py index f984a72f..bafeece2 100644 --- a/tests_integration_with_local_chain/markets/omen/test_local_chain.py +++ b/tests_integration_with_local_chain/markets/omen/test_local_chain.py @@ -104,7 +104,7 @@ def test_fresh_account_has_less_than_minimum_required_balance( def test_now(local_web3: Web3, test_keys: APIKeys) -> None: # we need to mint a new block to update timestamp mint_new_block(test_keys, local_web3) - allowed_difference = 5 # seconds + allowed_difference = 15 # seconds chain_timestamp = DebuggingContract().getNow(local_web3) utc_timestamp = int(utcnow().timestamp()) assert ( @@ -126,10 +126,10 @@ def test_now_failed(local_web3: Web3, test_keys: APIKeys) -> None: def test_now_datetime(local_web3: Web3, test_keys: APIKeys) -> None: # we need to mint a new block to update timestamp mint_new_block(test_keys, local_web3) - allowed_difference = 5 # seconds + allowed_difference = 15 # seconds chain_datetime = DebuggingContract().get_now(local_web3) utc_datetime = utcnow() - actual_difference = abs(chain_datetime - utc_datetime) - assert actual_difference <= timedelta( - seconds=allowed_difference + actual_difference = (utc_datetime - chain_datetime).total_seconds() + assert ( + actual_difference <= allowed_difference ), f"chain_datetime and utc_datetime differ by more than {allowed_difference} seconds: {chain_datetime=} {utc_datetime=} {actual_difference=}" From 51ac55f7bcb3b8ad085c5367413266355a15a911 Mon Sep 17 00:00:00 2001 From: Peter Jung Date: Mon, 30 Sep 2024 12:19:33 +0200 Subject: [PATCH 8/9] lint --- .../markets/omen/test_local_chain.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests_integration_with_local_chain/markets/omen/test_local_chain.py b/tests_integration_with_local_chain/markets/omen/test_local_chain.py index bafeece2..e6fd14e2 100644 --- a/tests_integration_with_local_chain/markets/omen/test_local_chain.py +++ b/tests_integration_with_local_chain/markets/omen/test_local_chain.py @@ -1,5 +1,4 @@ import time -from datetime import timedelta from ape_test import TestAccount from eth_account import Account From 853fe3e11749cc7a0222712d4e85178aec3314c4 Mon Sep 17 00:00:00 2001 From: Peter Jung Date: Mon, 30 Sep 2024 12:49:47 +0200 Subject: [PATCH 9/9] fix bad merge --- prediction_market_agent_tooling/tools/langfuse_client_utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/prediction_market_agent_tooling/tools/langfuse_client_utils.py b/prediction_market_agent_tooling/tools/langfuse_client_utils.py index 043b7ba5..0f08af2d 100644 --- a/prediction_market_agent_tooling/tools/langfuse_client_utils.py +++ b/prediction_market_agent_tooling/tools/langfuse_client_utils.py @@ -147,9 +147,8 @@ def get_trace_for_bet( else: # In-case there are multiple traces for the same market, get the closest # trace to the bet - bet_timestamp = add_utc_timezone_validator(bet.created_time) + bet_timestamp = convert_to_utc_datetime(bet.created_time) closest_trace_index = get_closest_datetime_from_list( - convert_to_utc_datetime(bet.created_time), bet_timestamp, [t.timestamp for t in traces_for_bet], )