diff --git a/skale/contracts/__init__.py b/skale/contracts/__init__.py
index aa5d2f87..e064dd7a 100644
--- a/skale/contracts/__init__.py
+++ b/skale/contracts/__init__.py
@@ -1,6 +1,6 @@
# flake8: noqa
-from skale.contracts.base_contract import BaseContract
+from skale.contracts.base_contract import BaseContract, transaction_method
from skale.contracts.manager import Manager
from skale.contracts.contract_manager import ContractManager
diff --git a/skale/contracts/base_contract.py b/skale/contracts/base_contract.py
index ca47e45c..3b04b391 100644
--- a/skale/contracts/base_contract.py
+++ b/skale/contracts/base_contract.py
@@ -18,8 +18,30 @@
# along with SKALE.py. If not, see .
""" SKALE base contract class """
+from functools import wraps
+
from web3 import Web3
+from skale.utils.web3_utils import TransactionFailedError, wait_receipt
+
+
+def transaction_method(transaction):
+ @wraps(transaction_method)
+ def wrapper(self, *args, wait_for=False, **kwargs):
+ res = transaction(self, *args, **kwargs)
+ if wait_for:
+ receipt = wait_receipt(self.skale.web3, res['tx'])
+ if receipt.get('status') == 1:
+ return receipt
+ else:
+ raise TransactionFailedError(
+ 'Transaction {transaction_method.__name__} failed with '
+ 'receipt {receipt}'
+ )
+ else:
+ return res
+ return wrapper
+
class BaseContract:
def __init__(self, skale, name, address, abi):
diff --git a/skale/contracts/constants.py b/skale/contracts/constants.py
index d7eeeede..acb59b4e 100644
--- a/skale/contracts/constants.py
+++ b/skale/contracts/constants.py
@@ -17,12 +17,13 @@
# You should have received a copy of the GNU Affero General Public License
# along with SKALE.py. If not, see .
-from skale.contracts import BaseContract
+from skale.contracts import BaseContract, transaction_method
from skale.transactions.tools import post_transaction
from skale.utils.constants import GAS
class Constants(BaseContract):
+ @transaction_method
def set_periods(self, new_reward_period, new_delta_period):
op = self.contract.functions.setPeriods(new_reward_period, new_delta_period)
tx = post_transaction(self.skale.wallet, op, GAS['set_periods'])
@@ -34,6 +35,7 @@ def get_reward_period(self):
def get_delta_period(self):
return self.contract.functions.deltaPeriod().call()
+ @transaction_method
def set_check_time(self, new_check_time):
op = self.contract.functions.setCheckTime(new_check_time)
tx = post_transaction(self.skale.wallet, op, GAS['set_check_time'])
@@ -42,6 +44,7 @@ def set_check_time(self, new_check_time):
def get_check_time(self):
return self.contract.functions.checkTime().call()
+ @transaction_method
def set_latency(self, new_allowable_latency):
op = self.contract.functions.setLatency(new_allowable_latency)
tx = post_transaction(self.skale.wallet, op, GAS['set_latency'])
diff --git a/skale/contracts/manager.py b/skale/contracts/manager.py
index c81aa1ed..e9da1d8e 100644
--- a/skale/contracts/manager.py
+++ b/skale/contracts/manager.py
@@ -23,7 +23,7 @@
import socket
-from skale.contracts import BaseContract
+from skale.contracts import BaseContract, transaction_method
from skale.transactions.tools import post_transaction
from skale.utils import helper
from skale.utils.constants import GAS, NODE_DEPOSIT, OP_TYPES
@@ -32,6 +32,7 @@
class Manager(BaseContract):
+ @transaction_method
def create_node(self, ip, port, name, public_ip=None):
logger.info(
f'create_node: {ip}:{port}, public ip: {public_ip} name: {name}')
@@ -77,6 +78,14 @@ def create_node_data_to_bytes(self, ip, public_ip, port, name, pk, nonce):
return data_bytes
+ def create_default_schain(self, name):
+ lifetime = 3600
+ nodes_type = 4
+ price_in_wei = self.skale.schains.get_schain_price(nodes_type, lifetime)
+ return self.create_schain(lifetime, nodes_type, price_in_wei, name,
+ wait_for=True)
+
+ @transaction_method
def create_schain(self, lifetime, type_of_nodes, deposit, name):
logger.info(
f'create_schain: type_of_nodes: {type_of_nodes}, name: {name}')
@@ -108,33 +117,39 @@ def create_schain_data_to_bytes(self, lifetime, type_of_nodes, name,
)
return data_bytes
+ @transaction_method
def get_bounty(self, node_id):
op = self.contract.functions.getBounty(node_id)
tx = post_transaction(self.skale.wallet, op, GAS['get_bounty'])
return {'tx': tx}
+ @transaction_method
def send_verdict(self, validator, node_id, downtime, latency):
op = self.contract.functions.sendVerdict(validator, node_id, downtime,
latency)
tx = post_transaction(self.skale.wallet, op, GAS['send_verdict'])
return {'tx': tx}
+ @transaction_method
def send_verdicts(self, validator, nodes_ids, downtimes, latencies):
op = self.contract.functions.sendVerdicts(validator, nodes_ids,
downtimes, latencies)
tx = post_transaction(self.skale.wallet, op, GAS['send_verdicts'])
return {'tx': tx}
+ @transaction_method
def deregister(self, node_id):
op = self.contract.functions.deleteNode(node_id)
tx = post_transaction(self.skale.wallet, op, GAS['delete_node'])
return {'tx': tx}
+ @transaction_method
def delete_schain(self, schain_name):
op = self.contract.functions.deleteSchain(schain_name)
tx = post_transaction(self.skale.wallet, op, GAS['delete_schain'])
return {'tx': tx}
+ @transaction_method
def delete_node_by_root(self, node_id):
op = self.contract.functions.deleteNodeByRoot(node_id)
tx = post_transaction(self.skale.wallet, op, GAS['delete_node_by_root'])
diff --git a/skale/contracts/token.py b/skale/contracts/token.py
index c626dd89..d5b21e39 100644
--- a/skale/contracts/token.py
+++ b/skale/contracts/token.py
@@ -18,12 +18,13 @@
# along with SKALE.py. If not, see .
""" SKALE token operations """
-from skale.contracts import BaseContract
+from skale.contracts import BaseContract, transaction_method
from skale.transactions.tools import post_transaction
from skale.utils.constants import GAS
class Token(BaseContract):
+ @transaction_method
def transfer(self, address, value):
op = self.contract.functions.send(address, value, b'')
tx = post_transaction(self.skale.wallet, op, GAS['token_transfer'])
@@ -32,6 +33,7 @@ def transfer(self, address, value):
def get_balance(self, address):
return self.contract.functions.balanceOf(address).call()
+ @transaction_method
def add_authorized(self, address, wallet): # pragma: no cover
op = self.contract.functions.addAuthorized(address)
tx = post_transaction(self.skale.wallet, op, GAS['token_transfer'])
diff --git a/skale/utils/web3_utils.py b/skale/utils/web3_utils.py
index 646fb32d..15193e81 100644
--- a/skale/utils/web3_utils.py
+++ b/skale/utils/web3_utils.py
@@ -29,6 +29,10 @@
logger = logging.getLogger(__name__)
+class TransactionFailedError(Exception):
+ pass
+
+
def get_provider(endpoint):
scheme = urlparse(endpoint).scheme
if scheme == 'ws' or scheme == 'wss':
diff --git a/tests/contracts/manager_test.py b/tests/contracts/manager_test.py
index 74e4c273..cd8d88e4 100644
--- a/tests/contracts/manager_test.py
+++ b/tests/contracts/manager_test.py
@@ -1,12 +1,14 @@
""" SKALE manager test """
import mock
+import pytest
import web3
from hexbytes import HexBytes
import skale.utils.helper as helper
from skale.utils.constants import GAS
-from skale.utils.web3_utils import wait_receipt, private_key_to_public
+from skale.utils.web3_utils import (wait_receipt, private_key_to_public,
+ TransactionFailedError)
from tests.constants import DEFAULT_NODE_NAME, SECOND_NODE_NAME
from tests.utils import generate_random_node_data, generate_random_schain_data
@@ -232,3 +234,43 @@ def test_create_delete_schain(skale):
for sid in schains_ids_after
]
assert name not in schains_names
+
+
+def test_create_delete_default_schain(skale):
+ schains_ids = skale.schains_data.get_all_schains_ids()
+ name = 'default-schain'
+ res = skale.manager.create_default_schain(name)
+ assert res['status'] == 1
+
+ schains_ids_number_after = skale.schains_data.get_schains_number()
+ assert schains_ids_number_after == len(schains_ids) + 1
+ schains_ids_after = skale.schains_data.get_all_schains_ids()
+
+ schains_names = [
+ skale.schains_data.get(sid)['name']
+ for sid in schains_ids_after
+ ]
+ assert name in schains_names
+
+ res = skale.manager.delete_schain(name, wait_for=True)
+ assert res['status'] == 1
+
+ schains_ids_number_after = skale.schains_data.get_schains_number()
+ assert schains_ids_number_after == len(schains_ids)
+ schains_ids_after = skale.schains_data.get_all_schains_ids()
+
+ schains_names = [
+ skale.schains_data.get(sid)['name']
+ for sid in schains_ids_after
+ ]
+ assert name not in schains_names
+
+
+def test_create_node_status_0(skale):
+ ip, public_ip, port, name = generate_random_node_data()
+ with mock.patch.object(web3.eth.Eth, 'sendRawTransaction') as send_tx_mock:
+ send_tx_mock.return_value = b'hexstring'
+ with mock.patch('skale.contracts.base_contract.wait_receipt',
+ return_value={'status': 0}):
+ with pytest.raises(TransactionFailedError):
+ skale.manager.create_node(ip, port, name, wait_for=True)