예제 #1
0
 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))
예제 #2
0
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))
예제 #3
0
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}
예제 #4
0
 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))
예제 #5
0
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))
예제 #6
0
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)}")
예제 #7
0
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))
예제 #8
0
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)}")
예제 #9
0
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))
예제 #10
0
 def index_page(request: Request):
     return templates.TemplateResponse("index.j2", md(request))
예제 #11
0
 def create_worker_page(request: Request):
     form = WorkerCreateForm()
     return templates.TemplateResponse("create_worker.j2",
                                       md(request, form))
예제 #12
0
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})
예제 #13
0
 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))
예제 #14
0
 def delete_by_category(category: str):
     return j(app.dlog_collection.delete_many(md(category)))
예제 #15
0
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)
예제 #16
0
 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))
예제 #17
0
 def update_dconfig_page():
     form = ImportDConfigForm()
     return templates.render("update_dconfig.j2", md(form))
예제 #18
0
 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))
예제 #19
0
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)}")
예제 #20
0
 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))