diff --git a/mev_inspect/arbitrages.py b/mev_inspect/arbitrages.py index 08c13f4..06293c1 100644 --- a/mev_inspect/arbitrages.py +++ b/mev_inspect/arbitrages.py @@ -1,8 +1,7 @@ from itertools import groupby from typing import List, Optional -from mev_inspect.schemas.arbitrages import Arbitrage -from mev_inspect.schemas.swaps import Swap +from mev_inspect.schemas.classified_traces import Swap, Arbitrage def get_arbitrages(swaps: List[Swap]) -> List[Arbitrage]: diff --git a/mev_inspect/schemas/arbitrages.py b/mev_inspect/schemas/arbitrages.py index 0c39917..05ab0ef 100644 --- a/mev_inspect/schemas/arbitrages.py +++ b/mev_inspect/schemas/arbitrages.py @@ -2,7 +2,7 @@ from typing import List from pydantic import BaseModel -from .swaps import Swap +from .classified_traces import Swap class Arbitrage(BaseModel): diff --git a/mev_inspect/schemas/blocks.py b/mev_inspect/schemas/blocks.py index 0c24abf..82a7cbe 100644 --- a/mev_inspect/schemas/blocks.py +++ b/mev_inspect/schemas/blocks.py @@ -45,11 +45,8 @@ class TraceType(Enum): class Trace(CamelModel): - action: dict - block_hash: str block_number: int result: Optional[dict] - subtraces: int trace_address: List[int] transaction_hash: Optional[str] transaction_position: Optional[int] diff --git a/mev_inspect/schemas/classified_traces.py b/mev_inspect/schemas/classified_traces.py index a55a494..634c364 100644 --- a/mev_inspect/schemas/classified_traces.py +++ b/mev_inspect/schemas/classified_traces.py @@ -1,9 +1,14 @@ from enum import Enum + from typing import Any, Dict, List, Optional from pydantic import BaseModel -from .blocks import TraceType +from .blocks import Trace + +from .utils import CamelModel + +# -------- Enums ------------------------------------------------------------------------------ class Classification(Enum): @@ -23,23 +28,29 @@ class Protocol(Enum): curve = "curve" -class ClassifiedTrace(BaseModel): +# -------- Trace Models ------------------------------------------------------------------------------ + + +class ClassifiedTrace(Trace): + transaction_hash: str block_number: int - trace_type: TraceType trace_address: List[int] classification: Classification - protocol: Optional[Protocol] - abi_name: Optional[str] - function_name: Optional[str] - function_signature: Optional[str] - inputs: Optional[Dict[str, Any]] + error: Optional[str] + block_hash: Optional[str] + subtraces: Optional[int] + action: Optional[dict] to_address: Optional[str] from_address: Optional[str] gas: Optional[int] value: Optional[int] gas_used: Optional[int] - error: Optional[str] + protocol: Optional[Protocol] + function_name: Optional[str] + function_signature: Optional[str] + inputs: Optional[Dict[str, Any]] + abi_name: Optional[str] class Config: json_encoders = { @@ -49,6 +60,135 @@ class ClassifiedTrace(BaseModel): } +class Call(ClassifiedTrace): + + to_address: str + from_address: str + inputs: Dict[str, Any] + + +class ClassifiedCall(ClassifiedTrace): + gas: Optional[int] + gas_used: Optional[int] + function_name: Optional[str] + function_signature: Optional[str] + abi_name: str + + class Config: + json_encoders = { + # a little lazy but fine for now + # this is used for bytes value inputs + bytes: lambda b: b.hex(), + } + + +# -------- Swaps ------------------------------------------------------------------------------ + + +class Swap(BaseModel): + abi_name: str + transaction_hash: str + block_number: int + trace_address: List[int] + protocol: Optional[Protocol] + pool_address: str + from_address: str + to_address: str + token_in_address: str + token_in_amount: int + token_out_address: str + token_out_amount: int + error: Optional[str] + + +class Arbitrage(BaseModel): + swaps: List[Swap] + block_number: int + transaction_hash: str + account_address: str + profit_token_address: str + start_amount: int + end_amount: int + profit_amount: int + + +# -------- Transfers------------------------------------------------------------------------------ + + +class Transfer(BaseModel): + transaction_hash: str + trace_address: List[int] + from_address: str + to_address: str + amount: int + token_address: str + + @classmethod + def from_trace(cls, trace: ClassifiedTrace) -> "Transfer": + if trace.classification != Classification.transfer or trace.inputs is None: + raise ValueError("Invalid transfer") + + if trace.protocol == Protocol.weth: + return cls( + transaction_hash=trace.transaction_hash, + trace_address=trace.trace_address, + amount=trace.inputs["wad"], + to_address=trace.inputs["dst"], + from_address=trace.from_address, + token_address=trace.to_address, + ) + else: + return cls( + transaction_hash=trace.transaction_hash, + trace_address=trace.trace_address, + amount=trace.inputs["amount"], + to_address=trace.inputs["recipient"], + from_address=trace.inputs.get("sender", trace.from_address), + token_address=trace.to_address, + ) + + +# -------- Liquidations ------------------------------------------------------------------------------ + + +class LiquidationType(Enum): + compound_v2_ceth_liquidation = "compound_v2_ceth_liquidation" + compound_v2_ctoken_liquidation = "compound_v2_ctoken_liquidation" # TODO: add logic to handle ctoken liquidations + + +class LiquidationStatus(Enum): + seized = "seized" # succesfully completed + check = "check" # just a liquidation check. i.e searcher only checks if opportunity is still available and reverts accordingly + out_of_gas = "out_of_gas" # tx ran out of gas + + +class LiquidationCollateralSource(Enum): + aave_flashloan = "aave_flashloan" + dydx_flashloan = "dydx_flashloan" + uniswap_flashloan = "uniswap_flashloan" + searcher_eoa = "searcher_eoa" # searchers own funds + searcher_contract = "searcher_contract" + other = "other" + + +class Liquidation(CamelModel): + tx_hash: str + borrower: str # account that got liquidated + collateral_provided: str # collateral provided by searcher, 'ether' or token contract address + collateral_provided_amount: int # amount of collateral provided + asset_seized: str # asset that was given to searcher at a discount upon liquidation + asset_seized_amount: int # amount of asset that was given to searcher upon liquidation + profit_in_eth: int # profit estimated by strategy inspector + tokenflow_estimate_in_eth: int # profit estimated by tokenflow + tokenflow_diff: int # diff between tokenflow and strategy inspector + status: LiquidationStatus + type: LiquidationType + collateral_source: LiquidationCollateralSource + + +# -------- Config ------------------------------------------------------------------------------ + + class ClassifierSpec(BaseModel): abi_name: str protocol: Optional[Protocol] = None diff --git a/mev_inspect/schemas/swaps.py b/mev_inspect/schemas/swaps.py deleted file mode 100644 index 2350142..0000000 --- a/mev_inspect/schemas/swaps.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import List, Optional - -from pydantic import BaseModel - -from mev_inspect.schemas.classified_traces import Protocol - - -class Swap(BaseModel): - abi_name: str - transaction_hash: str - block_number: int - trace_address: List[int] - protocol: Optional[Protocol] - pool_address: str - from_address: str - to_address: str - token_in_address: str - token_in_amount: int - token_out_address: str - token_out_amount: int - error: Optional[str] diff --git a/mev_inspect/schemas/transfers.py b/mev_inspect/schemas/transfers.py deleted file mode 100644 index af14e73..0000000 --- a/mev_inspect/schemas/transfers.py +++ /dev/null @@ -1,57 +0,0 @@ -from typing import List, TypeVar - -from pydantic import BaseModel - -from .classified_traces import Classification, ClassifiedTrace, Protocol - - -class Transfer(BaseModel): - transaction_hash: str - trace_address: List[int] - from_address: str - to_address: str - amount: int - - -# To preserve the specific Transfer type -TransferGeneric = TypeVar("TransferGeneric", bound="Transfer") - - -class EthTransfer(Transfer): - @classmethod - def from_trace(cls, trace: ClassifiedTrace) -> "EthTransfer": - return cls( - transaction_hash=trace.transaction_hash, - trace_address=trace.trace_address, - amount=trace.value, - to_address=trace.to_address, - from_address=trace.from_address, - ) - - -class ERC20Transfer(Transfer): - token_address: str - - @classmethod - def from_trace(cls, trace: ClassifiedTrace) -> "ERC20Transfer": - if trace.classification != Classification.transfer or trace.inputs is None: - raise ValueError("Invalid transfer") - - if trace.protocol == Protocol.weth: - return cls( - transaction_hash=trace.transaction_hash, - trace_address=trace.trace_address, - amount=trace.inputs["wad"], - to_address=trace.inputs["dst"], - from_address=trace.from_address, - token_address=trace.to_address, - ) - else: - return cls( - transaction_hash=trace.transaction_hash, - trace_address=trace.trace_address, - amount=trace.inputs["amount"], - to_address=trace.inputs["recipient"], - from_address=trace.inputs.get("sender", trace.from_address), - token_address=trace.to_address, - ) diff --git a/mev_inspect/swaps.py b/mev_inspect/swaps.py index 00c71c3..6de342d 100644 --- a/mev_inspect/swaps.py +++ b/mev_inspect/swaps.py @@ -3,10 +3,16 @@ from typing import List, Optional from mev_inspect.schemas.classified_traces import ( ClassifiedTrace, Classification, + Swap, + Transfer, ) +<<<<<<< HEAD from mev_inspect.schemas.swaps import Swap from mev_inspect.schemas.transfers import ERC20Transfer from mev_inspect.traces import get_traces_by_transaction_hash +======= + +>>>>>>> Swamps, transfers, tests imp from mev_inspect.transfers import ( get_child_transfers, filter_transfers, diff --git a/mev_inspect/transfers.py b/mev_inspect/transfers.py index fbe3a48..361d1ce 100644 --- a/mev_inspect/transfers.py +++ b/mev_inspect/transfers.py @@ -1,7 +1,15 @@ from typing import Dict, List, Optional, Sequence +<<<<<<< HEAD from mev_inspect.schemas.classified_traces import Classification, ClassifiedTrace from mev_inspect.schemas.transfers import ERC20Transfer, EthTransfer, TransferGeneric +======= +from mev_inspect.schemas.classified_traces import ( + Classification, + ClassifiedTrace, + Transfer, +) +>>>>>>> Swamps, transfers, tests imp from mev_inspect.traces import is_child_trace_address, get_child_traces diff --git a/tests/helpers.py b/tests/helpers.py index 0ebcf0f..81b50d2 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,7 +1,12 @@ from typing import List from mev_inspect.schemas.blocks import TraceType -from mev_inspect.schemas.classified_traces import Classification, ClassifiedTrace +from mev_inspect.schemas.classified_traces import ( + Classification, + ClassifiedTrace, + Call, + ClassifiedCall, +) def make_transfer_trace( @@ -13,10 +18,10 @@ def make_transfer_trace( token_address: str, amount: int, ): - return ClassifiedTrace( + return Call( transaction_hash=transaction_hash, block_number=block_number, - trace_type=TraceType.call, + type=TraceType.call, trace_address=trace_address, classification=Classification.transfer, from_address=from_address, @@ -38,10 +43,10 @@ def make_swap_trace( recipient_address: str, recipient_input_key: str, ): - return ClassifiedTrace( + return ClassifiedCall( transaction_hash=transaction_hash, block_number=block_number, - trace_type=TraceType.call, + type=TraceType.call, trace_address=trace_address, classification=Classification.swap, from_address=from_address, @@ -59,7 +64,7 @@ def make_unknown_trace( return ClassifiedTrace( transaction_hash=transaction_hash, block_number=block_number, - trace_type=TraceType.call, + type=TraceType.call, trace_address=trace_address, classification=Classification.unknown, ) diff --git a/tests/test_arbitrages.py b/tests/test_arbitrages.py index 18af3bd..fe9c9f1 100644 --- a/tests/test_arbitrages.py +++ b/tests/test_arbitrages.py @@ -1,5 +1,5 @@ from mev_inspect.arbitrages import get_arbitrages -from mev_inspect.schemas.swaps import Swap +from mev_inspect.schemas.classified_traces import Swap from mev_inspect.swaps import ( UNISWAP_V2_PAIR_ABI_NAME, UNISWAP_V3_POOL_ABI_NAME, diff --git a/tests/test_transfers.py b/tests/test_transfers.py index ad97ff5..4883422 100644 --- a/tests/test_transfers.py +++ b/tests/test_transfers.py @@ -1,4 +1,8 @@ +<<<<<<< HEAD from mev_inspect.schemas.transfers import ERC20Transfer +======= +from mev_inspect.schemas.classified_traces import Transfer +>>>>>>> Swamps, transfers, tests imp from mev_inspect.transfers import remove_child_transfers_of_transfers