Ejemplo n.º 1
0
    def create(self, password='', restore_sentence=None):
        """
        Creates new wallet that means private key with an attached address using os.urandom CSPRNG
        :param password: Is used as extra randomness to whatever randomness your OS can provide
        :param restore_sentence: Used in case of restoring wallet from mnemonic sentence.
        :return: object with private key
        """
        extra_entropy = password

        mnemonic = Mnemonic("english")
        if restore_sentence is None:
            self.mnemonic_sentence = mnemonic.generate()
        else:
            self.mnemonic_sentence = restore_sentence

        seed = mnemonic.to_seed(self.mnemonic_sentence, extra_entropy)
        master_private_key = seed[32:]

        # self.account = Account.create(extra_entropy)
        self.account = self.set_account(master_private_key)

        # update config address
        self.conf.update_eth_address(self.account.address)
        # update config public key
        priv_key = keys.PrivateKey(self.account.privateKey)
        pub_key = priv_key.public_key
        self.conf.update_public_key(pub_key.to_hex())

        self.w3 = Infura().get_web3()
        return self
Ejemplo n.º 2
0
 def get_balance(self, address):
     """
     Read balance from the Ethereum network in ether
     :return: number of ether on users account
     """
     self.w3 = Infura().get_web3()
     eth_balance = self.w3.fromWei(self.w3.eth.getBalance(address), 'ether')
     return eth_balance
Ejemplo n.º 3
0
    def __init__(self, configuration, address):
        """
        Constructor
        :param address: contract address or ESN name
        :type address: string
        """
        self.conf = configuration
        self.address = address

        self.w3 = Infura().get_web3()
        self.contract = self.w3.eth.contract(address=address, abi=get_abi_json())
        self.contract_decimals = self.contract.functions.decimals().call()
Ejemplo n.º 4
0
    def send_transaction(
        configuration,
        keystore_password,
        to_address,
        value,
        token_symbol=None,
        gas_price_speed=20  # MetaMask default transaction speedup is gasPrice*10
    ):
        """
        Sign and send transaction
        :param configuration: loaded configuration file instance
        :param keystore_password: password from encrypted keystore with private key for transaction sign
        :param to_address: address in hex string where originator's funds will be sent
        :param value: amount of funds to send in ETH or token defined in token_symbol
        :param token_symbol: None for ETH, ERC20 symbol for other tokens transaction
        :param gas_price_speed: gas price will be multiplied with this number to speed up transaction
        :return: tuple of transaction hash and transaction cost
        """
        # my MetaMask address: 0xAAD533eb7Fe7F2657960AC7703F87E10c73ae73b
        wallet = Wallet(configuration).load_keystore(keystore_password)
        w3 = Infura().get_web3()
        transaction = Transaction(account=wallet.get_account(), w3=w3)

        # check if value to send is possible to convert to the number
        try:
            float(value)
        except ValueError:
            raise InvalidValueException()

        if token_symbol is None:  # create ETH transaction dictionary
            tx_dict = transaction.build_transaction(
                to_address=to_address,
                value=Web3.toWei(value, "ether"),
                gas=
                21000,  # fixed gasLimit to transfer ether from one EOA to another EOA (doesn't include contracts)
                gas_price=w3.eth.gasPrice * gas_price_speed,
                # be careful about sending more transactions in row, nonce will be duplicated
                nonce=w3.eth.getTransactionCount(wallet.get_address()),
                chain_id=configuration.network)
        else:  # create ERC20 contract transaction dictionary
            try:  # check if token is added to the wallet
                contract_address = configuration.contracts[token_symbol]
            except KeyError:
                raise ERC20NotExistsException()
            contract = Contract(configuration, contract_address)
            erc20_decimals = contract.get_decimals()
            token_amount = int(float(value) * (10**erc20_decimals))
            data_for_contract = Transaction.get_tx_erc20_data_field(
                to_address, token_amount)

            # check whether there is sufficient ERC20 token balance
            erc20_balance, _ = WalletAPI.get_balance(configuration,
                                                     token_symbol)
            if float(value) > erc20_balance:
                raise InsufficientERC20FundsException()

            # calculate how much gas I need, unused gas is returned to the wallet
            estimated_gas = w3.eth.estimateGas({
                'to': contract_address,
                'from': wallet.get_address(),
                'data': data_for_contract
            })

            tx_dict = transaction.build_transaction(
                to_address=
                contract_address,  # receiver address is defined in data field for this contract
                value=
                0,  # amount of tokens to send is defined in data field for contract
                gas=estimated_gas,
                gas_price=w3.eth.gasPrice * gas_price_speed,
                # be careful about sending more transactions in row, nonce will be duplicated
                nonce=w3.eth.getTransactionCount(wallet.get_address()),
                chain_id=configuration.network,
                data=data_for_contract)

        # check whether to address is valid checksum address
        if not Web3.isChecksumAddress(to_address):
            raise InvalidAddress()

        # check whether there is sufficient eth balance for this transaction
        balance, _ = WalletAPI.get_balance(configuration)
        transaction_const_wei = tx_dict['gas'] * tx_dict['gasPrice']
        transaction_const_eth = w3.fromWei(transaction_const_wei, 'ether')
        if token_symbol is None:
            if (transaction_const_eth + Decimal(value)) > balance:
                raise InsufficientFundsException()
        else:
            if transaction_const_eth > balance:
                raise InsufficientFundsException()

        # send transaction
        tx_hash = transaction.send_transaction(tx_dict)

        print('Pending', end='', flush=True)
        while True:
            tx_receipt = w3.eth.getTransactionReceipt(tx_hash)
            if tx_receipt is None:
                print('.', end='', flush=True)
                time.sleep(1)
            else:
                print('\nTransaction mined!')
                break

        return tx_hash, transaction_const_eth
Ejemplo n.º 5
0
class Wallet:
    """
    Class defining the main wallet account
    """
    def __init__(self, configuration):
        self.conf = configuration
        self.account = None
        self.w3 = None
        self.mnemonic_sentence = None

    def create(self, password='', restore_sentence=None):
        """
        Creates new wallet that means private key with an attached address using os.urandom CSPRNG
        :param password: Is used as extra randomness to whatever randomness your OS can provide
        :param restore_sentence: Used in case of restoring wallet from mnemonic sentence.
        :return: object with private key
        """
        extra_entropy = password

        mnemonic = Mnemonic("english")
        if restore_sentence is None:
            self.mnemonic_sentence = mnemonic.generate()
        else:
            self.mnemonic_sentence = restore_sentence

        seed = mnemonic.to_seed(self.mnemonic_sentence, extra_entropy)
        master_private_key = seed[32:]

        # self.account = Account.create(extra_entropy)
        self.account = self.set_account(master_private_key)

        # update config address
        self.conf.update_eth_address(self.account.address)
        # update config public key
        priv_key = keys.PrivateKey(self.account.privateKey)
        pub_key = priv_key.public_key
        self.conf.update_public_key(pub_key.to_hex())

        self.w3 = Infura().get_web3()
        return self

    def restore(self, mnemonic_sentence, password):
        """
        Recreates wallet from mnemonic sentence
        :param mnemonic_sentence: remembered user mnemonic sentence
        :type mnemonic_sentence: str
        :param password: password from keystore which is used as entropy too
        :return: wallet
        """
        return self.create(password, mnemonic_sentence)

    def get_account(self):
        """
        Returns account
        :return: account object
        """
        return self.account

    def set_account(self, private_key):
        """
        Creates new account from private key with appropriate address
        :param private_key: in format hex str/bytes/int/eth_keys.datatypes.PrivateKey
        :return: currently created account
        """
        self.account = Account.privateKeyToAccount(private_key)
        return self.account

    def save_keystore(self, password):
        """
        Encrypts and save keystore to path
        :param password: user password from keystore
        :return: path
        """
        create_directory(self.conf.keystore_location)
        keystore_path = self.conf.keystore_location + self.conf.keystore_filename
        encrypted_private_key = Account.encrypt(self.account.privateKey,
                                                password)
        with open(keystore_path, 'w+') as outfile:
            json.dump(encrypted_private_key, outfile, ensure_ascii=False)
        return keystore_path

    def load_keystore(self, password):
        """
        Loads wallet account from decrypted keystore
        :param password: user password from keystore
        :return: instance of this class
        """
        keystore_path = self.conf.keystore_location + self.conf.keystore_filename
        with open(keystore_path) as keystore:
            keyfile_json = json.load(keystore)

        try:
            private_key = Account.decrypt(keyfile_json, password)
        except ValueError:
            raise InvalidPasswordException()

        self.set_account(private_key)
        return self

    def get_mnemonic(self):
        """
        Returns BIP39 mnemonic sentence
        :return: mnemonic words
        """
        return self.mnemonic_sentence

    def get_private_key(self):
        """
        Returns wallet private key
        :return: private key
        """
        return self.account.privateKey  # to print private key in hex use account.privateKey.hex() function

    def get_public_key(self):
        """
        Returns wallet public key
        :return: public key
        """
        return self.conf.public_key

    def get_address(self):
        """
        Returns wallet address
        :return: address
        """
        return self.conf.eth_address

    def get_balance(self, address):
        """
        Read balance from the Ethereum network in ether
        :return: number of ether on users account
        """
        self.w3 = Infura().get_web3()
        eth_balance = self.w3.fromWei(self.w3.eth.getBalance(address), 'ether')
        return eth_balance