From af2aab4940387d221413af584eff136bdff3f19c Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Wed, 10 Nov 2021 20:14:42 -0500 Subject: [PATCH 01/14] add cryptopunks trace classifier --- mev_inspect/abis/cryptopunks/cryptopunks.json | 1 + mev_inspect/classifiers/specs/cryptopunks.py | 13 +++++++++++++ mev_inspect/schemas/traces.py | 1 + 3 files changed, 15 insertions(+) create mode 100644 mev_inspect/abis/cryptopunks/cryptopunks.json create mode 100644 mev_inspect/classifiers/specs/cryptopunks.py diff --git a/mev_inspect/abis/cryptopunks/cryptopunks.json b/mev_inspect/abis/cryptopunks/cryptopunks.json new file mode 100644 index 0000000..200e561 --- /dev/null +++ b/mev_inspect/abis/cryptopunks/cryptopunks.json @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"punksOfferedForSale","outputs":[{"name":"isForSale","type":"bool"},{"name":"punkIndex","type":"uint256"},{"name":"seller","type":"address"},{"name":"minValue","type":"uint256"},{"name":"onlySellTo","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"punkIndex","type":"uint256"}],"name":"enterBidForPunk","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"punkIndex","type":"uint256"},{"name":"minPrice","type":"uint256"}],"name":"acceptBidForPunk","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[]"},{"name":"indices","type":"uint256[]"}],"name":"setInitialOwners","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"imageHash","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"nextPunkIndexToAssign","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"punkIndexToAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"standard","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"punkBids","outputs":[{"name":"hasBid","type":"bool"},{"name":"punkIndex","type":"uint256"},{"name":"bidder","type":"address"},{"name":"value","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"allInitialOwnersAssigned","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"allPunksAssigned","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"punkIndex","type":"uint256"}],"name":"buyPunk","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"punkIndex","type":"uint256"}],"name":"transferPunk","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"punkIndex","type":"uint256"}],"name":"withdrawBidForPunk","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"punkIndex","type":"uint256"}],"name":"setInitialOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"punkIndex","type":"uint256"},{"name":"minSalePriceInWei","type":"uint256"},{"name":"toAddress","type":"address"}],"name":"offerPunkForSaleToAddress","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"punksRemainingToAssign","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"punkIndex","type":"uint256"},{"name":"minSalePriceInWei","type":"uint256"}],"name":"offerPunkForSale","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"punkIndex","type":"uint256"}],"name":"getPunk","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"pendingWithdrawals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"punkIndex","type":"uint256"}],"name":"punkNoLongerForSale","outputs":[],"payable":false,"type":"function"},{"inputs":[],"payable":true,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"punkIndex","type":"uint256"}],"name":"Assign","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"punkIndex","type":"uint256"}],"name":"PunkTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"punkIndex","type":"uint256"},{"indexed":false,"name":"minValue","type":"uint256"},{"indexed":true,"name":"toAddress","type":"address"}],"name":"PunkOffered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"punkIndex","type":"uint256"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":true,"name":"fromAddress","type":"address"}],"name":"PunkBidEntered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"punkIndex","type":"uint256"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":true,"name":"fromAddress","type":"address"}],"name":"PunkBidWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"punkIndex","type":"uint256"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":true,"name":"fromAddress","type":"address"},{"indexed":true,"name":"toAddress","type":"address"}],"name":"PunkBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"punkIndex","type":"uint256"}],"name":"PunkNoLongerForSale","type":"event"}] \ No newline at end of file diff --git a/mev_inspect/classifiers/specs/cryptopunks.py b/mev_inspect/classifiers/specs/cryptopunks.py new file mode 100644 index 0000000..67a22f6 --- /dev/null +++ b/mev_inspect/classifiers/specs/cryptopunks.py @@ -0,0 +1,13 @@ +from mev_inspect.schemas.traces import ( + Protocol, +) + +from mev_inspect.schemas.classifiers import ( + ClassifierSpec, +) + +CRYPTO_PUNKS_SPEC = ClassifierSpec( + abi_name='cryptopunks', + protocol=Protocol.cryptopunks, + valid_contract_addresses=["0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB"] +) \ No newline at end of file diff --git a/mev_inspect/schemas/traces.py b/mev_inspect/schemas/traces.py index faee340..89fe42e 100644 --- a/mev_inspect/schemas/traces.py +++ b/mev_inspect/schemas/traces.py @@ -44,6 +44,7 @@ class Protocol(Enum): balancer_v1 = "balancer_v1" compound_v2 = "compound_v2" cream = "cream" + cryptopunks = "cryptopunks" class ClassifiedTrace(Trace): From 5d03c1fbfa8b478aff85ca18c89b7611d90b58e3 Mon Sep 17 00:00:00 2001 From: Gui Heise Date: Thu, 11 Nov 2021 10:39:24 -0500 Subject: [PATCH 02/14] Add classifier specs to init --- mev | 2 +- mev_inspect/classifiers/specs/__init__.py | 2 ++ mev_inspect/classifiers/specs/cryptopunks.py | 8 +++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mev b/mev index 7d7f79f..f9feafe 100755 --- a/mev +++ b/mev @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e diff --git a/mev_inspect/classifiers/specs/__init__.py b/mev_inspect/classifiers/specs/__init__.py index 6d2b4e3..6874e01 100644 --- a/mev_inspect/classifiers/specs/__init__.py +++ b/mev_inspect/classifiers/specs/__init__.py @@ -11,6 +11,7 @@ from .weth import WETH_CLASSIFIER_SPECS, WETH_ADDRESS from .zero_ex import ZEROX_CLASSIFIER_SPECS from .balancer import BALANCER_CLASSIFIER_SPECS from .compound import COMPOUND_CLASSIFIER_SPECS +from .cryptopunks import CRYPTOPUNKS_CLASSIFIER_SPECS ALL_CLASSIFIER_SPECS = ( ERC20_CLASSIFIER_SPECS @@ -21,6 +22,7 @@ ALL_CLASSIFIER_SPECS = ( + ZEROX_CLASSIFIER_SPECS + BALANCER_CLASSIFIER_SPECS + COMPOUND_CLASSIFIER_SPECS + + CRYPTOPUNKS_CLASSIFIER_SPECS ) _SPECS_BY_ABI_NAME_AND_PROTOCOL: Dict[ diff --git a/mev_inspect/classifiers/specs/cryptopunks.py b/mev_inspect/classifiers/specs/cryptopunks.py index 67a22f6..3f53854 100644 --- a/mev_inspect/classifiers/specs/cryptopunks.py +++ b/mev_inspect/classifiers/specs/cryptopunks.py @@ -7,7 +7,9 @@ from mev_inspect.schemas.classifiers import ( ) CRYPTO_PUNKS_SPEC = ClassifierSpec( - abi_name='cryptopunks', + abi_name="cryptopunks", protocol=Protocol.cryptopunks, - valid_contract_addresses=["0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB"] -) \ No newline at end of file + valid_contract_addresses=["0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB"], +) + +CRYPTOPUNKS_CLASSIFIER_SPECS = [CRYPTO_PUNKS_SPEC] From c5ab2be4e3c305ae67000bc9557a90b329f7f154 Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Mon, 15 Nov 2021 21:07:38 -0500 Subject: [PATCH 03/14] add punk classifications --- mev_inspect/schemas/traces.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mev_inspect/schemas/traces.py b/mev_inspect/schemas/traces.py index 89fe42e..6b4480b 100644 --- a/mev_inspect/schemas/traces.py +++ b/mev_inspect/schemas/traces.py @@ -31,6 +31,8 @@ class Classification(Enum): transfer = "transfer" liquidate = "liquidate" seize = "seize" + punk_bid = "punk_bid" + punk_accept_bid = "punk_accept_bid" class Protocol(Enum): From 5766abb9febdfbba61da3a6cf06453274cdaae6f Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Mon, 15 Nov 2021 21:08:07 -0500 Subject: [PATCH 04/14] feat: add punk classifiers --- mev_inspect/classifiers/specs/cryptopunks.py | 6 +++++ mev_inspect/schemas/classifiers.py | 24 ++++++++++++++++++++ mev_inspect/schemas/punk_accept_bid.py | 12 ++++++++++ mev_inspect/schemas/punk_bid.py | 11 +++++++++ 4 files changed, 53 insertions(+) create mode 100644 mev_inspect/schemas/punk_accept_bid.py create mode 100644 mev_inspect/schemas/punk_bid.py diff --git a/mev_inspect/classifiers/specs/cryptopunks.py b/mev_inspect/classifiers/specs/cryptopunks.py index 3f53854..9fd283d 100644 --- a/mev_inspect/classifiers/specs/cryptopunks.py +++ b/mev_inspect/classifiers/specs/cryptopunks.py @@ -4,12 +4,18 @@ from mev_inspect.schemas.traces import ( from mev_inspect.schemas.classifiers import ( ClassifierSpec, + PunkBidClassifier, + PunkAcceptBidClassifier, ) CRYPTO_PUNKS_SPEC = ClassifierSpec( abi_name="cryptopunks", protocol=Protocol.cryptopunks, valid_contract_addresses=["0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB"], + classifiers={ + "enterBidForPunk(uint)": PunkBidClassifier, + "acceptBidForPunk(uint,uint)": PunkAcceptBidClassifier, + }, ) CRYPTOPUNKS_CLASSIFIER_SPECS = [CRYPTO_PUNKS_SPEC] diff --git a/mev_inspect/schemas/classifiers.py b/mev_inspect/schemas/classifiers.py index ab50271..b1ed7a5 100644 --- a/mev_inspect/schemas/classifiers.py +++ b/mev_inspect/schemas/classifiers.py @@ -5,6 +5,8 @@ from pydantic import BaseModel from .traces import Classification, DecodedCallTrace, Protocol from .transfers import Transfer +from .punk_bid import Punk_Bid +from .punk_accept_bid import Punk_Accept_Bid class Classifier(ABC): @@ -14,6 +16,28 @@ class Classifier(ABC): raise NotImplementedError() +class PunkBidClassifier(Classifier): + @staticmethod + def get_classification() -> Classification: + return Classification.punk_bid + + @staticmethod + @abstractmethod + def get_bid(trace: DecodedCallTrace) -> Punk_Bid: + raise NotImplementedError() + + +class PunkAcceptBidClassifier(Classifier): + @staticmethod + def get_classification() -> Classification: + return Classification.punk_accept_bid + + @staticmethod + @abstractmethod + def get_accept_bid(trace: DecodedCallTrace) -> Punk_Accept_Bid: + raise NotImplementedError() + + class TransferClassifier(Classifier): @staticmethod def get_classification() -> Classification: diff --git a/mev_inspect/schemas/punk_accept_bid.py b/mev_inspect/schemas/punk_accept_bid.py new file mode 100644 index 0000000..dce2d1f --- /dev/null +++ b/mev_inspect/schemas/punk_accept_bid.py @@ -0,0 +1,12 @@ +from typing import List + +from pydantic import BaseModel + + +class Punk_Accept_Bid(BaseModel): + block_number: int + transaction_hash: str + trace_address: List[int] + from_address: str + punk_index: int + min_price: int diff --git a/mev_inspect/schemas/punk_bid.py b/mev_inspect/schemas/punk_bid.py new file mode 100644 index 0000000..fe38528 --- /dev/null +++ b/mev_inspect/schemas/punk_bid.py @@ -0,0 +1,11 @@ +from typing import List + +from pydantic import BaseModel + +class Punk_Bid(BaseModel): + block_number: int + transaction_hash: str + trace_address: List[int] + from_address: str + punk_index: int + amount: int \ No newline at end of file From 0d6215f82eb2ce449e8a8856aa256a97c1b3371e Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Mon, 15 Nov 2021 21:08:28 -0500 Subject: [PATCH 05/14] wip feat: getting punk bids / accepts --- mev_inspect/inspect_block.py | 4 ++ mev_inspect/punks.py | 80 ++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 mev_inspect/punks.py diff --git a/mev_inspect/inspect_block.py b/mev_inspect/inspect_block.py index c517923..656db7b 100644 --- a/mev_inspect/inspect_block.py +++ b/mev_inspect/inspect_block.py @@ -27,6 +27,7 @@ from mev_inspect.crud.liquidations import ( write_liquidations, ) from mev_inspect.miner_payments import get_miner_payments +from mev_inspect.punks import get_punk_bids from mev_inspect.swaps import get_swaps from mev_inspect.transfers import get_transfers from mev_inspect.liquidations import get_liquidations @@ -91,6 +92,9 @@ async def inspect_block( delete_liquidations_for_block(inspect_db_session, block_number) write_liquidations(inspect_db_session, liquidations) + punk_bids = get_punk_bids(classified_traces) + logger.info(f"Block: {block_number} -- Found {len(punk_bids)} punk bids") + miner_payments = get_miner_payments( block.miner, block.base_fee_per_gas, classified_traces, block.receipts ) diff --git a/mev_inspect/punks.py b/mev_inspect/punks.py new file mode 100644 index 0000000..f8e61c3 --- /dev/null +++ b/mev_inspect/punks.py @@ -0,0 +1,80 @@ +from typing import List + +from mev_inspect.schemas.traces import ( + ClassifiedTrace, + Classification, + DecodedCallTrace, +) +from mev_inspect.schemas.punk_bid import Punk_Bid +from mev_inspect.schemas.punk_accept_bid import Punk_Accept_Bid +from mev_inspect.traces import get_traces_by_transaction_hash + + +def get_punk_accept_bids(traces: List[ClassifiedTrace]) -> List[Punk_Accept_Bid]: + punk_accept_bids = [] + + for _, transaction_traces in get_traces_by_transaction_hash(traces).items(): + punk_accept_bids += _get_punk_accept_bids_for_transaction( + list(transaction_traces) + ) + + return punk_accept_bids + + +def _get_punk_accept_bids_for_transaction( + traces: List[ClassifiedTrace], +) -> List[Punk_Accept_Bid]: + ordered_traces = list(sorted(traces, key=lambda t: t.trace_address)) + + punk_accept_bids = [] + + for trace in ordered_traces: + if not isinstance(trace, DecodedCallTrace): + continue + + elif trace.classification == Classification.punk_accept_bid: + punk_accept_bid = Punk_Accept_Bid( + block_number=trace.block_number, + transaction_hash=trace.transaction_hash, + trace_address=trace.trace_address, + from_address=trace.from_address, + punk_index=trace.inputs["punk_index"], + min_price=trace.inputs["min_price"], + ) + + punk_accept_bids.append(punk_accept_bid) + + return punk_accept_bids + + +def get_punk_bids(traces: List[ClassifiedTrace]) -> List[Punk_Bid]: + punk_bids = [] + + for _, transaction_traces in get_traces_by_transaction_hash(traces).items(): + punk_bids += _get_punk_bids_for_transaction(list(transaction_traces)) + + return punk_bids + + +def _get_punk_bids_for_transaction(traces: List[ClassifiedTrace]) -> List[Punk_Bid]: + ordered_traces = list(sorted(traces, key=lambda t: t.trace_address)) + + punk_bids = [] + + for trace in ordered_traces: + if not isinstance(trace, DecodedCallTrace): + continue + + elif trace.classification == Classification.punk_bid: + punk_bid = Punk_Bid( + transaction_hash=trace.transaction_hash, + block_number=trace.block_number, + trace_address=trace.trace_address, + from_address=trace.from_address, + punk_index=trace.inputs["punk_index"], + value=trace.value, + ) + + punk_bids.append(punk_bid) + + return punk_bids From 96d2171daafd396c88000255f09147eed570a9e3 Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Tue, 16 Nov 2021 19:57:58 -0500 Subject: [PATCH 06/14] style: improve schema naming bcuz imagine complained --- mev_inspect/punks.py | 16 ++++++++-------- mev_inspect/schemas/classifiers.py | 8 ++++---- mev_inspect/schemas/punk_accept_bid.py | 2 +- mev_inspect/schemas/punk_bid.py | 5 +++-- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/mev_inspect/punks.py b/mev_inspect/punks.py index f8e61c3..bb38540 100644 --- a/mev_inspect/punks.py +++ b/mev_inspect/punks.py @@ -5,12 +5,12 @@ from mev_inspect.schemas.traces import ( Classification, DecodedCallTrace, ) -from mev_inspect.schemas.punk_bid import Punk_Bid -from mev_inspect.schemas.punk_accept_bid import Punk_Accept_Bid +from mev_inspect.schemas.punk_bid import PunkBid +from mev_inspect.schemas.punk_accept_bid import PunkAcceptBid from mev_inspect.traces import get_traces_by_transaction_hash -def get_punk_accept_bids(traces: List[ClassifiedTrace]) -> List[Punk_Accept_Bid]: +def get_punk_accept_bids(traces: List[ClassifiedTrace]) -> List[PunkAcceptBid]: punk_accept_bids = [] for _, transaction_traces in get_traces_by_transaction_hash(traces).items(): @@ -23,7 +23,7 @@ def get_punk_accept_bids(traces: List[ClassifiedTrace]) -> List[Punk_Accept_Bid] def _get_punk_accept_bids_for_transaction( traces: List[ClassifiedTrace], -) -> List[Punk_Accept_Bid]: +) -> List[PunkAcceptBid]: ordered_traces = list(sorted(traces, key=lambda t: t.trace_address)) punk_accept_bids = [] @@ -33,7 +33,7 @@ def _get_punk_accept_bids_for_transaction( continue elif trace.classification == Classification.punk_accept_bid: - punk_accept_bid = Punk_Accept_Bid( + punk_accept_bid = PunkAcceptBid( block_number=trace.block_number, transaction_hash=trace.transaction_hash, trace_address=trace.trace_address, @@ -47,7 +47,7 @@ def _get_punk_accept_bids_for_transaction( return punk_accept_bids -def get_punk_bids(traces: List[ClassifiedTrace]) -> List[Punk_Bid]: +def get_punk_bids(traces: List[ClassifiedTrace]) -> List[PunkBid]: punk_bids = [] for _, transaction_traces in get_traces_by_transaction_hash(traces).items(): @@ -56,7 +56,7 @@ def get_punk_bids(traces: List[ClassifiedTrace]) -> List[Punk_Bid]: return punk_bids -def _get_punk_bids_for_transaction(traces: List[ClassifiedTrace]) -> List[Punk_Bid]: +def _get_punk_bids_for_transaction(traces: List[ClassifiedTrace]) -> List[PunkBid]: ordered_traces = list(sorted(traces, key=lambda t: t.trace_address)) punk_bids = [] @@ -66,7 +66,7 @@ def _get_punk_bids_for_transaction(traces: List[ClassifiedTrace]) -> List[Punk_B continue elif trace.classification == Classification.punk_bid: - punk_bid = Punk_Bid( + punk_bid = PunkBid( transaction_hash=trace.transaction_hash, block_number=trace.block_number, trace_address=trace.trace_address, diff --git a/mev_inspect/schemas/classifiers.py b/mev_inspect/schemas/classifiers.py index b1ed7a5..f4c8bc6 100644 --- a/mev_inspect/schemas/classifiers.py +++ b/mev_inspect/schemas/classifiers.py @@ -5,8 +5,8 @@ from pydantic import BaseModel from .traces import Classification, DecodedCallTrace, Protocol from .transfers import Transfer -from .punk_bid import Punk_Bid -from .punk_accept_bid import Punk_Accept_Bid +from .punk_bid import PunkBid +from .punk_accept_bid import PunkAcceptBid class Classifier(ABC): @@ -23,7 +23,7 @@ class PunkBidClassifier(Classifier): @staticmethod @abstractmethod - def get_bid(trace: DecodedCallTrace) -> Punk_Bid: + def get_bid(trace: DecodedCallTrace) -> PunkBid: raise NotImplementedError() @@ -34,7 +34,7 @@ class PunkAcceptBidClassifier(Classifier): @staticmethod @abstractmethod - def get_accept_bid(trace: DecodedCallTrace) -> Punk_Accept_Bid: + def get_accept_bid(trace: DecodedCallTrace) -> PunkAcceptBid: raise NotImplementedError() diff --git a/mev_inspect/schemas/punk_accept_bid.py b/mev_inspect/schemas/punk_accept_bid.py index dce2d1f..f015723 100644 --- a/mev_inspect/schemas/punk_accept_bid.py +++ b/mev_inspect/schemas/punk_accept_bid.py @@ -3,7 +3,7 @@ from typing import List from pydantic import BaseModel -class Punk_Accept_Bid(BaseModel): +class PunkAcceptBid(BaseModel): block_number: int transaction_hash: str trace_address: List[int] diff --git a/mev_inspect/schemas/punk_bid.py b/mev_inspect/schemas/punk_bid.py index fe38528..91e0912 100644 --- a/mev_inspect/schemas/punk_bid.py +++ b/mev_inspect/schemas/punk_bid.py @@ -2,10 +2,11 @@ from typing import List from pydantic import BaseModel -class Punk_Bid(BaseModel): + +class PunkBid(BaseModel): block_number: int transaction_hash: str trace_address: List[int] from_address: str punk_index: int - amount: int \ No newline at end of file + amount: int From 1de1570939733f0d50256b216767da6658567045 Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Wed, 17 Nov 2021 21:51:56 -0500 Subject: [PATCH 07/14] feat: change to "punk bid acceptance" and get punk bid acceptances --- mev_inspect/classifiers/specs/cryptopunks.py | 4 ++-- mev_inspect/inspect_block.py | 5 ++++- mev_inspect/punks.py | 23 ++++++++++---------- mev_inspect/schemas/classifiers.py | 6 ++--- mev_inspect/schemas/punk_accept_bid.py | 2 +- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/mev_inspect/classifiers/specs/cryptopunks.py b/mev_inspect/classifiers/specs/cryptopunks.py index 9fd283d..5070f3d 100644 --- a/mev_inspect/classifiers/specs/cryptopunks.py +++ b/mev_inspect/classifiers/specs/cryptopunks.py @@ -5,7 +5,7 @@ from mev_inspect.schemas.traces import ( from mev_inspect.schemas.classifiers import ( ClassifierSpec, PunkBidClassifier, - PunkAcceptBidClassifier, + PunkBidAcceptanceClassifier, ) CRYPTO_PUNKS_SPEC = ClassifierSpec( @@ -14,7 +14,7 @@ CRYPTO_PUNKS_SPEC = ClassifierSpec( valid_contract_addresses=["0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB"], classifiers={ "enterBidForPunk(uint)": PunkBidClassifier, - "acceptBidForPunk(uint,uint)": PunkAcceptBidClassifier, + "acceptBidForPunk(uint,uint)": PunkBidAcceptanceClassifier, }, ) diff --git a/mev_inspect/inspect_block.py b/mev_inspect/inspect_block.py index 656db7b..ca473bd 100644 --- a/mev_inspect/inspect_block.py +++ b/mev_inspect/inspect_block.py @@ -27,7 +27,7 @@ from mev_inspect.crud.liquidations import ( write_liquidations, ) from mev_inspect.miner_payments import get_miner_payments -from mev_inspect.punks import get_punk_bids +from mev_inspect.punks import get_punk_bid_acceptances, get_punk_bids from mev_inspect.swaps import get_swaps from mev_inspect.transfers import get_transfers from mev_inspect.liquidations import get_liquidations @@ -95,6 +95,9 @@ async def inspect_block( punk_bids = get_punk_bids(classified_traces) logger.info(f"Block: {block_number} -- Found {len(punk_bids)} punk bids") + punk_bid_acceptances = get_punk_bid_acceptances(classified_traces) + logger.info(f"Block: {block_number} -- Found {len(punk_bid_acceptances)} punk bids") + miner_payments = get_miner_payments( block.miner, block.base_fee_per_gas, classified_traces, block.receipts ) diff --git a/mev_inspect/punks.py b/mev_inspect/punks.py index bb38540..4f45197 100644 --- a/mev_inspect/punks.py +++ b/mev_inspect/punks.py @@ -1,4 +1,5 @@ from typing import List +from mev_inspect.schemas import punk_accept_bid from mev_inspect.schemas.traces import ( ClassifiedTrace, @@ -6,34 +7,34 @@ from mev_inspect.schemas.traces import ( DecodedCallTrace, ) from mev_inspect.schemas.punk_bid import PunkBid -from mev_inspect.schemas.punk_accept_bid import PunkAcceptBid +from mev_inspect.schemas.punk_accept_bid import PunkBidAcceptance from mev_inspect.traces import get_traces_by_transaction_hash -def get_punk_accept_bids(traces: List[ClassifiedTrace]) -> List[PunkAcceptBid]: - punk_accept_bids = [] +def get_punk_bid_acceptances(traces: List[ClassifiedTrace]) -> List[PunkBidAcceptance]: + punk_bid_acceptances = [] for _, transaction_traces in get_traces_by_transaction_hash(traces).items(): - punk_accept_bids += _get_punk_accept_bids_for_transaction( + punk_bid_acceptances += _get_punk_bid_acceptances_for_transaction( list(transaction_traces) ) - return punk_accept_bids + return punk_bid_acceptances -def _get_punk_accept_bids_for_transaction( +def _get_punk_bid_acceptances_for_transaction( traces: List[ClassifiedTrace], -) -> List[PunkAcceptBid]: +) -> List[PunkBidAcceptance]: ordered_traces = list(sorted(traces, key=lambda t: t.trace_address)) - punk_accept_bids = [] + punk_bid_acceptances = [] for trace in ordered_traces: if not isinstance(trace, DecodedCallTrace): continue elif trace.classification == Classification.punk_accept_bid: - punk_accept_bid = PunkAcceptBid( + punk_accept_bid = PunkBidAcceptance( block_number=trace.block_number, transaction_hash=trace.transaction_hash, trace_address=trace.trace_address, @@ -42,9 +43,9 @@ def _get_punk_accept_bids_for_transaction( min_price=trace.inputs["min_price"], ) - punk_accept_bids.append(punk_accept_bid) + punk_bid_acceptances.append(punk_accept_bid) - return punk_accept_bids + return punk_bid_acceptances def get_punk_bids(traces: List[ClassifiedTrace]) -> List[PunkBid]: diff --git a/mev_inspect/schemas/classifiers.py b/mev_inspect/schemas/classifiers.py index f4c8bc6..5b55d91 100644 --- a/mev_inspect/schemas/classifiers.py +++ b/mev_inspect/schemas/classifiers.py @@ -6,7 +6,7 @@ from pydantic import BaseModel from .traces import Classification, DecodedCallTrace, Protocol from .transfers import Transfer from .punk_bid import PunkBid -from .punk_accept_bid import PunkAcceptBid +from .punk_accept_bid import PunkBidAcceptance class Classifier(ABC): @@ -27,14 +27,14 @@ class PunkBidClassifier(Classifier): raise NotImplementedError() -class PunkAcceptBidClassifier(Classifier): +class PunkBidAcceptanceClassifier(Classifier): @staticmethod def get_classification() -> Classification: return Classification.punk_accept_bid @staticmethod @abstractmethod - def get_accept_bid(trace: DecodedCallTrace) -> PunkAcceptBid: + def get_accept_bid(trace: DecodedCallTrace) -> PunkBidAcceptance: raise NotImplementedError() diff --git a/mev_inspect/schemas/punk_accept_bid.py b/mev_inspect/schemas/punk_accept_bid.py index f015723..29def93 100644 --- a/mev_inspect/schemas/punk_accept_bid.py +++ b/mev_inspect/schemas/punk_accept_bid.py @@ -3,7 +3,7 @@ from typing import List from pydantic import BaseModel -class PunkAcceptBid(BaseModel): +class PunkBidAcceptance(BaseModel): block_number: int transaction_hash: str trace_address: List[int] From f31430da30083349f1a33db88d896ca888adb1dc Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Fri, 19 Nov 2021 17:17:34 -0600 Subject: [PATCH 08/14] bug: update uint to uin256 --- mev_inspect/classifiers/specs/cryptopunks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mev_inspect/classifiers/specs/cryptopunks.py b/mev_inspect/classifiers/specs/cryptopunks.py index 5070f3d..37f9e2b 100644 --- a/mev_inspect/classifiers/specs/cryptopunks.py +++ b/mev_inspect/classifiers/specs/cryptopunks.py @@ -13,8 +13,8 @@ CRYPTO_PUNKS_SPEC = ClassifierSpec( protocol=Protocol.cryptopunks, valid_contract_addresses=["0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB"], classifiers={ - "enterBidForPunk(uint)": PunkBidClassifier, - "acceptBidForPunk(uint,uint)": PunkBidAcceptanceClassifier, + "enterBidForPunk(uint256)": PunkBidClassifier, + "acceptBidForPunk(uint256,uint256)": PunkBidAcceptanceClassifier, }, ) From 377137d9c8757286eaae03f40b4efeac5dc142a1 Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Fri, 19 Nov 2021 17:18:29 -0600 Subject: [PATCH 09/14] feat: add support for punk snipes --- mev_inspect/inspect_block.py | 9 ++++---- mev_inspect/punks.py | 35 +++++++++++++++++++++++++------ mev_inspect/schemas/punk_snipe.py | 13 ++++++++++++ 3 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 mev_inspect/schemas/punk_snipe.py diff --git a/mev_inspect/inspect_block.py b/mev_inspect/inspect_block.py index ca473bd..41810c8 100644 --- a/mev_inspect/inspect_block.py +++ b/mev_inspect/inspect_block.py @@ -27,7 +27,8 @@ from mev_inspect.crud.liquidations import ( write_liquidations, ) from mev_inspect.miner_payments import get_miner_payments -from mev_inspect.punks import get_punk_bid_acceptances, get_punk_bids +from mev_inspect.punks import get_punk_bid_acceptances, get_punk_bids, get_punk_snipes +from mev_inspect.schemas import punk_snipe from mev_inspect.swaps import get_swaps from mev_inspect.transfers import get_transfers from mev_inspect.liquidations import get_liquidations @@ -93,10 +94,10 @@ async def inspect_block( write_liquidations(inspect_db_session, liquidations) punk_bids = get_punk_bids(classified_traces) - logger.info(f"Block: {block_number} -- Found {len(punk_bids)} punk bids") - punk_bid_acceptances = get_punk_bid_acceptances(classified_traces) - logger.info(f"Block: {block_number} -- Found {len(punk_bid_acceptances)} punk bids") + + punk_snipes = get_punk_snipes(punk_bids, punk_bid_acceptances) + logger.info(f"Block: {block_number} -- Found {len(punk_snipes)} punk snipes") miner_payments = get_miner_payments( block.miner, block.base_fee_per_gas, classified_traces, block.receipts diff --git a/mev_inspect/punks.py b/mev_inspect/punks.py index 4f45197..bce838f 100644 --- a/mev_inspect/punks.py +++ b/mev_inspect/punks.py @@ -1,6 +1,4 @@ from typing import List -from mev_inspect.schemas import punk_accept_bid - from mev_inspect.schemas.traces import ( ClassifiedTrace, Classification, @@ -8,9 +6,34 @@ from mev_inspect.schemas.traces import ( ) from mev_inspect.schemas.punk_bid import PunkBid from mev_inspect.schemas.punk_accept_bid import PunkBidAcceptance +from mev_inspect.schemas.punk_snipe import PunkSnipe from mev_inspect.traces import get_traces_by_transaction_hash +def get_punk_snipes( + punk_bids: List[PunkBid], punk_bid_acceptances: List[PunkBidAcceptance] +) -> List[PunkSnipe]: + punk_snipe_list = [] + + for punk_bid in punk_bids: + for punk_bid_acceptance in punk_bid_acceptances: + if punk_bid.punk_index == punk_bid_acceptance.punk_index: + if punk_bid.amount > punk_bid_acceptance.min_price: + punk_snipe = PunkSnipe( + block_number=punk_bid.block_number, + transaction_hash=punk_bid.transaction_hash, + trace_address=punk_bid.trace_address, + from_address=punk_bid.from_address, + punk_index=punk_bid.punk_index, + min_acceptance_price=punk_bid_acceptance.min_price, + acceptance_price=punk_bid.amount, + ) + + punk_snipe_list.append(punk_snipe) + + return punk_snipe_list + + def get_punk_bid_acceptances(traces: List[ClassifiedTrace]) -> List[PunkBidAcceptance]: punk_bid_acceptances = [] @@ -39,8 +62,8 @@ def _get_punk_bid_acceptances_for_transaction( transaction_hash=trace.transaction_hash, trace_address=trace.trace_address, from_address=trace.from_address, - punk_index=trace.inputs["punk_index"], - min_price=trace.inputs["min_price"], + punk_index=trace.inputs["punkIndex"], + min_price=trace.inputs["minPrice"], ) punk_bid_acceptances.append(punk_accept_bid) @@ -72,8 +95,8 @@ def _get_punk_bids_for_transaction(traces: List[ClassifiedTrace]) -> List[PunkBi block_number=trace.block_number, trace_address=trace.trace_address, from_address=trace.from_address, - punk_index=trace.inputs["punk_index"], - value=trace.value, + punk_index=trace.inputs["punkIndex"], + amount=trace.value, ) punk_bids.append(punk_bid) diff --git a/mev_inspect/schemas/punk_snipe.py b/mev_inspect/schemas/punk_snipe.py new file mode 100644 index 0000000..6890802 --- /dev/null +++ b/mev_inspect/schemas/punk_snipe.py @@ -0,0 +1,13 @@ +from typing import List + +from pydantic import BaseModel + + +class PunkSnipe(BaseModel): + block_number: int + transaction_hash: str + trace_address: List[int] + from_address: str + punk_index: int + min_acceptance_price: int + acceptance_price: int From 3314056c8851491a395aa7ddb94edaf9b81d0076 Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Thu, 25 Nov 2021 12:23:46 -0500 Subject: [PATCH 10/14] revert change to mev --- mev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mev b/mev index f9feafe..7d7f79f 100755 --- a/mev +++ b/mev @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh set -e From 976ac9ea7754994a97831302bd9d9a46e29e6654 Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Thu, 25 Nov 2021 16:04:52 -0500 Subject: [PATCH 11/14] style: change punk_bid.amount to price --- mev_inspect/punks.py | 6 +++--- mev_inspect/schemas/punk_bid.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mev_inspect/punks.py b/mev_inspect/punks.py index bce838f..70e75f0 100644 --- a/mev_inspect/punks.py +++ b/mev_inspect/punks.py @@ -18,7 +18,7 @@ def get_punk_snipes( for punk_bid in punk_bids: for punk_bid_acceptance in punk_bid_acceptances: if punk_bid.punk_index == punk_bid_acceptance.punk_index: - if punk_bid.amount > punk_bid_acceptance.min_price: + if punk_bid.price > punk_bid_acceptance.min_price: punk_snipe = PunkSnipe( block_number=punk_bid.block_number, transaction_hash=punk_bid.transaction_hash, @@ -26,7 +26,7 @@ def get_punk_snipes( from_address=punk_bid.from_address, punk_index=punk_bid.punk_index, min_acceptance_price=punk_bid_acceptance.min_price, - acceptance_price=punk_bid.amount, + acceptance_price=punk_bid.price, ) punk_snipe_list.append(punk_snipe) @@ -96,7 +96,7 @@ def _get_punk_bids_for_transaction(traces: List[ClassifiedTrace]) -> List[PunkBi trace_address=trace.trace_address, from_address=trace.from_address, punk_index=trace.inputs["punkIndex"], - amount=trace.value, + price=trace.value, ) punk_bids.append(punk_bid) diff --git a/mev_inspect/schemas/punk_bid.py b/mev_inspect/schemas/punk_bid.py index 91e0912..d1ed0ae 100644 --- a/mev_inspect/schemas/punk_bid.py +++ b/mev_inspect/schemas/punk_bid.py @@ -9,4 +9,4 @@ class PunkBid(BaseModel): trace_address: List[int] from_address: str punk_index: int - amount: int + price: int From bb3ace07a1ae66c08ef86cc6e49094712167d3e6 Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Thu, 25 Nov 2021 16:48:48 -0500 Subject: [PATCH 12/14] =move punk classifiers out of classifer.py --- mev_inspect/classifiers/specs/cryptopunks.py | 20 ++++++++++++---- mev_inspect/schemas/classifiers.py | 24 -------------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/mev_inspect/classifiers/specs/cryptopunks.py b/mev_inspect/classifiers/specs/cryptopunks.py index 37f9e2b..a482b1f 100644 --- a/mev_inspect/classifiers/specs/cryptopunks.py +++ b/mev_inspect/classifiers/specs/cryptopunks.py @@ -1,13 +1,23 @@ -from mev_inspect.schemas.traces import ( - Protocol, -) +from mev_inspect.schemas.traces import Protocol, Classification from mev_inspect.schemas.classifiers import ( ClassifierSpec, - PunkBidClassifier, - PunkBidAcceptanceClassifier, + Classifier, ) + +class PunkBidAcceptanceClassifier(Classifier): + @staticmethod + def get_classification() -> Classification: + return Classification.punk_accept_bid + + +class PunkBidClassifier(Classifier): + @staticmethod + def get_classification() -> Classification: + return Classification.punk_bid + + CRYPTO_PUNKS_SPEC = ClassifierSpec( abi_name="cryptopunks", protocol=Protocol.cryptopunks, diff --git a/mev_inspect/schemas/classifiers.py b/mev_inspect/schemas/classifiers.py index 5b55d91..ab50271 100644 --- a/mev_inspect/schemas/classifiers.py +++ b/mev_inspect/schemas/classifiers.py @@ -5,8 +5,6 @@ from pydantic import BaseModel from .traces import Classification, DecodedCallTrace, Protocol from .transfers import Transfer -from .punk_bid import PunkBid -from .punk_accept_bid import PunkBidAcceptance class Classifier(ABC): @@ -16,28 +14,6 @@ class Classifier(ABC): raise NotImplementedError() -class PunkBidClassifier(Classifier): - @staticmethod - def get_classification() -> Classification: - return Classification.punk_bid - - @staticmethod - @abstractmethod - def get_bid(trace: DecodedCallTrace) -> PunkBid: - raise NotImplementedError() - - -class PunkBidAcceptanceClassifier(Classifier): - @staticmethod - def get_classification() -> Classification: - return Classification.punk_accept_bid - - @staticmethod - @abstractmethod - def get_accept_bid(trace: DecodedCallTrace) -> PunkBidAcceptance: - raise NotImplementedError() - - class TransferClassifier(Classifier): @staticmethod def get_classification() -> Classification: From 7638c97e8872abc5c809d878e21a8efb6f222333 Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Thu, 25 Nov 2021 19:32:30 -0500 Subject: [PATCH 13/14] =feat: change punk snipe to only check against the highest bid per punk --- mev_inspect/punks.py | 51 +++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/mev_inspect/punks.py b/mev_inspect/punks.py index 70e75f0..ecef500 100644 --- a/mev_inspect/punks.py +++ b/mev_inspect/punks.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional from mev_inspect.schemas.traces import ( ClassifiedTrace, Classification, @@ -10,26 +10,47 @@ from mev_inspect.schemas.punk_snipe import PunkSnipe from mev_inspect.traces import get_traces_by_transaction_hash +def _get_highest_punk_bid_per_index( + punk_bids: List[PunkBid], punk_index: int +) -> Optional[PunkBid]: + highest_punk_bid = None + + for punk_bid in punk_bids: + if punk_bid.punk_index == punk_index: + if highest_punk_bid is None: + highest_punk_bid = punk_bid + + elif punk_bid.price > highest_punk_bid.price: + highest_punk_bid = punk_bid + + return highest_punk_bid + + def get_punk_snipes( punk_bids: List[PunkBid], punk_bid_acceptances: List[PunkBidAcceptance] ) -> List[PunkSnipe]: punk_snipe_list = [] - for punk_bid in punk_bids: - for punk_bid_acceptance in punk_bid_acceptances: - if punk_bid.punk_index == punk_bid_acceptance.punk_index: - if punk_bid.price > punk_bid_acceptance.min_price: - punk_snipe = PunkSnipe( - block_number=punk_bid.block_number, - transaction_hash=punk_bid.transaction_hash, - trace_address=punk_bid.trace_address, - from_address=punk_bid.from_address, - punk_index=punk_bid.punk_index, - min_acceptance_price=punk_bid_acceptance.min_price, - acceptance_price=punk_bid.price, - ) + for punk_bid_acceptance in punk_bid_acceptances: + highest_punk_bid = _get_highest_punk_bid_per_index( + punk_bids, punk_bid_acceptance.punk_index + ) - punk_snipe_list.append(punk_snipe) + if highest_punk_bid is None: + continue + + if highest_punk_bid.price > punk_bid_acceptance.min_price: + punk_snipe = PunkSnipe( + block_number=highest_punk_bid.block_number, + transaction_hash=highest_punk_bid.transaction_hash, + trace_address=highest_punk_bid.trace_address, + from_address=highest_punk_bid.from_address, + punk_index=highest_punk_bid.punk_index, + min_acceptance_price=punk_bid_acceptance.min_price, + acceptance_price=highest_punk_bid.price, + ) + + punk_snipe_list.append(punk_snipe) return punk_snipe_list From b4097baa6819b44325e299f87adf2e85db337d64 Mon Sep 17 00:00:00 2001 From: Robert Miller Date: Thu, 25 Nov 2021 19:35:22 -0500 Subject: [PATCH 14/14] feat: remove unused punk_snipe import --- mev_inspect/inspect_block.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mev_inspect/inspect_block.py b/mev_inspect/inspect_block.py index 41810c8..d55eecc 100644 --- a/mev_inspect/inspect_block.py +++ b/mev_inspect/inspect_block.py @@ -28,7 +28,6 @@ from mev_inspect.crud.liquidations import ( ) from mev_inspect.miner_payments import get_miner_payments from mev_inspect.punks import get_punk_bid_acceptances, get_punk_bids, get_punk_snipes -from mev_inspect.schemas import punk_snipe from mev_inspect.swaps import get_swaps from mev_inspect.transfers import get_transfers from mev_inspect.liquidations import get_liquidations