def dlogs_page(request: Request): category_stats = {} for category in app.dlog_collection.collection.distinct("category"): category_stats[category] = app.dlog_collection.count(md(category)) form = DLogsFilterForm(request.query_params) form.category.choices = form_choices(list(category_stats.keys()), title="category") query = make_query(category=form.data["category"]) dlogs = app.dlog_collection.find(query, "-created_at", form.data["limit"]) return templates.render("dlogs.j2", md(dlogs, form, category_stats))
def cli(ctx, config_path, broadcast: bool): config = parse_config(ctx, config_path, Config) # address -> private_key accounts = { eth_account.private_to_address(private_key): private_key for private_key in config.private_keys } tx_fee = 21_000 * config.gas_price low_eth_balance: list[str] = [] signed_tx: list[SignedTx] = [] for address, private_key in accounts.items(): res = eth_rpc.eth_get_balance(config.random_node, address) if res.is_error(): return fatal(f"can't get balance: {res.error}") balance = res.ok if balance <= tx_fee: low_eth_balance.append(address) else: res = eth_rpc.eth_get_transaction_count(config.random_node, address) if res.is_error(): return fatal(f"can't get nonce: {res.error}") nonce = res.ok value = balance - tx_fee tx = eth_tx.sign_tx( nonce=nonce, gas_price=config.gas_price, gas=21_000, private_key=private_key, chain_id=config.chain_id, value=value, to=config.recipient, ) signed_tx.append(tx) if broadcast: result = [] for tx in signed_tx: res = eth_rpc.eth_send_raw_transaction(config.random_node, tx.raw_tx) result.append(res.ok_or_error) print_json(md(low_eth_balance, result)) else: txs = [] for tx in signed_tx: decoded = eth_tx.decode_raw_tx(tx.raw_tx) decoded = eth_utils.to_human_readable_tx(decoded) # type:ignore txs.append(tx.dict() | md(decoded)) print_json(md(low_eth_balance, signed_tx=txs))
def test_md(): a = "bla" b = 1 res = md(a, b) # print("ss", res) assert res == {"a": a, "b": b} d1, d2 = md(b, a, b), md(b, a, b) assert d1 == d2 res = md(a, b, c=777) assert res == {"a": a, "b": b, "c": 777}
def workers_page(request: Request): form = WorkersFilterForm(request.query_params) query = make_query(started=form.data["started"], name=form.data["name"]) workers = core.db.worker.find(query, "-created_at", form.data["limit"]) return templates.TemplateResponse("workers.j2", md(request, form, workers))
def get_block(node: str, block_number: int, timeout=10, proxy=None) -> Result[Block]: res = eth_rpc.eth_get_block_by_number(node, block_number, True, timeout=timeout, proxy=proxy) if res.is_error(): return res if not res.ok or "timestamp" not in res.ok or "transactions" not in res.ok: return res.new_error( "unknown_response: there is no `timestamp` or `transactions` in response" ) transactions = [] for tx in res.ok["transactions"]: tx_hash = tx["hash"] from_address = tx["from"].lower() to_address = tx.get("to", "") value = int(tx["value"], 16) input_data = tx["input"] gas = int(tx["gas"], 16) gas_price = int(tx["gasPrice"], 16) transactions.append( Block.Transaction(**md(tx_hash, from_address, to_address, value, input_data, gas, gas_price)), ) block_time = _hex_to_datetime(res.ok["timestamp"]) return res.new_ok(Block(block_number, block_time, transactions))
def get_address_info(address: str, timeout=10, proxy=None) -> Result[EtherscanInfo]: url = "https://etherscan.io/address/" + address res = hrequest(url, proxy=proxy, user_agent=FIREFOX_USER_AGENT, timeout=timeout) if res.is_error(): return res.to_error() if "maximum limit reached for this request" in res.body: return res.to_error("request_throttled") try: soap = BeautifulSoup(res.body, "html.parser") if soap.select_one("#mainaddress"): name = "" tags = [] site = "" tokens_usd = Decimal() # name name_el = soap.select_one( "span[title='Public Name Tag (viewable by anyone)']") if name_el: name = name_el.text.strip() # tags for link in soap.select("#content a.u-label"): if link.get("href") and link["href"].startswith( "/accounts/label/"): tags.append(link["href"].replace("/accounts/label/", "")) # token_usd tokens_usd_el = soap.select_one( "#ContentPlaceHolder1_tokenbalance #availableBalanceDropdown") if tokens_usd_el: m = re.search(r"\$([\d.,]+)", tokens_usd_el.text) if m: value = m.group(1).replace(",", "").replace("...", "").strip() tokens_usd = Decimal(value) # site site_el = soap.select_one("a[title='External Site - More Info']") if site_el: site = site_el.get("href", "") token = None if soap.select_one("a[title='View Token Tracker Page']"): token = get_etherscan_token(address, timeout, proxy).ok return res.to_ok( EtherscanInfo(**md(name, tags, site, tokens_usd, token))) return res.to_error("unknown_response") except Exception as e: return res.to_error(f"exception: {str(e)}")
def decode_raw_tx(raw_tx: str) -> DecodedRawTx: tx: Any = rlp.decode(hex_to_bytes(raw_tx), RPLTransaction) tx_hash = Web3.toHex(keccak(hex_to_bytes(raw_tx))) from_ = w3.eth.account.recover_transaction(raw_tx) to = w3.toChecksumAddress(tx.to) if tx.to else None data = w3.toHex(tx.data) r = hex(tx.r) s = hex(tx.s) chain_id = (tx.v - 35) // 2 if tx.v % 2 else (tx.v - 36) // 2 return DecodedRawTx(**md(tx_hash, from_, to, data, chain_id, r, s, tx.v, tx.gas, tx.gas_price, tx.value, tx.nonce))
def get_parity_trace(tx_hash: str, timeout=10, proxy=None) -> Result[ParityTrace]: url = f"https://etherscan.io/vmtrace?txhash={tx_hash}&type=parity#raw" res = hrequest(url, timeout=timeout, proxy=proxy, user_agent=FIREFOX_USER_AGENT) if res.is_error(): return res.to_error() if "maximum limit reached for this request" in res.body: return res.to_error("request_throttled") try: soap = BeautifulSoup(res.body, "html.parser") editor_el = soap.select_one("#editor") if not editor_el: return res.to_error("unknown_response") traces_json = json.loads(f"[{editor_el.text}]") block_number = traces_json[0]["blockNumber"] transaction_position = traces_json[0]["transactionPosition"] traces: list[ParityTrace.Trace] = [] for t in traces_json: call_type = t["action"]["callType"] from_ = t["action"]["from"] to = t["action"].get("to") gas = int(t["action"]["gas"], 16) value = int(t["action"]["value"], 16) input_ = t["action"]["input"] gas_used = int(t["result"]["gasUsed"], 16) output = t["result"]["output"] trace = md(call_type, from_, to, gas, value, gas_used, output, input=input_) traces.append(ParityTrace.Trace(**trace)) parity_trace = ParityTrace(block_number=block_number, transaction_position=transaction_position, traces=traces) return res.to_ok(parity_trace) except Exception as e: return res.to_error(f"exception: {str(e)}")
def encode_raw_tx_with_signature( *, nonce: int, gas_price: int, gas: int, v: int, r: str, s: str, data: Optional[str] = None, value: Optional[int] = None, to: Optional[str] = None, ): if to: to = hex_to_bytes(to) # type:ignore if data: data = hex_to_bytes(data) # type:ignore if not value: value = 0 r = int(r, 16) # type:ignore s = int(s, 16) # type:ignore tx = RPLTransaction.new_tx( **md(nonce, gas_price, gas, data, value, to, v, r, s)) return Web3.toHex(rlp.encode(tx))
def index_page(request: Request): return templates.TemplateResponse("index.j2", md(request))
def create_worker_page(request: Request): form = WorkerCreateForm() return templates.TemplateResponse("create_worker.j2", md(request, form))
def search_block_number_by_time(node: str, search_time: datetime, timeout=10, proxy=None) -> Result[int]: res = eth_rpc.eth_block_number(node, timeout=timeout, proxy=proxy) if res.is_error(): return res current_block_number = res.ok res = eth_rpc.eth_get_block_by_number(node, current_block_number, timeout=timeout, proxy=proxy) if res.is_error(): return res current_block = res.ok current_block_time = _hex_to_datetime(current_block["timestamp"]) res = eth_rpc.eth_get_block_by_number(node, 1, timeout=timeout, proxy=proxy) if res.is_error(): return res first_block = res.ok first_block_time = _hex_to_datetime(first_block["timestamp"]) if search_time < first_block_time: return Result(error="bad_request: before the first block time") if search_time > current_block_time: return Result(error="bad_request: after the current block time") min_block = 1 max_block = current_block_number middle_block_number = 0 counter = 0 while True: counter += 1 if counter > 30: return Result(ok=middle_block_number, data=md(counter)) middle_block_number = math.floor( (max_block - min_block) / 2) + min_block res = eth_rpc.eth_get_block_by_number(node, middle_block_number, timeout=timeout, proxy=proxy) if res.is_error(): return res middle_block = res.ok middle_block_time = _hex_to_datetime(middle_block["timestamp"]) if search_time > middle_block_time: min_block = middle_block_number else: max_block = middle_block_number diff = abs(search_time - middle_block_time) if diff < timedelta(hours=1): return Result(ok=middle_block_number, data={"counter": counter})
def data_page(request: Request): form = DataFilterForm(request.query_params) query = make_query(worker=form.data["worker"], status=form.data["status"]) data = core.db.data.find(query, "-created_at", form.data["limit"]) return templates.TemplateResponse("data.j2", md(request, form, data))
def delete_by_category(category: str): return j(app.dlog_collection.delete_many(md(category)))
def eth_call(node: str, to: str, data: str, timeout=10, proxy=None) -> Result: return rpc_call(node=node, method="eth_call", params=[md(to, data), "latest"], timeout=timeout, proxy=proxy)
def system_page(): stats = app.system_service.get_stats() telegram_is_started = telegram.is_started return templates.render("system.j2", md(stats, telegram_is_started))
def update_dconfig_page(): form = ImportDConfigForm() return templates.render("update_dconfig.j2", md(form))
def update_dvalue_page(key: str): form = UpdateValueForm(yaml_value=app.dvalue_service.get_dvalue_yaml_value(key)) return templates.render("update_dvalue.j2", md(form, key))
def get_etherscan_token(address: str, timeout=10, proxy=None) -> Result[EtherscanToken]: url = f"https://etherscan.io/token/{address}" res = hrequest(url, timeout=timeout, proxy=proxy, user_agent=FIREFOX_USER_AGENT) if res.is_error(): return res.to_error() if "maximum limit reached for this request" in res.body: return res.to_error("request_throttled") try: soap = BeautifulSoup(res.body, "html.parser") if "[ERC-20]" in res.body: erc = "erc20" elif "[ERC-721]" in res.body: erc = "erc721" else: erc = "unknown" symbol = None el = soap.select_one("#ContentPlaceHolder1_hdnSymbol") if el: symbol = el.get("value") total_supply = None el = soap.select_one("#ContentPlaceHolder1_hdnTotalSupply") if el: total_supply = el.get("value") if total_supply: total_supply = Decimal(total_supply.replace(",", "").strip()) decimals = None el = soap.select_one("#ContentPlaceHolder1_trDecimals") if el: decimals = el.text if decimals: decimals = int(decimals.replace("Decimals:", "").strip()) holders_count = None el = soap.select_one("#ContentPlaceHolder1_tr_tokenHolders") if el: holders_count = el.text if holders_count: holders_count = int( holders_count.replace("Holders:", "").replace( "addresses", "").replace("address", "").replace(",", "").strip(), ) price_usd = None el = soap.select_one( "#ContentPlaceHolder1_tr_valuepertoken span.d-block") if el and "@" in el.text: price_usd = Decimal(el.text.split("@")[0].strip().replace("$", "")) market_cap_usd = None el = soap.select_one("#pricebutton") if el and "$" in el.text: market_cap_usd = Decimal(el.text.strip().replace("$", "").replace( ",", "")) site = None el = soap.select_one("#ContentPlaceHolder1_tr_officialsite_1 a") if el: site = el["href"] transfers_count = get_token_transfers_count(address, timeout, proxy).ok info = md(erc, symbol, total_supply, decimals, holders_count, transfers_count, price_usd, market_cap_usd, site) return res.to_ok(EtherscanToken(**info)) except Exception as e: return res.to_error(f"exception: {str(e)}")
def data_page(request: Request): form = DataFilterForm(request.query_params) query = make_query(status=form.data["status"]) data = app.db.data.find(query, "-created_at", form.data["limit"]) return templates.render("data.j2", md(form, data))