From a70b07e83dea8bfc010b1164c5cff6e8bd8daaeb Mon Sep 17 00:00:00 2001 From: Serhy Date: Fri, 4 Dec 2020 19:27:21 +0200 Subject: [PATCH] Interact with ropsten web3 to send assets Signed-off-by: Churikova Tetiana --- ci/tests/Jenkinsfile.e2e-nightly | 4 + ci/tests/Jenkinsfile.e2e-prs | 4 + ci/tests/Jenkinsfile.e2e-upgrade | 4 + test/appium/requirements.txt | 3 +- test/appium/support/api/network_api.py | 19 ++- test/appium/support/api/web3_api.py | 118 ++++++++++++++++++ .../test_wallet_management.py | 2 +- .../transactions/test_keycard_wallet.py | 2 +- .../tests/atomic/transactions/test_wallet.py | 2 +- 9 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 test/appium/support/api/web3_api.py diff --git a/ci/tests/Jenkinsfile.e2e-nightly b/ci/tests/Jenkinsfile.e2e-nightly index e99d81fec3..7f8060f869 100644 --- a/ci/tests/Jenkinsfile.e2e-nightly +++ b/ci/tests/Jenkinsfile.e2e-nightly @@ -44,6 +44,10 @@ pipeline { credentialsId: 'etherscan-api-key', variable: 'ETHERSCAN_API_KEY' ), + string( + credentialsId: 'infura-e2e-token', + variable: 'WEB3_INFURA_PROJECT_ID' + ), ]) { dir('test/appium/tests') { sh """ diff --git a/ci/tests/Jenkinsfile.e2e-prs b/ci/tests/Jenkinsfile.e2e-prs index caa276eb40..8ccaae79d0 100644 --- a/ci/tests/Jenkinsfile.e2e-prs +++ b/ci/tests/Jenkinsfile.e2e-prs @@ -80,6 +80,10 @@ pipeline { credentialsId: 'etherscan-api-key', variable: 'ETHERSCAN_API_KEY' ), + string( + credentialsId: 'infura-e2e-token', + variable: 'WEB3_INFURA_PROJECT_ID' + ), ]) { dir('test/appium/tests') { sh """ diff --git a/ci/tests/Jenkinsfile.e2e-upgrade b/ci/tests/Jenkinsfile.e2e-upgrade index 5bd15d3b4b..70067b8694 100644 --- a/ci/tests/Jenkinsfile.e2e-upgrade +++ b/ci/tests/Jenkinsfile.e2e-upgrade @@ -48,6 +48,10 @@ pipeline { credentialsId: 'etherscan-api-key', variable: 'ETHERSCAN_API_KEY' ), + string( + credentialsId: 'infura-e2e-token', + variable: 'WEB3_INFURA_PROJECT_ID' + ), ]) { dir('test/appium/tests') { sh """ diff --git a/test/appium/requirements.txt b/test/appium/requirements.txt index 5ebd1b25ff..b28607f066 100644 --- a/test/appium/requirements.txt +++ b/test/appium/requirements.txt @@ -10,7 +10,7 @@ cytoolz==0.11.0 emoji==0.5.0 eth-hash==0.2.0 eth-keys==0.3.3 -eth-utils==2.0.0a0 +eth-utils==1.9.5 ethereum==2.3.2 execnet==1.7.1 future==0.18.2 @@ -48,3 +48,4 @@ yarl==1.6.3 zbarlight==3.0 docker==4.4.0 influxdb==5.3.1 +web3 diff --git a/test/appium/support/api/network_api.py b/test/appium/support/api/network_api.py index 8df2718838..36dc0ecde8 100644 --- a/test/appium/support/api/network_api.py +++ b/test/appium/support/api/network_api.py @@ -8,14 +8,14 @@ from json import JSONDecodeError from decimal import Decimal from os import environ import tests - +import support.api.web3_api as w3 class NetworkApi(object): def __init__(self): self.network_url = 'http://api-%s.etherscan.io/api?' % tests.pytest_config_global['network'] self.faucet_url = 'https://faucet-ropsten.status.im/donate' - self.faucet_backup_url = 'https://faucet.ropsten.be/donate' + self.faucet_backup_address = w3.account_address self.headers = { 'User-Agent':"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit\ /537.36 (KHTML, like Gecko) Chrome\/77.0.3865.90 Safari\/537.36", } @@ -155,20 +155,17 @@ class NetworkApi(object): pass def faucet_backup(self, address): - try: - self.log("Trying to get funds from %s" % self.faucet_backup_url) - return requests.request('GET', '%s/0x%s' % (self.faucet_backup_url, address)).json() - except JSONDecodeError as e: - self.log(str(e)) - pass + self.log("Trying to get funds from %s" % self.faucet_backup_address) + address = "0x" + address + w3.donate_testnet_eth(address=address, amount=0.005, inscrease_default_gas_price=10) - def get_donate(self, address, external_faucet=True, wait_time=300): + def get_donate(self, address, external_faucet=False, wait_time=300): initial_balance = self.get_balance(address) counter = 0 if initial_balance < 1000000000000000000: if external_faucet: self.faucet_backup(address) - response = self.faucet(address) + self.faucet(address) while True: if counter >= wait_time: pytest.fail("Donation was not received during %s seconds!" % wait_time) @@ -177,7 +174,7 @@ class NetworkApi(object): time.sleep(10) self.log('Waiting %s seconds for donation' % counter) else: - self.log('Got %s for %s' % (response["amount_eth"], address)) + self.log('Got %s Gwei for %s' % (self.get_balance(address), address)) return def start_chat_bot(self, chat_name: str, messages_number: int, interval: int = 1) -> list: diff --git a/test/appium/support/api/web3_api.py b/test/appium/support/api/web3_api.py new file mode 100644 index 0000000000..1f98466a4a --- /dev/null +++ b/test/appium/support/api/web3_api.py @@ -0,0 +1,118 @@ +from eth_utils import to_checksum_address +from web3.auto.infura.ropsten import w3 + +token_data = {"STT": [{ + "abi": '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"creationBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_newController","type":"address"}],"name":"changeController","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_blockNumber","type":"uint256"}],"name":"balanceOfAt","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_cloneTokenName","type":"string"},{"name":"_cloneDecimalUnits","type":"uint8"},{"name":"_cloneTokenSymbol","type":"string"},{"name":"_snapshotBlock","type":"uint256"},{"name":"_transfersEnabled","type":"bool"}],"name":"createCloneToken","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"parentToken","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_amount","type":"uint256"}],"name":"generateTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_blockNumber","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"transfersEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"parentSnapShotBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_amount","type":"uint256"}],"name":"destroyTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"}],"name":"claimTokens","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokenFactory","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_transfersEnabled","type":"bool"}],"name":"enableTransfers","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"controller","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[{"name":"_tokenFactory","type":"address"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_token","type":"address"},{"indexed":true,"name":"_controller","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"ClaimedTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_cloneToken","type":"address"},{"indexed":false,"name":"_snapshotBlock","type":"uint256"}],"name":"NewCloneToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Approval","type":"event"}]', + "address": "0xc55cF4B03948D7EBc8b9E8BAD92643703811d162"}]} + +ACCOUNT_PRIVATE_KEY = '0x5507f8c5c12707770c12fd0fae5d012b947d61f43b9203ae67916e703fd12ad7' + + +class Account(object): + + def __init__(self, account_private_key): + self.pk = account_private_key + + @property + def account_address(self): + return w3.eth.account.from_key(self.pk).address + + @property + def nonce(self): + return w3.eth.getTransactionCount(self.account_address) + + @property + def balance(self): + return w3.eth.getBalance(self.account_address) + + def send_eth(self, to_address, eth_value, gas_price_increment=0): + signed_txn = w3.eth.account.signTransaction(dict( + nonce=self.nonce, + gasPrice=w3.eth.gasPrice + gas_price_increment * 1000000000, + gas=21000, + to=to_address, + value=int(eth_value * 10 ** 18), + data=b'', + ), + self.pk, + ) + w3.eth.sendRawTransaction(signed_txn.rawTransaction) + return w3.toHex(w3.sha3(signed_txn.rawTransaction)) + + +class ContractInteractions(object): + + def __init__(self, contract_address, abi): + self.contract = w3.eth.contract(address=contract_address, abi=abi) + + @property + def decimals(self): + return self.contract.functions.decimals().call() + + def balance_of(self, account_address): + return self.contract.functions.balanceOf(account_address).call() + + def nonce(self, account_address): + return w3.eth.getTransactionCount(account_address) + + def transfer_token_to(self, from_address, to_address, number_of_tokens, nonce, gas_price_increment=0): + gas_price = w3.eth.gasPrice + gas_price_increment * 1000000000 + token_value = int(number_of_tokens * 10 ** self.decimals) + return self.contract.functions.transfer(to_address, token_value, ).buildTransaction({ + 'from': from_address, + 'gasPrice': gas_price, + 'gas': 600000, + 'nonce': nonce} + ) + + +def transaction_status(hash): + return w3.eth.getTransaction(transaction_hash=hash) + + +def to_checksumed_address(address): + return to_checksum_address(address) + + +def current_gas_price(): + return str(w3.eth.gasPrice / 1000000000) + + +def sign_transaction(tx_data, pk): + return w3.eth.account.signTransaction(tx_data, pk) + + +def broadcast_signed_tx(signed_txn): + w3.eth.sendRawTransaction(signed_txn.rawTransaction) + return w3.toHex(w3.sha3(signed_txn.rawTransaction)) + + +account = Account(ACCOUNT_PRIVATE_KEY) +account_address = account.account_address + + +def donate_testnet_eth(address=str(), amount=float(), inscrease_default_gas_price=int()): + """ + address: address where to send ETH to + amount: amount in Ether form + inscrease_default_gas_price: specify GWEI value (int) if you want to speed up transaction pick up + """ + return account.send_eth(address, amount, inscrease_default_gas_price) + + +def donate_testnet_token(token_name=str(), address=str(), amount=float(), inscrease_default_gas_price=int()): + """ + token_name: token 'name' value you want to send taken from token_data + address: address where to send ETH to + amount: amount in Ether form + inscrease_default_gas_price: specify GWEI value (int) if you want to speed up transaction pick up + """ + token_contract = ContractInteractions(token_data[token_name][0]['address'], token_data[token_name][0]['abi']) + to_address_data = token_contract.transfer_token_to( + from_address=account_address, + to_address=address, + number_of_tokens=amount, + nonce=token_contract.nonce(account_address), + gas_price_increment=inscrease_default_gas_price) + signed_tx = sign_transaction(tx_data=to_address_data, pk=account.pk) + return broadcast_signed_tx(signed_tx) diff --git a/test/appium/tests/atomic/account_management/test_wallet_management.py b/test/appium/tests/atomic/account_management/test_wallet_management.py index a07c392654..566daee195 100644 --- a/test/appium/tests/atomic/account_management/test_wallet_management.py +++ b/test/appium/tests/atomic/account_management/test_wallet_management.py @@ -118,7 +118,7 @@ class TestWalletManagement(SingleDeviceTestCase): if wallet.backup_recovery_phrase_warning_text.is_element_present(): self.driver.fail("'Back up your seed phrase' warning is shown on Wallet while no funds are present") address = wallet.get_wallet_address() - self.network_api.get_donate(address[2:], external_faucet=False) + self.network_api.get_donate(address[2:], external_faucet=True, wait_time=60) wallet.back_button.click() wallet.wait_balance_is_changed() if not wallet.backup_recovery_phrase_warning_text.is_element_present(30): diff --git a/test/appium/tests/atomic/transactions/test_keycard_wallet.py b/test/appium/tests/atomic/transactions/test_keycard_wallet.py index af76560dd6..7ac51f3837 100644 --- a/test/appium/tests/atomic/transactions/test_keycard_wallet.py +++ b/test/appium/tests/atomic/transactions/test_keycard_wallet.py @@ -113,7 +113,7 @@ class TestTransactionWalletSingleDevice(SingleDeviceTestCase): wallet_view.set_up_wallet() status_account_address = wallet_view.get_wallet_address()[2:] wallet_view.back_button.click() - self.network_api.get_donate(status_account_address, external_faucet=False) + self.network_api.get_donate(status_account_address, external_faucet=True) wallet_view.wait_balance_is_changed() account_name = 'subaccount' wallet_view.add_account(account_name, keycard=True) diff --git a/test/appium/tests/atomic/transactions/test_wallet.py b/test/appium/tests/atomic/transactions/test_wallet.py index 3e75c5799d..5530f48a04 100644 --- a/test/appium/tests/atomic/transactions/test_wallet.py +++ b/test/appium/tests/atomic/transactions/test_wallet.py @@ -247,7 +247,7 @@ class TestTransactionWalletSingleDevice(SingleDeviceTestCase): wallet_view.set_up_wallet() status_account_address = wallet_view.get_wallet_address()[2:] wallet_view.back_button.click() - self.network_api.get_donate(status_account_address, external_faucet=False) + self.network_api.get_donate(status_account_address, external_faucet=True) wallet_view.wait_balance_is_changed() account_name = 'subaccount' wallet_view.add_account(account_name)