Skip to content

Commit

Permalink
implement block query
Browse files Browse the repository at this point in the history
  • Loading branch information
tmcgroul committed Dec 22, 2023
1 parent 232e581 commit fcbc93f
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ repos:
rev: v1.7.1
hooks:
- id: mypy
additional_dependencies: [types-setuptools, pydantic]
additional_dependencies: [types-setuptools, pydantic, types-requests]

- repo: https://github.com/executablebooks/mdformat
rev: 0.7.17
Expand Down
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"python.linting.mypyEnabled": true,
"python.linting.enabled": true
}
97 changes: 97 additions & 0 deletions ape_subsquid/archive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from typing import NotRequired, Optional, TypedDict

from requests import Session


class BlockFieldSelection(TypedDict, total=False):
number: bool
hash: bool
parentHash: bool
timestamp: bool
transactionsRoot: bool
receiptsRoot: bool
stateRoot: bool
logsBloom: bool
sha3Uncles: bool
extraData: bool
miner: bool
nonce: bool
mixHash: bool
size: bool
gasLimit: bool
gasUsed: bool
difficulty: bool
totalDifficulty: bool
baseFeePerGas: bool


class FieldSelection(TypedDict, total=False):
block: BlockFieldSelection
# transaction: TxFieldSelection
# log: LogFieldSelection
# trace: TraceFieldSelection


class Query(TypedDict):
fromBlock: int
toBlock: NotRequired[int]
includeAllBlocks: NotRequired[bool]
fields: NotRequired[FieldSelection]
transactions: NotRequired[list[dict]]
# logs: NotRequired[list[LogRequest]]
# traces: NotRequired[list[TraceRequest]]


class BlockHeader(TypedDict):
number: int
hash: str
parentHash: str
size: int
sha3Uncles: str
miner: str
stateRoot: str
transactionsRoot: str
receiptsRoot: str
logsBloom: str
difficulty: str
totalDifficulty: str
gasLimit: str
gasUsed: str
timestamp: float
extraData: str
mixHash: str
nonce: str
baseFeePerGas: Optional[str]


class Log(TypedDict):
pass


class Transaction(TypedDict):
pass


class Trace(TypedDict):
pass


class Block(TypedDict):
header: BlockHeader
logs: NotRequired[list[Log]]
transactions: NotRequired[list[Transaction]]
traces: NotRequired[list[Trace]]


class Archive:
_session = Session()

def get_worker(self, start_block: int) -> str:
url = f"https://v2.archive.subsquid.io/network/ethereum-mainnet/{start_block}/worker"
response = self._session.get(url)
return response.text

def query(self, query: Query) -> list[Block]:
worker_url = self.get_worker(query["fromBlock"])
response = self._session.post(worker_url, json=query)
return response.json()
57 changes: 55 additions & 2 deletions ape_subsquid/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,30 @@
)
from ape.exceptions import QueryEngineError
from ape.utils import singledispatchmethod
from ape_ethereum import ecosystem

from ape_subsquid.archive import Archive, Block


def map_block(block: Block) -> ecosystem.Block:
return ecosystem.Block(
number=block["header"]["number"],
hash=block["header"]["hash"],
parentHash=block["header"]["parentHash"],
size=block["header"]["size"],
timestamp=int(block["header"]["timestamp"]),
num_transactions=len(block.get("transactions", [])),
gasLimit=block["header"]["gasLimit"],
gasUsed=block["header"]["gasUsed"],
baseFeePerGas=block["header"]["baseFeePerGas"],
difficulty=block["header"]["difficulty"],
totalDifficulty=block["header"]["totalDifficulty"],
)


class SubsquidQueryEngine(QueryAPI):
_archive = Archive()

@singledispatchmethod
def estimate_query(self, query: QueryType) -> Optional[int]:
return None
Expand Down Expand Up @@ -51,8 +72,40 @@ def perform_query(self, query: QueryType) -> Iterator:
)

@perform_query.register
def perform_block_query(self, query: BlockQuery):
return None
def perform_block_query(self, query: BlockQuery) -> Iterator[ecosystem.Block]:
from_block = query.start_block
while True:
data = self._archive.query(
{
"fromBlock": from_block,
"toBlock": query.stop_block,
"fields": {
"block": {
"number": True,
"hash": True,
"parentHash": True,
"size": True,
"timestamp": True,
"gasLimit": True,
"gasUsed": True,
"baseFeePerGas": True,
"difficulty": True,
"totalDifficulty": True,
},
},
"includeAllBlocks": True,
"transactions": [{}],
}
)

for block in data:
yield map_block(block)

last_block = data[-1]
if last_block["header"]["number"] == query.stop_block:
break

from_block = last_block["header"]["number"] + 1

@perform_query.register
def perform_block_transaction_query(self, query: BlockTransactionQuery):
Expand Down

0 comments on commit fcbc93f

Please sign in to comment.