diff --git a/mev_inspect/classifiers/specs/opensea.py b/mev_inspect/classifiers/specs/opensea.py new file mode 100644 index 0000000..4b6db39 --- /dev/null +++ b/mev_inspect/classifiers/specs/opensea.py @@ -0,0 +1,64 @@ +from mev_inspect.schemas.classified_traces import ( + DecodedCallTrace, + Protocol, +) +from mev_inspect.schemas.classifiers import ( + ClassifierSpec, + AtomicMatchClassifier, +) + +OPENSEA_ATOMIC_MATCH_ABI_NAME='atomicMatch_' + +OPENSEA_SPEC = [ + ClassifierSpec( + abi_name="atomicMatch_", + protocol=Protocol.opensea, + valid_contract_addresses=["0x7be8076f4ea4a4ad08075c2508e481d6c946d12b"], + ), +] + +UNISWAP_V3_GENERAL_SPECS = [ + ClassifierSpec( + abi_name=UNISWAP_V3_POOL_ABI_NAME, + classifiers={ + "swap(address,bool,int256,uint160,bytes)": UniswapV3SwapClassifier, + }, + ), + ClassifierSpec( + abi_name="IUniswapV3SwapCallback", + ), + ClassifierSpec( + abi_name="IUniswapV3MintCallback", + ), + ClassifierSpec( + abi_name="IUniswapV3FlashCallback", + ), +] + + +UNISWAPPY_V2_CONTRACT_SPECS = [ + ClassifierSpec( + abi_name="UniswapV2Router", + protocol=Protocol.uniswap_v2, + valid_contract_addresses=["0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"], + ), + ClassifierSpec( + abi_name="UniswapV2Router", + protocol=Protocol.sushiswap, + valid_contract_addresses=["0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F"], + ), +] + +UNISWAPPY_V2_PAIR_SPEC = ClassifierSpec( + abi_name=UNISWAP_V2_PAIR_ABI_NAME, + classifiers={ + "swap(uint256,uint256,address,bytes)": UniswapV2SwapClassifier, + }, +) + +UNISWAP_CLASSIFIER_SPECS = [ + *UNISWAP_V3_CONTRACT_SPECS, + *UNISWAPPY_V2_CONTRACT_SPECS, + *UNISWAP_V3_GENERAL_SPECS, + UNISWAPPY_V2_PAIR_SPEC, +] diff --git a/mev_inspect/crud/atomicmatch.py b/mev_inspect/crud/atomicmatch.py new file mode 100644 index 0000000..0869109 --- /dev/null +++ b/mev_inspect/crud/atomicmatch.py @@ -0,0 +1,26 @@ +import json +from typing import list + +from mev_inspect.models.atomicmatch import AtomicMatchModel +from mev_inspect.schemas.atomicmatch import AtomicMatch + +def delete_atomicmatch_for_block( + db_session, + block_number: int, +) -> None: + ( + db_session.query(AtomicMatchModel) + .filter(AtomicMatchModel.block_number == block_number) + .delete() + ) + + db_session.commit() + +def write_atomicmatch( + db_session, + atomicmatches: List[AtomicMatch], +) -> None: + models = [AtomicMatchModel(**json.loads(atomicmatch.json())) for atomicmatch in atomicmatches] + + db_session.bulk_save_objects(models) + db_session.commit() diff --git a/mev_inspect/inspect_block.py b/mev_inspect/inspect_block.py index 3128fd0..e8cecd0 100644 --- a/mev_inspect/inspect_block.py +++ b/mev_inspect/inspect_block.py @@ -24,6 +24,10 @@ from mev_inspect.crud.liquidations import ( delete_liquidations_for_block, write_liquidations, ) +from mev_inspect.crud.atomicmatch import ( + delete_atomicmatch_for_block, + write_atomicmatch, +) from mev_inspect.miner_payments import get_miner_payments from mev_inspect.swaps import get_swaps from mev_inspect.transfers import get_transfers diff --git a/mev_inspect/models/atomicmatch.py b/mev_inspect/models/atomicmatch.py new file mode 100644 index 0000000..e66cd30 --- /dev/null +++ b/mev_inspect/models/atomicmatch.py @@ -0,0 +1,17 @@ +from sqlalchemy import Column, Numeric, String, Integer, ARRAY + +from .base import Base + + +class AtomicMatchModel(Base): + __tablename__ = "atomic_match" + + block_number = Column(Numeric, nullable=False) + transaction_hash = Column(String, primary_key=True) + protocol = Column(String, nullable=True) + from_address = Column(String, nullable=False) + to_address = Column(String, nullable=False) + token_address = Column(String, nullable=False) + amount = Column(Numeric, nullable=False) + metadata = Column(ARRAY(String), nullable=False) + error = Column(String, nullable=True) diff --git a/mev_inspect/schemas/atomicmatch.py b/mev_inspect/schemas/atomicmatch.py new file mode 100644 index 0000000..5263140 --- /dev/null +++ b/mev_inspect/schemas/atomicmatch.py @@ -0,0 +1,14 @@ +from typing import list + +from pydantic import BaseModel + +ETH_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + +class AtomicMatch(BaseModel): + block_number: int + transaction_hash: str + protocol: str + from_address: str + to_address: str + amount: str + metadata: List[str] diff --git a/mev_inspect/schemas/classified_traces.py b/mev_inspect/schemas/classified_traces.py index 83f78e2..3af615a 100644 --- a/mev_inspect/schemas/classified_traces.py +++ b/mev_inspect/schemas/classified_traces.py @@ -10,6 +10,7 @@ class Classification(Enum): transfer = "transfer" liquidate = "liquidate" seize = "seize" + atomicmatch = "atomicmatch" class Protocol(Enum): @@ -22,6 +23,7 @@ class Protocol(Enum): zero_ex = "0x" balancer_v1 = "balancer_v1" compound_v2 = "compound_v2" + opensea = "opensea" class ClassifiedTrace(Trace): diff --git a/mev_inspect/schemas/classifiers.py b/mev_inspect/schemas/classifiers.py index a3320c6..0138ae7 100644 --- a/mev_inspect/schemas/classifiers.py +++ b/mev_inspect/schemas/classifiers.py @@ -41,6 +41,10 @@ class LiquidationClassifier(Classifier): def get_classification() -> Classification: return Classification.liquidate +class AtomicMatchClassifier(Classifier): + @staticmethod + def get_classification() -> Classification: + return Classification.atomicmatch class SeizeClassifier(Classifier): @staticmethod