olas-operate-middleware 0.8.2__tar.gz → 0.10.0__tar.gz

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 (93) hide show
  1. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/PKG-INFO +2 -2
  2. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/bridge/bridge_manager.py +5 -6
  3. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/bridge/providers/native_bridge_provider.py +1 -1
  4. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/bridge/providers/provider.py +4 -5
  5. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/bridge/providers/relay_provider.py +1 -1
  6. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/cli.py +128 -48
  7. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/constants.py +9 -9
  8. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/keys.py +26 -14
  9. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/ledger/__init__.py +4 -4
  10. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/ledger/profiles.py +9 -11
  11. olas_operate_middleware-0.10.0/operate/migration.py +389 -0
  12. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/operate_types.py +9 -27
  13. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/quickstart/analyse_logs.py +3 -6
  14. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/quickstart/claim_staking_rewards.py +1 -4
  15. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/quickstart/reset_configs.py +0 -3
  16. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/quickstart/reset_password.py +0 -3
  17. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/quickstart/reset_staking.py +3 -5
  18. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/quickstart/run_service.py +5 -7
  19. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/quickstart/stop_service.py +3 -4
  20. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/quickstart/terminate_on_chain_service.py +1 -4
  21. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/quickstart/utils.py +4 -7
  22. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/resource.py +37 -5
  23. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/deployment_runner.py +170 -38
  24. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/health_checker.py +5 -8
  25. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/manage.py +103 -164
  26. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/protocol.py +5 -5
  27. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/service.py +42 -242
  28. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/utils/__init__.py +44 -0
  29. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/utils/gnosis.py +25 -17
  30. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/wallet/master.py +20 -24
  31. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/pyproject.toml +2 -2
  32. olas_operate_middleware-0.8.2/operate/migration.py +0 -63
  33. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/LICENSE +0 -0
  34. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/README.md +0 -0
  35. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/__init__.py +0 -0
  36. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/account/__init__.py +0 -0
  37. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/account/user.py +0 -0
  38. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/bridge/providers/lifi_provider.py +0 -0
  39. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/README.md +0 -0
  40. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/__init__.py +0 -0
  41. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/__init__.py +0 -0
  42. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/dual_staking_token/__init__.py +0 -0
  43. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/dual_staking_token/build/DualStakingToken.json +0 -0
  44. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/dual_staking_token/contract.py +0 -0
  45. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/dual_staking_token/contract.yaml +0 -0
  46. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/foreign_omnibridge/__init__.py +0 -0
  47. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/foreign_omnibridge/build/ForeignOmnibridge.json +0 -0
  48. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/foreign_omnibridge/contract.py +0 -0
  49. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/foreign_omnibridge/contract.yaml +0 -0
  50. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/home_omnibridge/__init__.py +0 -0
  51. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/home_omnibridge/build/HomeOmnibridge.json +0 -0
  52. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/home_omnibridge/contract.py +0 -0
  53. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/home_omnibridge/contract.yaml +0 -0
  54. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/l1_standard_bridge/__init__.py +0 -0
  55. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/l1_standard_bridge/build/L1StandardBridge.json +0 -0
  56. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/l1_standard_bridge/contract.py +0 -0
  57. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/l1_standard_bridge/contract.yaml +0 -0
  58. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/l2_standard_bridge/__init__.py +0 -0
  59. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/l2_standard_bridge/build/L2StandardBridge.json +0 -0
  60. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/l2_standard_bridge/contract.py +0 -0
  61. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/l2_standard_bridge/contract.yaml +0 -0
  62. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/mech_activity/__init__.py +0 -0
  63. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/mech_activity/build/MechActivity.json +0 -0
  64. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/mech_activity/contract.py +0 -0
  65. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/mech_activity/contract.yaml +0 -0
  66. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/optimism_mintable_erc20/__init__.py +0 -0
  67. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/optimism_mintable_erc20/build/OptimismMintableERC20.json +0 -0
  68. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/optimism_mintable_erc20/contract.py +0 -0
  69. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/optimism_mintable_erc20/contract.yaml +0 -0
  70. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/requester_activity_checker/__init__.py +0 -0
  71. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/requester_activity_checker/build/RequesterActivityChecker.json +0 -0
  72. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/requester_activity_checker/contract.py +0 -0
  73. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/requester_activity_checker/contract.yaml +0 -0
  74. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/staking_token/__init__.py +0 -0
  75. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/staking_token/build/StakingToken.json +0 -0
  76. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/staking_token/contract.py +0 -0
  77. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/staking_token/contract.yaml +0 -0
  78. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/uniswap_v2_erc20/__init__.py +0 -0
  79. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/uniswap_v2_erc20/build/IUniswapV2ERC20.json +0 -0
  80. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/uniswap_v2_erc20/contract.py +0 -0
  81. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/uniswap_v2_erc20/contract.yaml +0 -0
  82. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/uniswap_v2_erc20/tests/__init__.py +0 -0
  83. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/data/contracts/uniswap_v2_erc20/tests/test_contract.py +0 -0
  84. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/operate_http/__init__.py +0 -0
  85. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/operate_http/exceptions.py +0 -0
  86. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/pearl.py +0 -0
  87. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/__init__.py +0 -0
  88. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/agent_runner.py +0 -0
  89. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/utils/__init__.py +0 -0
  90. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/utils/mech.py +0 -0
  91. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/services/utils/tendermint.py +0 -0
  92. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/utils/ssl.py +0 -0
  93. {olas_operate_middleware-0.8.2 → olas_operate_middleware-0.10.0}/operate/wallet/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: olas-operate-middleware
3
- Version: 0.8.2
3
+ Version: 0.10.0
4
4
  Summary:
5
5
  Author: David Vilela
6
6
  Author-email: dvilelaf@gmail.com
@@ -34,7 +34,7 @@ Requires-Dist: open-aea-cli-ipfs (==1.65.0)
34
34
  Requires-Dist: open-aea-ledger-cosmos (==1.65.0)
35
35
  Requires-Dist: open-aea-ledger-ethereum (==1.65.0)
36
36
  Requires-Dist: open-aea-ledger-ethereum-flashbots (==1.65.0)
37
- Requires-Dist: open-autonomy (>=0.19.11,<0.20.0)
37
+ Requires-Dist: open-autonomy (>=0.20.0,<0.21.0)
38
38
  Requires-Dist: psutil (>=5.9.8,<6.0.0)
39
39
  Requires-Dist: pyinstaller (>=6.8.0,<7.0.0)
40
40
  Requires-Dist: requests-toolbelt (==1.0.0)
@@ -29,7 +29,6 @@ from dataclasses import dataclass
29
29
  from pathlib import Path
30
30
  from typing import cast
31
31
 
32
- from aea.helpers.logging import setup_logger
33
32
  from deepdiff import DeepDiff
34
33
  from web3 import Web3
35
34
 
@@ -77,7 +76,7 @@ NATIVE_BRIDGE_PROVIDER_CONFIGS: t.Dict[str, t.Any] = {
77
76
  "native-ethereum-to-optimism": {
78
77
  "from_chain": "ethereum",
79
78
  "from_bridge": "0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1",
80
- "to_chain": "optimistic",
79
+ "to_chain": "optimism",
81
80
  "to_bridge": "0x4200000000000000000000000000000000000010",
82
81
  "bridge_eta": 300,
83
82
  "bridge_contract_adaptor_class": OptimismContractAdaptor,
@@ -97,8 +96,8 @@ ROUTES = {
97
96
  (
98
97
  Chain.ETHEREUM, # from_chain
99
98
  USDC[Chain.ETHEREUM], # from_token
100
- Chain.OPTIMISTIC, # to_chain
101
- USDC[Chain.OPTIMISTIC], # to_token
99
+ Chain.OPTIMISM, # to_chain
100
+ USDC[Chain.OPTIMISM], # to_token
102
101
  ): LIFI_PROVIDER_ID,
103
102
  (
104
103
  Chain.ETHEREUM, # from_chain
@@ -187,13 +186,13 @@ class BridgeManager:
187
186
  self,
188
187
  path: Path,
189
188
  wallet_manager: MasterWalletManager,
190
- logger: t.Optional[logging.Logger] = None,
189
+ logger: logging.Logger,
191
190
  quote_validity_period: int = DEFAULT_BUNDLE_VALIDITY_PERIOD,
192
191
  ) -> None:
193
192
  """Initialize bridge manager."""
194
193
  self.path = path
195
194
  self.wallet_manager = wallet_manager
196
- self.logger = logger or setup_logger(name="operate.bridge.BridgeManager")
195
+ self.logger = logger
197
196
  self.quote_validity_period = quote_validity_period
198
197
  self.path.mkdir(exist_ok=True)
199
198
  (self.path / EXECUTED_BUNDLES_PATH).mkdir(exist_ok=True)
@@ -417,7 +417,7 @@ class NativeBridgeProvider(Provider):
417
417
  bridge_contract_adaptor: BridgeContractAdaptor,
418
418
  provider_id: str,
419
419
  wallet_manager: MasterWalletManager,
420
- logger: t.Optional[logging.Logger] = None,
420
+ logger: logging.Logger,
421
421
  ) -> None:
422
422
  """Initialize the provider."""
423
423
  self.bridge_contract_adaptor = bridge_contract_adaptor
@@ -31,7 +31,6 @@ from dataclasses import dataclass
31
31
  from math import ceil
32
32
 
33
33
  from aea.crypto.base import LedgerApi
34
- from aea.helpers.logging import setup_logger
35
34
  from autonomy.chain.tx import TxSettler
36
35
  from web3 import Web3
37
36
  from web3.middleware import geth_poa_middleware
@@ -145,12 +144,12 @@ class Provider(ABC):
145
144
  self,
146
145
  wallet_manager: MasterWalletManager,
147
146
  provider_id: str,
148
- logger: t.Optional[logging.Logger] = None,
147
+ logger: logging.Logger,
149
148
  ) -> None:
150
149
  """Initialize the provider."""
151
150
  self.wallet_manager = wallet_manager
152
151
  self.provider_id = provider_id
153
- self.logger = logger or setup_logger(name="operate.bridge.providers.Provider")
152
+ self.logger = logger
154
153
 
155
154
  def description(self) -> str:
156
155
  """Get a human-readable description of the provider."""
@@ -231,7 +230,7 @@ class Provider(ABC):
231
230
  ledger_api = wallet.ledger_api(chain)
232
231
 
233
232
  # TODO: Backport to open aea/autonomy
234
- if chain == Chain.OPTIMISTIC:
233
+ if chain == Chain.OPTIMISM:
235
234
  ledger_api.api.middleware_onion.inject(geth_poa_middleware, layer=0)
236
235
 
237
236
  return ledger_api
@@ -244,7 +243,7 @@ class Provider(ABC):
244
243
  ledger_api = wallet.ledger_api(chain)
245
244
 
246
245
  # TODO: Backport to open aea/autonomy
247
- if chain == Chain.OPTIMISTIC:
246
+ if chain == Chain.OPTIMISM:
248
247
  ledger_api.api.middleware_onion.inject(geth_poa_middleware, layer=0)
249
248
 
250
249
  return ledger_api
@@ -91,7 +91,7 @@ RELAY_DEFAULT_GAS = {
91
91
  "swap": 1_500_000,
92
92
  "send": 1,
93
93
  },
94
- Chain.OPTIMISTIC: {
94
+ Chain.OPTIMISM: {
95
95
  "deposit": 50_000,
96
96
  "approve": 200_000,
97
97
  "authorize": 1,
@@ -28,10 +28,13 @@ import traceback
28
28
  import typing as t
29
29
  import uuid
30
30
  from concurrent.futures import ThreadPoolExecutor
31
+ from contextlib import asynccontextmanager, suppress
31
32
  from http import HTTPStatus
32
33
  from pathlib import Path
33
34
  from types import FrameType
34
35
 
36
+ import psutil
37
+ import requests
35
38
  from aea.helpers.logging import setup_logger
36
39
  from clea import group, params, run
37
40
  from compose.project import ProjectError
@@ -47,11 +50,10 @@ from operate import services
47
50
  from operate.account.user import UserAccount
48
51
  from operate.bridge.bridge_manager import BridgeManager
49
52
  from operate.constants import (
50
- KEY,
51
- KEYS,
53
+ KEYS_DIR,
52
54
  MIN_PASSWORD_LENGTH,
53
55
  OPERATE_HOME,
54
- SERVICES,
56
+ SERVICES_DIR,
55
57
  ZERO_ADDRESS,
56
58
  )
57
59
  from operate.ledger.profiles import (
@@ -69,6 +71,7 @@ from operate.quickstart.reset_staking import reset_staking
69
71
  from operate.quickstart.run_service import run_service
70
72
  from operate.quickstart.stop_service import stop_service
71
73
  from operate.quickstart.terminate_on_chain_service import terminate_service
74
+ from operate.services.deployment_runner import stop_deployment_manager
72
75
  from operate.services.health_checker import HealthChecker
73
76
  from operate.utils import subtract_dicts
74
77
  from operate.utils.gnosis import get_assets_balances
@@ -83,6 +86,7 @@ ACCOUNT_NOT_FOUND_ERROR = JSONResponse(
83
86
  content={"error": "User account not found."},
84
87
  status_code=HTTPStatus.NOT_FOUND,
85
88
  )
89
+ TRY_TO_SHUTDOWN_PREVIOUS_INSTANCE = True
86
90
 
87
91
 
88
92
  def service_not_found_error(service_config_id: str) -> JSONResponse:
@@ -104,13 +108,12 @@ class OperateApp:
104
108
  """Initialize object."""
105
109
  super().__init__()
106
110
  self._path = (home or OPERATE_HOME).resolve()
107
- self._services = self._path / SERVICES
108
- self._keys = self._path / KEYS
109
- self._master_key = self._path / KEY
111
+ self._services = self._path / SERVICES_DIR
112
+ self._keys = self._path / KEYS_DIR
110
113
  self.setup()
111
114
 
112
115
  self.logger = logger or setup_logger(name="operate")
113
- self.keys_manager = services.manage.KeysManager(
116
+ services.manage.KeysManager(
114
117
  path=self._keys,
115
118
  logger=self.logger,
116
119
  )
@@ -118,6 +121,9 @@ class OperateApp:
118
121
 
119
122
  mm = MigrationManager(self._path, self.logger)
120
123
  mm.migrate_user_account()
124
+ mm.migrate_services(self.service_manager())
125
+ mm.migrate_wallets(self.wallet_manager)
126
+ mm.migrate_qs_configs()
121
127
 
122
128
  def create_user_account(self, password: str) -> UserAccount:
123
129
  """Create a user account."""
@@ -164,7 +170,6 @@ class OperateApp:
164
170
  """Load service manager."""
165
171
  return services.manage.ServiceManager(
166
172
  path=self._services,
167
- keys_manager=self.keys_manager,
168
173
  wallet_manager=self.wallet_manager,
169
174
  logger=self.logger,
170
175
  skip_dependency_check=skip_dependency_check,
@@ -185,6 +190,7 @@ class OperateApp:
185
190
  manager = MasterWalletManager(
186
191
  path=self._path / "wallets",
187
192
  password=self.password,
193
+ logger=self.logger,
188
194
  )
189
195
  manager.setup()
190
196
  return manager
@@ -195,6 +201,7 @@ class OperateApp:
195
201
  manager = BridgeManager(
196
202
  path=self._path / "bridge",
197
203
  wallet_manager=self.wallet_manager,
204
+ logger=self.logger,
198
205
  )
199
206
  return manager
200
207
 
@@ -230,26 +237,16 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
230
237
  logger.warning("Healthchecker is off!!!")
231
238
  operate = OperateApp(home=home, logger=logger)
232
239
 
233
- operate.service_manager().log_directories()
234
- logger.info("Migrating service configs...")
235
- operate.service_manager().migrate_service_configs()
236
- logger.info("Migrating service configs done.")
237
- operate.service_manager().log_directories()
238
-
239
- logger.info("Migrating wallet configs...")
240
- operate.wallet_manager.migrate_wallet_configs()
241
- logger.info("Migrating wallet configs done.")
242
-
243
240
  funding_jobs: t.Dict[str, asyncio.Task] = {}
244
241
  health_checker = HealthChecker(
245
- operate.service_manager(), number_of_fails=number_of_fails
242
+ operate.service_manager(), number_of_fails=number_of_fails, logger=logger
246
243
  )
247
244
  # Create shutdown endpoint
248
245
  shutdown_endpoint = uuid.uuid4().hex
249
246
  (operate._path / "operate.kill").write_text( # pylint: disable=protected-access
250
247
  shutdown_endpoint
251
248
  )
252
- thread_pool_executor = ThreadPoolExecutor()
249
+ thread_pool_executor = ThreadPoolExecutor(max_workers=12)
253
250
 
254
251
  async def run_in_executor(fn: t.Callable, *args: t.Any) -> t.Any:
255
252
  loop = asyncio.get_event_loop()
@@ -301,6 +298,12 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
301
298
  logger.info("Stopping services on startup done.")
302
299
 
303
300
  def pause_all_services() -> None:
301
+ service_manager = operate.service_manager()
302
+ if not service_manager.validate_services():
303
+ logger.error(
304
+ "Some services are not valid. Only pausing the valid services."
305
+ )
306
+
304
307
  service_config_ids = [
305
308
  i["service_config_id"] for i in operate.service_manager().json
306
309
  ]
@@ -319,7 +322,12 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
319
322
  if deployment.status == DeploymentStatus.DELETED:
320
323
  continue
321
324
  logger.info(f"stopping service {service_config_id}")
322
- deployment.stop(force=True)
325
+ try:
326
+ deployment.stop(force=True)
327
+ except Exception: # pylint: disable=broad-except
328
+ logger.exception(
329
+ f"Deployment {service_config_id} stopping failed. but continue"
330
+ )
323
331
  logger.info(f"Cancelling funding job for {service_config_id}")
324
332
  cancel_funding_job(service_config_id=service_config_id)
325
333
  health_checker.stop_for_service(service_config_id=service_config_id)
@@ -338,7 +346,51 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
338
346
  # stop all services at middleware exit
339
347
  atexit.register(pause_all_services)
340
348
 
341
- app = FastAPI()
349
+ @asynccontextmanager
350
+ async def lifespan(app: FastAPI):
351
+ # Load the ML model
352
+ watchdog_task = set_parent_watchdog(app)
353
+ yield
354
+ # Clean up the ML models and release the resources
355
+
356
+ with suppress(Exception):
357
+ watchdog_task.cancel()
358
+
359
+ with suppress(Exception):
360
+ await watchdog_task
361
+
362
+ app = FastAPI(lifespan=lifespan)
363
+
364
+ def set_parent_watchdog(app):
365
+ async def stop_app():
366
+ logger.info("Stopping services on demand...")
367
+
368
+ stop_deployment_manager() # TODO: make it async?
369
+ await run_in_executor(pause_all_services)
370
+
371
+ logger.info("Stopping services on demand done.")
372
+ app._server.should_exit = True # pylint: disable=protected-access
373
+ logger.info("Stopping app.")
374
+
375
+ async def check_parent_alive():
376
+ try:
377
+ logger.info(
378
+ f"Parent alive check task started: ppid is {os.getppid()} and own pid is {os.getpid()}"
379
+ )
380
+ while True:
381
+ parent = psutil.Process(os.getpid()).parent()
382
+ if not parent:
383
+ logger.info("Parent is not alive, going to stop")
384
+ await stop_app()
385
+ return
386
+ await asyncio.sleep(3)
387
+
388
+ except Exception: # pylint: disable=broad-except
389
+ logger.exception("Parent alive check crashed!")
390
+
391
+ loop = asyncio.get_running_loop()
392
+ task = loop.create_task(check_parent_alive())
393
+ return task
342
394
 
343
395
  app.add_middleware(
344
396
  CORSMiddleware,
@@ -382,16 +434,17 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
382
434
  async def _kill_server(request: Request) -> JSONResponse:
383
435
  """Kill backend server from inside."""
384
436
  os.kill(os.getpid(), signal.SIGINT)
437
+ return JSONResponse(content={})
385
438
 
386
439
  @app.get("/shutdown")
387
440
  async def _shutdown(request: Request) -> JSONResponse:
388
441
  """Kill backend server from inside."""
389
442
  logger.info("Stopping services on demand...")
390
- pause_all_services()
443
+ await run_in_executor(pause_all_services)
391
444
  logger.info("Stopping services on demand done.")
392
445
  app._server.should_exit = True # pylint: disable=protected-access
393
446
  await asyncio.sleep(0.3)
394
- return {"stopped": True}
447
+ return JSONResponse(content={"stopped": True})
395
448
 
396
449
  @app.get("/api")
397
450
  @with_retries
@@ -777,6 +830,21 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
777
830
  """Get all services."""
778
831
  return JSONResponse(content=operate.service_manager().json)
779
832
 
833
+ @app.get("/api/v2/services/validate")
834
+ @with_retries
835
+ async def _validate_services(request: Request) -> JSONResponse:
836
+ """Validate all services."""
837
+ service_manager = operate.service_manager()
838
+ service_ids = service_manager.get_all_service_ids()
839
+ _services = [
840
+ service.service_config_id
841
+ for service in service_manager.get_all_services()[0]
842
+ ]
843
+
844
+ return JSONResponse(
845
+ content={service_id: service_id in _services for service_id in service_ids}
846
+ )
847
+
780
848
  @app.get("/api/v2/service/{service_config_id}")
781
849
  @with_retries
782
850
  async def _get_service(request: Request) -> JSONResponse:
@@ -954,38 +1022,40 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
954
1022
  service = service_manager.load(service_config_id=service_config_id)
955
1023
 
956
1024
  # terminate the service on chain
957
- for chain in service.chain_configs:
1025
+ for chain, chain_config in service.chain_configs.items():
958
1026
  service_manager.terminate_service_on_chain_from_safe(
959
1027
  service_config_id=service_config_id,
960
1028
  chain=chain,
961
1029
  withdrawal_address=withdrawal_address,
962
1030
  )
963
1031
 
964
- # drain the master safe and master signer for the home chain
965
- chain = Chain(service.home_chain)
966
- master_wallet = service_manager.wallet_manager.load(
967
- ledger_type=chain.ledger_type
968
- )
1032
+ # drain the master safe and master signer for the home chain
1033
+ chain = Chain(service.home_chain)
1034
+ master_wallet = service_manager.wallet_manager.load(
1035
+ ledger_type=chain.ledger_type
1036
+ )
969
1037
 
970
- # drain the master safe
971
- logger.info(
972
- f"Draining the Master Safe {master_wallet.safes[chain]} on chain {chain.value} (withdrawal address {withdrawal_address})."
973
- )
974
- master_wallet.drain(
975
- withdrawal_address=withdrawal_address,
976
- chain=chain,
977
- from_safe=True,
978
- )
1038
+ # drain the master safe
1039
+ logger.info(
1040
+ f"Draining the Master Safe {master_wallet.safes[chain]} on chain {chain.value} (withdrawal address {withdrawal_address})."
1041
+ )
1042
+ master_wallet.drain(
1043
+ withdrawal_address=withdrawal_address,
1044
+ chain=chain,
1045
+ from_safe=True,
1046
+ rpc=chain_config.ledger_config.rpc,
1047
+ )
979
1048
 
980
- # drain the master signer
981
- logger.info(
982
- f"Draining the Master Signer {master_wallet.address} on chain {chain.value} (withdrawal address {withdrawal_address})."
983
- )
984
- master_wallet.drain(
985
- withdrawal_address=withdrawal_address,
986
- chain=chain,
987
- from_safe=False,
988
- )
1049
+ # drain the master signer
1050
+ logger.info(
1051
+ f"Draining the Master Signer {master_wallet.address} on chain {chain.value} (withdrawal address {withdrawal_address})."
1052
+ )
1053
+ master_wallet.drain(
1054
+ withdrawal_address=withdrawal_address,
1055
+ chain=chain,
1056
+ from_safe=False,
1057
+ rpc=chain_config.ledger_config.rpc,
1058
+ )
989
1059
  except Exception as e: # pylint: disable=broad-except
990
1060
  logger.error(f"Withdrawal failed: {e}\n{traceback.format_exc()}")
991
1061
  return JSONResponse(
@@ -1136,6 +1206,16 @@ def _daemon(
1136
1206
  }
1137
1207
  )
1138
1208
 
1209
+ # try automatically shutdown previous instance
1210
+ if TRY_TO_SHUTDOWN_PREVIOUS_INSTANCE:
1211
+ url = f"http{'s' if ssl_keyfile and ssl_certfile else ''}://{host}:{port}/shutdown"
1212
+ logger.info(f"trying to stop previous instance with {url}")
1213
+ try:
1214
+ requests.get(url, timeout=3, verify=False) # nosec
1215
+ logger.info("previous instance stopped")
1216
+ except Exception: # pylint: disable=broad-except
1217
+ logger.exception("failed to stop previous instance. probably not running")
1218
+
1139
1219
  server = Server(Config(**config_kwargs))
1140
1220
  app._server = server # pylint: disable=protected-access
1141
1221
  server.run()
@@ -24,16 +24,11 @@ from pathlib import Path
24
24
 
25
25
  OPERATE = ".operate"
26
26
  OPERATE_HOME = Path.cwd() / OPERATE
27
- CONFIG = "config.json"
28
- SERVICES = "services"
29
- KEYS = "keys"
30
- DEPLOYMENT = "deployment"
27
+ SERVICES_DIR = "services"
28
+ KEYS_DIR = "keys"
29
+ DEPLOYMENT_DIR = "deployment"
31
30
  DEPLOYMENT_JSON = "deployment.json"
32
- CONFIG = "config.json"
33
- KEY = "key"
34
- KEYS_JSON = "keys.json"
35
- DOCKER_COMPOSE_YAML = "docker-compose.yaml"
36
- SERVICE_YAML = "service.yaml"
31
+ CONFIG_JSON = "config.json"
37
32
  ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
38
33
 
39
34
  ON_CHAIN_INTERACT_TIMEOUT = 120.0
@@ -53,3 +48,8 @@ MECH_ACTIVITY_CHECKER_JSON_URL = "https://raw.githubusercontent.com/valory-xyz/a
53
48
  SERVICE_REGISTRY_TOKEN_UTILITY_JSON_URL = "https://raw.githubusercontent.com/valory-xyz/open-autonomy/refs/tags/v0.18.4/packages/valory/contracts/service_registry_token_utility/build/ServiceRegistryTokenUtility.json" # nosec
54
49
  MECH_AGENT_FACTORY_JSON_URL = "https://raw.githubusercontent.com/valory-xyz/ai-registry-mech/main/abis/0.8.25/AgentFactory.json"
55
50
  MECH_MARKETPLACE_JSON_URL = "https://raw.githubusercontent.com/valory-xyz/mech-quickstart/refs/heads/main/contracts/MechMarketplace.json"
51
+ NO_STAKING_PROGRAM_ID = "no_staking"
52
+
53
+
54
+ DEPLOYMENT_START_TRIES_NUM = 3
55
+ IPFS_CHECK_URL = "https://gateway.autonolas.tech/ipfs/bafybeigcllaxn4ycjjvika3zd6eicksuriez2wtg67gx7pamhcazl3tv54/echo/README.md"
@@ -20,18 +20,18 @@
20
20
  """Keys manager."""
21
21
 
22
22
  import json
23
- import logging
24
23
  import os
25
24
  import shutil
26
- import typing as t
25
+ import tempfile
27
26
  from dataclasses import dataclass
28
27
  from pathlib import Path
28
+ from typing import Any
29
29
 
30
- from aea.helpers.logging import setup_logger
31
30
  from aea_ledger_ethereum.ethereum import EthereumCrypto
32
31
 
33
32
  from operate.operate_types import LedgerType
34
33
  from operate.resource import LocalResource
34
+ from operate.utils import SingletonMeta
35
35
 
36
36
 
37
37
  @dataclass
@@ -48,25 +48,21 @@ class Key(LocalResource):
48
48
  return super().load(path) # type: ignore
49
49
 
50
50
 
51
- Keys = t.List[Key]
52
-
53
-
54
- class KeysManager:
51
+ class KeysManager(metaclass=SingletonMeta):
55
52
  """Keys manager."""
56
53
 
57
- def __init__(
58
- self,
59
- path: Path,
60
- logger: t.Optional[logging.Logger] = None,
61
- ) -> None:
54
+ def __init__(self, **kwargs: Any) -> None:
62
55
  """
63
56
  Initialize keys manager
64
57
 
65
58
  :param path: Path to keys storage.
66
59
  :param logger: logging.Logger object.
67
60
  """
68
- self.path = path
69
- self.logger = logger or setup_logger(name="operate.keys")
61
+ if "path" not in kwargs:
62
+ raise ValueError("Path must be provided for KeysManager")
63
+
64
+ self.path = kwargs["path"]
65
+ self.logger = kwargs["logger"]
70
66
 
71
67
  def setup(self) -> None:
72
68
  """Setup service manager."""
@@ -83,6 +79,22 @@ class KeysManager:
83
79
  )
84
80
  )
85
81
 
82
+ def get_crypto_instance(self, address: str) -> EthereumCrypto:
83
+ """Get EthereumCrypto instance for the given address."""
84
+ key: Key = Key.from_json( # type: ignore
85
+ obj=json.loads(
86
+ (self.path / address).read_text(
87
+ encoding="utf-8",
88
+ )
89
+ )
90
+ )
91
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".txt") as temp_file:
92
+ temp_file.write(key.private_key)
93
+ temp_file.flush()
94
+ crypto = EthereumCrypto(private_key_path=temp_file.name)
95
+
96
+ return crypto
97
+
86
98
  def create(self) -> str:
87
99
  """Creates new key."""
88
100
  crypto = EthereumCrypto()
@@ -46,7 +46,7 @@ PUBLIC_RPCS = {
46
46
  Chain.SOLANA: SOLANA_PUBLIC_RPC,
47
47
  Chain.BASE: BASE_PUBLIC_RPC,
48
48
  Chain.CELO: CELO_PUBLIC_RPC,
49
- Chain.OPTIMISTIC: OPTIMISM_PUBLIC_RPC,
49
+ Chain.OPTIMISM: OPTIMISM_PUBLIC_RPC,
50
50
  Chain.MODE: MODE_PUBLIC_RPC,
51
51
  }
52
52
 
@@ -56,7 +56,7 @@ DEFAULT_RPCS = {
56
56
  Chain.SOLANA: SOLANA_RPC,
57
57
  Chain.BASE: BASE_RPC,
58
58
  Chain.CELO: CELO_RPC,
59
- Chain.OPTIMISTIC: OPTIMISM_RPC,
59
+ Chain.OPTIMISM: OPTIMISM_RPC,
60
60
  Chain.MODE: MODE_RPC,
61
61
  }
62
62
 
@@ -67,7 +67,7 @@ CURRENCY_DENOMS = {
67
67
  Chain.SOLANA: "SOL",
68
68
  Chain.BASE: "ETH",
69
69
  Chain.CELO: "CELO",
70
- Chain.OPTIMISTIC: "ETH",
70
+ Chain.OPTIMISM: "ETH",
71
71
  Chain.MODE: "ETH",
72
72
  }
73
73
 
@@ -78,7 +78,7 @@ CURRENCY_SMALLEST_UNITS = {
78
78
  Chain.SOLANA: "Lamport",
79
79
  Chain.BASE: "Wei",
80
80
  Chain.CELO: "Wei",
81
- Chain.OPTIMISTIC: "Wei",
81
+ Chain.OPTIMISM: "Wei",
82
82
  Chain.MODE: "Wei",
83
83
  }
84
84
 
@@ -21,12 +21,10 @@
21
21
 
22
22
  import typing as t
23
23
 
24
- from operate.constants import ZERO_ADDRESS
24
+ from operate.constants import NO_STAKING_PROGRAM_ID, ZERO_ADDRESS
25
25
  from operate.operate_types import Chain, ContractAddresses
26
26
 
27
27
 
28
- NO_STAKING_PROGRAM_ID = "no_staking"
29
-
30
28
  CONTRACTS: t.Dict[Chain, ContractAddresses] = {
31
29
  Chain.GNOSIS: ContractAddresses(
32
30
  {
@@ -38,7 +36,7 @@ CONTRACTS: t.Dict[Chain, ContractAddresses] = {
38
36
  "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
39
37
  }
40
38
  ),
41
- Chain.OPTIMISTIC: ContractAddresses(
39
+ Chain.OPTIMISM: ContractAddresses(
42
40
  {
43
41
  "service_manager": "0xFbBEc0C8b13B38a9aC0499694A69a10204c5E2aB",
44
42
  "service_registry": "0x3d77596beb0f130a4415df3D2D8232B3d3D31e44",
@@ -123,7 +121,7 @@ STAKING: t.Dict[Chain, t.Dict[str, str]] = {
123
121
  "marketplace_demand_alpha_1": "0x9d6e7aB0B5B48aE5c146936147C639fEf4575231",
124
122
  "marketplace_demand_alpha_2": "0x9fb17E549FefcCA630dd92Ea143703CeE4Ea4340",
125
123
  },
126
- Chain.OPTIMISTIC: {
124
+ Chain.OPTIMISM: {
127
125
  "optimus_alpha_1": "0x88996bbdE7f982D93214881756840cE2c77C4992",
128
126
  "optimus_alpha_2": "0xBCA056952D2A7a8dD4A002079219807CFDF9fd29",
129
127
  "optimus_alpha_3": "0x0f69f35652B1acdbD769049334f1AC580927E139",
@@ -170,7 +168,7 @@ DEFAULT_PRIORITY_MECH = { # maps mech marketplace address to its default priori
170
168
  # ERC20 token addresses
171
169
  OLAS: t.Dict[Chain, str] = {
172
170
  Chain.GNOSIS: "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f",
173
- Chain.OPTIMISTIC: "0xFC2E6e6BCbd49ccf3A5f029c79984372DcBFE527",
171
+ Chain.OPTIMISM: "0xFC2E6e6BCbd49ccf3A5f029c79984372DcBFE527",
174
172
  Chain.BASE: "0x54330d28ca3357F294334BDC454a032e7f353416",
175
173
  Chain.ETHEREUM: "0x0001A500A6B18995B03f44bb040A5fFc28E45CB0",
176
174
  Chain.MODE: "0xcfD1D50ce23C46D3Cf6407487B2F8934e96DC8f9",
@@ -178,7 +176,7 @@ OLAS: t.Dict[Chain, str] = {
178
176
 
179
177
  USDC: t.Dict[Chain, str] = {
180
178
  Chain.GNOSIS: "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83",
181
- Chain.OPTIMISTIC: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
179
+ Chain.OPTIMISM: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
182
180
  Chain.BASE: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
183
181
  Chain.ETHEREUM: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
184
182
  Chain.MODE: "0xd988097fb8612cc24eeC14542bC03424c656005f",
@@ -186,7 +184,7 @@ USDC: t.Dict[Chain, str] = {
186
184
 
187
185
  WRAPPED_NATIVE_ASSET = {
188
186
  Chain.GNOSIS: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d",
189
- Chain.OPTIMISTIC: "0x4200000000000000000000000000000000000006",
187
+ Chain.OPTIMISM: "0x4200000000000000000000000000000000000006",
190
188
  Chain.BASE: "0x4200000000000000000000000000000000000006",
191
189
  Chain.ETHEREUM: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
192
190
  Chain.MODE: "0x4200000000000000000000000000000000000006",
@@ -209,7 +207,7 @@ DEFAULT_NEW_SAFE_FUNDS: t.Dict[Chain, t.Dict[str, int]] = {
209
207
  Chain.MODE: {
210
208
  ZERO_ADDRESS: int(1e15 / 4),
211
209
  },
212
- Chain.OPTIMISTIC: {
210
+ Chain.OPTIMISM: {
213
211
  ZERO_ADDRESS: int(1e15 / 4),
214
212
  },
215
213
  }
@@ -219,7 +217,7 @@ DEFAULT_MASTER_EOA_FUNDS = {
219
217
  Chain.ETHEREUM: {ZERO_ADDRESS: 20_000_000_000_000_000},
220
218
  Chain.GNOSIS: {ZERO_ADDRESS: 1_500_000_000_000_000_000},
221
219
  Chain.MODE: {ZERO_ADDRESS: 500_000_000_000_000},
222
- Chain.OPTIMISTIC: {ZERO_ADDRESS: 5_000_000_000_000_000},
220
+ Chain.OPTIMISM: {ZERO_ADDRESS: 5_000_000_000_000_000},
223
221
  }
224
222
 
225
223
  EXPLORER_URL = {
@@ -239,7 +237,7 @@ EXPLORER_URL = {
239
237
  "tx": "https://modescan.io/tx/{tx_hash}",
240
238
  "address": "https://modescan.io/address/{address}",
241
239
  },
242
- Chain.OPTIMISTIC: {
240
+ Chain.OPTIMISM: {
243
241
  "tx": "https://optimistic.etherscan.io/tx/{tx_hash}",
244
242
  "address": "https://optimistic.etherscan.io/address/{address}",
245
243
  },