Optimize imports and return BlockProtocolChanges msg

This commit is contained in:
Thales Lima
2024-07-15 15:05:03 +02:00
committed by tvinagre
parent f6fba2805a
commit c075fdb668
7 changed files with 105 additions and 96 deletions

View File

@@ -2,10 +2,8 @@ from decimal import Decimal
from logging import getLogger from logging import getLogger
from typing import Any from typing import Any
from protosim_py import SimulationEngine
from tycho.tycho.exceptions import TychoDecodeError from tycho.tycho.exceptions import TychoDecodeError
from tycho.tycho.models import EVMBlock, EthereumToken, DatabaseType from tycho.tycho.models import EVMBlock, EthereumToken
from tycho.tycho.pool_state import ThirdPartyPool from tycho.tycho.pool_state import ThirdPartyPool
from tycho.tycho.utils import decode_tycho_exchange from tycho.tycho.utils import decode_tycho_exchange
@@ -87,7 +85,8 @@ class ThirdPartyPoolTychoDecoder:
"stateless_contracts": stateless_contracts, "stateless_contracts": stateless_contracts,
} }
def decode_balances(self, snap, tokens): @staticmethod
def decode_balances(snap, tokens):
balances = {} balances = {}
for addr, balance in snap["state"]["balances"].items(): for addr, balance in snap["state"]["balances"].items():
checksum_addr = addr checksum_addr = addr
@@ -97,9 +96,9 @@ class ThirdPartyPoolTychoDecoder:
) )
return balances return balances
@staticmethod
def apply_update( def apply_update(
self, pool: ThirdPartyPool,
pool: ThirdPartyPool,
pool_update: dict[str, Any], pool_update: dict[str, Any],
balance_updates: dict[str, Any], balance_updates: dict[str, Any],
block: EVMBlock, block: EVMBlock,

View File

@@ -53,3 +53,7 @@ class OutOfGas(RecoverableSimulationException):
""" """
pass pass
class TychoClientException(Exception):
pass

View File

@@ -1,9 +1,12 @@
import datetime import datetime
from dataclasses import dataclass
from enum import Enum, IntEnum, auto from enum import Enum, IntEnum, auto
from typing import Union from typing import Union
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from tycho.tycho.pool_state import ThirdPartyPool
Address = str Address = str
@@ -43,3 +46,23 @@ class Capability(IntEnum):
ConstantPrice = auto() ConstantPrice = auto()
TokenBalanceIndependent = auto() TokenBalanceIndependent = auto()
ScaledPrice = auto() ScaledPrice = auto()
class SynchronizerState(Enum):
started = "started"
ready = "ready"
stale = "stale"
delayed = "delayed"
advanced = "advanced"
ended = "ended"
@dataclass(repr=False)
class BlockProtocolChanges:
block: EVMBlock
pool_states: dict[Address, ThirdPartyPool]
"""All updated pools"""
removed_pools: set[Address]
sync_states: dict[str, SynchronizerState]
deserialization_time: float
"""The time it took to deserialize the pool states from the tycho feed message"""

View File

@@ -7,26 +7,20 @@ from fractions import Fraction
from logging import getLogger from logging import getLogger
from typing import Optional, cast, TypeVar from typing import Optional, cast, TypeVar
from eth_typing import HexStr
from protosim_py import SimulationEngine, AccountInfo from protosim_py import SimulationEngine, AccountInfo
from pydantic import BaseModel, PrivateAttr, Field from pydantic import BaseModel, PrivateAttr, Field
from tycho.tycho.adapter_contract import AdapterContract from tycho.tycho.adapter_contract import AdapterContract
from tycho.tycho.constants import MAX_BALANCE, EXTERNAL_ACCOUNT from tycho.tycho.constants import MAX_BALANCE, EXTERNAL_ACCOUNT
from tycho.tycho.exceptions import RecoverableSimulationException from tycho.tycho.exceptions import RecoverableSimulationException
from tycho.tycho.models import ( from tycho.tycho.models import EVMBlock, Capability, Address, EthereumToken
EVMBlock,
DatabaseType,
Capability,
Address,
EthereumToken,
)
from tycho.tycho.utils import ( from tycho.tycho.utils import (
create_engine, create_engine,
get_contract_bytecode, get_contract_bytecode,
frac_to_decimal, frac_to_decimal,
ERC20OverwriteFactory, ERC20OverwriteFactory,
) )
from eth_typing import HexStr
ADAPTER_ADDRESS = "0xA2C5C98A892fD6656a7F39A2f63228C0Bc846270" ADAPTER_ADDRESS = "0xA2C5C98A892fD6656a7F39A2f63228C0Bc846270"
@@ -167,8 +161,6 @@ class ThirdPartyPool(BaseModel):
sell_token: EthereumToken, sell_token: EthereumToken,
sell_amount: Decimal, sell_amount: Decimal,
buy_token: EthereumToken, buy_token: EthereumToken,
slippage: Decimal = Decimal(0),
create_new_pool: bool = True,
) -> tuple[Decimal, int, TPoolState]: ) -> tuple[Decimal, int, TPoolState]:
# if the pool has a hard limit and the sell amount exceeds that, simulate and # if the pool has a hard limit and the sell amount exceeds that, simulate and
# raise a partial trade # raise a partial trade
@@ -276,7 +268,7 @@ class ThirdPartyPool(BaseModel):
simulate the same block. This is fine, see simulate the same block. This is fine, see
https://datarevenue.atlassian.net/browse/ROC-1301 https://datarevenue.atlassian.net/browse/ROC-1301
Not naming this method _copy to not confuse with pydantic's .copy method. Not naming this method _copy to not confuse with Pydantic's .copy method.
""" """
return type(self)( return type(self)(
exchange=self.exchange, exchange=self.exchange,
@@ -379,8 +371,8 @@ def _merge(a: dict, b: dict, path=None):
to keep track of the ancestry of nested dictionaries. to keep track of the ancestry of nested dictionaries.
Returns: Returns:
a (dict): The merged dictionary which includes all key-value pairs from b a (dict): The merged dictionary which includes all key-value pairs from `b`
added into a. If they have nested dictionaries with same keys, those are also merged. added into `a`. If they have nested dictionaries with same keys, those are also merged.
On key conflicts, preference is given to values from b. On key conflicts, preference is given to values from b.
""" """
if path is None: if path is None:

View File

@@ -1,44 +1,34 @@
import asyncio import asyncio
import json import json
import platform import platform
import requests
import time import time
from asyncio.subprocess import STDOUT, PIPE from asyncio.subprocess import STDOUT, PIPE
from collections import defaultdict from collections import defaultdict
from datetime import datetime from datetime import datetime
from decimal import Decimal from decimal import Decimal
from http.client import HTTPException from http.client import HTTPException
from logging import getLogger from logging import getLogger
from typing import Tuple, Any, Optional, Dict from typing import Any, Optional, Dict
from protosim_py import ( import requests
AccountUpdate, from protosim_py import AccountUpdate, AccountInfo, BlockHeader
AccountInfo,
BlockHeader,
TychoDB,
SimulationEngine,
)
from tycho.tycho.constants import ( from tycho.tycho.constants import TYCHO_CLIENT_LOG_FOLDER, TYCHO_CLIENT_FOLDER
TYCHO_CLIENT_LOG_FOLDER,
TYCHO_CLIENT_FOLDER,
EXTERNAL_ACCOUNT,
MAX_BALANCE,
)
from tycho.tycho.decoders import ThirdPartyPoolTychoDecoder from tycho.tycho.decoders import ThirdPartyPoolTychoDecoder
from tycho.tycho.exceptions import APIRequestError from tycho.tycho.exceptions import APIRequestError, TychoClientException
from tycho.tycho.models import Blockchain, EVMBlock, EthereumToken from tycho.tycho.models import (
from tycho.tycho.pool_state import ThirdPartyPool Blockchain,
from tycho.tycho.utils import create_engine, TychoDBSingleton EVMBlock,
EthereumToken,
BlockProtocolChanges,
SynchronizerState,
)
from tycho.tycho.tycho_db import TychoDBSingleton
from tycho.tycho.utils import create_engine
log = getLogger(__name__) log = getLogger(__name__)
class TychoClientException(Exception):
pass
class TokenLoader: class TokenLoader:
def __init__( def __init__(
self, self,
@@ -128,7 +118,7 @@ class TychoPoolStateStreamAdapter:
# Create engine # Create engine
# TODO: This should be initialized outside the adapter? # TODO: This should be initialized outside the adapter?
TychoDBSingleton.initialize(tycho_http_url=self.tycho_url) TychoDBSingleton.initialize(tycho_http_url=self.tycho_url)
self._engine = create_engine([], state_block=None, trace=True) self._engine = create_engine([], trace=True)
# Loads tokens from Tycho # Loads tokens from Tycho
self._tokens: dict[str, EthereumToken] = TokenLoader( self._tokens: dict[str, EthereumToken] = TokenLoader(
@@ -222,7 +212,7 @@ class TychoPoolStateStreamAdapter:
sync_state = msg["sync_states"][self.protocol] sync_state = msg["sync_states"][self.protocol]
state_msg = msg["state_msgs"][self.protocol] state_msg = msg["state_msgs"][self.protocol]
log.info(f"Received sync state for {self.protocol}: {sync_state}") log.info(f"Received sync state for {self.protocol}: {sync_state}")
if not sync_state["status"] != "ready": if not sync_state["status"] != SynchronizerState.ready.value:
raise ValueError("Tycho-indexer is not synced") raise ValueError("Tycho-indexer is not synced")
except KeyError: except KeyError:
raise ValueError("Invalid message received from tycho-client.") raise ValueError("Invalid message received from tycho-client.")
@@ -265,10 +255,9 @@ class TychoPoolStateStreamAdapter:
log.info(f"Could not to decode {failed_count}/{total} pool snapshots") log.info(f"Could not to decode {failed_count}/{total} pool snapshots")
return BlockProtocolChanges( return BlockProtocolChanges(
block=self.current_block, block=block,
pool_states=pool_states, pool_states=decoded_pools,
removed_pools=removed_pools, removed_pools=removed_pools,
sync_states=exchanges_states,
deserialization_time=round(deserialization_time, 3), deserialization_time=round(deserialization_time, 3),
) )

View File

@@ -0,0 +1,48 @@
from protosim_py import TychoDB
class TychoDBSingleton:
"""
A singleton wrapper around the TychoDB class.
This class ensures that there is only one instance of TychoDB throughout the lifetime of the program,
avoiding the overhead of creating multiple instances.
"""
_instance = None
@classmethod
def initialize(cls, tycho_http_url: str):
"""
Initialize the TychoDB instance with the given URLs.
Parameters
----------
tycho_http_url : str
The URL of the Tycho HTTP server.
"""
cls._instance = TychoDB(tycho_http_url=tycho_http_url)
@classmethod
def get_instance(cls) -> TychoDB:
"""
Retrieve the singleton instance of TychoDB.
If the TychoDB instance does not exist, it creates a new one.
If it already exists, it returns the existing instance.
Returns
-------
TychoDB
The singleton instance of TychoDB.
"""
if cls._instance is None:
raise ValueError(
"TychoDB instance not initialized. Call initialize() first."
)
return cls._instance
@classmethod
def clear_instance(cls):
cls._instance = None

View File

@@ -10,12 +10,13 @@ from typing import Final, Any
import eth_abi import eth_abi
from eth_typing import HexStr from eth_typing import HexStr
from hexbytes import HexBytes from hexbytes import HexBytes
from protosim_py import SimulationEngine, TychoDB, AccountInfo from protosim_py import SimulationEngine, AccountInfo
from web3 import Web3 from web3 import Web3
from tycho.tycho.constants import EXTERNAL_ACCOUNT, MAX_BALANCE from tycho.tycho.constants import EXTERNAL_ACCOUNT, MAX_BALANCE
from tycho.tycho.exceptions import OutOfGas from tycho.tycho.exceptions import OutOfGas
from tycho.tycho.models import Address, EthereumToken from tycho.tycho.models import Address, EthereumToken
from tycho.tycho.tycho_db import TychoDBSingleton
log = getLogger(__name__) log = getLogger(__name__)
@@ -25,53 +26,6 @@ def decode_tycho_exchange(exchange: str) -> (str, bool):
return (exchange.split(":")[1], False) if "vm:" in exchange else (exchange, True) return (exchange.split(":")[1], False) if "vm:" in exchange else (exchange, True)
class TychoDBSingleton:
"""
A singleton wrapper around the TychoDB class.
This class ensures that there is only one instance of TychoDB throughout the lifetime of the program,
avoiding the overhead of creating multiple instances.
"""
_instance = None
@classmethod
def initialize(cls, tycho_http_url: str):
"""
Initialize the TychoDB instance with the given URLs.
Parameters
----------
tycho_http_url : str
The URL of the Tycho HTTP server.
"""
cls._instance = TychoDB(tycho_http_url=tycho_http_url)
@classmethod
def get_instance(cls) -> TychoDB:
"""
Retrieve the singleton instance of TychoDB.
If the TychoDB instance does not exist, it creates a new one.
If it already exists, it returns the existing instance.
Returns
-------
TychoDB
The singleton instance of TychoDB.
"""
if cls._instance is None:
raise ValueError(
"TychoDB instance not initialized. Call initialize() first."
)
return cls._instance
@classmethod
def clear_instance(cls):
cls._instance = None
def create_engine( def create_engine(
mocked_tokens: list[Address], trace: bool = False mocked_tokens: list[Address], trace: bool = False
) -> SimulationEngine: ) -> SimulationEngine: