Maximal extractable value inspector for Ethereum, to illuminate the dark forest 🌲 πŸ’‘

Overview

mev-inspect-py

standard-readme compliant Discord

Maximal extractable value inspector for Ethereum, to illuminate the dark forest 🌲 πŸ’‘

Given a block, mev-inspect finds:

  • miner payments (gas + coinbase)
  • tokens transfers and profit
  • swaps and arbitrages
  • ...and more

Data is stored in Postgres for analysis.

Install

mev-inspect-py is built to run on kubernetes locally and in production.

Dependencies

Set up

Create a new cluster with:

kind create cluster

Set an environment variable RPC_URL to an RPC for fetching blocks.

mev-inspect-py currently requires a node with support for Erigon traces and receipts (not geth yet πŸ˜” ).

pokt.network's "Ethereum Mainnet Archival with trace calls" is a good hosted option.

Example:

export RPC_URL="http://111.111.111.111:8546"

Next, start all services with:

tilt up

Press "space" to see a browser of the services starting up.

On first startup, you'll need to apply database migrations with:

./mev exec alembic upgrade head

Usage

Inspect a single block

Inspecting block 12914944:

./mev inspect 12914944

Inspect many blocks

Inspecting blocks 12914944 to 12914954:

./mev inspect-many 12914944 12914954

Inspect all incoming blocks

Start a block listener with:

./mev listener start

By default, it will pick up wherever you left off. If running for the first time, listener starts at the latest block.

Tail logs for the listener with:

./mev listener tail

And stop the listener with:

./mev listener stop

Backfilling

For larger backfills, you can inspect many blocks in parallel using kubernetes

To inspect blocks 12914944 to 12915044 divided across 10 worker pods:

./mev backfill 12914944 12915044 10

You can see worker pods spin up then complete by watching the status of all pods

watch kubectl get pods

To watch the logs for a given pod, take its pod name using the above, then run:

kubectl logs -f pod/mev-inspect-backfill-abcdefg

(where mev-inspect-backfill-abcdefg is your actual pod name)

Exploring

All inspect output data is stored in Postgres.

To connect to the local Postgres database for querying, launch a client container with:

./mev db

When you see the prompt:

mev_inspect=#

You're ready to query!

Try finding the total number of swaps decoded with UniswapV3Pool:

SELECT COUNT(*) FROM swaps WHERE abi_name='UniswapV3Pool';

or top 10 arbs by gross profit that took profit in WETH:

SELECT *
FROM arbitrages
WHERE profit_token_address = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
ORDER BY profit_amount DESC
LIMIT 10;

Postgres tip: Enter \x to enter "Explanded display" mode which looks nicer for results with many columns.

FAQ

How do I delete / reset my local postgres data?

Stop the system if running:

tilt down

Delete it with:

kubectl delete pvc data-postgresql-postgresql-0

Start back up again:

tilt up

And rerun migrations to create the tables again:

./mev exec alembic upgrade head

I was using the docker-compose setup and want to switch to kube, now what?

Re-add the old docker-compose.yml file to your mev-inspect-py directory.

A copy can be found here

Tear down docker-compose resources:

docker compose down

Then go through the steps in the current README for kube setup.

Error from server (AlreadyExists): pods "postgres-client" already exists

This means the postgres client container didn't shut down correctly.

Delete this one with:

kubectl delete pod/postgres-client

Then start it back up again.

Maintainers

Contributing

Flashbots is a research and development collective working on mitigating the negative externalities of decentralized economies. We contribute with the larger free software community to illuminate the dark forest.

You are welcome here <3.

  • If you want to join us, come and say hi in our Discord chat.
  • If you have a question, feedback or a bug report for this project, please open a new Issue.
  • If you would like to contribute with code, check the CONTRIBUTING file.
  • We just ask you to be nice.

Security

If you find a security vulnerability on this project or any other initiative related to Flashbots, please let us know sending an email to [email protected].


Made with β˜€οΈ by the ⚑ πŸ€– collective.

Comments
  • Adjust arbitrage calculation logic

    Adjust arbitrage calculation logic

    Arbitrage logic changes

    • Arbitrage logic was previously dependent on pool address chaining. This is only the case in uni-router style pools that are swapped via the uni-v2/v3 routers, where each pool transfers tokens directly to the next. This approach doesn't work for the case of an arb contract executing the swaps individually against the pools nor other AMMs which do not have the same router paradigm (ex: balancer).

    More concretely, there are two different types of arb transaction series (and potentially many more):

    • Uni router style: MEV_BOT -> Pool A -> Pool B -> MEV_BOT (pools transfer directly to each other)
    • Standard: MEV_BOT -> Pool A -> MEV_BOT -> Pool B -> MEV_BOT

    The new algorithm works as follows:

    1. Starting with a set of Swaps, we find potential start and end swap pairs. These are defined as
    • start_swap.token_in == end_swap.token_out
    • start_swap.from_address == end_swap.to_address
    • !swap_pool_addrs.contains(start_swap.from_address)
      • !swap_pool_addrs.contains(end_swap.from_address)
    1. Find all routes between start_swap/end_swap as defined by prev_swap.token_out == next_swap.token_in
    2. If we find multiple arbitrage routes, filter only to those that are valid based on trace order. (Details on this can be found in the PR commentary below or the _get_arbitrages_from_swaps doc string.

    Results

    Results differ slightly from mev-inspect-rs. Currently picks up all the same arbs as mev-inspect-rs plus some. Also does not depend on profitability which mev-inspect-rs depends on.

    Testing

    • Unit tests for sub functions of routing algo
    • Existing arb tests work with new logic (+ an additional tx for tests/test_arbitrage_integration.py)
    • Spot tested against txs picked up by mev-inspect-rs across balancer/sushi/univ2/univ3 in single tx
    opened by sragss 4
  • Finalize architecture

    Finalize architecture

    In particular we need to answer these questions:

    • Should profit estimation happen at the inspector level? Or elsewhere?
    • Is it possible to use one script, e.g. an analysis of what tokens flowed in and out of a transaction, to estimate profit?
    • Where and when are we posting data to the database?
    opened by bertmiller 4
  • Improve dockerfile

    Improve dockerfile

    1.) Changed the parent docker image to buster slim (still debian based but a lot more slimmer) 2.) Added a separate flashbot user (for security), it doesn't run as the root user. 3.) Also worth mentioning, since the container now runs as a non root user you won't be able to install any stuff in the image.

    The resulting docker image is half the size of the original one, which will come in handy with the CI/CD stuff. I'm not sure how you handle the CI/CD part, I didn't find any build info (I guess for now you use git pull or something like that)

    Tom

    opened by tmikulin 3
  • Support sandwiches including multiple pools

    Support sandwiches including multiple pools

    Fixes this: https://github.com/flashbots/mev-inspect-py/issues/198

    For sandwiches, the backrun swap doesn't need to be funded by the sandwicher, it just should end up cycling back to their account

    opened by lukevs 3
  • Balancer Contracts

    Balancer Contracts

    Currently all balancer transactions are being classified as ERC20 or Vault despite the addresses and abi's being unique. Coult use a second set of eyes on this.

    opened by pmdaly 3
  • Add poetry

    Add poetry

    Poetry solves a lot of common package management challenges and greatly simplifies development. It's modeled after Rust's Cargo and very similar to Yarn. The following changes were added in the migration to Poetry.

    1. Requirements are now in pyproject under dependencies and dev dependencies, versioning is now managed. New dependencies can be added via poetry add package or poetry add --dev package
    2. Python version file is now apart of pyproject, if the original structure is required by another package we can revert this, but I think this should be fine.
    3. Scripts are now added in the dev_tools file and can be accessed via `poetry run lint' for example. Commands include lint, test (uses pytest now), isort, isortcheck, mypy, black, blackcheck, and start, build and start_it which are docker commands
    4. Pytest was added along with coverage and some "sugar" to "prettify" the output. Pytest will work with unnittest so no changes are required but I would encourage tests to be written using pytest, it's far simpler.

    Setting up a project is now as simple as poetry install which will install all the dependencies and setup an environment. The env can be accessed via poetry shell.

    opened by pmdaly 3
  • Add BlockCall class

    Add BlockCall class

    This PR

    Adds a data class for the "calls" field in Block Also adds some generic utilities for helping in translating from camel case to Python-standard snake case Also moves the logic for building a new block to fetch_block to use for manual testing (+ cleaner)

    Writing this:

    • to make it easier for inspector implementors to see what data they have available in calls (still TODO actions + result types)
    • as a first example of adding a child type on Block + handling the awkwardness around camel case

    Todo in future PRs:

    • add typing for the different actions and results for each call type
    • adopt these in the inspectors - to make things easy for now I switched the call objects back to JSON when passing to inspectors so they don't need to update, but this should change to use the objects when we're ready

    Testing

    Ensured there are no validation errors from running against different ranges of block numbers

        base_provider = Web3.HTTPProvider(rpc)
        w3 = Web3(base_provider)
    
        for i, block_number in enumerate(range(block_start, block_start + block_limit)):
            typer.echo(f"Testing {block_number} ({i+1}/{block_limit})")
            # includes trying to load the block
            fetch_block(w3, base_provider, block_number)
    
    opened by lukevs 3
  • Random failures with ValueError: {'message': 'Internal JSON-RPC error.', 'code': -32603}

    Random failures with ValueError: {'message': 'Internal JSON-RPC error.', 'code': -32603}

    Thanks for this great project. I installed this project and tried to do ./mev inspect 12914944. I am using the RPC endpoint on Pokt as recommended. Sometimes I can get it to work and dump to the postgres, sometime I get this error: File "/app/mev_inspect/block.py", line 115, in _fetch_block_traces traces_json = await w3.eth.trace_block(block_number) File "/home/flashbot/.local/lib/python3.9/site-packages/web3/module.py", line 72, in caller result = await w3.manager.coro_request(method_str, File "/home/flashbot/.local/lib/python3.9/site-packages/web3/manager.py", line 203, in coro_request return self.formatted_response(response, File "/home/flashbot/.local/lib/python3.9/site-packages/web3/manager.py", line 168, in formatted_response raise ValueError(response["error"]) ValueError: {'message': 'Internal JSON-RPC error.', 'code': -32603} command terminated with exit code 1

    It is truly random -- all it takes for error to go away is to run the command again. What is going on here?

    opened by marsupialtail 2
  • Queue backfills with Redis

    Queue backfills with Redis

    Right now

    To backfill a large set of blocks, we:

    • Specify the range to backfill and a parallelism N
    • Spin up N pods each dedicated to len(range) / N blocks
    • Wait for each pod to complete

    Some issues with this:

    • It's difficult to tell how quickly we're processing the block range / how close we are to done
    • If one block fails in the range, that entire pod fails causing it to start over
    • if pods finish early, the parallelism drops (only the remaining ones keep working) - we'd prefer to have parallelism of N throughout the full time
    • If we want to increase or decrease parallelism during the run, we're out of luck
    • If we want to add more blocks to this backfill without changing parallelism, we're out of luck

    This PR

    Switches to a new model using a simple queue over Redis called dramatiq

    Now to backfill a range we:

    • Queue up many small batches (ex: 10 blocks) of work for a large range
    • Spin up a set parallelism of worker nodes to process one batch at a time
    • If a batch fails, it's automatically retried
    • if it continues to fail after retries, it stops being retried and is placed in a "dead letter" queue for manual inspection, allowing the pod to continue working on the rest of the data

    Using this model we can:

    • Query redis to see how far along we are and any blocks being retried or failed
    • Increase or decrease our parallelism by adjusting the number of workers
    • Increase or decrease the number of blocks being worked on by adding / removing elements from Redis
    opened by lukevs 2
  • Failed migrations

    Failed migrations

    Conflict between migrations causing error on:

    ./mev exec alembic upgrade head
    

    Output:

    ERROR [alembic.util.messaging] Requested revision 52d75a7e0533 overlaps with other requested revisions b9fa1ecc9929
    
    opened by gheise 2
  • Add sandwiches tables

    Add sandwiches tables

    Adds a table for sandwiches and another for the swaps being sandwiched

    Currently doesn't include profit data as this table's immediate purpose is for filtering arbitrages that participate in sandwiches

    opened by lukevs 2
  • ValueError: {'code': -32601, 'message': 'the method trace_block does not exist/is not available'}

    ValueError: {'code': -32601, 'message': 'the method trace_block does not exist/is not available'}

    I am using an endpoint from pokt.network: https://eth-mainnet.gateway.pokt.network/v1/lb/xxxx

    I have done all steps mentioned in the README, while got following error:

    ➜  mev-inspect-py git:(main) ./mev inspect 12914944
    
    Inspecting block 12914944
    Skipping virtualenv creation, as specified in config file.
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/home/flashbot/.local/lib/python3.9/site-packages/click/core.py", line 1137, in __call__
        return self.main(*args, **kwargs)
      File "/home/flashbot/.local/lib/python3.9/site-packages/click/core.py", line 1062, in main
        rv = self.invoke(ctx)
      File "/home/flashbot/.local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
        return ctx.invoke(self.callback, **ctx.params)
      File "/home/flashbot/.local/lib/python3.9/site-packages/click/core.py", line 763, in invoke
        return __callback(*args, **kwargs)
      File "/app/mev_inspect/concurrency.py", line 18, in wrapper
        loop.run_until_complete(f(*args, **kwargs))
      File "/usr/local/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
        return future.result()
      File "/app/cli.py", line 45, in inspect_block_command
        await inspector.inspect_single_block(
      File "/app/mev_inspect/inspector.py", line 56, in inspect_single_block
        return await inspect_block(
      File "/app/mev_inspect/inspect_block.py", line 68, in inspect_block
        await inspect_many_blocks(
      File "/app/mev_inspect/inspect_block.py", line 105, in inspect_many_blocks
        block = await create_from_block_number(
      File "/app/mev_inspect/block.py", line 31, in create_from_block_number
        block_timestamp, receipts, traces, base_fee_per_gas = await asyncio.gather(
      File "/app/mev_inspect/block.py", line 86, in _find_or_fetch_block_traces
        return await _fetch_block_traces(w3, block_number)
      File "/app/mev_inspect/block.py", line 115, in _fetch_block_traces
        traces_json = await w3.eth.trace_block(block_number)
      File "/home/flashbot/.local/lib/python3.9/site-packages/web3/module.py", line 72, in caller
        result = await w3.manager.coro_request(method_str,
      File "/home/flashbot/.local/lib/python3.9/site-packages/web3/manager.py", line 203, in coro_request
        return self.formatted_response(response,
      File "/home/flashbot/.local/lib/python3.9/site-packages/web3/manager.py", line 168, in formatted_response
        raise ValueError(response["error"])
    ValueError: {'code': -32601, 'message': 'the method trace_block does not exist/is not available'}
    command terminated with exit code 1
    
    opened by jerryleooo 2
  • running inspect on post-merge blocks fails

    running inspect on post-merge blocks fails

    mev-inspect-py is unable to process any blocks since the merge. The reason seems to be that the 'miner' of each block is determined by looking for a trace of type reward. Presumably since there is no block subsidy post-merge, there are no longer any traces of this type.

    Accordingly _get_miner_address_from_traces returns None and so create_block_from_number fails.

    This could be easily fixed by getting the 'miner' from w3.eth.get_block(block_number), which returns the fee recipient for post-merge blocks.

    opened by pintail-xyz 0
  • Reverted liquidiations on Compound not handled properly causing block processing to fail

    Reverted liquidiations on Compound not handled properly causing block processing to fail

    Reverted Compound liquidations are not properly handled and cause an exception during block processing. For example:

    block 15049646 processing fails due to transaction 0x6dd0d8be8a77651f64ef399b47fbc87011bd796b43349c3164ff7da965e0b345

    other examples are:

    0x4ad1e75751058213ece676f2b68fcccdd8290d5db86539773f8c0b15961e0bc3 (block 15352204) 0x56fe1a490b1cea68957721754c0275b4737ef836ca564c114c051cab88b19938 (block 15382895) 0x779537496f27aa54db49dbcb1c3abc710cf46f9f25a2e4f433db063c6ca5e4ad (block 15382904) etc

    To reproduce run ./mev inspect with any of the above block numbers.

    opened by pintail-xyz 0
  • Aave ETH liquidations fixed

    Aave ETH liquidations fixed

    What does this PR do?

    Added a separate transfer classifier spec for the transferToReserve() function on the LendingPoolCore contract. This makes sure that the debt transfer is detected and classified even when the reserve is ETH

    Related issue

    #313 ETH liquidations on Aave not detected

    Testing

    Unit test for block 10384319 to check if liquidations for ETH are detected.

    Checklist before merging

    • [x] Read the contributing guide
    • [x] Installed and ran pre-commit hooks
    • [x] All tests pass with ./mev test
    opened by atharvamitragotri 0
  • ETH liquidations on Aave not detected

    ETH liquidations on Aave not detected

    The Aave spec is not able to detect liquidations for ETH borrowed from the reserve against a collateral token.

    tx1

    The ETH sent with the liquidation call is not classified as a transfer, hence the whole liquidation is not detected on running ./mev inspect 10384319

    aavescan

    opened by atharvamitragotri 0
  • GET requests are not accepted. Use POST instead.

    GET requests are not accepted. Use POST instead.

    image

    I am using an archival node with trace calls from pokt network as suggested but am unable to make a request. Everything works fine until I run the line -> ./mev inspect 12914944

    opened by simrat12 0
Owner
Flashbots
Flashbots
A simple Ethereum mining pool

A simple getWork pool for ethereum mining

null 93 Oct 5, 2022
Ethereum ETL lets you convert blockchain data into convenient formats like CSVs and relational databases.

Python scripts for ETL (extract, transform and load) jobs for Ethereum blocks, transactions, ERC20 / ERC721 tokens, transfers, receipts, logs, contracts, internal transactions.

Blockchain ETL 2.3k Jan 1, 2023
How to setup a multi-client ethereum Eth1-Eth2 merge testnet

Mergenet tutorial Let's set up a local eth1-eth2 merge testnet! Preparing the setup environment In this tutorial, we use a series of scripts to genera

Diederik Loerakker 24 Jun 17, 2022
Basic Ethereum Miner Lib

EthMine ⛏ Basic Ethereum Miner Library. Developers can integrate this algorithm to mine blocks from their ethereum supported chain efficiently. Instal

Jaival Patel 1 Oct 30, 2021
Tutela: an Ethereum and Tornado Cash Anonymity Tool

Tutela: an Ethereum and Tornado Cash Anonymity Tool The repo contains open-source code for Tutela, an anonymity tool for Ethereum and Tornado Cash use

TutelaLabs 96 Dec 5, 2022
Lottery by Ethereum Blockchain

Lottery by Ethereum Blockchain Set your web3 provider url in .env PROVIDER=https://mainnet.infura.io/v3/<YOUR-INFURA-TOKEN> Create your source file .

John Torres 3 Dec 23, 2021
GStreamer Inspector GUI

gst-explorer GStreamer GUI Interface Tool GUI interface for inspecting GStreamer Plugins, Elements and Type Finders. Expects Python3 Qt, PyQt5 and GSt

Jetsonhacks 31 Nov 29, 2022
A thin Python Wrapper for the Dark Sky (formerly forecast.io) weather API

Dark Sky Wrapper This is a wrapper for the Dark Sky (formerly forecast.io) API. It allows you to get the weather for any location, now, in the past, o

Ze'ev Gilovitz 414 Nov 16, 2022
A Cataclysm: Dark Days Ahead launcher with additional features

CDDA Game Launcher A Cataclysm: Dark Days Ahead launcher with additional features. Download here. Implemented features Launching the game Detecting th

RΓ©my Roy 402 Jan 2, 2023
Change between dark/light mode depending on the ambient light intensity

svart Change between dark/light mode depending on the ambient light intensity Installation Install using pip $ python3 -m pip install --user svart Ins

Siddharth Dushantha 169 Nov 26, 2022
A DeepStack custom model for detecting common objects in dark/night images and videos.

DeepStack_ExDark This repository provides a custom DeepStack model that has been trained and can be used for creating a new object detection API for d

MOSES OLAFENWA 98 Dec 24, 2022
Replacement for the default Dark Sky Home Assistant integration using Pirate Weather

Pirate Weather Integrations This integration is designed to replace the default Dark Sky integration in Home Assistant with a slightly modified, but f

Alexander Rey 129 Jan 6, 2023
This package creates clean and beautiful matplotlib plots that work on light and dark backgrounds

This package creates clean and beautiful matplotlib plots that work on light and dark backgrounds. Inspired by the work of Edward Tufte.

Nico SchlΓΆmer 205 Jan 7, 2023
Kaggle Tweet Sentiment Extraction Competition: 1st place solution (Dark of the Moon team)

Kaggle Tweet Sentiment Extraction Competition: 1st place solution (Dark of the Moon team)

Artsem Zhyvalkouski 64 Nov 30, 2022
Seeing Dynamic Scene in the Dark: High-Quality Video Dataset with Mechatronic Alignment (ICCV2021)

Seeing Dynamic Scene in the Dark: High-Quality Video Dataset with Mechatronic Alignment This is a pytorch project for the paper Seeing Dynamic Scene i

DV Lab 21 Nov 28, 2022
NeoDays-based tileset for the roguelike CDDA (Cataclysm Dark Days Ahead)

NeoDaysPlus Reduced contrast, expanded, and continuously developed version of the CDDA tileset NeoDays that's being completed with new sprites for mis

null 0 Nov 12, 2022
Disable dark mode in Django admin user interface in Django 3.2.x.

Django Non Dark Admin Disable or enable dark mode user interface in Django admin panel (Django==3.2). Installation For install this app run in termina

Artem Galichkin 6 Nov 23, 2022
Simple calculator with random number button and dark gray theme created with PyQt6

Calculator Application Simple calculator with random number button and dark gray theme created with : PyQt6 Python 3.9.7 you can download the dark gra

Flamingo 2 Mar 7, 2022
Dark Utilities - Cloudflare Uam Bypass

Dark Utilities - Cloudflare Uam Bypass

Inplex-sys 26 Dec 14, 2022
Dark-Fb No Login 100% safe

Dark-Fb No Login 100% safe TERMUX β€’ pkg install python2 && git -y β€’ pip2 install requests mechanize tqdm β€’ git clone https://github.com/BOT-033/Sensei

Bukan Hamkel 1 Dec 4, 2021