async based sqlachemy

This commit is contained in:
carlomazzaferro 2021-11-24 09:34:09 +01:00
parent bf4570c8a3
commit 2370e4ba92
No known key found for this signature in database
GPG Key ID: 0CED3103EF7B2187
18 changed files with 252 additions and 277 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@ cache
# env # env
.envrc .envrc
/out/

View File

@ -1,7 +1,9 @@
import asyncio
from logging.config import fileConfig from logging.config import fileConfig
from sqlalchemy import engine_from_config from sqlalchemy import engine_from_config
from sqlalchemy import pool from sqlalchemy import pool
from sqlalchemy.ext.asyncio import AsyncEngine
from alembic import context from alembic import context
@ -52,27 +54,34 @@ def run_migrations_offline():
context.run_migrations() context.run_migrations()
def run_migrations_online(): def do_run_migrations(connection):
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
async def run_migrations_online():
"""Run migrations in 'online' mode. """Run migrations in 'online' mode.
In this scenario we need to create an Engine In this scenario we need to create an Engine
and associate a connection with the context. and associate a connection with the context.
""" """
connectable = engine_from_config( connectable = AsyncEngine(
config.get_section(config.config_ini_section), engine_from_config(
prefix="sqlalchemy.", config.get_section(config.config_ini_section),
poolclass=pool.NullPool, prefix="sqlalchemy.",
poolclass=pool.NullPool,
future=True,
)
) )
with connectable.connect() as connection: async with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata) await connection.run_sync(do_run_migrations)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode(): if context.is_offline_mode():
run_migrations_offline() run_migrations_offline()
else: else:
run_migrations_online() asyncio.run(run_migrations_online())

16
cli.py
View File

@ -5,7 +5,6 @@ import sys
import click import click
from mev_inspect.concurrency import coro from mev_inspect.concurrency import coro
from mev_inspect.db import get_inspect_session, get_trace_session
from mev_inspect.inspector import MEVInspector from mev_inspect.inspector import MEVInspector
RPC_URL_ENV = "RPC_URL" RPC_URL_ENV = "RPC_URL"
@ -23,10 +22,7 @@ def cli():
@click.option("--rpc", default=lambda: os.environ.get(RPC_URL_ENV, "")) @click.option("--rpc", default=lambda: os.environ.get(RPC_URL_ENV, ""))
@coro @coro
async def inspect_block_command(block_number: int, rpc: str): async def inspect_block_command(block_number: int, rpc: str):
inspect_db_session = get_inspect_session() inspector = MEVInspector(rpc)
trace_db_session = get_trace_session()
inspector = MEVInspector(rpc, inspect_db_session, trace_db_session)
await inspector.inspect_single_block(block=block_number) await inspector.inspect_single_block(block=block_number)
@ -35,10 +31,7 @@ async def inspect_block_command(block_number: int, rpc: str):
@click.option("--rpc", default=lambda: os.environ.get(RPC_URL_ENV, "")) @click.option("--rpc", default=lambda: os.environ.get(RPC_URL_ENV, ""))
@coro @coro
async def fetch_block_command(block_number: int, rpc: str): async def fetch_block_command(block_number: int, rpc: str):
inspect_db_session = get_inspect_session() inspector = MEVInspector(rpc)
trace_db_session = get_trace_session()
inspector = MEVInspector(rpc, inspect_db_session, trace_db_session)
block = await inspector.create_from_block(block_number=block_number) block = await inspector.create_from_block(block_number=block_number)
print(block.json()) print(block.json())
@ -64,13 +57,8 @@ async def inspect_many_blocks_command(
max_concurrency: int, max_concurrency: int,
request_timeout: int, request_timeout: int,
): ):
inspect_db_session = get_inspect_session()
trace_db_session = get_trace_session()
inspector = MEVInspector( inspector = MEVInspector(
rpc, rpc,
inspect_db_session,
trace_db_session,
max_concurrency=max_concurrency, max_concurrency=max_concurrency,
request_timeout=request_timeout, request_timeout=request_timeout,
) )

View File

@ -10,12 +10,10 @@ from mev_inspect.crud.latest_block_update import (
find_latest_block_update, find_latest_block_update,
update_latest_block, update_latest_block,
) )
from mev_inspect.db import get_inspect_session, get_trace_session
from mev_inspect.inspector import MEVInspector from mev_inspect.inspector import MEVInspector
from mev_inspect.provider import get_base_provider from mev_inspect.provider import get_base_provider
from mev_inspect.signal_handler import GracefulKiller from mev_inspect.signal_handler import GracefulKiller
logging.basicConfig(filename="listener.log", filemode="a", level=logging.INFO) logging.basicConfig(filename="listener.log", filemode="a", level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -35,16 +33,13 @@ async def run():
killer = GracefulKiller() killer = GracefulKiller()
inspect_db_session = get_inspect_session() inspector = MEVInspector(rpc, None, None)
trace_db_session = get_trace_session()
inspector = MEVInspector(rpc, inspect_db_session, trace_db_session)
base_provider = get_base_provider(rpc) base_provider = get_base_provider(rpc)
while not killer.kill_now: while not killer.kill_now:
await inspect_next_block( await inspect_next_block(
inspector, inspector,
inspect_db_session, None,
base_provider, base_provider,
healthcheck_url, healthcheck_url,
) )

View File

@ -5,6 +5,7 @@ from typing import List, Optional
from sqlalchemy import orm from sqlalchemy import orm
from web3 import Web3 from web3 import Web3
from mev_inspect.db import get_trace_session
from mev_inspect.fees import fetch_base_fee_per_gas from mev_inspect.fees import fetch_base_fee_per_gas
from mev_inspect.schemas.blocks import Block from mev_inspect.schemas.blocks import Block
from mev_inspect.schemas.receipts import Receipt from mev_inspect.schemas.receipts import Receipt
@ -28,13 +29,12 @@ async def create_from_block_number(
base_provider, base_provider,
w3: Web3, w3: Web3,
block_number: int, block_number: int,
trace_db_session: Optional[orm.Session],
) -> Block: ) -> Block:
block: Optional[Block] = None block: Optional[Block] = None
if trace_db_session is not None: if get_trace_session() is not None:
block = _find_block(trace_db_session, block_number) async with get_trace_session() as session: # type: ignore
block = await _find_block(session, block_number)
if block is None: if block is None:
block = await _fetch_block(w3, base_provider, block_number) block = await _fetch_block(w3, base_provider, block_number)
return block return block
@ -75,14 +75,14 @@ async def _fetch_block(w3, base_provider, block_number: int, retries: int = 0) -
) )
def _find_block( async def _find_block(
trace_db_session: orm.Session, trace_db_session: orm.Session,
block_number: int, block_number: int,
) -> Optional[Block]: ) -> Optional[Block]:
traces = await _find_traces(trace_db_session, block_number)
receipts = await _find_receipts(trace_db_session, block_number)
base_fee_per_gas = await _find_base_fee(trace_db_session, block_number)
block_timestamp = _find_block_timestamp(trace_db_session, block_number) block_timestamp = _find_block_timestamp(trace_db_session, block_number)
traces = _find_traces(trace_db_session, block_number)
receipts = _find_receipts(trace_db_session, block_number)
base_fee_per_gas = _find_base_fee(trace_db_session, block_number)
if ( if (
block_timestamp is None block_timestamp is None
@ -123,11 +123,11 @@ def _find_block_timestamp(
return block_timestamp return block_timestamp
def _find_traces( async def _find_traces(
trace_db_session: orm.Session, trace_db_session: orm.Session,
block_number: int, block_number: int,
) -> Optional[List[Trace]]: ) -> Optional[List[Trace]]:
result = trace_db_session.execute( result = await trace_db_session.execute(
"SELECT raw_traces FROM block_traces WHERE block_number = :block_number", "SELECT raw_traces FROM block_traces WHERE block_number = :block_number",
params={"block_number": block_number}, params={"block_number": block_number},
).one_or_none() ).one_or_none()
@ -139,11 +139,11 @@ def _find_traces(
return [Trace(**trace_json) for trace_json in traces_json] return [Trace(**trace_json) for trace_json in traces_json]
def _find_receipts( async def _find_receipts(
trace_db_session: orm.Session, trace_db_session: orm.Session,
block_number: int, block_number: int,
) -> Optional[List[Receipt]]: ) -> Optional[List[Receipt]]:
result = trace_db_session.execute( result = await trace_db_session.execute(
"SELECT raw_receipts FROM block_receipts WHERE block_number = :block_number", "SELECT raw_receipts FROM block_receipts WHERE block_number = :block_number",
params={"block_number": block_number}, params={"block_number": block_number},
).one_or_none() ).one_or_none()
@ -155,11 +155,11 @@ def _find_receipts(
return [Receipt(**receipt) for receipt in receipts_json] return [Receipt(**receipt) for receipt in receipts_json]
def _find_base_fee( async def _find_base_fee(
trace_db_session: orm.Session, trace_db_session: orm.Session,
block_number: int, block_number: int,
) -> Optional[int]: ) -> Optional[int]:
result = trace_db_session.execute( result = await trace_db_session.execute(
"SELECT base_fee_in_wei FROM base_fee WHERE block_number = :block_number", "SELECT base_fee_in_wei FROM base_fee WHERE block_number = :block_number",
params={"block_number": block_number}, params={"block_number": block_number},
).one_or_none() ).one_or_none()

View File

@ -1,24 +1,18 @@
from typing import List from typing import List
from uuid import uuid4 from uuid import uuid4
from mev_inspect.crud.generic import delete_by_block_number
from mev_inspect.models.arbitrages import ArbitrageModel from mev_inspect.models.arbitrages import ArbitrageModel
from mev_inspect.schemas.arbitrages import Arbitrage from mev_inspect.schemas.arbitrages import Arbitrage
def delete_arbitrages_for_block( async def delete_arbitrages_for_block(db_session, block_number: int) -> None:
db_session, await delete_by_block_number(
block_number: int, db_session=db_session, block_number=block_number, model=ArbitrageModel
) -> None:
(
db_session.query(ArbitrageModel)
.filter(ArbitrageModel.block_number == block_number)
.delete()
) )
db_session.commit()
async def write_arbitrages(
def write_arbitrages(
db_session, db_session,
arbitrages: List[Arbitrage], arbitrages: List[Arbitrage],
) -> None: ) -> None:
@ -50,8 +44,10 @@ def write_arbitrages(
) )
if len(arbitrage_models) > 0: if len(arbitrage_models) > 0:
db_session.bulk_save_objects(arbitrage_models) db_session.add_all(arbitrage_models)
db_session.execute( await db_session.commit()
await db_session.flush()
await db_session.execute(
""" """
INSERT INTO arbitrage_swaps INSERT INTO arbitrage_swaps
(arbitrage_id, swap_transaction_hash, swap_trace_address) (arbitrage_id, swap_transaction_hash, swap_trace_address)
@ -60,5 +56,5 @@ def write_arbitrages(
""", """,
params=swap_arbitrage_ids, params=swap_arbitrage_ids,
) )
await db_session.commit()
db_session.commit() await db_session.flush()

View File

@ -1,26 +1,28 @@
from mev_inspect.schemas.blocks import Block from mev_inspect.schemas.blocks import Block
def delete_block( async def delete_block(
db_session, db_session,
block_number: int, block_number: int,
) -> None: ) -> None:
db_session.execute( await db_session.execute(
"DELETE FROM blocks WHERE block_number = :block_number", "DELETE FROM blocks WHERE block_number = :block_number",
params={"block_number": block_number}, params={"block_number": block_number},
) )
db_session.commit() await db_session.commit()
await db_session.flush()
def write_block( async def write_block(
db_session, db_session,
block: Block, block: Block,
) -> None: ) -> None:
db_session.execute( await db_session.execute(
"INSERT INTO blocks (block_number, block_timestamp) VALUES (:block_number, :block_timestamp)", "INSERT INTO blocks (block_number, block_timestamp) VALUES (:block_number, :block_timestamp)",
params={ params={
"block_number": block.block_number, "block_number": block.block_number,
"block_timestamp": block.block_timestamp, "block_timestamp": block.block_timestamp,
}, },
) )
db_session.commit() await db_session.commit()
await db_session.flush()

View File

@ -0,0 +1,21 @@
import json
from typing import Sequence
from pydantic import BaseModel
from sqlalchemy import delete
from mev_inspect.models.base import Base
async def delete_by_block_number(db_session, block_number: int, model: Base):
statement = delete(model).where(model.block_number == block_number)
await db_session.execute(statement)
await db_session.commit()
await db_session.flush()
async def write_models(db_session, models: Sequence[BaseModel], db_model: Base):
models = [db_model(**json.loads(model.json())) for model in models]
db_session.add_all(models)
await db_session.commit()
await db_session.flush()

View File

@ -1,31 +1,15 @@
import json
from typing import List from typing import List
from mev_inspect.crud.generic import delete_by_block_number, write_models
from mev_inspect.models.liquidations import LiquidationModel from mev_inspect.models.liquidations import LiquidationModel
from mev_inspect.schemas.liquidations import Liquidation from mev_inspect.schemas.liquidations import Liquidation
def delete_liquidations_for_block( async def delete_liquidations_for_block(db_session, block_number: int) -> None:
db_session, await delete_by_block_number(
block_number: int, db_session=db_session, block_number=block_number, model=LiquidationModel
) -> None:
(
db_session.query(LiquidationModel)
.filter(LiquidationModel.block_number == block_number)
.delete()
) )
db_session.commit()
async def write_liquidations(db_session, liquidations: List[Liquidation]) -> None:
def write_liquidations( await write_models(db_session, liquidations, LiquidationModel)
db_session,
liquidations: List[Liquidation],
) -> None:
models = [
LiquidationModel(**json.loads(liquidation.json()))
for liquidation in liquidations
]
db_session.bulk_save_objects(models)
db_session.commit()

View File

@ -1,31 +1,15 @@
import json
from typing import List from typing import List
from mev_inspect.crud.generic import delete_by_block_number, write_models
from mev_inspect.models.miner_payments import MinerPaymentModel from mev_inspect.models.miner_payments import MinerPaymentModel
from mev_inspect.schemas.miner_payments import MinerPayment from mev_inspect.schemas.miner_payments import MinerPayment
def delete_miner_payments_for_block( async def delete_miner_payments_for_block(db_session, block_number: int) -> None:
db_session, await delete_by_block_number(
block_number: int, db_session=db_session, block_number=block_number, model=MinerPaymentModel
) -> None:
(
db_session.query(MinerPaymentModel)
.filter(MinerPaymentModel.block_number == block_number)
.delete()
) )
db_session.commit()
async def write_miner_payments(db_session, miner_payments: List[MinerPayment]) -> None:
def write_miner_payments( await write_models(db_session, miner_payments, MinerPaymentModel)
db_session,
miner_payments: List[MinerPayment],
) -> None:
models = [
MinerPaymentModel(**json.loads(miner_payment.json()))
for miner_payment in miner_payments
]
db_session.bulk_save_objects(models)
db_session.commit()

View File

@ -1,28 +1,15 @@
import json
from typing import List from typing import List
from mev_inspect.crud.generic import delete_by_block_number, write_models
from mev_inspect.models.swaps import SwapModel from mev_inspect.models.swaps import SwapModel
from mev_inspect.schemas.swaps import Swap from mev_inspect.schemas.swaps import Swap
def delete_swaps_for_block( async def delete_swaps_for_block(db_session, block_number: int) -> None:
db_session, await delete_by_block_number(
block_number: int, db_session=db_session, block_number=block_number, model=SwapModel
) -> None:
(
db_session.query(SwapModel)
.filter(SwapModel.block_number == block_number)
.delete()
) )
db_session.commit()
async def write_swaps(db_session, swaps: List[Swap]) -> None:
def write_swaps( await write_models(db_session, swaps, SwapModel)
db_session,
swaps: List[Swap],
) -> None:
models = [SwapModel(**json.loads(swap.json())) for swap in swaps]
db_session.bulk_save_objects(models)
db_session.commit()

View File

@ -1,26 +1,19 @@
import json import json
from typing import List from typing import List
from mev_inspect.crud.generic import delete_by_block_number
from mev_inspect.models.traces import ClassifiedTraceModel from mev_inspect.models.traces import ClassifiedTraceModel
from mev_inspect.schemas.traces import ClassifiedTrace from mev_inspect.schemas.traces import ClassifiedTrace
def delete_classified_traces_for_block( async def delete_classified_traces_for_block(db_session, block_number: int) -> None:
db_session, await delete_by_block_number(
block_number: int, db_session=db_session, block_number=block_number, model=ClassifiedTraceModel
) -> None:
(
db_session.query(ClassifiedTraceModel)
.filter(ClassifiedTraceModel.block_number == block_number)
.delete()
) )
db_session.commit()
async def write_classified_traces(
def write_classified_traces( db_session, classified_traces: List[ClassifiedTrace]
db_session,
classified_traces: List[ClassifiedTrace],
) -> None: ) -> None:
models = [] models = []
for trace in classified_traces: for trace in classified_traces:
@ -45,6 +38,6 @@ def write_classified_traces(
error=trace.error, error=trace.error,
) )
) )
db_session.add_all(models)
db_session.bulk_save_objects(models) await db_session.commit()
db_session.commit() await db_session.flush()

View File

@ -1,28 +1,15 @@
import json from typing import Sequence
from typing import List
from mev_inspect.crud.generic import delete_by_block_number, write_models
from mev_inspect.models.transfers import TransferModel from mev_inspect.models.transfers import TransferModel
from mev_inspect.schemas.transfers import Transfer from mev_inspect.schemas.transfers import Transfer
def delete_transfers_for_block( async def delete_transfers_for_block(db_session, block_number: int) -> None:
db_session, await delete_by_block_number(
block_number: int, db_session=db_session, block_number=block_number, model=TransferModel
) -> None:
(
db_session.query(TransferModel)
.filter(TransferModel.block_number == block_number)
.delete()
) )
db_session.commit()
async def write_transfers(db_session, transfers: Sequence[Transfer]) -> None:
def write_transfers( await write_models(db_session, transfers, TransferModel)
db_session,
transfers: List[Transfer],
) -> None:
models = [TransferModel(**json.loads(transfer.json())) for transfer in transfers]
db_session.bulk_save_objects(models)
db_session.commit()

View File

@ -1,8 +1,8 @@
import os import os
from typing import Optional from typing import Optional
from sqlalchemy import create_engine, orm
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
def get_trace_database_uri() -> Optional[str]: def get_trace_database_uri() -> Optional[str]:
@ -12,7 +12,7 @@ def get_trace_database_uri() -> Optional[str]:
db_name = "trace_db" db_name = "trace_db"
if all(field is not None for field in [username, password, host]): if all(field is not None for field in [username, password, host]):
return f"postgresql://{username}:{password}@{host}/{db_name}" return f"postgresql+asyncpg://{username}:{password}@{host}/{db_name}"
return None return None
@ -22,24 +22,24 @@ def get_inspect_database_uri():
password = os.getenv("POSTGRES_PASSWORD") password = os.getenv("POSTGRES_PASSWORD")
host = os.getenv("POSTGRES_HOST") host = os.getenv("POSTGRES_HOST")
db_name = "mev_inspect" db_name = "mev_inspect"
return f"postgresql://{username}:{password}@{host}/{db_name}" return f"postgresql+asyncpg://{username}:{password}@{host}/{db_name}"
def _get_engine(uri: str): def _get_engine(uri: str):
return create_engine(uri) return create_async_engine(uri)
def _get_session(uri: str): def _get_session(uri: str):
Session = sessionmaker(bind=_get_engine(uri)) session = sessionmaker(bind=_get_engine(uri), class_=AsyncSession)
return Session() return session()
def get_inspect_session() -> orm.Session: def get_inspect_session() -> sessionmaker:
uri = get_inspect_database_uri() uri = get_inspect_database_uri()
return _get_session(uri) return _get_session(uri)
def get_trace_session() -> Optional[orm.Session]: def get_trace_session() -> Optional[sessionmaker]:
uri = get_trace_database_uri() uri = get_trace_database_uri()
if uri is not None: if uri is not None:

View File

@ -1,7 +1,6 @@
import logging import logging
from typing import Optional
from sqlalchemy import orm from sqlalchemy.ext.asyncio import AsyncSession
from web3 import Web3 from web3 import Web3
from mev_inspect.arbitrages import get_arbitrages from mev_inspect.arbitrages import get_arbitrages
@ -15,50 +14,42 @@ from mev_inspect.crud.blocks import (
delete_block, delete_block,
write_block, write_block,
) )
from mev_inspect.crud.traces import ( from mev_inspect.crud.liquidations import (
delete_classified_traces_for_block, delete_liquidations_for_block,
write_classified_traces, write_liquidations,
) )
from mev_inspect.crud.miner_payments import ( from mev_inspect.crud.miner_payments import (
delete_miner_payments_for_block, delete_miner_payments_for_block,
write_miner_payments, write_miner_payments,
) )
from mev_inspect.crud.swaps import delete_swaps_for_block, write_swaps from mev_inspect.crud.swaps import delete_swaps_for_block, write_swaps
from mev_inspect.crud.transfers import delete_transfers_for_block, write_transfers from mev_inspect.crud.traces import (
from mev_inspect.crud.liquidations import ( delete_classified_traces_for_block,
delete_liquidations_for_block, write_classified_traces,
write_liquidations,
) )
from mev_inspect.crud.transfers import delete_transfers_for_block, write_transfers
from mev_inspect.liquidations import get_liquidations
from mev_inspect.miner_payments import get_miner_payments from mev_inspect.miner_payments import get_miner_payments
from mev_inspect.swaps import get_swaps from mev_inspect.swaps import get_swaps
from mev_inspect.transfers import get_transfers from mev_inspect.transfers import get_transfers
from mev_inspect.liquidations import get_liquidations
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
async def inspect_block( async def inspect_block(
inspect_db_session: orm.Session, inspect_db_session: AsyncSession,
base_provider, base_provider,
w3: Web3, w3: Web3,
trace_classifier: TraceClassifier, trace_classifier: TraceClassifier,
block_number: int, block_number: int,
trace_db_session: Optional[orm.Session],
should_write_classified_traces: bool = True, should_write_classified_traces: bool = True,
): ):
block = await create_from_block_number( block = await create_from_block_number(base_provider, w3, block_number)
base_provider,
w3,
block_number,
trace_db_session,
)
logger.info(f"Block: {block_number} -- Total traces: {len(block.traces)}") logger.info(f"Block: {block_number} -- Total traces: {len(block.traces)}")
delete_block(inspect_db_session, block_number) await delete_block(inspect_db_session, block_number)
write_block(inspect_db_session, block) await write_block(inspect_db_session, block)
total_transactions = len( total_transactions = len(
set(t.transaction_hash for t in block.traces if t.transaction_hash is not None) set(t.transaction_hash for t in block.traces if t.transaction_hash is not None)
@ -71,36 +62,33 @@ async def inspect_block(
) )
if should_write_classified_traces: if should_write_classified_traces:
delete_classified_traces_for_block(inspect_db_session, block_number) await delete_classified_traces_for_block(inspect_db_session, block_number)
write_classified_traces(inspect_db_session, classified_traces) await write_classified_traces(inspect_db_session, classified_traces)
transfers = get_transfers(classified_traces) transfers = get_transfers(classified_traces)
logger.info(f"Block: {block_number} -- Found {len(transfers)} transfers") logger.info(f"Block: {block_number} -- Found {len(transfers)} transfers")
delete_transfers_for_block(inspect_db_session, block_number) await delete_transfers_for_block(inspect_db_session, block_number)
write_transfers(inspect_db_session, transfers) await write_transfers(inspect_db_session, transfers)
swaps = get_swaps(classified_traces) swaps = get_swaps(classified_traces)
logger.info(f"Block: {block_number} -- Found {len(swaps)} swaps") logger.info(f"Block: {block_number} -- Found {len(swaps)} swaps")
await delete_swaps_for_block(inspect_db_session, block_number)
delete_swaps_for_block(inspect_db_session, block_number) await write_swaps(inspect_db_session, swaps)
write_swaps(inspect_db_session, swaps)
arbitrages = get_arbitrages(swaps) arbitrages = get_arbitrages(swaps)
logger.info(f"Block: {block_number} -- Found {len(arbitrages)} arbitrages") logger.info(f"Block: {block_number} -- Found {len(arbitrages)} arbitrages")
await delete_arbitrages_for_block(inspect_db_session, block_number)
delete_arbitrages_for_block(inspect_db_session, block_number) await write_arbitrages(inspect_db_session, arbitrages)
write_arbitrages(inspect_db_session, arbitrages)
liquidations = get_liquidations(classified_traces) liquidations = get_liquidations(classified_traces)
logger.info(f"Block: {block_number} -- Found {len(liquidations)} liquidations") logger.info(f"Block: {block_number} -- Found {len(liquidations)} liquidations")
await delete_liquidations_for_block(inspect_db_session, block_number)
delete_liquidations_for_block(inspect_db_session, block_number) await write_liquidations(inspect_db_session, liquidations)
write_liquidations(inspect_db_session, liquidations)
miner_payments = get_miner_payments( miner_payments = get_miner_payments(
block.miner, block.base_fee_per_gas, classified_traces, block.receipts block.miner, block.base_fee_per_gas, classified_traces, block.receipts
) )
await delete_miner_payments_for_block(inspect_db_session, block_number)
delete_miner_payments_for_block(inspect_db_session, block_number) await write_miner_payments(inspect_db_session, miner_payments)
write_miner_payments(inspect_db_session, miner_payments) await inspect_db_session.commit()

View File

@ -2,14 +2,13 @@ import asyncio
import logging import logging
import traceback import traceback
from asyncio import CancelledError from asyncio import CancelledError
from typing import Optional
from sqlalchemy import orm
from web3 import Web3 from web3 import Web3
from web3.eth import AsyncEth from web3.eth import AsyncEth
from mev_inspect.block import create_from_block_number from mev_inspect.block import create_from_block_number
from mev_inspect.classifiers.trace import TraceClassifier from mev_inspect.classifiers.trace import TraceClassifier
from mev_inspect.db import get_inspect_session
from mev_inspect.inspect_block import inspect_block from mev_inspect.inspect_block import inspect_block
from mev_inspect.provider import get_base_provider from mev_inspect.provider import get_base_provider
@ -20,13 +19,9 @@ class MEVInspector:
def __init__( def __init__(
self, self,
rpc: str, rpc: str,
inspect_db_session: orm.Session,
trace_db_session: Optional[orm.Session],
max_concurrency: int = 1, max_concurrency: int = 1,
request_timeout: int = 300, request_timeout: int = 300,
): ):
self.inspect_db_session = inspect_db_session
self.trace_db_session = trace_db_session
self.base_provider = get_base_provider(rpc, request_timeout=request_timeout) self.base_provider = get_base_provider(rpc, request_timeout=request_timeout)
self.w3 = Web3(self.base_provider, modules={"eth": (AsyncEth,)}, middlewares=[]) self.w3 = Web3(self.base_provider, modules={"eth": (AsyncEth,)}, middlewares=[])
self.trace_classifier = TraceClassifier() self.trace_classifier = TraceClassifier()
@ -34,21 +29,14 @@ class MEVInspector:
async def create_from_block(self, block_number: int): async def create_from_block(self, block_number: int):
return await create_from_block_number( return await create_from_block_number(
base_provider=self.base_provider, base_provider=self.base_provider, w3=self.w3, block_number=block_number
w3=self.w3,
block_number=block_number,
trace_db_session=self.trace_db_session,
) )
async def inspect_single_block(self, block: int): async def inspect_single_block(self, block: int):
return await inspect_block( async with get_inspect_session() as session:
self.inspect_db_session, return await inspect_block(
self.base_provider, session, self.base_provider, self.w3, self.trace_classifier, block
self.w3, )
self.trace_classifier,
block,
trace_db_session=self.trace_db_session,
)
async def inspect_many_blocks(self, after_block: int, before_block: int): async def inspect_many_blocks(self, after_block: int, before_block: int):
tasks = [] tasks = []
@ -64,16 +52,16 @@ class MEVInspector:
except CancelledError: except CancelledError:
logger.info("Requested to exit, cleaning up...") logger.info("Requested to exit, cleaning up...")
except Exception as e: except Exception as e:
logger.error(f"Existed due to {type(e)}") logger.error(f"Exited due to {type(e)}")
traceback.print_exc() traceback.print_exc()
async def safe_inspect_block(self, block_number: int): async def safe_inspect_block(self, block_number: int):
async with self.max_concurrency: async with get_inspect_session() as session:
return await inspect_block( async with self.max_concurrency:
self.inspect_db_session, return await inspect_block(
self.base_provider, session,
self.w3, self.base_provider,
self.trace_classifier, self.w3,
block_number, self.trace_classifier,
trace_db_session=self.trace_db_session, block_number,
) )

124
poetry.lock generated
View File

@ -66,6 +66,19 @@ python-versions = ">=3.6"
[package.dependencies] [package.dependencies]
typing-extensions = ">=3.6.5" typing-extensions = ">=3.6.5"
[[package]]
name = "asyncpg"
version = "0.25.0"
description = "An asyncio PostgreSQL driver"
category = "main"
optional = false
python-versions = ">=3.6.0"
[package.extras]
dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"]
docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"]
test = ["pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"]
[[package]] [[package]]
name = "atomicwrites" name = "atomicwrites"
version = "1.4.0" version = "1.4.0"
@ -387,7 +400,7 @@ python-versions = ">=3.6"
name = "greenlet" name = "greenlet"
version = "1.1.1" version = "1.1.1"
description = "Lightweight in-process concurrent programming" description = "Lightweight in-process concurrent programming"
category = "dev" category = "main"
optional = false optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
@ -870,26 +883,27 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]] [[package]]
name = "sqlalchemy" name = "sqlalchemy"
version = "1.4.23" version = "1.4.27"
description = "Database Abstraction Library" description = "Database Abstraction Library"
category = "dev" category = "main"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
[package.dependencies] [package.dependencies]
greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and platform_machine in \"x86_64 X86_64 aarch64 AARCH64 ppc64le PPC64LE amd64 AMD64 win32 WIN32\""} greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
[package.extras] [package.extras]
aiomysql = ["greenlet (!=0.4.17)", "aiomysql"] aiomysql = ["greenlet (!=0.4.17)", "aiomysql"]
aiosqlite = ["greenlet (!=0.4.17)", "aiosqlite"] aiosqlite = ["typing_extensions (!=3.10.0.1)", "greenlet (!=0.4.17)", "aiosqlite"]
asyncio = ["greenlet (!=0.4.17)"] asyncio = ["greenlet (!=0.4.17)"]
asyncmy = ["greenlet (!=0.4.17)", "asyncmy (>=0.2.3)"]
mariadb_connector = ["mariadb (>=1.0.1)"] mariadb_connector = ["mariadb (>=1.0.1)"]
mssql = ["pyodbc"] mssql = ["pyodbc"]
mssql_pymssql = ["pymssql"] mssql_pymssql = ["pymssql"]
mssql_pyodbc = ["pyodbc"] mssql_pyodbc = ["pyodbc"]
mypy = ["sqlalchemy2-stubs", "mypy (>=0.910)"] mypy = ["sqlalchemy2-stubs", "mypy (>=0.910)"]
mysql = ["mysqlclient (>=1.4.0,<2)", "mysqlclient (>=1.4.0)"] mysql = ["mysqlclient (>=1.4.0,<2)", "mysqlclient (>=1.4.0)"]
mysql_connector = ["mysqlconnector"] mysql_connector = ["mysql-connector-python"]
oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"] oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"]
postgresql = ["psycopg2 (>=2.7)"] postgresql = ["psycopg2 (>=2.7)"]
postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"] postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"]
@ -1032,7 +1046,7 @@ multidict = ">=4.0"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "03aa2d5981665ade1b81682c1e797a06b56c5fb68d61ae69fd2f1e95bd32cfb6" content-hash = "fe3f1ed98bb9236905ee77aa29577089d8ae16560fb97fbcf9d6221683a883fe"
[metadata.files] [metadata.files]
aiohttp = [ aiohttp = [
@ -1125,6 +1139,34 @@ async-timeout = [
{file = "async-timeout-4.0.0.tar.gz", hash = "sha256:7d87a4e8adba8ededb52e579ce6bc8276985888913620c935094c2276fd83382"}, {file = "async-timeout-4.0.0.tar.gz", hash = "sha256:7d87a4e8adba8ededb52e579ce6bc8276985888913620c935094c2276fd83382"},
{file = "async_timeout-4.0.0-py3-none-any.whl", hash = "sha256:f3303dddf6cafa748a92747ab6c2ecf60e0aeca769aee4c151adfce243a05d9b"}, {file = "async_timeout-4.0.0-py3-none-any.whl", hash = "sha256:f3303dddf6cafa748a92747ab6c2ecf60e0aeca769aee4c151adfce243a05d9b"},
] ]
asyncpg = [
{file = "asyncpg-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf5e3408a14a17d480f36ebaf0401a12ff6ae5457fdf45e4e2775c51cc9517d3"},
{file = "asyncpg-0.25.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bc197fc4aca2fd24f60241057998124012469d2e414aed3f992579db0c88e3a"},
{file = "asyncpg-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a70783f6ffa34cc7dd2de20a873181414a34fd35a4a208a1f1a7f9f695e4ec4"},
{file = "asyncpg-0.25.0-cp310-cp310-win32.whl", hash = "sha256:43cde84e996a3afe75f325a68300093425c2f47d340c0fc8912765cf24a1c095"},
{file = "asyncpg-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:56d88d7ef4341412cd9c68efba323a4519c916979ba91b95d4c08799d2ff0c09"},
{file = "asyncpg-0.25.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a84d30e6f850bac0876990bcd207362778e2208df0bee8be8da9f1558255e634"},
{file = "asyncpg-0.25.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:beaecc52ad39614f6ca2e48c3ca15d56e24a2c15cbfdcb764a4320cc45f02fd5"},
{file = "asyncpg-0.25.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6f8f5fc975246eda83da8031a14004b9197f510c41511018e7b1bedde6968e92"},
{file = "asyncpg-0.25.0-cp36-cp36m-win32.whl", hash = "sha256:ddb4c3263a8d63dcde3d2c4ac1c25206bfeb31fa83bd70fd539e10f87739dee4"},
{file = "asyncpg-0.25.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bf6dc9b55b9113f39eaa2057337ce3f9ef7de99a053b8a16360395ce588925cd"},
{file = "asyncpg-0.25.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:acb311722352152936e58a8ee3c5b8e791b24e84cd7d777c414ff05b3530ca68"},
{file = "asyncpg-0.25.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0a61fb196ce4dae2f2fa26eb20a778db21bbee484d2e798cb3cc988de13bdd1b"},
{file = "asyncpg-0.25.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2633331cbc8429030b4f20f712f8d0fbba57fa8555ee9b2f45f981b81328b256"},
{file = "asyncpg-0.25.0-cp37-cp37m-win32.whl", hash = "sha256:863d36eba4a7caa853fd7d83fad5fd5306f050cc2fe6e54fbe10cdb30420e5e9"},
{file = "asyncpg-0.25.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fe471ccd915b739ca65e2e4dbd92a11b44a5b37f2e38f70827a1c147dafe0fa8"},
{file = "asyncpg-0.25.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:72a1e12ea0cf7c1e02794b697e3ca967b2360eaa2ce5d4bfdd8604ec2d6b774b"},
{file = "asyncpg-0.25.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4327f691b1bdb222df27841938b3e04c14068166b3a97491bec2cb982f49f03e"},
{file = "asyncpg-0.25.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:739bbd7f89a2b2f6bc44cb8bf967dab12c5bc714fcbe96e68d512be45ecdf962"},
{file = "asyncpg-0.25.0-cp38-cp38-win32.whl", hash = "sha256:18d49e2d93a7139a2fdbd113e320cc47075049997268a61bfbe0dde680c55471"},
{file = "asyncpg-0.25.0-cp38-cp38-win_amd64.whl", hash = "sha256:191fe6341385b7fdea7dbdcf47fd6db3fd198827dcc1f2b228476d13c05a03c6"},
{file = "asyncpg-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fab7f1b2c29e187dd8781fce896249500cf055b63471ad66332e537e9b5f7e"},
{file = "asyncpg-0.25.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a738f1b2876f30d710d3dc1e7858160a0afe1603ba16bf5f391f5316eb0ed855"},
{file = "asyncpg-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4105f57ad1e8fbc8b1e535d8fcefa6ce6c71081228f08680c6dea24384ff0e"},
{file = "asyncpg-0.25.0-cp39-cp39-win32.whl", hash = "sha256:f55918ded7b85723a5eaeb34e86e7b9280d4474be67df853ab5a7fa0cc7c6bf2"},
{file = "asyncpg-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:649e2966d98cc48d0646d9a4e29abecd8b59d38d55c256d5c857f6b27b7407ac"},
{file = "asyncpg-0.25.0.tar.gz", hash = "sha256:63f8e6a69733b285497c2855464a34de657f2cccd25aeaeeb5071872e9382540"},
]
atomicwrites = [ atomicwrites = [
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
@ -1662,6 +1704,8 @@ protobuf = [
{file = "protobuf-3.17.3.tar.gz", hash = "sha256:72804ea5eaa9c22a090d2803813e280fb273b62d5ae497aaf3553d141c4fdd7b"}, {file = "protobuf-3.17.3.tar.gz", hash = "sha256:72804ea5eaa9c22a090d2803813e280fb273b62d5ae497aaf3553d141c4fdd7b"},
] ]
psycopg2 = [ psycopg2 = [
{file = "psycopg2-2.9.1-cp310-cp310-win32.whl", hash = "sha256:25615574419dd9bda6fdfdcd58afb22e721f5b807cb3d5e62f488c8acf8cb754"},
{file = "psycopg2-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:e5a8ed9dbfca8dc162c4ada5ab017e10d5a66c542b4c73569f103fa5f342f498"},
{file = "psycopg2-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:7f91312f065df517187134cce8e395ab37f5b601a42446bdc0f0d51773621854"}, {file = "psycopg2-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:7f91312f065df517187134cce8e395ab37f5b601a42446bdc0f0d51773621854"},
{file = "psycopg2-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:830c8e8dddab6b6716a4bf73a09910c7954a92f40cf1d1e702fb93c8a919cc56"}, {file = "psycopg2-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:830c8e8dddab6b6716a4bf73a09910c7954a92f40cf1d1e702fb93c8a919cc56"},
{file = "psycopg2-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:89409d369f4882c47f7ea20c42c5046879ce22c1e4ea20ef3b00a4dfc0a7f188"}, {file = "psycopg2-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:89409d369f4882c47f7ea20c42c5046879ce22c1e4ea20ef3b00a4dfc0a7f188"},
@ -1890,36 +1934,42 @@ six = [
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
] ]
sqlalchemy = [ sqlalchemy = [
{file = "SQLAlchemy-1.4.23-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:25e9b2e5ca088879ce3740d9ccd4d58cb9061d49566a0b5e12166f403d6f4da0"}, {file = "SQLAlchemy-1.4.27-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:6afa9e4e63f066e0fd90a21db7e95e988d96127f52bfb298a0e9bec6999357a9"},
{file = "SQLAlchemy-1.4.23-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d9667260125688c71ccf9af321c37e9fb71c2693575af8210f763bfbbee847c7"}, {file = "SQLAlchemy-1.4.27-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec1c908fa721f2c5684900cc8ff75555b1a5a2ae4f5a5694eb0e37a5263cea44"},
{file = "SQLAlchemy-1.4.23-cp27-cp27m-win32.whl", hash = "sha256:cec1a4c6ddf5f82191301a25504f0e675eccd86635f0d5e4c69e0661691931c5"}, {file = "SQLAlchemy-1.4.27-cp27-cp27m-win32.whl", hash = "sha256:0438bccc16349db2d5203598be6073175ce16d4e53b592d6e6cef880c197333e"},
{file = "SQLAlchemy-1.4.23-cp27-cp27m-win_amd64.whl", hash = "sha256:ae07895b55c7d58a7dd47438f437ac219c0f09d24c2e7d69fdebc1ea75350f00"}, {file = "SQLAlchemy-1.4.27-cp27-cp27m-win_amd64.whl", hash = "sha256:435b1980c1333ffe3ab386ad28d7b209590b0fa83ea8544d853e7a22f957331b"},
{file = "SQLAlchemy-1.4.23-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:967307ea52985985224a79342527c36ec2d1daa257a39748dd90e001a4be4d90"}, {file = "SQLAlchemy-1.4.27-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:486f7916ef77213103467924ef25f5ea1055ae901f385fe4d707604095fdf6a9"},
{file = "SQLAlchemy-1.4.23-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:be185b3daf651c6c0639987a916bf41e97b60e68f860f27c9cb6574385f5cbb4"}, {file = "SQLAlchemy-1.4.27-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:d81c84c9d2523b3ea20f8e3aceea68615768a7464c0f9a9899600ce6592ec570"},
{file = "SQLAlchemy-1.4.23-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d3b3d51c83a66f5b72c57e1aad061406e4c390bd42cf1fda94effe82fac81"}, {file = "SQLAlchemy-1.4.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5881644fc51af7b232ab8d64f75c0f32295dfe88c2ee188023795cdbd4cf99b"},
{file = "SQLAlchemy-1.4.23-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a8395c4db3e1450eef2b68069abf500cc48af4b442a0d98b5d3c9535fe40cde8"}, {file = "SQLAlchemy-1.4.27-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:24828c5e74882cf41516740c0b150702bee4c6817d87d5c3d3bafef2e6896f80"},
{file = "SQLAlchemy-1.4.23-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b128a78581faea7a5ee626ad4471353eee051e4e94616dfeff4742b6e5ba262"}, {file = "SQLAlchemy-1.4.27-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7d0a1b1258efff7d7f2e6cfa56df580d09ba29d35a1e3f604f867e1f685feb2"},
{file = "SQLAlchemy-1.4.23-cp36-cp36m-win32.whl", hash = "sha256:43fc207be06e50158e4dae4cc4f27ce80afbdbfa7c490b3b22feb64f6d9775a0"}, {file = "SQLAlchemy-1.4.27-cp310-cp310-win32.whl", hash = "sha256:aadc6d1e58e14010ae4764d1ba1fd0928dbb9423b27a382ea3a1444f903f4084"},
{file = "SQLAlchemy-1.4.23-cp36-cp36m-win_amd64.whl", hash = "sha256:e9d4f4552aa5e0d1417fc64a2ce1cdf56a30bab346ba6b0dd5e838eb56db4d29"}, {file = "SQLAlchemy-1.4.27-cp310-cp310-win_amd64.whl", hash = "sha256:9134e5810262203388b203c2022bbcbf1a22e89861eef9340e772a73dd9076fa"},
{file = "SQLAlchemy-1.4.23-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:512f52a8872e8d63d898e4e158eda17e2ee40b8d2496b3b409422e71016db0bd"}, {file = "SQLAlchemy-1.4.27-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:fa52534076394af7315306a8701b726a6521b591d95e8f4e5121c82f94790e8d"},
{file = "SQLAlchemy-1.4.23-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:355024cf061ed04271900414eb4a22671520241d2216ddb691bdd8a992172389"}, {file = "SQLAlchemy-1.4.27-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2717ceae35e71de1f58b0d1ee7e773d3aab5c403c6e79e8d262277c7f7f95269"},
{file = "SQLAlchemy-1.4.23-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:82c03325111eab88d64e0ff48b6fe15c75d23787429fa1d84c0995872e702787"}, {file = "SQLAlchemy-1.4.27-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e93624d186ea7a738ada47314701c8830e0e4b021a6bce7fbe6f39b87ee1516"},
{file = "SQLAlchemy-1.4.23-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0aa312f9906ecebe133d7f44168c3cae4c76f27a25192fa7682f3fad505543c9"}, {file = "SQLAlchemy-1.4.27-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:987fe2f84ceaf744fa0e48805152abe485a9d7002c9923b18a4b2529c7bff218"},
{file = "SQLAlchemy-1.4.23-cp37-cp37m-win32.whl", hash = "sha256:059c5f41e8630f51741a234e6ba2a034228c11b3b54a15478e61d8b55fa8bd9d"}, {file = "SQLAlchemy-1.4.27-cp36-cp36m-win32.whl", hash = "sha256:2146ef996181e3d4dd20eaf1d7325eb62d6c8aa4dc1677c1872ddfa8561a47d9"},
{file = "SQLAlchemy-1.4.23-cp37-cp37m-win_amd64.whl", hash = "sha256:cd68c5f9d13ffc8f4d6802cceee786678c5b1c668c97bc07b9f4a60883f36cd1"}, {file = "SQLAlchemy-1.4.27-cp36-cp36m-win_amd64.whl", hash = "sha256:ad8ec6b69d03e395db48df8991aa15fce3cd23e378b73e01d46a26a6efd5c26d"},
{file = "SQLAlchemy-1.4.23-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:6a8dbf3d46e889d864a57ee880c4ad3a928db5aa95e3d359cbe0da2f122e50c4"}, {file = "SQLAlchemy-1.4.27-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:52f23a76544ed29573c0f3ee41f0ca1aedbab3a453102b60b540cc6fa55448ad"},
{file = "SQLAlchemy-1.4.23-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c15191f2430a30082f540ec6f331214746fc974cfdf136d7a1471d1c61d68ff"}, {file = "SQLAlchemy-1.4.27-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd421a14edf73cfe01e8f51ed8966294ee3b3db8da921cacc88e497fd6e977af"},
{file = "SQLAlchemy-1.4.23-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd0e85dd2067159848c7672acd517f0c38b7b98867a347411ea01b432003f8d9"}, {file = "SQLAlchemy-1.4.27-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:10230364479429437f1b819a8839f1edc5744c018bfeb8d01320930f97695bc9"},
{file = "SQLAlchemy-1.4.23-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:370f4688ce47f0dc1e677a020a4d46252a31a2818fd67f5c256417faefc938af"}, {file = "SQLAlchemy-1.4.27-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78943451ab3ffd0e27876f9cea2b883317518b418f06b90dadf19394534637e9"},
{file = "SQLAlchemy-1.4.23-cp38-cp38-win32.whl", hash = "sha256:bd41f8063a9cd11b76d6d7d6af8139ab3c087f5dbbe5a50c02cb8ece7da34d67"}, {file = "SQLAlchemy-1.4.27-cp37-cp37m-win32.whl", hash = "sha256:a81e40dfa50ed3c472494adadba097640bfcf43db160ed783132045eb2093cb1"},
{file = "SQLAlchemy-1.4.23-cp38-cp38-win_amd64.whl", hash = "sha256:2bca9a6e30ee425cc321d988a152a5fe1be519648e7541ac45c36cd4f569421f"}, {file = "SQLAlchemy-1.4.27-cp37-cp37m-win_amd64.whl", hash = "sha256:015511c52c650eebf1059ed8a21674d9d4ae567ebfd80fc73f8252faccd71864"},
{file = "SQLAlchemy-1.4.23-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:4803a481d4c14ce6ad53dc35458c57821863e9a079695c27603d38355e61fb7f"}, {file = "SQLAlchemy-1.4.27-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:cc49fb8ff103900c20e4a9c53766c82a7ebbc183377fb357a8298bad216e9cdd"},
{file = "SQLAlchemy-1.4.23-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07b9099a95dd2b2620498544300eda590741ac54915c6b20809b6de7e3c58090"}, {file = "SQLAlchemy-1.4.27-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9369f927f4d19b58322cfea8a51710a3f7c47a0e7f3398d94a4632760ecd74f6"},
{file = "SQLAlchemy-1.4.23-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:37f2bd1b8e32c5999280f846701712347fc0ee7370e016ede2283c71712e127a"}, {file = "SQLAlchemy-1.4.27-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6510f4a5029643301bdfe56b61e806093af2101d347d485c42a5535847d2c699"},
{file = "SQLAlchemy-1.4.23-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:448612570aa1437a5d1b94ada161805778fe80aba5b9a08a403e8ae4e071ded6"}, {file = "SQLAlchemy-1.4.27-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:771eca9872b47a629010665ff92de1c248a6979b8d1603daced37773d6f6e365"},
{file = "SQLAlchemy-1.4.23-cp39-cp39-win32.whl", hash = "sha256:e0ce4a2e48fe0a9ea3a5160411a4c5135da5255ed9ac9c15f15f2bcf58c34194"}, {file = "SQLAlchemy-1.4.27-cp38-cp38-win32.whl", hash = "sha256:4d1d707b752137e6bf45720648e1b828d5e4881d690df79cca07f7217ea06365"},
{file = "SQLAlchemy-1.4.23-cp39-cp39-win_amd64.whl", hash = "sha256:0aa746d1173587743960ff17b89b540e313aacfe6c1e9c81aa48393182c36d4f"}, {file = "SQLAlchemy-1.4.27-cp38-cp38-win_amd64.whl", hash = "sha256:c035184af4e58e154b0977eea52131edd096e0754a88f7d5a847e7ccb3510772"},
{file = "SQLAlchemy-1.4.23.tar.gz", hash = "sha256:76ff246881f528089bf19385131b966197bb494653990396d2ce138e2a447583"}, {file = "SQLAlchemy-1.4.27-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:bac949be7579fed824887eed6672f44b7c4318abbfb2004b2c6968818b535a2f"},
{file = "SQLAlchemy-1.4.27-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ac8306e04275d382d6393e557047b0a9d7ddf9f7ca5da9b3edbd9323ea75bd9"},
{file = "SQLAlchemy-1.4.27-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8327e468b1775c0dfabc3d01f39f440585bf4d398508fcbbe2f0d931c502337d"},
{file = "SQLAlchemy-1.4.27-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b02eee1577976acb4053f83d32b7826424f8b9f70809fa756529a52c6537eda4"},
{file = "SQLAlchemy-1.4.27-cp39-cp39-win32.whl", hash = "sha256:5beeff18b4e894f6cb73c8daf2c0d8768844ef40d97032bb187d75b1ec8de24b"},
{file = "SQLAlchemy-1.4.27-cp39-cp39-win_amd64.whl", hash = "sha256:8dbe5f639e6d035778ebf700be6d573f82a13662c3c2c3aa0f1dba303b942806"},
{file = "SQLAlchemy-1.4.27.tar.gz", hash = "sha256:d768359daeb3a86644f3854c6659e4496a3e6bba2b4651ecc87ce7ad415b320c"},
] ]
termcolor = [ termcolor = [
{file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"},

View File

@ -12,6 +12,8 @@ hexbytes = "^0.2.1"
click = "^8.0.1" click = "^8.0.1"
psycopg2 = "^2.9.1" psycopg2 = "^2.9.1"
aiohttp = "^3.8.0" aiohttp = "^3.8.0"
SQLAlchemy = {extras = ["asyncio"], version = "^1.4.27"}
asyncpg = "^0.25.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pre-commit = "^2.13.0" pre-commit = "^2.13.0"