olas-operate-middleware 0.8.2__py3-none-any.whl → 0.10.0__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.
Files changed (34) hide show
  1. {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/METADATA +2 -2
  2. {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/RECORD +34 -34
  3. operate/bridge/bridge_manager.py +5 -6
  4. operate/bridge/providers/native_bridge_provider.py +1 -1
  5. operate/bridge/providers/provider.py +4 -5
  6. operate/bridge/providers/relay_provider.py +1 -1
  7. operate/cli.py +128 -48
  8. operate/constants.py +9 -9
  9. operate/keys.py +26 -14
  10. operate/ledger/__init__.py +4 -4
  11. operate/ledger/profiles.py +9 -11
  12. operate/migration.py +326 -0
  13. operate/operate_types.py +9 -27
  14. operate/quickstart/analyse_logs.py +3 -6
  15. operate/quickstart/claim_staking_rewards.py +1 -4
  16. operate/quickstart/reset_configs.py +0 -3
  17. operate/quickstart/reset_password.py +0 -3
  18. operate/quickstart/reset_staking.py +3 -5
  19. operate/quickstart/run_service.py +5 -7
  20. operate/quickstart/stop_service.py +3 -4
  21. operate/quickstart/terminate_on_chain_service.py +1 -4
  22. operate/quickstart/utils.py +4 -7
  23. operate/resource.py +37 -5
  24. operate/services/deployment_runner.py +170 -38
  25. operate/services/health_checker.py +5 -8
  26. operate/services/manage.py +103 -164
  27. operate/services/protocol.py +5 -5
  28. operate/services/service.py +42 -242
  29. operate/utils/__init__.py +44 -0
  30. operate/utils/gnosis.py +25 -17
  31. operate/wallet/master.py +20 -24
  32. {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/LICENSE +0 -0
  33. {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/WHEEL +0 -0
  34. {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/entry_points.txt +0 -0
@@ -23,8 +23,6 @@ import asyncio
23
23
  import json
24
24
  import logging
25
25
  import os
26
- import shutil
27
- import time
28
26
  import traceback
29
27
  import typing as t
30
28
  from collections import Counter, defaultdict
@@ -35,19 +33,19 @@ from pathlib import Path
35
33
 
36
34
  import requests
37
35
  from aea.helpers.base import IPFSHash
38
- from aea.helpers.logging import setup_logger
39
- from aea_ledger_ethereum import EthereumCrypto, LedgerApi
36
+ from aea_ledger_ethereum import LedgerApi
40
37
  from autonomy.chain.base import registry_contracts
41
38
  from autonomy.chain.config import CHAIN_PROFILES, ChainType
39
+ from autonomy.chain.metadata import IPFS_URI_PREFIX
42
40
 
43
- from operate.constants import ZERO_ADDRESS
41
+ from operate.constants import IPFS_ADDRESS, ZERO_ADDRESS
44
42
  from operate.data import DATA_DIR
45
43
  from operate.data.contracts.mech_activity.contract import MechActivityContract
46
44
  from operate.data.contracts.requester_activity_checker.contract import (
47
45
  RequesterActivityCheckerContract,
48
46
  )
49
47
  from operate.data.contracts.staking_token.contract import StakingTokenContract
50
- from operate.keys import Key, KeysManager
48
+ from operate.keys import KeysManager
51
49
  from operate.ledger import PUBLIC_RPCS, get_currency_denom
52
50
  from operate.ledger.profiles import (
53
51
  CONTRACTS,
@@ -71,41 +69,22 @@ from operate.operate_types import (
71
69
  from operate.services.protocol import EthSafeTxBuilder, OnChainManager, StakingState
72
70
  from operate.services.service import (
73
71
  ChainConfig,
74
- DELETE_PREFIX,
75
72
  Deployment,
76
73
  NON_EXISTENT_MULTISIG,
77
74
  NON_EXISTENT_TOKEN,
78
75
  OnChainData,
79
76
  SERVICE_CONFIG_PREFIX,
77
+ SERVICE_CONFIG_VERSION,
80
78
  Service,
81
79
  )
82
80
  from operate.services.utils.mech import deploy_mech
83
- from operate.utils.gnosis import (
84
- NULL_ADDRESS,
85
- drain_eoa,
86
- get_asset_balance,
87
- get_assets_balances,
88
- )
81
+ from operate.utils.gnosis import drain_eoa, get_asset_balance, get_assets_balances
89
82
  from operate.utils.gnosis import transfer as transfer_from_safe
90
83
  from operate.utils.gnosis import transfer_erc20_from_safe
91
84
  from operate.wallet.master import MasterWalletManager
92
85
 
93
86
 
94
87
  # pylint: disable=redefined-builtin
95
-
96
- OPERATE = ".operate"
97
- CONFIG = "config.json"
98
- SERVICES = "services"
99
- KEYS = "keys"
100
- DEPLOYMENT = "deployment"
101
- CONFIG = "config.json"
102
- KEY = "master-key.txt"
103
- KEYS_JSON = "keys.json"
104
- DOCKER_COMPOSE_YAML = "docker-compose.yaml"
105
- SERVICE_YAML = "service.yaml"
106
- HTTP_OK = 200
107
- URI_HASH_POSITION = 7
108
- IPFS_GATEWAY = "https://gateway.autonolas.tech/ipfs/"
109
88
  DEFAULT_TOPUP_THRESHOLD = 0.5
110
89
  # At the moment, we only support running one agent per service locally on a machine.
111
90
  # If multiple agents are provided in the service.yaml file, only the 0th index config will be used.
@@ -118,9 +97,8 @@ class ServiceManager:
118
97
  def __init__(
119
98
  self,
120
99
  path: Path,
121
- keys_manager: KeysManager,
122
100
  wallet_manager: MasterWalletManager,
123
- logger: t.Optional[logging.Logger] = None,
101
+ logger: logging.Logger,
124
102
  skip_dependency_check: t.Optional[bool] = False,
125
103
  ) -> None:
126
104
  """
@@ -132,43 +110,66 @@ class ServiceManager:
132
110
  :param logger: logging.Logger object.
133
111
  """
134
112
  self.path = path
135
- self.keys_manager = keys_manager
113
+ self.keys_manager = KeysManager()
136
114
  self.wallet_manager = wallet_manager
137
- self.logger = logger or setup_logger(name="operate.manager")
115
+ self.logger = logger
138
116
  self.skip_depencency_check = skip_dependency_check
139
117
 
140
118
  def setup(self) -> None:
141
119
  """Setup service manager."""
142
120
  self.path.mkdir(exist_ok=True)
143
121
 
144
- def _get_all_services(self) -> t.List[Service]:
122
+ def get_all_service_ids(self) -> t.List[str]:
123
+ """
124
+ Get all service ids.
125
+
126
+ :return: List of service ids.
127
+ """
128
+ return [
129
+ path.name
130
+ for path in self.path.iterdir()
131
+ if path.is_dir() and path.name.startswith(SERVICE_CONFIG_PREFIX)
132
+ ]
133
+
134
+ def get_all_services(self) -> t.Tuple[t.List[Service], bool]:
135
+ """Get all services."""
145
136
  services = []
137
+ success = True
146
138
  for path in self.path.iterdir():
147
139
  if not path.name.startswith(SERVICE_CONFIG_PREFIX):
148
140
  continue
149
141
  try:
150
142
  service = Service.load(path=path)
143
+ if service.version != SERVICE_CONFIG_VERSION:
144
+ self.logger.warning(
145
+ f"Service {path.name} has an unsupported version: {service.version}."
146
+ )
147
+ success = False
148
+ continue
149
+
151
150
  services.append(service)
152
- except ValueError as e:
153
- raise e
154
151
  except Exception as e: # pylint: disable=broad-except
155
152
  self.logger.error(
156
153
  f"Failed to load service: {path.name}. Exception {e}: {traceback.format_exc()}"
157
154
  )
158
- # Rename the invalid path
159
- timestamp = int(time.time())
160
- invalid_path = path.parent / f"invalid_{timestamp}_{path.name}"
161
- os.rename(path, invalid_path)
162
- self.logger.info(
163
- f"Renamed invalid service: {path.name} to {invalid_path.name}"
164
- )
155
+ success = False
165
156
 
166
- return services
157
+ return services, success
158
+
159
+ def validate_services(self) -> bool:
160
+ """
161
+ Validate all services.
162
+
163
+ :return: True if all services are valid, False otherwise.
164
+ """
165
+ _, success = self.get_all_services()
166
+ return success
167
167
 
168
168
  @property
169
169
  def json(self) -> t.List[t.Dict]:
170
170
  """Returns the list of available services."""
171
- return [service.json for service in self._get_all_services()]
171
+ services, _ = self.get_all_services()
172
+ return [service.json for service in services]
172
173
 
173
174
  def exists(self, service_config_id: str) -> bool:
174
175
  """Check if service exists."""
@@ -196,14 +197,14 @@ class ServiceManager:
196
197
  self,
197
198
  hash: str,
198
199
  service_template: t.Optional[ServiceTemplate] = None,
199
- keys: t.Optional[t.List[Key]] = None,
200
+ agent_addresses: t.Optional[t.List[str]] = None,
200
201
  ) -> Service:
201
202
  """
202
203
  Create or load a service
203
204
 
204
205
  :param hash: Service hash
205
206
  :param service_template: Service template
206
- :param keys: Keys
207
+ :param agent_addresses: Agents' addresses to be used for the service.
207
208
  :return: Service instance
208
209
  """
209
210
  path = self.path / hash
@@ -222,21 +223,10 @@ class ServiceManager:
222
223
  "'service_template' cannot be None when creating a new service"
223
224
  )
224
225
 
225
- service = Service.new(
226
- keys=keys or [],
227
- storage=self.path,
228
- service_template=service_template,
226
+ return self.create(
227
+ service_template=service_template, agent_addresses=agent_addresses
229
228
  )
230
229
 
231
- if not service.keys:
232
- service.keys = [
233
- self.keys_manager.get(self.keys_manager.create())
234
- for _ in range(NUM_LOCAL_AGENT_INSTANCES)
235
- ]
236
- service.store()
237
-
238
- return service
239
-
240
230
  def load(
241
231
  self,
242
232
  service_config_id: str,
@@ -253,25 +243,24 @@ class ServiceManager:
253
243
  def create(
254
244
  self,
255
245
  service_template: ServiceTemplate,
256
- keys: t.Optional[t.List[Key]] = None,
246
+ agent_addresses: t.Optional[t.List[str]] = None,
257
247
  ) -> Service:
258
248
  """
259
249
  Create a service
260
250
 
261
251
  :param service_template: Service template
262
- :param keys: Keys
252
+ :param agent_addresses: Agents' addresses to be used for the service.
263
253
  :return: Service instance
264
254
  """
265
255
  service = Service.new(
266
- keys=keys or [],
256
+ agent_addresses=agent_addresses or [],
267
257
  storage=self.path,
268
258
  service_template=service_template,
269
259
  )
270
260
 
271
- if not service.keys:
272
- service.keys = [
273
- self.keys_manager.get(self.keys_manager.create())
274
- for _ in range(NUM_LOCAL_AGENT_INSTANCES)
261
+ if not service.agent_addresses:
262
+ service.agent_addresses = [
263
+ self.keys_manager.create() for _ in range(NUM_LOCAL_AGENT_INSTANCES)
275
264
  ]
276
265
  service.store()
277
266
 
@@ -297,7 +286,7 @@ class ServiceManager:
297
286
  sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)
298
287
  info = sftxb.info(token_id=chain_data.token)
299
288
  config_hash = info["config_hash"]
300
- url = f"{IPFS_GATEWAY}f01701220{config_hash}"
289
+ url = IPFS_ADDRESS.format(hash=config_hash)
301
290
  self.logger.info(f"Fetching {url=}...")
302
291
  res = requests.get(url, timeout=30)
303
292
  if res.status_code == HTTPStatus.OK:
@@ -334,8 +323,6 @@ class ServiceManager:
334
323
  ledger_config = chain_config.ledger_config
335
324
  chain_data = chain_config.chain_data
336
325
  user_params = chain_config.chain_data.user_params
337
- keys = service.keys
338
- instances = [key.address for key in keys]
339
326
  ocm = self.get_on_chain_manager(ledger_config=ledger_config)
340
327
 
341
328
  # TODO fix this
@@ -403,7 +390,7 @@ class ServiceManager:
403
390
  )
404
391
 
405
392
  on_chain_metadata = self._get_on_chain_metadata(chain_config=chain_config)
406
- on_chain_hash = on_chain_metadata.get("code_uri", "")[URI_HASH_POSITION:]
393
+ on_chain_hash = on_chain_metadata.get("code_uri", "")[len(IPFS_URI_PREFIX) :]
407
394
  on_chain_description = on_chain_metadata.get("description")
408
395
 
409
396
  current_agent_bond = staking_params[
@@ -447,7 +434,7 @@ class ServiceManager:
447
434
  if user_params.use_staking
448
435
  else user_params.cost_of_bond
449
436
  ),
450
- threshold=user_params.threshold,
437
+ threshold=len(service.agent_addresses),
451
438
  nft=IPFSHash(user_params.nft),
452
439
  update_token=chain_data.token if is_update else None,
453
440
  token=(
@@ -479,8 +466,8 @@ class ServiceManager:
479
466
  agent_id = staking_params["agent_ids"][0]
480
467
  ocm.register(
481
468
  service_id=chain_data.token,
482
- instances=instances,
483
- agents=[agent_id for _ in instances],
469
+ instances=service.agent_addresses,
470
+ agents=[agent_id for _ in service.agent_addresses],
484
471
  token=(OLAS[ledger_config.chain] if user_params.use_staking else None),
485
472
  )
486
473
  on_chain_state = OnChainState.FINISHED_REGISTRATION
@@ -630,8 +617,6 @@ class ServiceManager:
630
617
  ledger_config = chain_config.ledger_config
631
618
  chain_data = chain_config.chain_data
632
619
  user_params = chain_config.chain_data.user_params
633
- keys = service.keys
634
- instances = [key.address for key in keys]
635
620
  wallet = self.wallet_manager.load(ledger_config.chain.ledger_type)
636
621
  sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)
637
622
  safe = wallet.safes[Chain(chain)]
@@ -653,13 +638,13 @@ class ServiceManager:
653
638
 
654
639
  current_staking_program = self._get_current_staking_program(service, chain)
655
640
  fallback_params = dict( # nosec
656
- staking_contract=NULL_ADDRESS,
641
+ staking_contract=ZERO_ADDRESS,
657
642
  agent_ids=[user_params.agent_id],
658
643
  service_registry="0x9338b5153AE39BB89f50468E608eD9d764B755fD", # nosec
659
- staking_token=NULL_ADDRESS, # nosec
644
+ staking_token=ZERO_ADDRESS, # nosec
660
645
  service_registry_token_utility="0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8", # nosec
661
646
  min_staking_deposit=20000000000000000000,
662
- activity_checker=NULL_ADDRESS, # nosec
647
+ activity_checker=ZERO_ADDRESS, # nosec
663
648
  )
664
649
 
665
650
  current_staking_params = sftxb.get_staking_params(
@@ -710,7 +695,7 @@ class ServiceManager:
710
695
  "GNOSIS_LEDGER_RPC": PUBLIC_RPCS[Chain.GNOSIS],
711
696
  "BASE_LEDGER_RPC": PUBLIC_RPCS[Chain.BASE],
712
697
  "CELO_LEDGER_RPC": PUBLIC_RPCS[Chain.CELO],
713
- "OPTIMISM_LEDGER_RPC": PUBLIC_RPCS[Chain.OPTIMISTIC],
698
+ "OPTIMISM_LEDGER_RPC": PUBLIC_RPCS[Chain.OPTIMISM],
714
699
  "MODE_LEDGER_RPC": PUBLIC_RPCS[Chain.MODE],
715
700
  f"{chain.upper()}_LEDGER_RPC": ledger_config.rpc,
716
701
  "STAKING_CONTRACT_ADDRESS": target_staking_params.get(
@@ -739,11 +724,14 @@ class ServiceManager:
739
724
  }
740
725
  )
741
726
 
742
- # TODO: yet another agent specific logic for memeooorr and optimus, which should be abstracted
743
- if "memeooorr" in service.name.lower() or "optimus" in service.name.lower():
744
- store_path = service.path / "persistent_data"
745
- store_path.mkdir(parents=True, exist_ok=True)
746
- env_var_to_value.update({"STORE_PATH": os.path.join(str(store_path), "")})
727
+ # Set environment variables for the service
728
+ for dir_name, env_var_name in (
729
+ ("persistent_data", "STORE_PATH"),
730
+ ("benchmarks", "LOG_DIR"),
731
+ ):
732
+ dir_path = service.path / dir_name
733
+ dir_path.mkdir(parents=True, exist_ok=True)
734
+ env_var_to_value.update({env_var_name: str(dir_path)})
747
735
 
748
736
  service.update_env_variables_values(env_var_to_value)
749
737
 
@@ -792,7 +780,7 @@ class ServiceManager:
792
780
  target_staking_params["agent_ids"] = [agent_id]
793
781
 
794
782
  on_chain_metadata = self._get_on_chain_metadata(chain_config=chain_config)
795
- on_chain_hash = on_chain_metadata.get("code_uri", "")[URI_HASH_POSITION:]
783
+ on_chain_hash = on_chain_metadata.get("code_uri", "")[len(IPFS_URI_PREFIX) :]
796
784
  on_chain_description = on_chain_metadata.get("description")
797
785
 
798
786
  current_agent_bond = sftxb.get_agent_bond(
@@ -854,7 +842,7 @@ class ServiceManager:
854
842
  if user_params.use_staking
855
843
  else user_params.cost_of_bond
856
844
  ),
857
- threshold=user_params.threshold,
845
+ threshold=len(service.agent_addresses),
858
846
  nft=IPFSHash(user_params.nft),
859
847
  update_token=chain_data.token,
860
848
  token=(
@@ -904,7 +892,7 @@ class ServiceManager:
904
892
  if user_params.use_staking
905
893
  else user_params.cost_of_bond
906
894
  ),
907
- threshold=user_params.threshold,
895
+ threshold=len(service.agent_addresses),
908
896
  nft=IPFSHash(user_params.nft),
909
897
  update_token=None,
910
898
  token=(
@@ -1033,10 +1021,10 @@ class ServiceManager:
1033
1021
  self.logger.info(
1034
1022
  f"Approved {token_utility_allowance} OLAS from {safe} to {token_utility}"
1035
1023
  )
1036
- cost_of_bond = 1 * len(instances)
1024
+ cost_of_bond = 1 * len(service.agent_addresses)
1037
1025
 
1038
1026
  self.logger.info(
1039
- f"Registering agent instances: {chain_data.token} -> {instances}"
1027
+ f"Registering agent instances: {chain_data.token} -> {service.agent_addresses}"
1040
1028
  )
1041
1029
 
1042
1030
  native_balance = get_asset_balance(
@@ -1053,8 +1041,8 @@ class ServiceManager:
1053
1041
  sftxb.new_tx().add(
1054
1042
  sftxb.get_register_instances_data(
1055
1043
  service_id=chain_data.token,
1056
- instances=instances,
1057
- agents=[agent_id for _ in instances],
1044
+ instances=service.agent_addresses,
1045
+ agents=[agent_id for _ in service.agent_addresses],
1058
1046
  cost_of_bond=cost_of_bond,
1059
1047
  )
1060
1048
  ).settle()
@@ -1067,7 +1055,7 @@ class ServiceManager:
1067
1055
 
1068
1056
  reuse_multisig = True
1069
1057
  info = sftxb.info(token_id=chain_data.token)
1070
- if info["multisig"] == NULL_ADDRESS:
1058
+ if info["multisig"] == ZERO_ADDRESS:
1071
1059
  reuse_multisig = False
1072
1060
 
1073
1061
  self.logger.info(f"{reuse_multisig=}")
@@ -1135,9 +1123,7 @@ class ServiceManager:
1135
1123
  staking_chain = None
1136
1124
  for chain_, config in service.chain_configs.items():
1137
1125
  if config.chain_data.user_params.use_staking:
1138
- staking_chain = chain_.replace(
1139
- "optimistic", "optimism"
1140
- ) # TODO: remove this hack, when it's renamed in open-autonomy
1126
+ staking_chain = chain_
1141
1127
  break
1142
1128
 
1143
1129
  service.update_env_variables_values(
@@ -1148,9 +1134,7 @@ class ServiceManager:
1148
1134
  for chain, config in service.chain_configs.items()
1149
1135
  },
1150
1136
  separators=(",", ":"),
1151
- ).replace(
1152
- "optimistic", "optimism"
1153
- ), # TODO: remove this hack, when it's renamed in open-autonomy
1137
+ ),
1154
1138
  "STAKING_CHAIN": staking_chain,
1155
1139
  }
1156
1140
  )
@@ -1203,8 +1187,6 @@ class ServiceManager:
1203
1187
  chain_config = service.chain_configs[chain]
1204
1188
  ledger_config = chain_config.ledger_config
1205
1189
  chain_data = chain_config.chain_data
1206
- keys = service.keys
1207
- instances = [key.address for key in keys]
1208
1190
  wallet = self.wallet_manager.load(ledger_config.chain.ledger_type)
1209
1191
  safe = wallet.safes[Chain(chain)] # type: ignore
1210
1192
 
@@ -1269,7 +1251,7 @@ class ServiceManager:
1269
1251
  # Swap service safe
1270
1252
  current_safe_owners = sftxb.get_service_safe_owners(service_id=chain_data.token)
1271
1253
  counter_current_safe_owners = Counter(s.lower() for s in current_safe_owners)
1272
- counter_instances = Counter(s.lower() for s in instances)
1254
+ counter_instances = Counter(s.lower() for s in service.agent_addresses)
1273
1255
 
1274
1256
  if withdrawal_address is not None:
1275
1257
  # we don't drain signer yet, because the owner swapping tx may need to happen
@@ -1314,16 +1296,15 @@ class ServiceManager:
1314
1296
  ) # noqa: E800
1315
1297
 
1316
1298
  if withdrawal_address is not None:
1299
+ ethereum_crypto = KeysManager().get_crypto_instance(
1300
+ service.agent_addresses[0]
1301
+ )
1317
1302
  # drain all native tokens from service signer key
1318
1303
  drain_eoa(
1319
1304
  ledger_api=self.wallet_manager.load(
1320
1305
  ledger_config.chain.ledger_type
1321
1306
  ).ledger_api(chain=ledger_config.chain, rpc=ledger_config.rpc),
1322
- crypto=EthereumCrypto(
1323
- private_key_path=service.path
1324
- / "deployment"
1325
- / "ethereum_private_key.txt",
1326
- ),
1307
+ crypto=ethereum_crypto,
1327
1308
  withdrawal_address=withdrawal_address,
1328
1309
  chain_id=ledger_config.chain.id,
1329
1310
  )
@@ -1799,11 +1780,11 @@ class ServiceManager:
1799
1780
  on_chain_state = self._get_on_chain_state(service=service, chain=chain)
1800
1781
  if on_chain_state != OnChainState.DEPLOYED:
1801
1782
  if chain_data.user_params.use_staking:
1802
- on_chain_operations_buffer = 1 + len(service.keys)
1783
+ on_chain_operations_buffer = 1 + len(service.agent_addresses)
1803
1784
  else:
1804
1785
  on_chain_operations_buffer = (
1805
1786
  chain_data.user_params.cost_of_bond
1806
- * (1 + len(service.keys))
1787
+ * (1 + len(service.agent_addresses))
1807
1788
  )
1808
1789
 
1809
1790
  asset_funding_values = (
@@ -1817,21 +1798,21 @@ class ServiceManager:
1817
1798
  else fund_requirements.agent
1818
1799
  )
1819
1800
 
1820
- for key in service.keys:
1801
+ for agent_address in service.agent_addresses:
1821
1802
  agent_balance = get_asset_balance(
1822
1803
  ledger_api=ledger_api,
1823
1804
  asset_address=asset_address,
1824
- address=key.address,
1805
+ address=agent_address,
1825
1806
  )
1826
1807
  self.logger.info(
1827
- f"[FUNDING_JOB] Agent {key.address} Asset: {asset_address} balance: {agent_balance}"
1808
+ f"[FUNDING_JOB] Agent {agent_address} Asset: {asset_address} balance: {agent_balance}"
1828
1809
  )
1829
1810
  if agent_fund_threshold > 0:
1830
1811
  self.logger.info(
1831
1812
  f"[FUNDING_JOB] Required balance: {agent_fund_threshold}"
1832
1813
  )
1833
1814
  if agent_balance < agent_fund_threshold:
1834
- self.logger.info(f"[FUNDING_JOB] Funding agent {key.address}")
1815
+ self.logger.info(f"[FUNDING_JOB] Funding agent {agent_address}")
1835
1816
  target_balance = (
1836
1817
  asset_funding_values["agent"]["topup"]
1837
1818
  if asset_funding_values is not None
@@ -1849,11 +1830,11 @@ class ServiceManager:
1849
1830
  min(available_balance, target_balance - agent_balance), 0
1850
1831
  )
1851
1832
  self.logger.info(
1852
- f"[FUNDING_JOB] Transferring {to_transfer} units (asset {asset_address}) to agent {key.address}"
1833
+ f"[FUNDING_JOB] Transferring {to_transfer} units (asset {asset_address}) to agent {agent_address}"
1853
1834
  )
1854
1835
  wallet.transfer_asset(
1855
1836
  asset=asset_address,
1856
- to=key.address,
1837
+ to=agent_address,
1857
1838
  amount=int(to_transfer),
1858
1839
  chain=ledger_config.chain,
1859
1840
  from_safe=from_safe,
@@ -1955,9 +1936,9 @@ class ServiceManager:
1955
1936
  or chain_data.user_params.fund_requirements[ZERO_ADDRESS].agent
1956
1937
  )
1957
1938
 
1958
- for key in service.keys:
1959
- agent_balance = ledger_api.get_balance(address=key.address)
1960
- self.logger.info(f"Agent {key.address} balance: {agent_balance}")
1939
+ for agent_address in service.agent_addresses:
1940
+ agent_balance = ledger_api.get_balance(address=agent_address)
1941
+ self.logger.info(f"Agent {agent_address} balance: {agent_balance}")
1961
1942
  self.logger.info(f"Required balance: {agent_fund_threshold}")
1962
1943
  if agent_balance < agent_fund_threshold:
1963
1944
  self.logger.info("Funding agents")
@@ -1965,10 +1946,10 @@ class ServiceManager:
1965
1946
  agent_topup
1966
1947
  or chain_data.user_params.fund_requirements[ZERO_ADDRESS].agent
1967
1948
  )
1968
- self.logger.info(f"Transferring {to_transfer} units to {key.address}")
1949
+ self.logger.info(f"Transferring {to_transfer} units to {agent_address}")
1969
1950
  wallet.transfer_erc20(
1970
1951
  token=token,
1971
- to=key.address,
1952
+ to=agent_address,
1972
1953
  amount=int(to_transfer),
1973
1954
  chain=ledger_config.chain,
1974
1955
  from_safe=from_safe,
@@ -2003,7 +1984,7 @@ class ServiceManager:
2003
1984
  rpc=rpc or ledger_config.rpc,
2004
1985
  )
2005
1986
 
2006
- def drain_service_safe(
1987
+ def drain_service_safe( # pylint: disable=too-many-locals
2007
1988
  self,
2008
1989
  service_config_id: str,
2009
1990
  withdrawal_address: str,
@@ -2019,9 +2000,7 @@ class ServiceManager:
2019
2000
  chain_data = chain_config.chain_data
2020
2001
  wallet = self.wallet_manager.load(ledger_config.chain.ledger_type)
2021
2002
  ledger_api = wallet.ledger_api(chain=ledger_config.chain, rpc=ledger_config.rpc)
2022
- ethereum_crypto = EthereumCrypto(
2023
- private_key_path=service.path / "deployment" / "ethereum_private_key.txt",
2024
- )
2003
+ ethereum_crypto = KeysManager().get_crypto_instance(service.agent_addresses[0])
2025
2004
 
2026
2005
  # drain ERC20 tokens from service safe
2027
2006
  for token_name, token_address in (
@@ -2195,44 +2174,6 @@ class ServiceManager:
2195
2174
  )
2196
2175
  return service
2197
2176
 
2198
- def migrate_service_configs(self) -> None:
2199
- """Migrate old service config formats to new ones, if applies."""
2200
-
2201
- bafybei_count = sum(
2202
- 1 for path in self.path.iterdir() if path.name.startswith("bafybei")
2203
- )
2204
- if bafybei_count > 1:
2205
- self.log_directories()
2206
- raise RuntimeError(
2207
- f"Your services folder contains {bafybei_count} folders starting with 'bafybei'. This is an unintended situation. Please contact support."
2208
- )
2209
-
2210
- paths = list(self.path.iterdir())
2211
- for path in paths:
2212
- try:
2213
- if path.name.startswith(DELETE_PREFIX):
2214
- shutil.rmtree(path)
2215
- self.logger.info(f"Deleted folder: {path.name}")
2216
-
2217
- if path.name.startswith(SERVICE_CONFIG_PREFIX) or path.name.startswith(
2218
- "bafybei"
2219
- ):
2220
- self.logger.info(f"migrate_service_configs {str(path)}")
2221
- migrated = Service.migrate_format(path)
2222
- if migrated:
2223
- self.logger.info(f"Folder {str(path)} has been migrated.")
2224
- except Exception as e: # pylint: disable=broad-except
2225
- self.logger.error(
2226
- f"Failed to migrate service: {path.name}. Exception {e}: {traceback.format_exc()}"
2227
- )
2228
- # Rename the invalid path
2229
- timestamp = int(time.time())
2230
- invalid_path = path.parent / f"invalid_{timestamp}_{path.name}"
2231
- os.rename(path, invalid_path)
2232
- self.logger.info(
2233
- f"Renamed invalid service: {path.name} to {invalid_path.name}"
2234
- )
2235
-
2236
2177
  def refill_requirements( # pylint: disable=too-many-locals,too-many-statements,too-many-nested-blocks
2237
2178
  self, service_config_id: str
2238
2179
  ) -> t.Dict:
@@ -2260,11 +2201,9 @@ class ServiceManager:
2260
2201
  master_safe_exists = wallet.safes.get(Chain(chain)) is not None
2261
2202
  master_safe = wallet.safes.get(Chain(chain), "master_safe")
2262
2203
 
2263
- agent_addresses = {key.address for key in service.keys}
2204
+ agent_addresses = set(service.agent_addresses)
2264
2205
  service_safe = (
2265
- chain_data.multisig
2266
- if chain_data.multisig and chain_data.multisig != NON_EXISTENT_MULTISIG
2267
- else "service_safe"
2206
+ chain_data.multisig if chain_data.multisig else "service_safe"
2268
2207
  )
2269
2208
 
2270
2209
  if not master_safe_exists:
@@ -61,6 +61,7 @@ from operate.constants import (
61
61
  ON_CHAIN_INTERACT_RETRIES,
62
62
  ON_CHAIN_INTERACT_SLEEP,
63
63
  ON_CHAIN_INTERACT_TIMEOUT,
64
+ ZERO_ADDRESS,
64
65
  )
65
66
  from operate.data import DATA_DIR
66
67
  from operate.data.contracts.dual_staking_token.contract import DualStakingTokenContract
@@ -69,7 +70,6 @@ from operate.operate_types import Chain as OperateChain
69
70
  from operate.operate_types import ContractAddresses
70
71
  from operate.utils.gnosis import (
71
72
  MultiSendOperation,
72
- NULL_ADDRESS,
73
73
  SafeOperation,
74
74
  hash_payload_to_hex,
75
75
  skill_input_hex_to_payload,
@@ -710,7 +710,7 @@ class _ChainUtil:
710
710
  token_id=service_id,
711
711
  )
712
712
 
713
- if multisig_address == NULL_ADDRESS:
713
+ if multisig_address == ZERO_ADDRESS:
714
714
  return []
715
715
 
716
716
  return registry_contracts.gnosis_safe.get_owners(
@@ -1605,7 +1605,7 @@ def get_reuse_multisig_from_safe_payload( # pylint: disable=too-many-locals
1605
1605
  chain_type=chain_type,
1606
1606
  token_id=service_id,
1607
1607
  )
1608
- if multisig_address == NULL_ADDRESS:
1608
+ if multisig_address == ZERO_ADDRESS:
1609
1609
  return None, None, "Cannot reuse multisig, No previous deployment exist!"
1610
1610
 
1611
1611
  multisend_address = ContractConfigs.get(MULTISEND_CONTRACT.name).contracts[
@@ -1723,8 +1723,8 @@ def get_reuse_multisig_from_safe_payload( # pylint: disable=too-many-locals
1723
1723
  0, # safe tx gas
1724
1724
  0, # bas gas
1725
1725
  0, # safe gas price
1726
- NULL_ADDRESS, # gas token
1727
- NULL_ADDRESS, # refund receiver
1726
+ ZERO_ADDRESS, # gas token
1727
+ ZERO_ADDRESS, # refund receiver
1728
1728
  signature_bytes, # signatures
1729
1729
  ],
1730
1730
  )