From ca70f97917ffe101bda84035a99176a9bdbc8e11 Mon Sep 17 00:00:00 2001 From: badrogger Date: Wed, 8 Nov 2023 19:11:10 +0000 Subject: [PATCH 1/7] Add TxRecordStatus --- skale/wallets/redis_wallet.py | 45 +++++++++++++++++++---------- tests/wallets/redis_adapter_test.py | 11 ++++--- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/skale/wallets/redis_wallet.py b/skale/wallets/redis_wallet.py index 8df872e7..2d68b71f 100644 --- a/skale/wallets/redis_wallet.py +++ b/skale/wallets/redis_wallet.py @@ -22,6 +22,7 @@ import logging import os import time +from enum import Enum from typing import Dict, Optional, Tuple from redis import Redis @@ -59,7 +60,16 @@ class RedisWalletWaitError(RedisWalletError, TransactionWaitError): pass -class RedisWalletAdapter: +class TxRecordStatus(str, Enum): + DROPPED = 'DROPPED' + SUCCESS = 'SUCCESS' + FAILED = 'FAILED' + + def __str__(self) -> str: + return str.__str__(self) + + +class RedisWalletAdapter(BaseWallet): ID_SIZE = 16 def __init__( @@ -172,23 +182,28 @@ def wait( timeout: int = MAX_WAITING_TIME ) -> Dict: start_ts = time.time() - status = None - - while time.time() - start_ts < timeout: + status, result = None, None + while status not in [ + TxRecordStatus.DROPPED, + TxRecordStatus.SUCCESS, + TxRecordStatus.FAILED + ] and time.time() - start_ts < timeout: try: - status = self.get_status(tx_id) - if status == 'DROPPED': - break - if status in ('SUCCESS', 'FAILED'): - r = self.get_record(tx_id) - return get_receipt(self.wallet._web3, r['tx_hash']) - except Exception as err: - logger.exception(f'Waiting for tx {tx_id} errored') - raise RedisWalletWaitError(err) + record = self.get_record(tx_id) + if record is not None: + status = record.get('status') + if status in (TxRecordStatus.SUCCESS, TxRecordStatus.FAILED): + result = get_receipt(self.wallet._web3, record['tx_hash']) + except Exception as e: + logger.exception('Waiting for tx %s errored', tx_id) + raise RedisWalletWaitError(e) + + if result: + return result if status is None: - raise RedisWalletEmptyStatusError('Tx status is None') - if status == 'DROPPED': + raise RedisWalletEmptyStatusError(f'Tx status is {status}') + elif status == TxRecordStatus.DROPPED: raise RedisWalletDroppedError('Tx was dropped after max retries') else: raise RedisWalletWaitError(f'Tx finished with status {status}') diff --git a/tests/wallets/redis_adapter_test.py b/tests/wallets/redis_adapter_test.py index 56642f29..4d032a13 100644 --- a/tests/wallets/redis_adapter_test.py +++ b/tests/wallets/redis_adapter_test.py @@ -72,25 +72,24 @@ def test_sign_and_send(rdp): tx_id = rdp.sign_and_send(tx, multiplier=2, priority=5) -def test_wait(rdp): +def test_rdp_wait(rdp): tx_id = 'tx-tttttttttttttttt' - rdp.get_status = mock.Mock(return_value=None) + rdp.get_record = mock.Mock(return_value=None) with in_time(3): with pytest.raises(RedisWalletEmptyStatusError): rdp.wait(tx_id, timeout=2) - rdp.get_status = mock.Mock(return_value='DROPPED') + rdp.get_record = mock.Mock(return_value={'tx_hash': 'test', 'status': 'DROPPED'}) with in_time(2): with pytest.raises(RedisWalletDroppedError): rdp.wait(tx_id, timeout=100) - rdp.get_status = mock.Mock(side_effect=RedisTestError('test')) + rdp.get_record = mock.Mock(side_effect=RedisTestError('test')) with in_time(2): with pytest.raises(RedisWalletWaitError): rdp.wait(tx_id, timeout=100) - rdp.get_status = mock.Mock(return_value='SUCCESS') - rdp.get_record = mock.Mock(return_value={'tx_hash': 'test'}) + rdp.get_record = mock.Mock(return_value={'tx_hash': 'test', 'status': 'SUCCESS'}) fake_receipt = {'test': 'test'} with mock.patch( 'skale.wallets.redis_wallet.get_receipt', From 7c235eba0cfb740227e11e28464600c87acda588 Mon Sep 17 00:00:00 2001 From: badrogger Date: Wed, 8 Nov 2023 19:12:03 +0000 Subject: [PATCH 2/7] Introduce TxCallResult --- skale/contracts/base_contract.py | 27 +++++++++++++-------------- skale/transactions/result.py | 17 ++++++++--------- skale/transactions/tools.py | 32 ++++++++++++++++++++++++-------- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/skale/contracts/base_contract.py b/skale/contracts/base_contract.py index 092de5ec..db85a4bb 100644 --- a/skale/contracts/base_contract.py +++ b/skale/contracts/base_contract.py @@ -25,12 +25,8 @@ from web3 import Web3 import skale.config as config -from skale.transactions.result import ( - TxRes, - is_success, - is_success_or_not_performed -) -from skale.transactions.tools import make_dry_run_call, transaction_from_method +from skale.transactions.result import TxRes, is_success +from skale.transactions.tools import make_dry_run_call, transaction_from_method, TxStatus from skale.utils.web3_utils import ( DEFAULT_BLOCKS_TO_WAIT, get_eth_nonce, @@ -47,9 +43,13 @@ def execute_dry_run(skale, method, custom_gas_limit, value=0) -> tuple: dry_run_result = make_dry_run_call(skale, method, custom_gas_limit, value) estimated_gas_limit = None + revert = None if is_success(dry_run_result): estimated_gas_limit = dry_run_result['payload'] - return dry_run_result, estimated_gas_limit + else: + if dry_run_result.get('error') == 'revert': + revert = dry_run_result.get('message') + return dry_run_result, estimated_gas_limit, revert def transaction_method(transaction): @@ -76,19 +76,18 @@ def wrapper( **kwargs ): method = transaction(self, *args, **kwargs) - dry_run_result, tx_hash, receipt = None, None, None + dry_run_result, tx_hash, receipt, revert = None, None, None, None nonce = get_eth_nonce(self.skale.web3, self.skale.wallet.address) - estimated_gas_limit = None + tx_call_result, estimated_gas_limit = None should_dry_run = not skip_dry_run and not config.DISABLE_DRY_RUN if should_dry_run: - dry_run_result, estimated_gas_limit = execute_dry_run(self.skale, - method, gas_limit, value) + tx_call_result = make_dry_run_call(self.skale, method, gas_limit, value) - should_send = not dry_run_only and is_success_or_not_performed(dry_run_result) - gas_limit = gas_limit or estimated_gas_limit or config.DEFAULT_GAS_LIMIT + should_send = tx_call_result is not None and tx_call_result.status == TxStatus.SUCCESS + gas_limit = gas_limit or tx_call_result.data['gas'] or config.DEFAULT_GAS_LIMIT gas_price = gas_price or config.DEFAULT_GAS_PRICE_WEI or self.skale.gas_price if should_send: @@ -118,7 +117,7 @@ def wrapper( if should_confirm: wait_for_confirmation_blocks(self.skale.web3, confirmation_blocks) - tx_res = TxRes(dry_run_result, tx_hash, receipt) + tx_res = TxRes(dry_run_result, tx_hash, receipt, revert) if raise_for_status: tx_res.raise_for_status() diff --git a/skale/transactions/result.py b/skale/transactions/result.py index 4bb65948..2fe36421 100644 --- a/skale/transactions/result.py +++ b/skale/transactions/result.py @@ -45,10 +45,11 @@ def is_revert_error(result: Optional[dict]) -> bool: class TxRes: - def __init__(self, dry_run_result=None, tx_hash=None, receipt=None): + def __init__(self, dry_run_result=None, tx_hash=None, receipt=None, revert=None): self.dry_run_result = dry_run_result self.tx_hash = tx_hash self.receipt = receipt + self.revert = revert def __str__(self) -> str: return ( @@ -71,14 +72,12 @@ def tx_failed(self) -> bool: def raise_for_status(self) -> None: if self.receipt is not None: if not is_success(self.receipt): - error_msg = self.receipt['error'] - if is_revert_error(self.receipt): - raise TransactionRevertError(error_msg) + if self.revert is not None: + raise TransactionRevertError(self.revert) else: - raise TransactionFailedError(error_msg) + raise TransactionFailedError(self.revert) elif self.dry_run_result is not None and not is_success(self.dry_run_result): - error_msg = self.dry_run_result['message'] - if is_revert_error(self.dry_run_result): - raise DryRunRevertError(error_msg) + if self.revert is not None: + raise DryRunRevertError(self.revert) else: - raise DryRunFailedError(error_msg) + raise DryRunFailedError(self.revert) diff --git a/skale/transactions/tools.py b/skale/transactions/tools.py index c0abbfcb..73c5286c 100644 --- a/skale/transactions/tools.py +++ b/skale/transactions/tools.py @@ -17,10 +17,11 @@ # You should have received a copy of the GNU Affero General Public License # along with SKALE.py. If not, see . +import enum import logging import time from functools import partial, wraps -from typing import Dict, Optional +from typing import Dict, NamedTuple, Optional from web3 import Web3 from web3.exceptions import ContractLogicError, Web3Exception @@ -38,7 +39,19 @@ DEFAULT_ETH_SEND_GAS_LIMIT = 22000 -def make_dry_run_call(skale, method, gas_limit=None, value=0) -> dict: +class TxStatus(int, enum.IntEnum): + FAILED = 0 + SUCCESS = 1 + + +class TxCallResult(NamedTuple): + status: TxStatus + error: str + message: str + data: dict + + +def make_dry_run_call(skale, method, gas_limit=None, value=0) -> TxCallResult: opts = { 'from': skale.wallet.address, 'value': value @@ -49,6 +62,7 @@ def make_dry_run_call(skale, method, gas_limit=None, value=0) -> dict: f'wallet: {skale.wallet.__class__.__name__}, ' f'value: {value}, ' ) + estimated_gas = 0 try: if gas_limit: @@ -59,12 +73,14 @@ def make_dry_run_call(skale, method, gas_limit=None, value=0) -> dict: estimated_gas = estimate_gas(skale.web3, method, opts) logger.info(f'Estimated gas for {method.fn_name}: {estimated_gas}') except ContractLogicError as e: - return {'status': 0, 'error': 'revert', 'message': e.message, 'data': e.data} - except (Web3Exception, ValueError) as err: - logger.error('Dry run for %s failed', method, exc_info=err) - return {'status': 0, 'error': str(err)} - - return {'status': 1, 'payload': estimated_gas} + return TxCallResult(status=TxStatus.FAILED, + error='revert', message=e.message, data=e.data) + except (Web3Exception, ValueError) as e: + logger.exception('Dry run for %s failed', method) + return TxCallResult(status=TxStatus.FAILED, error='exception', message=str(e), data={}) + + return TxCallResult(status=TxStatus.SUCCESS, error='', + message='success', data={'gas': estimated_gas}) def estimate_gas(web3, method, opts): From ca6738dd842429543bd9d3a0eebbaa10022dd1fc Mon Sep 17 00:00:00 2001 From: badrogger Date: Fri, 10 Nov 2023 13:57:47 +0000 Subject: [PATCH 3/7] Extracts Tx structures to result.py. Add attempt field --- skale/transactions/result.py | 56 ++++++++++++++---------------------- skale/transactions/tools.py | 23 ++++----------- 2 files changed, 27 insertions(+), 52 deletions(-) diff --git a/skale/transactions/result.py b/skale/transactions/result.py index 2fe36421..f5c0251f 100644 --- a/skale/transactions/result.py +++ b/skale/transactions/result.py @@ -17,67 +17,53 @@ # You should have received a copy of the GNU Affero General Public License # along with SKALE.py. If not, see . -from typing import Optional +import enum +from typing import NamedTuple from skale.transactions.exceptions import ( DryRunFailedError, DryRunRevertError, - TransactionRevertError, TransactionFailedError ) -SUCCESS_STATUS = 1 +class TxStatus(int, enum.Enum): + FAILED = 0 + SUCCESS = 1 -def is_success(result: dict) -> bool: - return result.get('status') == SUCCESS_STATUS - -def is_success_or_not_performed(result: dict) -> bool: - return result is None or is_success(result) - - -def is_revert_error(result: Optional[dict]) -> bool: - if not result: - return False - error = result.get('error', None) - return error == 'revert' +class TxCallResult(NamedTuple): + status: TxStatus + error: str + message: str + data: dict class TxRes: - def __init__(self, dry_run_result=None, tx_hash=None, receipt=None, revert=None): - self.dry_run_result = dry_run_result + def __init__(self, tx_call_result=None, tx_hash=None, receipt=None, revert=None): + self.tx_call_result = tx_call_result self.tx_hash = tx_hash self.receipt = receipt - self.revert = revert + self.attempts = 0 def __str__(self) -> str: return ( - f'TxRes hash: {self.tx_hash}, dry_run_result {self.dry_run_result}, ' + f'TxRes hash: {self.tx_hash}, tx_call_result {self.tx_call_result}, ' f'receipt {self.receipt}' ) def __repr__(self) -> str: return ( - f'TxRes hash: {self.tx_hash}, dry_run_result {self.dry_run_result}, ' + f'TxRes hash: {self.tx_hash}, tx_call_result {self.tx_call_result}, ' f'receipt {self.receipt}' ) - def dry_run_failed(self) -> bool: - return not is_success_or_not_performed(self.dry_run_result) - - def tx_failed(self) -> bool: - return not is_success_or_not_performed(self.receipt) - def raise_for_status(self) -> None: if self.receipt is not None: - if not is_success(self.receipt): - if self.revert is not None: - raise TransactionRevertError(self.revert) - else: - raise TransactionFailedError(self.revert) - elif self.dry_run_result is not None and not is_success(self.dry_run_result): - if self.revert is not None: - raise DryRunRevertError(self.revert) + if self.receipt['status'] == TxStatus.FAILED: + raise TransactionFailedError(self.receipt) + elif self.tx_call_result is not None and self.tx_call_result.status == TxStatus.FAILED: + if self.tx_call_result.error == 'revert': + raise DryRunRevertError(self.tx_call_result.message) else: - raise DryRunFailedError(self.revert) + raise DryRunFailedError(self.tx_call_result.message) diff --git a/skale/transactions/tools.py b/skale/transactions/tools.py index 73c5286c..ef3216f3 100644 --- a/skale/transactions/tools.py +++ b/skale/transactions/tools.py @@ -17,11 +17,10 @@ # You should have received a copy of the GNU Affero General Public License # along with SKALE.py. If not, see . -import enum import logging import time from functools import partial, wraps -from typing import Dict, NamedTuple, Optional +from typing import Dict, Optional from web3 import Web3 from web3.exceptions import ContractLogicError, Web3Exception @@ -29,7 +28,7 @@ import skale.config as config from skale.transactions.exceptions import TransactionError -from skale.transactions.result import TxRes +from skale.transactions.result import TxCallResult, TxRes, TxStatus from skale.utils.web3_utils import get_eth_nonce @@ -39,18 +38,6 @@ DEFAULT_ETH_SEND_GAS_LIMIT = 22000 -class TxStatus(int, enum.IntEnum): - FAILED = 0 - SUCCESS = 1 - - -class TxCallResult(NamedTuple): - status: TxStatus - error: str - message: str - data: dict - - def make_dry_run_call(skale, method, gas_limit=None, value=0) -> TxCallResult: opts = { 'from': skale.wallet.address, @@ -202,7 +189,7 @@ def run_tx_with_retry(transaction, *args, max_retries=3, tx_res.raise_for_status() except TransactionError as e: error = e - logger.exception('Tx attempt %d/%d failed', attempt, max_retries) + logger.exception('Tx attempt %d/%d failed', attempt + 1, max_retries) timeout = exp_timeout if retry_timeout < 0 else exp_timeout time.sleep(timeout) @@ -214,7 +201,7 @@ def run_tx_with_retry(transaction, *args, max_retries=3, if error is None: logger.info( 'Tx %s completed after %d/%d retries', - transaction.__name__, attempt, max_retries + transaction.__name__, attempt + 1, max_retries ) else: logger.error( @@ -223,4 +210,6 @@ def run_tx_with_retry(transaction, *args, max_retries=3, ) if raise_for_status: raise error + if tx_res is not None: + tx_res.attempts = attempt return tx_res From b810263b545dc3ac1c709ad143cbf53ea1d55b63 Mon Sep 17 00:00:00 2001 From: badrogger Date: Fri, 10 Nov 2023 13:59:32 +0000 Subject: [PATCH 4/7] Fix transaction_method --- skale/contracts/base_contract.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/skale/contracts/base_contract.py b/skale/contracts/base_contract.py index db85a4bb..e984c1aa 100644 --- a/skale/contracts/base_contract.py +++ b/skale/contracts/base_contract.py @@ -25,7 +25,7 @@ from web3 import Web3 import skale.config as config -from skale.transactions.result import TxRes, is_success +from skale.transactions.result import TxRes from skale.transactions.tools import make_dry_run_call, transaction_from_method, TxStatus from skale.utils.web3_utils import ( DEFAULT_BLOCKS_TO_WAIT, @@ -40,18 +40,6 @@ logger = logging.getLogger(__name__) -def execute_dry_run(skale, method, custom_gas_limit, value=0) -> tuple: - dry_run_result = make_dry_run_call(skale, method, custom_gas_limit, value) - estimated_gas_limit = None - revert = None - if is_success(dry_run_result): - estimated_gas_limit = dry_run_result['payload'] - else: - if dry_run_result.get('error') == 'revert': - revert = dry_run_result.get('message') - return dry_run_result, estimated_gas_limit, revert - - def transaction_method(transaction): @wraps(transaction) def wrapper( @@ -76,21 +64,23 @@ def wrapper( **kwargs ): method = transaction(self, *args, **kwargs) - dry_run_result, tx_hash, receipt, revert = None, None, None, None nonce = get_eth_nonce(self.skale.web3, self.skale.wallet.address) - tx_call_result, estimated_gas_limit = None + call_result, tx_hash, receipt = None, None, None should_dry_run = not skip_dry_run and not config.DISABLE_DRY_RUN if should_dry_run: - tx_call_result = make_dry_run_call(self.skale, method, gas_limit, value) + call_result = make_dry_run_call(self.skale, method, gas_limit, value) + if call_result.status == TxStatus.SUCCESS: + gas_limit = gas_limit or call_result.data['gas'] - should_send = tx_call_result is not None and tx_call_result.status == TxStatus.SUCCESS - gas_limit = gas_limit or tx_call_result.data['gas'] or config.DEFAULT_GAS_LIMIT - gas_price = gas_price or config.DEFAULT_GAS_PRICE_WEI or self.skale.gas_price + should_send = not dry_run_only and \ + (not should_dry_run or call_result.status == TxStatus.SUCCESS) if should_send: + gas_limit = gas_limit or config.DEFAULT_GAS_LIMIT + gas_price = gas_price or config.DEFAULT_GAS_PRICE_WEI or self.skale.gas_price tx = transaction_from_method( method=method, gas_limit=gas_limit, @@ -113,11 +103,11 @@ def wrapper( if should_wait: receipt = self.skale.wallet.wait(tx_hash) - should_confirm = receipt and confirmation_blocks > 0 + should_confirm = receipt is not None and confirmation_blocks > 0 if should_confirm: wait_for_confirmation_blocks(self.skale.web3, confirmation_blocks) - tx_res = TxRes(dry_run_result, tx_hash, receipt, revert) + tx_res = TxRes(call_result, tx_hash, receipt) if raise_for_status: tx_res.raise_for_status() From dea828075f66be30d6c54705513e0578e7705f70 Mon Sep 17 00:00:00 2001 From: badrogger Date: Fri, 10 Nov 2023 14:00:19 +0000 Subject: [PATCH 5/7] Fix transaction/base_contract tests --- tests/manager/base_contract_test.py | 15 +++++++------ tests/transaction_tools_test.py | 33 +++++++++++++---------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/tests/manager/base_contract_test.py b/tests/manager/base_contract_test.py index e704ee80..5c3aeb8a 100644 --- a/tests/manager/base_contract_test.py +++ b/tests/manager/base_contract_test.py @@ -5,6 +5,7 @@ import pytest import skale.config as config from skale.transactions.exceptions import TransactionNotSentError +from skale.transactions.result import TxStatus from skale.transactions.tools import estimate_gas from skale.utils.account_tools import generate_account from skale.utils.contracts_provision.utils import generate_random_schain_data @@ -26,8 +27,8 @@ def test_dry_run(skale): balance_to_before = skale.token.get_balance(address_to) amount = 10 * ETH_IN_WEI tx_res = skale.token.transfer(address_to, amount, dry_run_only=True) - assert isinstance(tx_res.dry_run_result['payload'], int) - assert tx_res.dry_run_result['status'] == 1 + assert isinstance(tx_res.tx_call_result.data['gas'], int) + assert tx_res.tx_call_result.status == TxStatus.SUCCESS tx_res.raise_for_status() balance_from_after = skale.token.get_balance(address_from) @@ -54,7 +55,7 @@ def test_disable_dry_run_env(skale, disable_dry_run_env): address_to = account['address'] amount = 10 * ETH_IN_WEI with mock.patch( - 'skale.contracts.base_contract.execute_dry_run' + 'skale.contracts.base_contract.make_dry_run_call' ) as dry_run_mock: skale.token.transfer(address_to, amount) dry_run_mock.assert_not_called() @@ -76,7 +77,7 @@ def test_skip_dry_run(skale): ) assert tx_res.tx_hash is not None, tx_res assert tx_res.receipt is not None - assert tx_res.dry_run_result is None + assert tx_res.tx_call_result is None balance_from_after = skale.token.get_balance(address_from) assert balance_from_after == balance_from_before - amount balance_to_after = skale.token.get_balance(address_to) @@ -96,8 +97,8 @@ def test_wait_for_false(skale): tx_res = skale.token.transfer(address_to, amount, wait_for=False) assert tx_res.tx_hash is not None assert tx_res.receipt is None - assert isinstance(tx_res.dry_run_result['payload'], int) - assert tx_res.dry_run_result['status'] == 1 + assert isinstance(tx_res.tx_call_result.data['gas'], int) + assert tx_res.tx_call_result.status == TxStatus.SUCCESS tx_res.receipt = wait_for_receipt_by_blocks(skale.web3, tx_res.tx_hash) tx_res.raise_for_status() @@ -113,7 +114,7 @@ def test_tx_res_dry_run(skale): token_amount = 10 tx_res = skale.token.transfer( account['address'], token_amount, dry_run_only=True) - assert tx_res.dry_run_result is not None + assert tx_res.tx_call_result is not None assert tx_res.tx_hash is None assert tx_res.receipt is None tx_res.raise_for_status() diff --git a/tests/transaction_tools_test.py b/tests/transaction_tools_test.py index 551fdf03..f4eac7fd 100644 --- a/tests/transaction_tools_test.py +++ b/tests/transaction_tools_test.py @@ -8,16 +8,18 @@ ) from skale import Skale from skale.transactions.tools import ( - run_tx_with_retry, + get_block_gas_limit, estimate_gas, - get_block_gas_limit + TxCallResult, + TxStatus, + run_tx_with_retry ) from skale.utils.account_tools import generate_account from skale.utils.web3_utils import init_web3 from skale.wallets import Web3Wallet from skale.wallets.web3_wallet import generate_wallet -from tests.constants import ENDPOINT, TEST_ABI_FILEPATH, TEST_GAS_LIMIT +from tests.constants import ENDPOINT, TEST_ABI_FILEPATH from tests.constants import ( D_VALIDATOR_NAME, D_VALIDATOR_DESC, D_VALIDATOR_FEE, D_VALIDATOR_MIN_DEL, @@ -30,7 +32,7 @@ def generate_new_skale(): web3 = init_web3(ENDPOINT) account = generate_account(web3) wallet = Web3Wallet(account['private_key'], web3) - wallet.sign_and_send = mock.Mock() + # wallet.sign_and_send = mock.Mock() wallet.wait = mock.Mock() return Skale(ENDPOINT, TEST_ABI_FILEPATH, wallet) @@ -58,11 +60,12 @@ def test_run_tx_with_retry(skale): def test_run_tx_with_retry_dry_run_failed(skale): dry_run_call_mock = mock.Mock( - return_value={ - 'status': 0, - 'message': 'Dry run test failure', - 'error': 'revert' - } + return_value=TxCallResult( + status=TxStatus.FAILED, + error='revert', + message='Dry run test failure', + data={} + ) ) account = generate_account(skale.web3) token_amount = 10 * ETH_IN_WEI @@ -103,21 +106,15 @@ def test_run_tx_with_retry_tx_failed(failed_skale): def test_run_tx_with_retry_insufficient_balance(skale): sender_skale = generate_new_skale() token_amount = 10 * ETH_IN_WEI - skale.token.transfer(sender_skale.wallet.address, token_amount + 1, - skip_dry_run=True, - wait_for=True, - gas_limit=TEST_GAS_LIMIT) retries_number = 5 - sender_skale.wallet.wait = mock.MagicMock() - run_tx_with_retry( + tx_res = run_tx_with_retry( sender_skale.token.transfer, - skale.wallet.address, token_amount, wait_for=True, + skale.wallet.address, token_amount, raise_for_status=False, max_retries=retries_number, ) - assert sender_skale.wallet.sign_and_send.call_count == retries_number - assert sender_skale.wallet.wait.call_count == retries_number + assert tx_res.attempts == retries_number def test_estimate_gas(skale): From 6098200ac45f5fd4a7e27f4b1aa451cb033e209a Mon Sep 17 00:00:00 2001 From: badrogger Date: Fri, 10 Nov 2023 18:11:18 +0000 Subject: [PATCH 6/7] Bump version to 6.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 01444755..c3e6fa91 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( name='skale.py', - version='6.0', + version='6.1', description='SKALE client tools', long_description_markdown_filename='README.md', author='SKALE Labs', From 3727ce86aa9c2401a62ef1a0d10dfb58ea7b2e50 Mon Sep 17 00:00:00 2001 From: badrogger Date: Fri, 10 Nov 2023 18:44:54 +0000 Subject: [PATCH 7/7] Remove unnecessary comments --- tests/transaction_tools_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/transaction_tools_test.py b/tests/transaction_tools_test.py index f4eac7fd..bf142117 100644 --- a/tests/transaction_tools_test.py +++ b/tests/transaction_tools_test.py @@ -32,7 +32,6 @@ def generate_new_skale(): web3 = init_web3(ENDPOINT) account = generate_account(web3) wallet = Web3Wallet(account['private_key'], web3) - # wallet.sign_and_send = mock.Mock() wallet.wait = mock.Mock() return Skale(ENDPOINT, TEST_ABI_FILEPATH, wallet)