# standard imports import os import logging import uuid import random import sys # external imports from chainlib.chain import ChainSpec from chainlib.eth.constant import ZERO_ADDRESS from chainlib.eth.gas import ( balance, Gas, ) from hexathon import ( add_0x, strip_0x, ) from chainlib.eth.connection import EthHTTPSignerConnection from funga.eth.signer import EIP155Signer from funga.eth.keystore.sql import SQLKeystore from chainlib.cli.wallet import Wallet from chainlib.eth.address import AddressChecksum from chainlib.eth.nonce import RPCNonceOracle from chainlib.eth.gas import OverrideGasOracle from chainlib.eth.address import ( is_checksum_address, to_checksum_address, ) from chainlib.eth.tx import ( TxFormat, ) import chainlib.eth.cli script_dir = os.path.dirname(os.path.realpath(__file__)) config_dir = os.path.join(script_dir, '..', 'cic_signer', 'data', 'config') logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.WALLET argparser = chainlib.eth.cli.ArgumentParser(arg_flags) args = argparser.parse_args() config = chainlib.eth.cli.Config.from_args(args, arg_flags, base_config_dir=config_dir) # set up rpc chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC')) # connect to database dsn = 'postgresql://{}:{}@{}:{}/{}'.format( config.get('DATABASE_USER'), config.get('DATABASE_PASSWORD'), config.get('DATABASE_HOST'), config.get('DATABASE_PORT'), config.get('DATABASE_NAME'), ) logg.info('using dsn {}'.format(dsn)) keystore = SQLKeystore(dsn, symmetric_key=bytes.fromhex(config.get('SIGNER_SECRET'))) wallet = Wallet(EIP155Signer, keystore=keystore, checksummer=AddressChecksum) rpc = chainlib.eth.cli.Rpc(wallet=wallet) conn = rpc.connect_by_config(config) wallet.init() def main(): if config.get('_RECIPIENT') == None: sys.stderr.write('Missing sink address\n') sys.exit(1) sink_address = config.get('_RECIPIENT') if config.get('_UNSAFE'): sink_address = to_checksum_address(sink_address) if not is_checksum_address(sink_address): sys.stderr.write('Invalid sink address {}\n'.format(sink_address)) sys.exit(1) if (config.get('_RPC_SEND')): verify_string = random.randbytes(4).hex() verify_string_check = input("\033[;31m*** WARNING! WARNING! WARNING! ***\033[;39m\nThis action will transfer all remaining gas from all accounts in custodial care to account {}. To confirm, please enter the string: {}\n".format(config.get('_RECIPIENT'), verify_string)) if verify_string != verify_string_check: sys.stderr.write('Verify string mismatch. Aborting!\n') sys.exit(1) signer = rpc.get_signer() gas_oracle = rpc.get_gas_oracle() gas_pair = gas_oracle.get_fee() gas_price = gas_pair[0] gas_limit = 21000 gas_cost = gas_price * gas_limit gas_oracle = OverrideGasOracle(price=gas_price, limit=gas_limit) logg.info('using gas price {}'.format(gas_price)) for r in keystore.list(): account = r[0] o = balance(add_0x(account)) r = conn.do(o) account_balance = 0 try: r = strip_0x(r) account_balance = int(r, 16) except ValueError: account_balance = int(r) transfer_amount = account_balance - gas_cost if transfer_amount <= 0: logg.warning('address {} has balance {} which is less than gas cost {}, skipping'.format(account, account_balance, gas_cost)) continue nonce_oracle = RPCNonceOracle(account, conn) c = Gas(chain_spec, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, signer=signer) tx_hash_hex = None if (config.get('_RPC_SEND')): (tx_hash_hex, o) = c.create(account, config.get('_RECIPIENT'), transfer_amount) r = conn.do(o) else: (tx_hash_hex, o) = c.create(account, config.get('_RECIPIENT'), transfer_amount, tx_format=TxFormat.RLP_SIGNED) logg.info('address {} balance {} net transfer {} tx {}'.format(account, account_balance, transfer_amount, tx_hash_hex)) if __name__ == '__main__': main()