olas-operate-middleware 0.10.7__py3-none-any.whl → 0.10.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
operate/migration.py CHANGED
@@ -29,11 +29,10 @@ from time import time
29
29
 
30
30
  from aea_cli_ipfs.ipfs_utils import IPFSTool
31
31
 
32
- from operate.constants import ZERO_ADDRESS
32
+ from operate.constants import USER_JSON, ZERO_ADDRESS
33
33
  from operate.operate_types import Chain, LedgerType
34
34
  from operate.services.manage import ServiceManager
35
35
  from operate.services.service import (
36
- DEFAULT_TRADER_ENV_VARS,
37
36
  NON_EXISTENT_MULTISIG,
38
37
  SERVICE_CONFIG_PREFIX,
39
38
  SERVICE_CONFIG_VERSION,
@@ -43,11 +42,67 @@ from operate.utils import create_backup
43
42
  from operate.wallet.master import LEDGER_TYPE_TO_WALLET_CLASS, MasterWalletManager
44
43
 
45
44
 
45
+ DEFAULT_TRADER_ENV_VARS = {
46
+ "GNOSIS_LEDGER_RPC": {
47
+ "name": "Gnosis ledger RPC",
48
+ "description": "",
49
+ "value": "",
50
+ "provision_type": "computed",
51
+ },
52
+ "STAKING_CONTRACT_ADDRESS": {
53
+ "name": "Staking contract address",
54
+ "description": "",
55
+ "value": "",
56
+ "provision_type": "computed",
57
+ },
58
+ "MECH_MARKETPLACE_CONFIG": {
59
+ "name": "Mech marketplace configuration",
60
+ "description": "",
61
+ "value": "",
62
+ "provision_type": "computed",
63
+ },
64
+ "MECH_ACTIVITY_CHECKER_CONTRACT": {
65
+ "name": "Mech activity checker contract",
66
+ "description": "",
67
+ "value": "",
68
+ "provision_type": "computed",
69
+ },
70
+ "MECH_CONTRACT_ADDRESS": {
71
+ "name": "Mech contract address",
72
+ "description": "",
73
+ "value": "",
74
+ "provision_type": "computed",
75
+ },
76
+ "MECH_REQUEST_PRICE": {
77
+ "name": "Mech request price",
78
+ "description": "",
79
+ "value": "10000000000000000",
80
+ "provision_type": "computed",
81
+ },
82
+ "USE_MECH_MARKETPLACE": {
83
+ "name": "Use Mech marketplace",
84
+ "description": "",
85
+ "value": "False",
86
+ "provision_type": "computed",
87
+ },
88
+ "REQUESTER_STAKING_INSTANCE_ADDRESS": {
89
+ "name": "Requester staking instance address",
90
+ "description": "",
91
+ "value": "",
92
+ "provision_type": "computed",
93
+ },
94
+ "PRIORITY_MECH_ADDRESS": {
95
+ "name": "Priority Mech address",
96
+ "description": "",
97
+ "value": "",
98
+ "provision_type": "computed",
99
+ },
100
+ }
101
+
102
+
46
103
  class MigrationManager:
47
104
  """MigrationManager"""
48
105
 
49
- # TODO Backport here migration for services/config.json, etc.
50
-
51
106
  def __init__(
52
107
  self,
53
108
  home: Path,
@@ -62,12 +117,12 @@ class MigrationManager:
62
117
  """Log directories present in `path`."""
63
118
  directories = [f" - {str(p)}" for p in path.iterdir() if p.is_dir()]
64
119
  directories_str = "\n".join(directories)
65
- self.logger.info(f"Directories in {path}\n: {directories_str}")
120
+ self.logger.info(f"Directories in {path}:\n{directories_str}")
66
121
 
67
122
  def migrate_user_account(self) -> None:
68
123
  """Migrates user.json"""
69
124
 
70
- path = self._path / "user.json"
125
+ path = self._path / USER_JSON
71
126
  if not path.exists():
72
127
  return
73
128
 
@@ -102,18 +157,22 @@ class MigrationManager:
102
157
 
103
158
  self.logger.info("Migrating wallet configs done.")
104
159
 
105
- @staticmethod
106
160
  def _migrate_service( # pylint: disable=too-many-statements,too-many-locals
161
+ self,
107
162
  path: Path,
108
163
  ) -> bool:
109
164
  """Migrate the JSON file format if needed."""
110
165
 
111
166
  if not path.is_dir():
167
+ self.logger.warning(f"Service config path {path} is not a directory.")
112
168
  return False
113
169
 
114
170
  if not path.name.startswith(SERVICE_CONFIG_PREFIX) and not path.name.startswith(
115
171
  "bafybei"
116
172
  ):
173
+ self.logger.warning(
174
+ f"Service config path {path} is not a valid service config."
175
+ )
117
176
  return False
118
177
 
119
178
  if path.name.startswith("bafybei"):
@@ -155,6 +214,10 @@ class MigrationManager:
155
214
  if version == SERVICE_CONFIG_VERSION:
156
215
  return False
157
216
 
217
+ self.logger.info(
218
+ f"Migrating service config in {path} from version {version} to {SERVICE_CONFIG_VERSION}..."
219
+ )
220
+
158
221
  # Migration steps for older versions
159
222
  if version == 0:
160
223
  new_data = {
@@ -344,13 +407,9 @@ class MigrationManager:
344
407
  paths = list(service_manager.path.iterdir())
345
408
  for path in paths:
346
409
  try:
347
- if path.name.startswith(SERVICE_CONFIG_PREFIX) or path.name.startswith(
348
- "bafybei"
349
- ):
350
- self.logger.info(f"migrate_service_configs {str(path)}")
351
- migrated = self._migrate_service(path)
352
- if migrated:
353
- self.logger.info(f"Folder {str(path)} has been migrated.")
410
+ migrated = self._migrate_service(path)
411
+ if migrated:
412
+ self.logger.info(f"Folder {str(path)} has been migrated.")
354
413
  except Exception as e: # pylint: disable=broad-except
355
414
  self.logger.error(
356
415
  f"Failed to migrate service: {path.name}. Exception {e}: {traceback.format_exc()}"
@@ -25,14 +25,13 @@ import warnings
25
25
  from typing import TYPE_CHECKING, cast
26
26
 
27
27
  from operate.constants import SAFE_WEBAPP_URL
28
- from operate.ledger.profiles import get_staking_contract
29
28
  from operate.quickstart.run_service import (
30
29
  ask_password_if_needed,
31
30
  configure_local_config,
32
31
  get_service,
33
32
  load_local_config,
34
33
  )
35
- from operate.quickstart.utils import ask_yes_or_no, print_section, print_title
34
+ from operate.quickstart.utils import ask_yes_or_no, print_section
36
35
 
37
36
 
38
37
  if TYPE_CHECKING:
@@ -74,6 +73,10 @@ def claim_staking_rewards(operate: "OperateApp", config_path: str) -> None:
74
73
 
75
74
  # reload manger and config after setting operate.password
76
75
  manager = operate.service_manager()
76
+ chain_config = service.chain_configs[config.principal_chain]
77
+ wallet = operate.wallet_manager.load(
78
+ ledger_type=chain_config.ledger_config.chain.ledger_type
79
+ )
77
80
  config = load_local_config(operate=operate, service_name=cast(str, service.name))
78
81
  assert ( # nosec
79
82
  config.principal_chain is not None
@@ -81,23 +84,8 @@ def claim_staking_rewards(operate: "OperateApp", config_path: str) -> None:
81
84
  assert config.rpc is not None, "RPC not set in quickstart config" # nosec
82
85
  os.environ["CUSTOM_CHAIN_RPC"] = config.rpc[config.principal_chain]
83
86
 
84
- chain_config = service.chain_configs[config.principal_chain]
85
- sftxb = manager.get_eth_safe_tx_builder(
86
- ledger_config=chain_config.ledger_config,
87
- )
88
- staking_contract = get_staking_contract(
89
- chain=config.principal_chain,
90
- staking_program_id=config.staking_program_id,
91
- )
92
- if not staking_contract or not sftxb.staking_rewards_claimable(
93
- service_id=chain_config.chain_data.token,
94
- staking_contract=staking_contract,
95
- ):
96
- print("No rewards to claim. Exiting.")
97
- return
98
-
99
87
  try:
100
- tx_hash = manager.claim_on_chain_from_safe(
88
+ manager.claim_on_chain_from_safe(
101
89
  service_config_id=service.service_config_id,
102
90
  chain=config.principal_chain,
103
91
  )
@@ -109,7 +97,8 @@ def claim_staking_rewards(operate: "OperateApp", config_path: str) -> None:
109
97
  logging.error(e)
110
98
  return
111
99
 
112
- service_safe_address = chain_config.chain_data.multisig
113
- print_title(f"Claim transaction done. Hash: {tx_hash.hex()}")
114
- print(f"Claimed staking transferred to your service Safe {service_safe_address}.\n")
115
- print(f"You may connect to service Safe at {SAFE_WEBAPP_URL}{service_safe_address}")
100
+ master_safe = wallet.safes[chain_config.ledger_config.chain]
101
+ print(
102
+ f"Claimed staking rewards are transferred to your Master Safe {master_safe}.\n"
103
+ )
104
+ print(f"You may connect to the Master Safe at {SAFE_WEBAPP_URL}{master_safe}")
@@ -21,6 +21,7 @@
21
21
  from typing import TYPE_CHECKING
22
22
 
23
23
  from operate.account.user import UserAccount
24
+ from operate.constants import USER_JSON
24
25
  from operate.operate_types import LedgerType
25
26
  from operate.quickstart.run_service import ask_confirm_password
26
27
  from operate.quickstart.utils import ask_or_get_from_env, print_section, print_title
@@ -36,7 +37,7 @@ def reset_password(operate: "OperateApp") -> None:
36
37
  print_title("Reset your password")
37
38
 
38
39
  # check if agent was started before
39
- if not (operate._path / "user.json").exists():
40
+ if not (operate._path / USER_JSON).exists():
40
41
  print("No previous agent setup found. Exiting.")
41
42
  return
42
43
 
@@ -57,7 +58,7 @@ def reset_password(operate: "OperateApp") -> None:
57
58
  print("Resetting password of user account...")
58
59
  UserAccount.new(
59
60
  password=old_password,
60
- path=operate._path / "user.json",
61
+ path=operate._path / USER_JSON,
61
62
  ).update(
62
63
  old_password=old_password,
63
64
  new_password=new_password,
@@ -34,7 +34,12 @@ from halo import Halo # type: ignore[import]
34
34
  from web3.exceptions import Web3Exception
35
35
 
36
36
  from operate.account.user import UserAccount
37
- from operate.constants import IPFS_ADDRESS, NO_STAKING_PROGRAM_ID, OPERATE_HOME
37
+ from operate.constants import (
38
+ IPFS_ADDRESS,
39
+ NO_STAKING_PROGRAM_ID,
40
+ OPERATE_HOME,
41
+ USER_JSON,
42
+ )
38
43
  from operate.data import DATA_DIR
39
44
  from operate.data.contracts.staking_token.contract import StakingTokenContract
40
45
  from operate.ledger.profiles import STAKING, get_staking_contract
@@ -453,7 +458,7 @@ def ask_password_if_needed(operate: "OperateApp") -> None:
453
458
  password = ask_confirm_password()
454
459
  UserAccount.new(
455
460
  password=password,
456
- path=operate._path / "user.json",
461
+ path=operate._path / USER_JSON,
457
462
  )
458
463
  else:
459
464
  _password = None
@@ -23,7 +23,6 @@ import asyncio
23
23
  import json
24
24
  import logging
25
25
  import os
26
- import tempfile
27
26
  import traceback
28
27
  import typing as t
29
28
  from collections import Counter, defaultdict
@@ -31,16 +30,24 @@ from concurrent.futures import ThreadPoolExecutor
31
30
  from contextlib import suppress
32
31
  from http import HTTPStatus
33
32
  from pathlib import Path
33
+ from time import time
34
34
 
35
35
  import requests
36
36
  from aea.helpers.base import IPFSHash
37
- from aea_ledger_ethereum import EthereumCrypto, LedgerApi
37
+ from aea_ledger_ethereum import LedgerApi
38
38
  from autonomy.chain.base import registry_contracts
39
39
  from autonomy.chain.config import CHAIN_PROFILES, ChainType
40
40
  from autonomy.chain.metadata import IPFS_URI_PREFIX
41
41
  from web3 import Web3
42
42
 
43
- from operate.constants import IPFS_ADDRESS, ZERO_ADDRESS
43
+ from operate.constants import (
44
+ AGENT_LOG_DIR,
45
+ AGENT_LOG_ENV_VAR,
46
+ AGENT_PERSISTENT_STORAGE_DIR,
47
+ AGENT_PERSISTENT_STORAGE_ENV_VAR,
48
+ IPFS_ADDRESS,
49
+ ZERO_ADDRESS,
50
+ )
44
51
  from operate.data import DATA_DIR
45
52
  from operate.data.contracts.mech_activity.contract import MechActivityContract
46
53
  from operate.data.contracts.requester_activity_checker.contract import (
@@ -48,7 +55,7 @@ from operate.data.contracts.requester_activity_checker.contract import (
48
55
  )
49
56
  from operate.data.contracts.staking_token.contract import StakingTokenContract
50
57
  from operate.keys import KeysManager
51
- from operate.ledger import PUBLIC_RPCS, get_currency_denom
58
+ from operate.ledger import get_currency_denom, get_default_rpc
52
59
  from operate.ledger.profiles import (
53
60
  CONTRACTS,
54
61
  DEFAULT_MASTER_EOA_FUNDS,
@@ -695,12 +702,15 @@ class ServiceManager:
695
702
 
696
703
  env_var_to_value.update(
697
704
  {
698
- "ETHEREUM_LEDGER_RPC": PUBLIC_RPCS[Chain.ETHEREUM],
699
- "GNOSIS_LEDGER_RPC": PUBLIC_RPCS[Chain.GNOSIS],
700
- "BASE_LEDGER_RPC": PUBLIC_RPCS[Chain.BASE],
701
- "CELO_LEDGER_RPC": PUBLIC_RPCS[Chain.CELO],
702
- "OPTIMISM_LEDGER_RPC": PUBLIC_RPCS[Chain.OPTIMISM],
703
- "MODE_LEDGER_RPC": PUBLIC_RPCS[Chain.MODE],
705
+ "ARBITRUM_ONE_LEDGER_RPC": get_default_rpc(Chain.ARBITRUM_ONE),
706
+ "BASE_LEDGER_RPC": get_default_rpc(Chain.BASE),
707
+ "CELO_LEDGER_RPC": get_default_rpc(Chain.CELO),
708
+ "ETHEREUM_LEDGER_RPC": get_default_rpc(Chain.ETHEREUM),
709
+ "GNOSIS_LEDGER_RPC": get_default_rpc(Chain.GNOSIS),
710
+ "MODE_LEDGER_RPC": get_default_rpc(Chain.MODE),
711
+ "OPTIMISM_LEDGER_RPC": get_default_rpc(Chain.OPTIMISM),
712
+ "POLYGON_LEDGER_RPC": get_default_rpc(Chain.POLYGON),
713
+ "SOLANA_LEDGER_RPC": get_default_rpc(Chain.SOLANA),
704
714
  f"{chain.upper()}_LEDGER_RPC": ledger_config.rpc,
705
715
  "STAKING_CONTRACT_ADDRESS": target_staking_params.get(
706
716
  "staking_contract"
@@ -730,8 +740,8 @@ class ServiceManager:
730
740
 
731
741
  # Set environment variables for the service
732
742
  for dir_name, env_var_name in (
733
- ("persistent_data", "STORE_PATH"),
734
- ("benchmarks", "LOG_DIR"),
743
+ (AGENT_PERSISTENT_STORAGE_DIR, AGENT_PERSISTENT_STORAGE_ENV_VAR),
744
+ (AGENT_LOG_DIR, AGENT_LOG_ENV_VAR),
735
745
  ):
736
746
  dir_path = service.path / dir_name
737
747
  dir_path.mkdir(parents=True, exist_ok=True)
@@ -1257,6 +1267,12 @@ class ServiceManager:
1257
1267
  chain=chain,
1258
1268
  staking_program_id=current_staking_program,
1259
1269
  )
1270
+ else:
1271
+ # at least claim the rewards if we cannot unstake yet
1272
+ self.claim_on_chain_from_safe(
1273
+ service_config_id=service_config_id,
1274
+ chain=chain,
1275
+ )
1260
1276
 
1261
1277
  if self._get_on_chain_state(service=service, chain=chain) in (
1262
1278
  OnChainState.ACTIVE_REGISTRATION,
@@ -1318,18 +1334,17 @@ class ServiceManager:
1318
1334
  service_config_id=service_config_id, chain=chain
1319
1335
  )
1320
1336
  self.logger.info("Swapping Safe owners")
1321
- sftxb.swap( # noqa: E800
1322
- service_id=chain_data.token, # noqa: E800
1337
+ owner_crypto = self.keys_manager.get_crypto_instance(
1338
+ address=current_safe_owners[0]
1339
+ )
1340
+ sftxb.swap(
1341
+ service_id=chain_data.token,
1323
1342
  multisig=chain_data.multisig, # TODO this can be read from the registry
1324
- owner_key=str(
1325
- self.keys_manager.get(
1326
- key=current_safe_owners[0]
1327
- ).private_key # TODO allow multiple owners
1328
- ), # noqa: E800
1343
+ owner_cryptos=[owner_crypto], # TODO allow multiple owners
1329
1344
  new_owner_address=(
1330
1345
  safe if safe else wallet.crypto.address
1331
1346
  ), # TODO it should always be safe address
1332
- ) # noqa: E800
1347
+ )
1333
1348
 
1334
1349
  if withdrawal_address is not None:
1335
1350
  ethereum_crypto = KeysManager().get_crypto_instance(
@@ -1457,26 +1472,18 @@ class ServiceManager:
1457
1472
  if agent_is_service_safe_owner:
1458
1473
  self.logger.info("(Agent) Enabling recovery module in service Safe.")
1459
1474
  try:
1460
- with tempfile.NamedTemporaryFile(mode="w+", delete=True) as tmp_file:
1461
- private_key = self.keys_manager.get(key=agent_address).private_key
1462
- tmp_file.write(private_key)
1463
- tmp_file.flush()
1464
- crypto = EthereumCrypto(private_key_path=tmp_file.name)
1465
- EthSafeTxBuilder._new_tx( # pylint: disable=protected-access
1466
- ledger_api=sftxb.ledger_api,
1467
- crypto=crypto,
1468
- chain_type=ChainType(chain),
1469
- safe=service_safe_address,
1470
- ).add(
1471
- sftxb.get_enable_module_data(
1472
- module_address=recovery_module_address,
1473
- safe_address=service_safe_address,
1474
- )
1475
- ).settle()
1476
- tmp_file.seek(0)
1477
- tmp_file.write("\0" * len(private_key))
1478
- tmp_file.flush()
1479
-
1475
+ crypto = self.keys_manager.get_crypto_instance(address=agent_address)
1476
+ EthSafeTxBuilder._new_tx( # pylint: disable=protected-access
1477
+ ledger_api=sftxb.ledger_api,
1478
+ crypto=crypto,
1479
+ chain_type=ChainType(chain),
1480
+ safe=service_safe_address,
1481
+ ).add(
1482
+ sftxb.get_enable_module_data(
1483
+ module_address=recovery_module_address,
1484
+ safe_address=service_safe_address,
1485
+ )
1486
+ ).settle()
1480
1487
  self.logger.info(
1481
1488
  "(Agent) Recovery module enabled successfully in service Safe."
1482
1489
  )
@@ -1834,7 +1841,12 @@ class ServiceManager:
1834
1841
  staking_program_id: t.Optional[str] = None,
1835
1842
  force: bool = False,
1836
1843
  ) -> None:
1837
- """Unbond service on-chain"""
1844
+ """Unstake service on-chain"""
1845
+ # Claim the rewards first so that they are moved to the Master Safe
1846
+ self.claim_on_chain_from_safe(
1847
+ service_config_id=service_config_id,
1848
+ chain=chain,
1849
+ )
1838
1850
 
1839
1851
  self.logger.info("unstake_service_on_chain_from_safe")
1840
1852
  service = self.load(service_config_id=service_config_id)
@@ -1877,38 +1889,42 @@ class ServiceManager:
1877
1889
  self,
1878
1890
  service_config_id: str,
1879
1891
  chain: str,
1880
- ) -> str:
1881
- """Claim rewards from Safe and returns transaction hash"""
1892
+ ) -> int:
1893
+ """Claim rewards from staking and returns the claimed amount"""
1882
1894
  self.logger.info("claim_on_chain_from_safe")
1883
1895
  service = self.load(service_config_id=service_config_id)
1884
1896
  chain_config = service.chain_configs[chain]
1885
1897
  ledger_config = chain_config.ledger_config
1886
- chain_data = chain_config.chain_data
1887
- staking_program_id = chain_data.user_params.staking_program_id
1898
+ staking_program_id = chain_config.chain_data.user_params.staking_program_id
1888
1899
  wallet = self.wallet_manager.load(ledger_config.chain.ledger_type)
1889
1900
  ledger_api = wallet.ledger_api(chain=ledger_config.chain, rpc=ledger_config.rpc)
1890
- print(
1891
- f"OLAS Balance on service Safe {chain_data.multisig}: "
1892
- f"{get_asset_balance(ledger_api, OLAS[Chain(chain)], chain_data.multisig)}"
1901
+ self.logger.info(
1902
+ f"OLAS Balance on service Safe {chain_config.chain_data.multisig}: "
1903
+ f"{get_asset_balance(ledger_api, OLAS[Chain(chain)], chain_config.chain_data.multisig)}"
1893
1904
  )
1894
- if (
1895
- get_staking_contract(
1896
- chain=ledger_config.chain,
1897
- staking_program_id=staking_program_id,
1898
- )
1899
- is None
1900
- ):
1905
+ staking_contract = get_staking_contract(
1906
+ chain=ledger_config.chain,
1907
+ staking_program_id=staking_program_id,
1908
+ )
1909
+ if staking_contract is None:
1901
1910
  raise RuntimeError(
1902
1911
  "No staking contract found for the current staking_program_id: "
1903
1912
  f"{staking_program_id}. Not claiming the rewards."
1904
1913
  )
1905
1914
 
1906
1915
  sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)
1916
+ if not sftxb.staking_rewards_claimable(
1917
+ service_id=chain_config.chain_data.token,
1918
+ staking_contract=staking_contract,
1919
+ ):
1920
+ self.logger.info("No staking rewards claimable")
1921
+ return 0
1922
+
1907
1923
  receipt = (
1908
1924
  sftxb.new_tx()
1909
1925
  .add(
1910
1926
  sftxb.get_claiming_data(
1911
- service_id=chain_data.token,
1927
+ service_id=chain_config.chain_data.token,
1912
1928
  staking_contract=get_staking_contract(
1913
1929
  chain=ledger_config.chain,
1914
1930
  staking_program_id=staking_program_id,
@@ -1917,7 +1933,21 @@ class ServiceManager:
1917
1933
  )
1918
1934
  .settle()
1919
1935
  )
1920
- return receipt["transactionHash"]
1936
+
1937
+ # transfer claimed amount from agents safe to master safe
1938
+ # TODO: remove after staking contract directly starts sending the rewards to master safe
1939
+ amount_claimed = receipt["logs"][0]["data"]
1940
+ self.logger.info(f"Claimed amount: {amount_claimed}")
1941
+ ethereum_crypto = KeysManager().get_crypto_instance(service.agent_addresses[0])
1942
+ transfer_erc20_from_safe(
1943
+ ledger_api=ledger_api,
1944
+ crypto=ethereum_crypto,
1945
+ safe=chain_config.chain_data.multisig,
1946
+ token=receipt["logs"][0]["address"],
1947
+ to=wallet.safes[Chain(chain)],
1948
+ amount=amount_claimed,
1949
+ )
1950
+ return amount_claimed
1921
1951
 
1922
1952
  def fund_service( # pylint: disable=too-many-arguments,too-many-locals
1923
1953
  self,
@@ -2254,6 +2284,7 @@ class ServiceManager:
2254
2284
  task = asyncio.current_task()
2255
2285
  task_id = id(task) if task else "Unknown task_id"
2256
2286
  with ThreadPoolExecutor() as executor:
2287
+ last_claim = 0
2257
2288
  while True:
2258
2289
  try:
2259
2290
  await loop.run_in_executor(
@@ -2285,6 +2316,22 @@ class ServiceManager:
2285
2316
  logging.info(
2286
2317
  f"Error occured while funding the service\n{traceback.format_exc()}"
2287
2318
  )
2319
+
2320
+ # try claiming rewards every hour
2321
+ if last_claim + 3600 < time():
2322
+ try:
2323
+ await loop.run_in_executor(
2324
+ executor,
2325
+ self.claim_on_chain_from_safe,
2326
+ service_config_id=service_config_id,
2327
+ chain=service.home_chain,
2328
+ )
2329
+ except Exception: # pylint: disable=broad-except
2330
+ logging.info(
2331
+ f"Error occured while claiming rewards\n{traceback.format_exc()}"
2332
+ )
2333
+ last_claim = time()
2334
+
2288
2335
  await asyncio.sleep(60)
2289
2336
 
2290
2337
  def deploy_service_locally(
@@ -2341,12 +2388,6 @@ class ServiceManager:
2341
2388
  deployment.delete()
2342
2389
  return deployment
2343
2390
 
2344
- def log_directories(self) -> None:
2345
- """Log directories."""
2346
- directories = [f" - {str(p)}" for p in self.path.iterdir() if p.is_dir()]
2347
- directories_str = "\n".join(directories)
2348
- self.logger.info(f"Directories in {self.path}\n: {directories_str}")
2349
-
2350
2391
  def update(
2351
2392
  self,
2352
2393
  service_config_id: str,
@@ -34,7 +34,6 @@ from typing import Optional, Union, cast
34
34
  from aea.configurations.data_types import PackageType
35
35
  from aea.crypto.base import Crypto, LedgerApi
36
36
  from aea.helpers.base import IPFSHash, cd
37
- from aea_ledger_ethereum.ethereum import EthereumCrypto
38
37
  from autonomy.chain.base import registry_contracts
39
38
  from autonomy.chain.config import ChainConfigs, ChainType, ContractConfigs
40
39
  from autonomy.chain.constants import (
@@ -722,7 +721,11 @@ class _ChainUtil:
722
721
  ).get("owners", [])
723
722
 
724
723
  def swap( # pylint: disable=too-many-arguments,too-many-locals
725
- self, service_id: int, multisig: str, owner_key: str, new_owner_address: str
724
+ self,
725
+ service_id: int,
726
+ multisig: str,
727
+ owner_cryptos: t.List[Crypto],
728
+ new_owner_address: str,
726
729
  ) -> None:
727
730
  """Swap safe owner."""
728
731
  logging.info(f"Swapping safe for service {service_id} [{multisig}]...")
@@ -736,11 +739,6 @@ class _ChainUtil:
736
739
  retries=ON_CHAIN_INTERACT_RETRIES,
737
740
  sleep=ON_CHAIN_INTERACT_SLEEP,
738
741
  )
739
- with tempfile.TemporaryDirectory() as temp_dir:
740
- key_file = Path(temp_dir, "key.txt")
741
- key_file.write_text(owner_key, encoding="utf-8")
742
- owner_crypto = EthereumCrypto(private_key_path=str(key_file))
743
- owner_cryptos: t.List[EthereumCrypto] = [owner_crypto]
744
742
  owners = [
745
743
  manager.ledger_api.api.to_checksum_address(owner_crypto.address)
746
744
  for owner_crypto in owner_cryptos
@@ -797,7 +795,7 @@ class _ChainUtil:
797
795
  tx = registry_contracts.gnosis_safe.get_raw_safe_transaction(
798
796
  ledger_api=manager.ledger_api,
799
797
  contract_address=multisig,
800
- sender_address=owner_crypto.address,
798
+ sender_address=owner_cryptos[0].address,
801
799
  owners=tuple(owners), # type: ignore
802
800
  to_address=tx_params["to_address"],
803
801
  value=tx_params["ether_value"],
@@ -806,7 +804,7 @@ class _ChainUtil:
806
804
  signatures_by_owner=owner_to_signature,
807
805
  operation=SafeOperation.DELEGATE_CALL.value,
808
806
  )
809
- stx = owner_crypto.sign_transaction(tx)
807
+ stx = owner_cryptos[0].sign_transaction(tx)
810
808
  tx_digest = manager.ledger_api.send_signed_transaction(stx)
811
809
  receipt = manager.ledger_api.api.eth.wait_for_transaction_receipt(tx_digest)
812
810
  if receipt["status"] != 1: