Exemplo n.º 1
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))
Exemplo n.º 2
0
def cli(path):
    path = Path(path)
    addresses: list = []
    if path.is_file():
        _parse_file(path, addresses)
    elif path.is_dir():
        _parse_dir(path, addresses)
    else:
        fatal("can't open path")

    if addresses:
        for a in pydash.uniq(addresses):
            click.echo(a)
    else:
        click.echo("nothing found")
Exemplo n.º 3
0
def sign_txs(config: Config) -> list[SignedTx]:
    result = []

    for acc in config.accounts:
        for tx in acc.txs:
            nonce = _get_nonce(config, acc, tx)
            private_key = _get_private_key(config, acc)
            data = _get_data(config, tx)
            value = tx.value if tx.value is not None else config.value
            gas = (tx.gas if tx.gas is not None else config.gas) or 21_000
            gas_price = tx.gas_price if tx.gas_price else config.gas_price
            if not gas_price:
                return fatal("Set global gas_price or tx.gas_price")

            raw_tx = eth_tx.sign_tx(
                private_key=private_key,
                nonce=nonce,
                to=tx.to,
                data=data,
                value=value,
                gas_price=gas_price,
                gas=gas,
                chain_id=config.chain_id,
            )
            result.append(raw_tx)
    return result
Exemplo n.º 4
0
    def _get_token_info(self, address: str) -> TokenInfo:
        res = eth_erc20.get_symbol(self.config.random_node,
                                   address,
                                   timeout=TIMEOUT)
        if res.is_error():
            return fatal(f"can't get symbol for token {address}: {res.error}")
        symbol = res.ok

        res = eth_erc20.get_decimals(self.config.random_node,
                                     address,
                                     timeout=TIMEOUT)
        if res.is_error():
            return fatal(
                f"can't get decimals for token {address}: {res.error}")
        decimals = res.ok

        return TokenInfo(address=address, symbol=symbol, decimals=decimals)
Exemplo n.º 5
0
def _get_private_key(config: Config, acc: Config.Account) -> str:
    private_keys: dict[str, str] = {}  # address -> private_key
    for private_key in config.private_keys:
        private_keys[eth_account.private_to_address(
            private_key).lower()] = private_key.lower()
    private_key = private_keys.get(acc.from_.lower())
    if not private_key:
        return fatal("There is no private_key for " + acc.from_)
    return private_key
Exemplo n.º 6
0
def _get_data(config: Config, tx: Config.Account.Tx) -> Optional[str]:
    if tx.erc20_transfer:
        if not eth_account.is_address(tx.to):
            return fatal(
                "tx.to must be a valid address for a erc20_transfer tx")
        if not eth_account.is_address(tx.erc20_transfer.recipient):
            return fatal(f"{tx.erc20_transfer.recipient} is not valid address")
        if tx.data is not None:
            return fatal("tx.data must be null for a erc_transfer tx")

        if tx.erc20_transfer.value.isdigit():
            value = int(tx.erc20_transfer.value)
        else:
            token_address = cast(str, tx.to)
            token_address = token_address.lower()
            token_info = pydash.find(_token_infos,
                                     lambda t: t.address == token_address)
            if not token_info:
                res = eth_erc20.get_symbol(config.random_node, token_address)
                if res.is_error():
                    return fatal(
                        f"can't get symbol for token {token_address}: {res.error}"
                    )
                symbol = res.ok
                res = eth_erc20.get_decimals(config.random_node, token_address)
                if res.is_error():
                    return fatal(
                        f"can't get decimals for token {tx.to}: {res.error}")
                decimals = res.ok
                token_info = TokenInfo(symbol=symbol,
                                       decimal=decimals,
                                       address=token_address)
                _token_infos.append(token_info)

            try:
                value = eth_utils.to_wei_token(tx.erc20_transfer.value,
                                               token_info.symbol,
                                               token_info.decimal)
            except ValueError as e:
                return fatal(
                    f"can't parse token value: '{tx.erc20_transfer.value}', symbol: {token_info.symbol}, decimals: {token_info.decimal}, error: {str(e)}"  # noqa
                )

        return eth_erc20.encode_transfer_input_data(
            tx.erc20_transfer.recipient, value)
    return tx.data
Exemplo n.º 7
0
def _get_gas_price(config: Config, tx: Config.Account.Tx) -> int:
    gas_price = tx.gas_price if tx.gas_price else config.gas_price
    if not gas_price:
        return fatal("Use global gas_price or for each tx")
    return gas_price