2021-07-15 10:39:32 -04:00

107 lines
3.2 KiB
Python

import json
from pathlib import Path
from typing import List
from web3 import Web3
from mev_inspect.schemas import Block, BlockCall, BlockCallType
cache_directory = './cache'
## Creates a block object, either from the cache or from the chain itself
## Note that you need to pass in the provider, not the web3 wrapped provider object!
## This is because only the provider allows you to make json rpc requests
def createFromBlockNumber(block_number: int, base_provider) -> Block:
cache_path = _get_cache_path(block_number)
if (cache_path.is_file()):
print(
f'Cache for block {block_number} exists, ' \
'loading data from cache'
)
return Block.parse_file(cache_path)
else:
print(
f"Cache for block {block_number} did not exist, getting data"
)
w3 = Web3(base_provider)
block = fetch_block(w3, base_provider, block_number)
cache_block(cache_path, block)
return block
def fetch_block(w3, base_provider, block_number: int) -> Block:
## Get block data
block_data = w3.eth.get_block(block_number, True)
## Get the block receipts
## TODO: evaluate whether or not this is sufficient or if gas used needs to be converted to a proper big number.
## In inspect-ts it needed to be converted
block_receipts_raw = base_provider.make_request("eth_getBlockReceipts", [block_number])
## Trace the whole block, return those calls
block_calls_json = w3.parity.trace_block(block_number)
block_calls = [
BlockCall(**call_json)
for call_json in block_calls_json
]
## Get the logs
block_hash = (block_data.hash).hex()
block_logs = w3.eth.get_logs({'blockHash': block_hash})
## Get gas used by individual txs and store them too
txs_gas_data = {}
for transaction in block_data['transactions']:
tx_hash = (transaction.hash).hex()
tx_data = w3.eth.get_transaction(tx_hash)
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
txs_gas_data[tx_hash] = {
'gasUsed': tx_receipt['gasUsed'], # fix: why does this return 0 for certain txs?
'gasPrice': tx_data['gasPrice'],
'netFeePaid': tx_data['gasPrice'] * tx_receipt['gasUsed']
}
transaction_hashes = get_transaction_hashes(block_calls)
## Create a new object
return Block(
block_number=block_number,
data=block_data,
receipts=block_receipts_raw,
calls=block_calls,
logs=block_logs,
transaction_hashes=transaction_hashes,
txs_gas_data=txs_gas_data,
)
def get_transaction_hashes(calls: List[BlockCall]) -> List[str]:
result = []
for call in calls:
if call.type != BlockCallType.reward:
if call.transaction_hash not in result:
result.append(call.transaction_hash)
return result
def cache_block(cache_path: Path, block: Block):
write_mode = "w" if cache_path.is_file() else "x"
with open(cache_path, mode=write_mode) as cache_file:
cache_file.write(block.json())
def _get_cache_path(block_number: int) -> Path:
cache_directory_path = Path(cache_directory)
return cache_directory_path / f"{block_number}-new.json"