def get_wallet(configuration): """ Get account address and private key from default keystore location :param configuration: loaded configuration file instance :return: account object """ address = Wallet(configuration).get_address() pub_key = Wallet(configuration).get_public_key() return address, pub_key
def new_wallet(configuration, password): """ Create new wallet account and save encrypted keystore :param configuration: loaded configuration file instance :param password: set password from keystore and used also as entropy for CSPRNG :return: created wallet object and saved keystore path """ wallet = Wallet(configuration).create(password) wallet.save_keystore(password) return wallet
def restore_wallet(configuration, mnemonic_sentence, passphrase): """ Create new wallet account and save encrypted keystore :param configuration: loaded configuration file instance :param mnemonic_sentence: user's mnemonic sentence to restore wallet :param password: set password from keystore and used also as entropy for CSPRNG :return: restored wallet object and saved keystore path """ wallet = Wallet(configuration).restore(mnemonic_sentence, passphrase) wallet.save_keystore(passphrase) return wallet
def get_balance(configuration, token_symbol=None): """ Get balance from account address. :param configuration: loaded configuration file instance :param token_symbol: None for ETH, ERC20 symbol for other tokens :return: """ wallet_address = Wallet(configuration).get_address() if token_symbol is None: balance = Wallet(configuration).get_balance(wallet_address) else: contract_address = configuration.contracts[token_symbol] contract = Contract(configuration, contract_address) balance = contract.get_balance(wallet_address) return balance, wallet_address
def get_balance(configuration, token_symbol=None): """ Get balance from account address. :param configuration: loaded configuration file instance :param token_symbol: None for ETH, ERC20 symbol for other tokens :return: """ wallet_address = Wallet(configuration).get_address() if token_symbol is None: balance = Wallet(configuration).get_balance(wallet_address) else: try: # check if token is added to the wallet contract_address = configuration.contracts[token_symbol] except KeyError: raise ERC20NotExistsException() contract = Contract(configuration, contract_address) balance = contract.get_balance(wallet_address) return balance, wallet_address
def get_private_key(configuration, keystore_password): """ Get account private key from default keystore location :param configuration: loaded configuration file instance :param keystore_password: user password from keystore :return: account object """ wallet = Wallet(configuration).load_keystore(keystore_password) return wallet
def test_lengths(configuration): wallet = Wallet(configuration) wallet.create('eXtR4 EntroPy Str1Ng') # check length of private key assert len(wallet.get_private_key()) == 32 # check length of public key public_key_bytes = decode_hex(wallet.get_public_key()) assert len(public_key_bytes) == 64 # check length of address address_bytes = decode_hex(wallet.get_address()) assert len(address_bytes) == 20
def test_from_seed(): wallet = Wallet() wallet.from_seed(seed=SEED) wallet.from_path("m/44'/60'/0'/0/0'") assert wallet.entropy() is None assert wallet.mnemonic() is None assert wallet.language() is None assert wallet.passphrase() is None assert wallet.seed() == "bd421c81fbbb1cea7910851857817ac18f1fce9b9577a3e732ad28ae8ba4e097b072c48e694e5053df5db" \ "8a86d5cbacbbfee02b42d12c554d06313a0dadacf6b" assert wallet.private_key( ) == "656905e908b7349a8a894dda3fe1d1792a5d3484a29175e318caa8bd8dbfb10e" assert wallet.public_key( ) == "03d9ce6ac4d32b2b711016d4eddf3c28c2169e2f5a393a59569f9232ce21ec2fdf" assert wallet.uncompressed() == \ "d9ce6ac4d32b2b711016d4eddf3c28c2169e2f5a393a59569f9232ce21ec2fdf621eb61a050b753b6fb775e4a319aa15682df03d3" \ "93ac3dc798c03f68a977355" assert wallet.compressed( ) == "03d9ce6ac4d32b2b711016d4eddf3c28c2169e2f5a393a59569f9232ce21ec2fdf" assert wallet.wallet_import_format( ) == "Kzcqd9d9jTzbeh7bNSjGkqQhNNFXnPT7gWopsLSuYbP3MEUAwwJC" assert wallet.finger_print() == "f91947eb" assert wallet.chain_code( ) == "d7b70a1271342a03662b93112c388662b7e96714913c358e0dd3db57702259ef" assert wallet.path() == "m/44'/60'/0'/0/0'" assert wallet.address() == "0x9A610e8A2B40d010C9804451742233E182052851" assert wallet.extended_key(private_key=True, encoded=False) == \ "0488ade40589afb94b80000000d7b70a1271342a03662b93112c388662b7e96714913c358e0dd3db57702259ef00656905e908b73" \ "49a8a894dda3fe1d1792a5d3484a29175e318caa8bd8dbfb10e" assert wallet.extended_key(private_key=False, encoded=False) == \ "0488b21e0589afb94b80000000d7b70a1271342a03662b93112c388662b7e96714913c358e0dd3db57702259ef03d9ce6ac4d32b2" \ "b711016d4eddf3c28c2169e2f5a393a59569f9232ce21ec2fdf" assert wallet.extended_key(private_key=True, encoded=True) == \ "xprvA3SPJ3k2vNjT7Fa3V6dg8vEieXud4ARAhLwFd4GM6i4TxsjADB4wrm2AzhcYjc5nSEkXB9EX4kuGeZS4v9Ut2PNpZdAR9zKvqiBFA" \ "sqGrqv" assert wallet.extended_key(private_key=False, encoded=True) == \ "xpub6GRjhZGvkkHkKjeWb8AgW4BTCZk7Td924ZrrRSfxf3bSqg4JkiPCQZLer1UKXzncTRrVvy5Lpc9SQNjJPqjoiMLRjqzPnYiAnbXx2" \ "12kVLC" assert wallet.dumps() == { "entropy": None, "mnemonic": None, "language": None, "passphrase": None, "seed": "bd421c81fbbb1cea7910851857817ac18f1fce9b9577a3e732ad28ae8ba4e097b072c48e694e5053df5db8a86d5cbacbbfee02b42d12c554d06313a0dadacf6b", "root_private_key": "5464f216361795edff8cdde60381fb52d9dc2b67a5fa1307caadb5ef5a041df192d4e95bbb37efcfad579f3319ab685b4aae0a448eddde606e6d3b0f0c6d5f57", "root_public_key": "8458cbbf8183c95f480b5b2d06f7faa9993e946e2bb9e55f9d7a033bbc8bcbeaeae6bab68b074725ca4efdcbc0986de0384f192cae4c6184fe1f3c4523252824", "uncompressed": "d9ce6ac4d32b2b711016d4eddf3c28c2169e2f5a393a59569f9232ce21ec2fdf621eb61a050b753b6fb775e4a319aa15682df03d393ac3dc798c03f68a977355", "compressed": "03d9ce6ac4d32b2b711016d4eddf3c28c2169e2f5a393a59569f9232ce21ec2fdf", "chain_code": "d7b70a1271342a03662b93112c388662b7e96714913c358e0dd3db57702259ef", "private_key": "656905e908b7349a8a894dda3fe1d1792a5d3484a29175e318caa8bd8dbfb10e", "public_key": "03d9ce6ac4d32b2b711016d4eddf3c28c2169e2f5a393a59569f9232ce21ec2fdf", "wif": "Kzcqd9d9jTzbeh7bNSjGkqQhNNFXnPT7gWopsLSuYbP3MEUAwwJC", "finger_print": "f91947eb", "path": "m/44'/60'/0'/0/0'", "address": "0x9A610e8A2B40d010C9804451742233E182052851", "serialized": { "private_key_hex": "0488ade40589afb94b80000000d7b70a1271342a03662b93112c388662b7e96714913c358e0dd3db57702259ef00656905e908b7349a8a894dda3fe1d1792a5d3484a29175e318caa8bd8dbfb10e", "public_key_hex": "0488b21e0589afb94b80000000d7b70a1271342a03662b93112c388662b7e96714913c358e0dd3db57702259ef03d9ce6ac4d32b2b711016d4eddf3c28c2169e2f5a393a59569f9232ce21ec2fdf", "private_key_base58": "xprvA3SPJ3k2vNjT7Fa3V6dg8vEieXud4ARAhLwFd4GM6i4TxsjADB4wrm2AzhcYjc5nSEkXB9EX4kuGeZS4v9Ut2PNpZdAR9zKvqiBFAsqGrqv", "public_key_base58": "xpub6GRjhZGvkkHkKjeWb8AgW4BTCZk7Td924ZrrRSfxf3bSqg4JkiPCQZLer1UKXzncTRrVvy5Lpc9SQNjJPqjoiMLRjqzPnYiAnbXx212kVLC" } } with pytest.raises( ValueError, match="Bad path, please insert like this type of path \"m/0'/0\"! " ): assert wallet.from_path("meherett") with pytest.raises(ValueError, match="Bad index, Please import only integer number!"): assert wallet.from_index("meherett")
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
def test_addresses(configuration): wallet = Wallet(configuration) wallet.create('eXtR4 EntroPy Str1Ng') assert Web3.isAddress(wallet.get_address()) assert Web3.isChecksumAddress(wallet.get_address())
def test_from_private_key(): wallet = Wallet() wallet.from_root_private_key(root_private_key=ROOT_PRIVATE_KEY) wallet.from_index(44, harden=True) wallet.from_index(60, harden=True) wallet.from_index(0, harden=True) wallet.from_index(0) wallet.from_index(0, harden=True) assert wallet.entropy() is None assert wallet.mnemonic() is None assert wallet.language() is None assert wallet.passphrase() is None assert wallet.seed() is None assert wallet.private_key() == "678d12447649cc9eeede87562069c2ff1524f31df025f521be255d4a6c8eaed0" assert wallet.public_key() == "03df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f" assert wallet.uncompressed() == \ "df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f1e4f78a8cae1daf5dd1a716a96475b16" \ "dcfb455e7d97fe75dd8f1f8ea5cc0a41" assert wallet.compressed() == "03df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f" assert wallet.wallet_import_format() == "KzgzzJkyzqcy8bLJDPLwXV5VmJER3f35q9zyAgSdMLn6aqMovQ66" assert wallet.finger_print() == "2004e94c" assert wallet.chain_code() == "67a537696eecbfdd5755734eee16dca622e62bb3c98f66db89cad93def287754" assert wallet.path() == "m/44'/60'/0'/0/0'" assert wallet.address() == "0xAaB4E88BCa0d7C1e40CE540b9642558d6f9a3a05" assert wallet.extended_key(private_key=True, encoded=False) assert wallet.extended_key(private_key=False, encoded=False) assert wallet.extended_key(private_key=True, encoded=True) assert wallet.extended_key(private_key=False, encoded=True) assert wallet.dumps() == { "entropy": None, "mnemonic": None, "language": None, "passphrase": None, "seed": None, "root_private_key": "f7239ecece685b42e768a3b3d5f8f67946e6f8e5f008a46fb80d34a69977be9a93d3aaf6c13639763807811805101ccab1ef09774e71afa028847dadfb0232e0", "root_public_key": "222d5011dfc66eabaa4d994ee66754a54eac0cf2633e4c9920abb7ba94ab4b7cb18aed261ccdd9d50e2bd2ca6a776d826201e1b1b0d953fae75f4da1167337e7", "uncompressed": "df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f1e4f78a8cae1daf5dd1a716a96475b16dcfb455e7d97fe75dd8f1f8ea5cc0a41", "compressed": "03df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f", "chain_code": "67a537696eecbfdd5755734eee16dca622e62bb3c98f66db89cad93def287754", "private_key": "678d12447649cc9eeede87562069c2ff1524f31df025f521be255d4a6c8eaed0", "public_key": "03df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f", "wif": "KzgzzJkyzqcy8bLJDPLwXV5VmJER3f35q9zyAgSdMLn6aqMovQ66", "finger_print": "2004e94c", "path": "m/44'/60'/0'/0/0'", "address": "0xAaB4E88BCa0d7C1e40CE540b9642558d6f9a3a05", "serialized": { "private_key_hex": "0488ade405f450d7af8000000067a537696eecbfdd5755734eee16dca622e62bb3c98f66db89cad93def28775400678d12447649cc9eeede87562069c2ff1524f31df025f521be255d4a6c8eaed0", "public_key_hex": "0488b21e05f450d7af8000000067a537696eecbfdd5755734eee16dca622e62bb3c98f66db89cad93def28775403df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f", "private_key_base58": "xprvA4Dqp8w5LcQejoog9rci7sadBJetitNj1wvbG7uHLLkSxoFhkzVnZ7A8Z6Kz4VRMpFSJ2Kct9KFf8uVpuWiqU9vBrkh9w6dVfThmF4RUxHV", "public_key_base58": "xpub6HDCDeTyAyxwxHt9Ft9iV1XMjLVP8M6aPArC4WJttgHRqbarJXp36uUcQQDJwBK3GEhKy7cbV1Fs4UoRDRpbhTFRZmPJ3coUuenN2TARncR" } } assert wallet.clean_derivation() assert wallet.path() is None
def test_from_mnemonic(): wallet = Wallet() wallet.from_mnemonic(mnemonic=MNEMONIC, passphrase=PASSPHRASE, language=LANGUAGE) wallet.from_index(44, harden=True) wallet.from_index(60, harden=True) wallet.from_index(0, harden=True) wallet.from_index(0) wallet.from_index(0, harden=True) assert wallet.entropy() is None assert wallet.mnemonic() == \ "indicate warm sock mistake code spot acid ribbon sing over taxi toast" assert wallet.language() == "english" assert wallet.passphrase() is None assert wallet.seed() == "baff3e1fe60e1f2a2d840d304acc98d1818140c79354a353b400fb019bfb256bc392d7aa9047adff1f14b" \ "ce0342e14605c6743a6c08e02150588375eb2eb7d49" assert wallet.private_key( ) == "678d12447649cc9eeede87562069c2ff1524f31df025f521be255d4a6c8eaed0" assert wallet.public_key( ) == "03df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f" assert wallet.uncompressed() == "df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f1e4f78a8cae1d" \ "af5dd1a716a96475b16dcfb455e7d97fe75dd8f1f8ea5cc0a41" assert wallet.compressed( ) == "03df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f" assert wallet.wallet_import_format( ) == "KzgzzJkyzqcy8bLJDPLwXV5VmJER3f35q9zyAgSdMLn6aqMovQ66" assert wallet.finger_print() == "2004e94c" assert wallet.chain_code( ) == "67a537696eecbfdd5755734eee16dca622e62bb3c98f66db89cad93def287754" assert wallet.path() == "m/44'/60'/0'/0/0'" assert wallet.address() == "0xAaB4E88BCa0d7C1e40CE540b9642558d6f9a3a05" assert wallet.extended_key(private_key=True, encoded=False) == "0488ade405f450d7af8000000067a537696eecbfdd5755" \ "734eee16dca622e62bb3c98f66db89cad93def28775400" \ "678d12447649cc9eeede87562069c2ff1524f31df025f5" \ "21be255d4a6c8eaed0" assert wallet.extended_key(private_key=False, encoded=False) == "0488b21e05f450d7af8000000067a537696eecbfdd575" \ "5734eee16dca622e62bb3c98f66db89cad93def287754" \ "03df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8" \ "074fc2415d78ed1a3e73f" assert wallet.extended_key(private_key=True, encoded=True) == "xprvA4Dqp8w5LcQejoog9rci7sadBJetitNj1wvbG7uHLLk" \ "SxoFhkzVnZ7A8Z6Kz4VRMpFSJ2Kct9KFf8uVpuWiqU9vBrk" \ "h9w6dVfThmF4RUxHV" assert wallet.extended_key(private_key=False, encoded=True) == "xpub6HDCDeTyAyxwxHt9Ft9iV1XMjLVP8M6aPArC4WJttg" \ "HRqbarJXp36uUcQQDJwBK3GEhKy7cbV1Fs4UoRDRpbhTFR" \ "ZmPJ3coUuenN2TARncR" assert wallet.dumps() == { "entropy": None, "mnemonic": "indicate warm sock mistake code spot acid ribbon sing over taxi toast", "language": "english", "passphrase": None, "seed": "baff3e1fe60e1f2a2d840d304acc98d1818140c79354a353b400fb019bfb256bc392d7aa9047adff1f14bce0342e14605c6743a6c08e02150588375eb2eb7d49", "root_private_key": "f7239ecece685b42e768a3b3d5f8f67946e6f8e5f008a46fb80d34a69977be9a93d3aaf6c13639763807811805101ccab1ef09774e71afa028847dadfb0232e0", "root_public_key": "222d5011dfc66eabaa4d994ee66754a54eac0cf2633e4c9920abb7ba94ab4b7cb18aed261ccdd9d50e2bd2ca6a776d826201e1b1b0d953fae75f4da1167337e7", "uncompressed": "df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f1e4f78a8cae1daf5dd1a716a96475b16dcfb455e7d97fe75dd8f1f8ea5cc0a41", "compressed": "03df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f", "chain_code": "67a537696eecbfdd5755734eee16dca622e62bb3c98f66db89cad93def287754", "private_key": "678d12447649cc9eeede87562069c2ff1524f31df025f521be255d4a6c8eaed0", "public_key": "03df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f", "wif": "KzgzzJkyzqcy8bLJDPLwXV5VmJER3f35q9zyAgSdMLn6aqMovQ66", "finger_print": "2004e94c", "path": "m/44'/60'/0'/0/0'", "address": "0xAaB4E88BCa0d7C1e40CE540b9642558d6f9a3a05", "serialized": { "private_key_hex": "0488ade405f450d7af8000000067a537696eecbfdd5755734eee16dca622e62bb3c98f66db89cad93def28775400678d12447649cc9eeede87562069c2ff1524f31df025f521be255d4a6c8eaed0", "public_key_hex": "0488b21e05f450d7af8000000067a537696eecbfdd5755734eee16dca622e62bb3c98f66db89cad93def28775403df79315f83cfeaadbd88bfc0033367ebd2ca7e08df8074fc2415d78ed1a3e73f", "private_key_base58": "xprvA4Dqp8w5LcQejoog9rci7sadBJetitNj1wvbG7uHLLkSxoFhkzVnZ7A8Z6Kz4VRMpFSJ2Kct9KFf8uVpuWiqU9vBrkh9w6dVfThmF4RUxHV", "public_key_base58": "xpub6HDCDeTyAyxwxHt9Ft9iV1XMjLVP8M6aPArC4WJttgHRqbarJXp36uUcQQDJwBK3GEhKy7cbV1Fs4UoRDRpbhTFRZmPJ3coUuenN2TARncR" } } with pytest.raises(ValueError, match=r".*12 word mnemonic.*"): wallet.from_mnemonic(mnemonic=MNEMONIC, passphrase=PASSPHRASE, language="chinese_traditional") assert wallet.from_mnemonic(mnemonic=MNEMONIC, passphrase=PASSPHRASE, language=None) with pytest.raises(ValueError, match=r"Invalid language, .*"): wallet.from_mnemonic(mnemonic=MNEMONIC, passphrase=PASSPHRASE, language="amharic")