def validate_address(value): """ Helper function for validating an address """ if not isinstance(value, str): raise TypeError( 'Address {} must be provided as a string'.format(value)) if not is_hex_address(value): raise InvalidAddress( "Address must be 20 bytes, as a hex string with a 0x prefix", value) if not is_checksum_address(value): if value == value.lower(): raise InvalidAddress( "Web3.py only accepts checksum addresses. " "The software that gave you this non-checksum address should be considered unsafe, " "please file it as a bug on their platform. " "Try using an ENS name instead. Or, if you must accept lower safety, " "use Web3.toChecksumAddress(lower_case_address).", value, ) else: raise InvalidAddress( "Address has an invalid EIP-55 checksum. " "After looking up the address from the original source, try again.", value, )
def _validate_set_ens(self) -> None: if not self.web3: raise InvalidAddress( "Could not look up ENS address because no web3 " "connection available") elif not self.web3.ens: raise InvalidAddress( "Could not look up ENS address because web3.ens is " "set to None")
def abi_ens_resolver(w3, abi_type, val): if abi_type == 'address' and is_ens_name(val): if w3 is None: raise InvalidAddress("Could not look up name, because no web3 connection available") elif w3.ens is None: raise InvalidAddress("Could not look up name, because ENS is set to None") else: return (abi_type, validate_name_has_address(w3.ens, val)) else: return (abi_type, val)
def validate_address(value): """ Helper function for validating an address """ if not isinstance(value, str): raise TypeError( 'Address {} must be provided as a string'.format(value)) if not is_hex_address(value): raise InvalidAddress( "Address must be 20 bytes, as a hex string with a 0x prefix", value) if not is_checksum_address(value): raise InvalidAddress("Address has an invalid EIP checksum", value)
def abi_ens_resolver(w3, type_str, val): if type_str == 'address' and is_ens_name(val): if w3 is None: raise InvalidAddress("Could not look up name %r because no web3" " connection available" % (val)) elif w3.ens is None: raise InvalidAddress("Could not look up name %r because ENS is" " set to None" % (val)) elif int(w3.net.version) != 1 and not isinstance(w3.ens, StaticENS): raise InvalidAddress("Could not look up name %r because web3 is" " not connected to mainnet" % (val)) else: return type_str, validate_name_has_address(w3.ens, val) else: return type_str, val
def __to_checksum_address(address, web3): if eth_utils.address.is_address(address): if eth_utils.address.is_checksum_address(address): return address else: return eth_utils.address.to_checksum_address(address) elif is_ens_name(address): return __resolve_ens_name_to_address(address, web3=web3) else: raise InvalidAddress("%a is not valid address or ENS name" % address)
def abi_ens_resolver(w3: "Web3", type_str: TypeStr, val: Any) -> Tuple[TypeStr, Any]: if type_str == 'address' and is_ens_name(val): if w3 is None: raise InvalidAddress( f"Could not look up name {val!r} because no web3" " connection available") elif w3.ens is None: raise InvalidAddress( f"Could not look up name {val!r} because ENS is" " set to None") elif int(w3.net.version) != 1 and not isinstance(w3.ens, StaticENS): raise InvalidAddress( f"Could not look up name {val!r} because web3 is" " not connected to mainnet") else: return type_str, validate_name_has_address(w3.ens, val) else: return type_str, val
def validate_address(value): """ Helper function for validating an address """ if not isinstance(value, str): raise TypeError('Address {} must be provided as a string'.format(value)) if not is_hex_address(value): raise InvalidAddress("Address must be 20 bytes, as a hex string with a 0x prefix", value) if not is_checksum_address(value): # Shamelessly ignore EIP checksum validation # https://github.com/ethereum/web3.py/issues/674 # raise InvalidAddress("Address has an invalid EIP checksum", value) pass
def __to_checksum_address(address, web3): if eth_utils.address.is_address(address): if eth_utils.address.is_checksum_address(address): return address else: return eth_utils.address.to_checksum_address(address) elif (re.match("^4[a-zA-Z0-9]+$", address) is not None) and (len(address) == 48): return address elif is_ens_name(address): return __resolve_ens_name_to_address(address, web3=web3) else: raise InvalidAddress("%a is not valid address or ENS name" % address)
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