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.
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/METADATA +1 -1
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/RECORD +20 -19
- operate/__init__.py +12 -0
- operate/cli.py +145 -23
- operate/constants.py +9 -0
- operate/keys.py +11 -4
- operate/ledger/__init__.py +36 -33
- operate/ledger/profiles.py +21 -28
- operate/migration.py +73 -14
- operate/quickstart/claim_staking_rewards.py +11 -22
- operate/quickstart/reset_password.py +3 -2
- operate/quickstart/run_service.py +7 -2
- operate/services/manage.py +104 -63
- operate/services/protocol.py +7 -9
- operate/services/service.py +40 -70
- operate/wallet/master.py +7 -4
- operate/wallet/wallet_recovery_manager.py +210 -0
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/LICENSE +0 -0
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/WHEEL +0 -0
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/entry_points.txt +0 -0
{olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/RECORD
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
operate/__init__.py,sha256=
|
|
1
|
+
operate/__init__.py,sha256=PY5BUTF5cmNI-7UwAj2jJlPl6KJ44LoY5e16p7IrE3g,1335
|
|
2
2
|
operate/account/__init__.py,sha256=suJ_vBMO7hLCvLYe3MVDtLXTNDd6P03og7bvUN7fZsE,804
|
|
3
3
|
operate/account/user.py,sha256=y7DqqDpqgHjbVmnfL_cN0Me_JWl3Dh6GSVt2-9FdRVw,3044
|
|
4
4
|
operate/bridge/bridge_manager.py,sha256=sUfhB1pZvBrF-kBfDOfFVoNeesCe7wd4_YahUF3FZDU,17119
|
|
@@ -6,8 +6,8 @@ operate/bridge/providers/lifi_provider.py,sha256=FpAlBAA_gOt-oOHKhGaOQhhTZIL-hgY
|
|
|
6
6
|
operate/bridge/providers/native_bridge_provider.py,sha256=gG8bSyxUoAVEF6_J9tn1qKRv1PnXuMJdMwUAVJ4GJz8,24647
|
|
7
7
|
operate/bridge/providers/provider.py,sha256=i54RL7m4wMSADM_D_V_quQump_ipPTmByUc-c-AOLPQ,19687
|
|
8
8
|
operate/bridge/providers/relay_provider.py,sha256=L7D-PKepsuBadarJmrNneZk0bsrh88u05uD6_2MSss4,17341
|
|
9
|
-
operate/cli.py,sha256=
|
|
10
|
-
operate/constants.py,sha256=
|
|
9
|
+
operate/cli.py,sha256=REHgtFvaSs7jm7B0K1tPkEKco7Bmd06KQvYAQstFPyw,55783
|
|
10
|
+
operate/constants.py,sha256=ilmLiv0H_xKZyfqmfEdqMci-TlLQyNPp9rrxxqtkqFI,2992
|
|
11
11
|
operate/data/README.md,sha256=jGPyZTvg2LCGdllvmYxmFMkkkiXb6YWatbqIkcX3kv4,879
|
|
12
12
|
operate/data/__init__.py,sha256=ttC51Yqk9c4ehpIgs1Qbe7aJvzkrbbdZ1ClaCxJYByE,864
|
|
13
13
|
operate/data/contracts/__init__.py,sha256=_th54_WvL0ibGy-b6St0Ne9DX-fyjsh-tNOKDn-cWrg,809
|
|
@@ -57,20 +57,20 @@ operate/data/contracts/uniswap_v2_erc20/contract.py,sha256=MwBks4QmZ3XouMT_TqWLn
|
|
|
57
57
|
operate/data/contracts/uniswap_v2_erc20/contract.yaml,sha256=XUdz-XtKtmZgLfItbO8usP-QPbtUkAxKGn0hL7OftAg,741
|
|
58
58
|
operate/data/contracts/uniswap_v2_erc20/tests/__init__.py,sha256=3Arw8dsCsJz6hVOl0t9UjFASHXbV9yp3hw6x4HqgXpU,847
|
|
59
59
|
operate/data/contracts/uniswap_v2_erc20/tests/test_contract.py,sha256=FzZbw9OTcr_yvjOXpk9YcO-K40eyDARyybcfSHDg2Ps,13392
|
|
60
|
-
operate/keys.py,sha256=
|
|
61
|
-
operate/ledger/__init__.py,sha256=
|
|
62
|
-
operate/ledger/profiles.py,sha256=
|
|
63
|
-
operate/migration.py,sha256=
|
|
60
|
+
operate/keys.py,sha256=mPZ2KZujPjHZ1RNVD2vkLzK_3q6J32o9q0WCB6Raxs4,5237
|
|
61
|
+
operate/ledger/__init__.py,sha256=s8GEcV-VtKdBQb9DqW7uf-tj_NpFNn13aNHCh0eqfaQ,3037
|
|
62
|
+
operate/ledger/profiles.py,sha256=LaYPyz-1Cy-KRRuXk60W0ZXQJ2cwoWmDxYrL_psIdKo,11993
|
|
63
|
+
operate/migration.py,sha256=mh921s8FG48XMfDu63ojrdhU7j_RZsdl5E2BvxsbkWE,16937
|
|
64
64
|
operate/operate_http/__init__.py,sha256=dxCIVSUos23M4R-PFZZG6k5QrOlEiK0SxhCYSFNxh7U,4711
|
|
65
65
|
operate/operate_http/exceptions.py,sha256=4UFzrn-GyDD71RhkaOyFPBynL6TrrtP3eywaaU3o4fc,1339
|
|
66
66
|
operate/operate_types.py,sha256=oVOzd6K_CTbmAe1pbSFr4LCajNJ_n0y6J5Wwdf_6ev8,8102
|
|
67
67
|
operate/pearl.py,sha256=yrTpSXLu_ML3qT-uNxq3kScOyo31JyxBujiSMfMUbcg,1690
|
|
68
68
|
operate/quickstart/analyse_logs.py,sha256=K11AWWevkddUIUzTe75J3fYVS6aLfi6kT_dAI9OjrX8,4195
|
|
69
|
-
operate/quickstart/claim_staking_rewards.py,sha256=
|
|
69
|
+
operate/quickstart/claim_staking_rewards.py,sha256=K7X1Yq0mxe3qWmFLb1Xu9-Jghhml95lS_LpM_BXii0o,3533
|
|
70
70
|
operate/quickstart/reset_configs.py,sha256=ipPpbYyB9gQ4KOKS-xBrRi8fT5LvwctSkQi-8XiUMig,3341
|
|
71
|
-
operate/quickstart/reset_password.py,sha256=
|
|
71
|
+
operate/quickstart/reset_password.py,sha256=78riP7zyFM2JMa1H8Dh6pg-PtLJLQS7PFzx9SECPACQ,2571
|
|
72
72
|
operate/quickstart/reset_staking.py,sha256=SB5LZq9EctG4SYn2M6oPZ7R7ARHSFLRGzAqfKkpRcy0,5111
|
|
73
|
-
operate/quickstart/run_service.py,sha256=
|
|
73
|
+
operate/quickstart/run_service.py,sha256=ruEShJadhIa0E2IsPWu41ObkzZsaWF9XkVH8Vb7uRUU,27146
|
|
74
74
|
operate/quickstart/stop_service.py,sha256=CNcCucI4sqfZG0wTxxh-k14xGcbOh50UGyXqTZVQJP0,2048
|
|
75
75
|
operate/quickstart/terminate_on_chain_service.py,sha256=5ENU8_mkj06i80lKUX-v1QbLU0YzKeOZDUL1e_jzySE,2914
|
|
76
76
|
operate/quickstart/utils.py,sha256=rmd9e7whQIsYpRKqWBEQxMA_SHrivBg6DppFY5ECtQQ,9135
|
|
@@ -79,9 +79,9 @@ operate/services/__init__.py,sha256=isrThS-Ccu5Sc15JZgkN4uTAVaSg-NwUUSDeTyJEqLk,
|
|
|
79
79
|
operate/services/agent_runner.py,sha256=6tJePUJmlRxlIugT2fDaCJHSrQlDnl1t9pbg3-7EmCQ,7560
|
|
80
80
|
operate/services/deployment_runner.py,sha256=Su73o7cdH6fkQfj468K77J04a_TWiokJwbMyVQ25xko,27067
|
|
81
81
|
operate/services/health_checker.py,sha256=2KSEDxG3YmGolUDU--648ny0UJpTAAKvxkcr_VZQv-I,9654
|
|
82
|
-
operate/services/manage.py,sha256=
|
|
83
|
-
operate/services/protocol.py,sha256=
|
|
84
|
-
operate/services/service.py,sha256=
|
|
82
|
+
operate/services/manage.py,sha256=cmZ2pb3P1pHZrOdLAYkM0Azxv81dZdNQwnanM5ag7Zo,118090
|
|
83
|
+
operate/services/protocol.py,sha256=FoJmHz5nukI01-VYtReMsIl3JT5RgZZSLX-mf9EXQ90,62903
|
|
84
|
+
operate/services/service.py,sha256=ZbkTryuK3kBkdvhxd9QDyXRxRFsGGG9WZqbLGOUPloc,39301
|
|
85
85
|
operate/services/utils/__init__.py,sha256=TvioaZ1mfTRUSCtrQoLNAp4WMVXyqEJqFJM4PxSQCRU,24
|
|
86
86
|
operate/services/utils/mech.py,sha256=W2x4dqodivNKXjWU-Brp40QhoUHsIMyNAO7-caMoR0Q,3821
|
|
87
87
|
operate/services/utils/tendermint.py,sha256=3h9nDb2Z89T0RwUr_AaVjqtymQmsu3u6DAVCfL_k1U0,25591
|
|
@@ -89,9 +89,10 @@ operate/utils/__init__.py,sha256=DZNUgg0V9yfNfDrUynp10PErSieJkoxU0AKvsEFIhAw,467
|
|
|
89
89
|
operate/utils/gnosis.py,sha256=CS07ZqvrO7uelkFe09VMyPBcLzKONUI1ZqFvi41BDhc,17924
|
|
90
90
|
operate/utils/ssl.py,sha256=O5DrDoZD4T4qQuHP8GLwWUVxQ-1qXeefGp6uDJiF2lM,4308
|
|
91
91
|
operate/wallet/__init__.py,sha256=NGiozD3XhvkBi7_FaOWQ8x1thZPK4uGpokJaeDY_o2w,813
|
|
92
|
-
operate/wallet/master.py,sha256=
|
|
93
|
-
|
|
94
|
-
olas_operate_middleware-0.10.
|
|
95
|
-
olas_operate_middleware-0.10.
|
|
96
|
-
olas_operate_middleware-0.10.
|
|
97
|
-
olas_operate_middleware-0.10.
|
|
92
|
+
operate/wallet/master.py,sha256=jLFLcRgO6ADok2DigpK59PFJ4mnc_0H9a4RRioYzlvw,30811
|
|
93
|
+
operate/wallet/wallet_recovery_manager.py,sha256=sXEZyvFMePxQKf9NJg4HT90mPg4-7ZcTbvggMnKKzhA,7795
|
|
94
|
+
olas_operate_middleware-0.10.9.dist-info/LICENSE,sha256=mdBDB-mWKV5Cz4ejBzBiKqan6Z8zVLAh9xwM64O2FW4,11339
|
|
95
|
+
olas_operate_middleware-0.10.9.dist-info/METADATA,sha256=ljzrFEYIuZlVQMFuMZtspZeau_-1Yp0SvIeyPJiONP8,2035
|
|
96
|
+
olas_operate_middleware-0.10.9.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
97
|
+
olas_operate_middleware-0.10.9.dist-info/entry_points.txt,sha256=dM1g2I7ODApKQFcgl5J4NGA7pfBTo6qsUTXM-j2OLlw,44
|
|
98
|
+
olas_operate_middleware-0.10.9.dist-info/RECORD,,
|
operate/__init__.py
CHANGED
|
@@ -20,6 +20,18 @@
|
|
|
20
20
|
"""Operate app."""
|
|
21
21
|
|
|
22
22
|
import logging
|
|
23
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
23
24
|
|
|
24
25
|
|
|
26
|
+
try:
|
|
27
|
+
# Prefer the distribution name if installed; fall back to the module name
|
|
28
|
+
__version__ = version("olas-operate-middleware")
|
|
29
|
+
except PackageNotFoundError:
|
|
30
|
+
try:
|
|
31
|
+
__version__ = version("operate")
|
|
32
|
+
except PackageNotFoundError:
|
|
33
|
+
logger = logging.getLogger("operate")
|
|
34
|
+
logger.warning("Could not determine version, using 0.0.0+local")
|
|
35
|
+
__version__ = "0.0.0+local"
|
|
36
|
+
|
|
25
37
|
logging.getLogger("aea").setLevel(logging.ERROR)
|
operate/cli.py
CHANGED
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"""Operate app CLI module."""
|
|
21
21
|
import asyncio
|
|
22
22
|
import atexit
|
|
23
|
-
import logging
|
|
24
23
|
import multiprocessing
|
|
25
24
|
import os
|
|
26
25
|
import signal
|
|
@@ -46,7 +45,7 @@ from typing_extensions import Annotated
|
|
|
46
45
|
from uvicorn.config import Config
|
|
47
46
|
from uvicorn.server import Server
|
|
48
47
|
|
|
49
|
-
from operate import services
|
|
48
|
+
from operate import __version__, services
|
|
50
49
|
from operate.account.user import UserAccount
|
|
51
50
|
from operate.bridge.bridge_manager import BridgeManager
|
|
52
51
|
from operate.constants import (
|
|
@@ -54,6 +53,9 @@ from operate.constants import (
|
|
|
54
53
|
MIN_PASSWORD_LENGTH,
|
|
55
54
|
OPERATE_HOME,
|
|
56
55
|
SERVICES_DIR,
|
|
56
|
+
USER_JSON,
|
|
57
|
+
WALLETS_DIR,
|
|
58
|
+
WALLET_RECOVERY_DIR,
|
|
57
59
|
ZERO_ADDRESS,
|
|
58
60
|
)
|
|
59
61
|
from operate.ledger.profiles import (
|
|
@@ -76,18 +78,28 @@ from operate.services.health_checker import HealthChecker
|
|
|
76
78
|
from operate.utils import subtract_dicts
|
|
77
79
|
from operate.utils.gnosis import get_assets_balances
|
|
78
80
|
from operate.wallet.master import MasterWalletManager
|
|
81
|
+
from operate.wallet.wallet_recovery_manager import (
|
|
82
|
+
WalletRecoveryError,
|
|
83
|
+
WalletRecoveryManager,
|
|
84
|
+
)
|
|
79
85
|
|
|
80
86
|
|
|
81
87
|
DEFAULT_MAX_RETRIES = 3
|
|
82
88
|
USER_NOT_LOGGED_IN_ERROR = JSONResponse(
|
|
83
89
|
content={"error": "User not logged in."}, status_code=HTTPStatus.UNAUTHORIZED
|
|
84
90
|
)
|
|
91
|
+
USER_LOGGED_IN_ERROR = JSONResponse(
|
|
92
|
+
content={"error": "User must be logged out to perform this operation."},
|
|
93
|
+
status_code=HTTPStatus.FORBIDDEN,
|
|
94
|
+
)
|
|
85
95
|
ACCOUNT_NOT_FOUND_ERROR = JSONResponse(
|
|
86
96
|
content={"error": "User account not found."},
|
|
87
97
|
status_code=HTTPStatus.NOT_FOUND,
|
|
88
98
|
)
|
|
89
99
|
TRY_TO_SHUTDOWN_PREVIOUS_INSTANCE = True
|
|
90
100
|
|
|
101
|
+
logger = setup_logger(name="operate")
|
|
102
|
+
|
|
91
103
|
|
|
92
104
|
def service_not_found_error(service_config_id: str) -> JSONResponse:
|
|
93
105
|
"""Service not found error response"""
|
|
@@ -103,7 +115,6 @@ class OperateApp:
|
|
|
103
115
|
def __init__(
|
|
104
116
|
self,
|
|
105
117
|
home: t.Optional[Path] = None,
|
|
106
|
-
logger: t.Optional[logging.Logger] = None,
|
|
107
118
|
) -> None:
|
|
108
119
|
"""Initialize object."""
|
|
109
120
|
super().__init__()
|
|
@@ -112,14 +123,13 @@ class OperateApp:
|
|
|
112
123
|
self._keys = self._path / KEYS_DIR
|
|
113
124
|
self.setup()
|
|
114
125
|
|
|
115
|
-
self.logger = logger or setup_logger(name="operate")
|
|
116
126
|
services.manage.KeysManager(
|
|
117
127
|
path=self._keys,
|
|
118
|
-
logger=
|
|
128
|
+
logger=logger,
|
|
119
129
|
)
|
|
120
130
|
self.password: t.Optional[str] = os.environ.get("OPERATE_USER_PASSWORD")
|
|
121
131
|
|
|
122
|
-
mm = MigrationManager(self._path,
|
|
132
|
+
mm = MigrationManager(self._path, logger)
|
|
123
133
|
mm.migrate_user_account()
|
|
124
134
|
mm.migrate_services(self.service_manager())
|
|
125
135
|
mm.migrate_wallets(self.wallet_manager)
|
|
@@ -130,14 +140,14 @@ class OperateApp:
|
|
|
130
140
|
self.password = password
|
|
131
141
|
return UserAccount.new(
|
|
132
142
|
password=password,
|
|
133
|
-
path=self._path /
|
|
143
|
+
path=self._path / USER_JSON,
|
|
134
144
|
)
|
|
135
145
|
|
|
136
146
|
def update_password(self, old_password: str, new_password: str) -> None:
|
|
137
147
|
"""Updates current password"""
|
|
138
148
|
|
|
139
149
|
if not new_password:
|
|
140
|
-
raise ValueError("'
|
|
150
|
+
raise ValueError("'new_password' is required.")
|
|
141
151
|
|
|
142
152
|
if not (
|
|
143
153
|
self.user_account.is_valid(old_password)
|
|
@@ -154,7 +164,7 @@ class OperateApp:
|
|
|
154
164
|
"""Updates current password using the mnemonic"""
|
|
155
165
|
|
|
156
166
|
if not new_password:
|
|
157
|
-
raise ValueError("'
|
|
167
|
+
raise ValueError("'new_password' is required.")
|
|
158
168
|
|
|
159
169
|
mnemonic = mnemonic.strip().lower()
|
|
160
170
|
if not self.wallet_manager.is_mnemonic_valid(mnemonic):
|
|
@@ -171,37 +181,45 @@ class OperateApp:
|
|
|
171
181
|
return services.manage.ServiceManager(
|
|
172
182
|
path=self._services,
|
|
173
183
|
wallet_manager=self.wallet_manager,
|
|
174
|
-
logger=
|
|
184
|
+
logger=logger,
|
|
175
185
|
skip_dependency_check=skip_dependency_check,
|
|
176
186
|
)
|
|
177
187
|
|
|
178
188
|
@property
|
|
179
189
|
def user_account(self) -> t.Optional[UserAccount]:
|
|
180
190
|
"""Load user account."""
|
|
181
|
-
|
|
182
|
-
UserAccount.load(self._path /
|
|
183
|
-
|
|
184
|
-
else None
|
|
185
|
-
)
|
|
191
|
+
if (self._path / USER_JSON).exists():
|
|
192
|
+
return UserAccount.load(self._path / USER_JSON)
|
|
193
|
+
return None
|
|
186
194
|
|
|
187
195
|
@property
|
|
188
196
|
def wallet_manager(self) -> MasterWalletManager:
|
|
189
|
-
"""Load
|
|
197
|
+
"""Load wallet manager."""
|
|
190
198
|
manager = MasterWalletManager(
|
|
191
|
-
path=self._path /
|
|
199
|
+
path=self._path / WALLETS_DIR,
|
|
192
200
|
password=self.password,
|
|
193
|
-
logger=
|
|
201
|
+
logger=logger,
|
|
194
202
|
)
|
|
195
203
|
manager.setup()
|
|
196
204
|
return manager
|
|
197
205
|
|
|
206
|
+
@property
|
|
207
|
+
def wallet_recoverey_manager(self) -> WalletRecoveryManager:
|
|
208
|
+
"""Load wallet recovery manager."""
|
|
209
|
+
manager = WalletRecoveryManager(
|
|
210
|
+
path=self._path / WALLET_RECOVERY_DIR,
|
|
211
|
+
wallet_manager=self.wallet_manager,
|
|
212
|
+
logger=logger,
|
|
213
|
+
)
|
|
214
|
+
return manager
|
|
215
|
+
|
|
198
216
|
@property
|
|
199
217
|
def bridge_manager(self) -> BridgeManager:
|
|
200
|
-
"""Load
|
|
218
|
+
"""Load bridge manager."""
|
|
201
219
|
manager = BridgeManager(
|
|
202
220
|
path=self._path / "bridge",
|
|
203
221
|
wallet_manager=self.wallet_manager,
|
|
204
|
-
logger=
|
|
222
|
+
logger=logger,
|
|
205
223
|
)
|
|
206
224
|
return manager
|
|
207
225
|
|
|
@@ -232,10 +250,9 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
|
|
|
232
250
|
)
|
|
233
251
|
)
|
|
234
252
|
|
|
235
|
-
logger = setup_logger(name="operate")
|
|
236
253
|
if HEALTH_CHECKER_OFF:
|
|
237
254
|
logger.warning("Healthchecker is off!!!")
|
|
238
|
-
operate = OperateApp(home=home
|
|
255
|
+
operate = OperateApp(home=home)
|
|
239
256
|
|
|
240
257
|
funding_jobs: t.Dict[str, asyncio.Task] = {}
|
|
241
258
|
health_checker = HealthChecker(
|
|
@@ -877,6 +894,21 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
|
|
|
877
894
|
deployment_json["healthcheck"] = service.get_latest_healthcheck()
|
|
878
895
|
return JSONResponse(content=deployment_json)
|
|
879
896
|
|
|
897
|
+
@app.get("/api/v2/service/{service_config_id}/agent_performance")
|
|
898
|
+
@with_retries
|
|
899
|
+
async def _get_agent_performance(request: Request) -> JSONResponse:
|
|
900
|
+
"""Get the service refill requirements."""
|
|
901
|
+
service_config_id = request.path_params["service_config_id"]
|
|
902
|
+
|
|
903
|
+
if not operate.service_manager().exists(service_config_id=service_config_id):
|
|
904
|
+
return service_not_found_error(service_config_id=service_config_id)
|
|
905
|
+
|
|
906
|
+
return JSONResponse(
|
|
907
|
+
content=operate.service_manager()
|
|
908
|
+
.load(service_config_id=service_config_id)
|
|
909
|
+
.get_agent_performance()
|
|
910
|
+
)
|
|
911
|
+
|
|
880
912
|
@app.get("/api/v2/service/{service_config_id}/refill_requirements")
|
|
881
913
|
@with_retries
|
|
882
914
|
async def _get_refill_requirements(request: Request) -> JSONResponse:
|
|
@@ -1166,12 +1198,103 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
|
|
|
1166
1198
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1167
1199
|
)
|
|
1168
1200
|
|
|
1201
|
+
@app.post("/api/wallet/recovery/initiate")
|
|
1202
|
+
@with_retries
|
|
1203
|
+
async def _wallet_recovery_initiate(request: Request) -> JSONResponse:
|
|
1204
|
+
"""Initiate wallet recovery."""
|
|
1205
|
+
if operate.user_account is None:
|
|
1206
|
+
return ACCOUNT_NOT_FOUND_ERROR
|
|
1207
|
+
|
|
1208
|
+
if operate.password:
|
|
1209
|
+
return USER_LOGGED_IN_ERROR
|
|
1210
|
+
|
|
1211
|
+
data = await request.json()
|
|
1212
|
+
new_password = data.get("new_password")
|
|
1213
|
+
|
|
1214
|
+
if not new_password or len(new_password) < MIN_PASSWORD_LENGTH:
|
|
1215
|
+
return JSONResponse(
|
|
1216
|
+
content={
|
|
1217
|
+
"error": f"New password must be at least {MIN_PASSWORD_LENGTH} characters long."
|
|
1218
|
+
},
|
|
1219
|
+
status_code=HTTPStatus.BAD_REQUEST,
|
|
1220
|
+
)
|
|
1221
|
+
|
|
1222
|
+
try:
|
|
1223
|
+
output = operate.wallet_recoverey_manager.initiate_recovery(
|
|
1224
|
+
new_password=new_password
|
|
1225
|
+
)
|
|
1226
|
+
return JSONResponse(
|
|
1227
|
+
content=output,
|
|
1228
|
+
status_code=HTTPStatus.OK,
|
|
1229
|
+
)
|
|
1230
|
+
except (ValueError, WalletRecoveryError) as e:
|
|
1231
|
+
logger.error(f"_recovery_initiate error: {e}")
|
|
1232
|
+
return JSONResponse(
|
|
1233
|
+
content={"error": f"Failed to initiate recovery: {e}"},
|
|
1234
|
+
status_code=HTTPStatus.BAD_REQUEST,
|
|
1235
|
+
)
|
|
1236
|
+
except Exception as e: # pylint: disable=broad-except
|
|
1237
|
+
logger.error(f"_recovery_initiate error: {e}\n{traceback.format_exc()}")
|
|
1238
|
+
return JSONResponse(
|
|
1239
|
+
content={
|
|
1240
|
+
"error": "Failed to initiate recovery. Please check the logs."
|
|
1241
|
+
},
|
|
1242
|
+
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1243
|
+
)
|
|
1244
|
+
|
|
1245
|
+
@app.post("/api/wallet/recovery/complete")
|
|
1246
|
+
@with_retries
|
|
1247
|
+
async def _wallet_recovery_complete(request: Request) -> JSONResponse:
|
|
1248
|
+
"""Complete wallet recovery."""
|
|
1249
|
+
if operate.user_account is None:
|
|
1250
|
+
return ACCOUNT_NOT_FOUND_ERROR
|
|
1251
|
+
|
|
1252
|
+
if operate.password:
|
|
1253
|
+
return USER_LOGGED_IN_ERROR
|
|
1254
|
+
|
|
1255
|
+
data = await request.json()
|
|
1256
|
+
bundle_id = data.get("id")
|
|
1257
|
+
password = data.get("password")
|
|
1258
|
+
raise_if_inconsistent_owners = data.get("require_consistent_owners", True)
|
|
1259
|
+
|
|
1260
|
+
try:
|
|
1261
|
+
operate.wallet_recoverey_manager.complete_recovery(
|
|
1262
|
+
bundle_id=bundle_id,
|
|
1263
|
+
password=password,
|
|
1264
|
+
raise_if_inconsistent_owners=raise_if_inconsistent_owners,
|
|
1265
|
+
)
|
|
1266
|
+
return JSONResponse(
|
|
1267
|
+
content=operate.wallet_manager.json,
|
|
1268
|
+
status_code=HTTPStatus.OK,
|
|
1269
|
+
)
|
|
1270
|
+
except KeyError as e:
|
|
1271
|
+
logger.error(f"_recovery_complete error: {e}")
|
|
1272
|
+
return JSONResponse(
|
|
1273
|
+
content={"error": f"Failed to complete recovery: {e}"},
|
|
1274
|
+
status_code=HTTPStatus.NOT_FOUND,
|
|
1275
|
+
)
|
|
1276
|
+
except (ValueError, WalletRecoveryError) as e:
|
|
1277
|
+
logger.error(f"_recovery_complete error: {e}")
|
|
1278
|
+
return JSONResponse(
|
|
1279
|
+
content={"error": f"Failed to complete recovery: {e}"},
|
|
1280
|
+
status_code=HTTPStatus.BAD_REQUEST,
|
|
1281
|
+
)
|
|
1282
|
+
except Exception as e: # pylint: disable=broad-except
|
|
1283
|
+
logger.error(f"_recovery_complete error: {e}\n{traceback.format_exc()}")
|
|
1284
|
+
return JSONResponse(
|
|
1285
|
+
content={
|
|
1286
|
+
"error": "Failed to complete recovery. Please check the logs."
|
|
1287
|
+
},
|
|
1288
|
+
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1289
|
+
)
|
|
1290
|
+
|
|
1169
1291
|
return app
|
|
1170
1292
|
|
|
1171
1293
|
|
|
1172
1294
|
@group(name="operate")
|
|
1173
1295
|
def _operate() -> None:
|
|
1174
1296
|
"""Operate - deploy autonomous services."""
|
|
1297
|
+
logger.info(f"Operate version: {__version__}")
|
|
1175
1298
|
|
|
1176
1299
|
|
|
1177
1300
|
@_operate.command(name="daemon")
|
|
@@ -1188,7 +1311,6 @@ def _daemon(
|
|
|
1188
1311
|
) -> None:
|
|
1189
1312
|
"""Launch operate daemon."""
|
|
1190
1313
|
app = create_app(home=home)
|
|
1191
|
-
logger = setup_logger(name="daemon")
|
|
1192
1314
|
|
|
1193
1315
|
config_kwargs = {
|
|
1194
1316
|
"app": app,
|
operate/constants.py
CHANGED
|
@@ -26,9 +26,18 @@ OPERATE = ".operate"
|
|
|
26
26
|
OPERATE_HOME = Path.cwd() / OPERATE
|
|
27
27
|
SERVICES_DIR = "services"
|
|
28
28
|
KEYS_DIR = "keys"
|
|
29
|
+
WALLETS_DIR = "wallets"
|
|
30
|
+
WALLET_RECOVERY_DIR = "wallet_recovery"
|
|
29
31
|
DEPLOYMENT_DIR = "deployment"
|
|
30
32
|
DEPLOYMENT_JSON = "deployment.json"
|
|
31
33
|
CONFIG_JSON = "config.json"
|
|
34
|
+
USER_JSON = "user.json"
|
|
35
|
+
|
|
36
|
+
AGENT_PERSISTENT_STORAGE_DIR = "persistent_data"
|
|
37
|
+
AGENT_PERSISTENT_STORAGE_ENV_VAR = "STORE_PATH"
|
|
38
|
+
AGENT_LOG_DIR = "benchmarks"
|
|
39
|
+
AGENT_LOG_ENV_VAR = "LOG_DIR"
|
|
40
|
+
|
|
32
41
|
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
|
33
42
|
|
|
34
43
|
ON_CHAIN_INTERACT_TIMEOUT = 120.0
|
operate/keys.py
CHANGED
|
@@ -85,6 +85,7 @@ class KeysManager(metaclass=SingletonMeta):
|
|
|
85
85
|
)
|
|
86
86
|
)
|
|
87
87
|
)
|
|
88
|
+
private_key = key.private_key
|
|
88
89
|
# Create temporary file with delete=False to handle it manually
|
|
89
90
|
with tempfile.NamedTemporaryFile(
|
|
90
91
|
dir=self.path,
|
|
@@ -92,16 +93,22 @@ class KeysManager(metaclass=SingletonMeta):
|
|
|
92
93
|
suffix=".txt",
|
|
93
94
|
delete=False, # Handle cleanup manually
|
|
94
95
|
) as temp_file:
|
|
95
|
-
temp_file.
|
|
96
|
+
temp_file_name = temp_file.name
|
|
97
|
+
temp_file.write(private_key)
|
|
96
98
|
temp_file.flush()
|
|
97
99
|
temp_file.close() # Close the file before reading
|
|
98
100
|
|
|
99
101
|
# Set proper file permissions (readable by owner only)
|
|
100
|
-
os.chmod(
|
|
101
|
-
crypto = EthereumCrypto(private_key_path=
|
|
102
|
+
os.chmod(temp_file_name, 0o600)
|
|
103
|
+
crypto = EthereumCrypto(private_key_path=temp_file_name)
|
|
102
104
|
|
|
103
105
|
try:
|
|
104
|
-
|
|
106
|
+
with open(temp_file_name, "r+", encoding="utf-8") as f:
|
|
107
|
+
f.seek(0)
|
|
108
|
+
f.write("\0" * len(private_key))
|
|
109
|
+
f.flush()
|
|
110
|
+
f.close()
|
|
111
|
+
os.unlink(temp_file_name) # Clean up the temporary file
|
|
105
112
|
except OSError as e:
|
|
106
113
|
self.logger.error(f"Failed to delete temp file {temp_file.name}: {e}")
|
|
107
114
|
|
operate/ledger/__init__.py
CHANGED
|
@@ -24,62 +24,65 @@ import os
|
|
|
24
24
|
from operate.operate_types import Chain
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
CHAINS = [
|
|
28
|
+
Chain.ARBITRUM_ONE,
|
|
29
|
+
Chain.BASE,
|
|
30
|
+
Chain.CELO,
|
|
31
|
+
Chain.ETHEREUM,
|
|
32
|
+
Chain.GNOSIS,
|
|
33
|
+
Chain.MODE,
|
|
34
|
+
Chain.OPTIMISM,
|
|
35
|
+
Chain.POLYGON,
|
|
36
|
+
Chain.SOLANA,
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
ARBITRUM_ONE_RPC = os.environ.get("ARBITRUM_ONE_RPC", "https://arb1.arbitrum.io/rpc")
|
|
38
40
|
BASE_RPC = os.environ.get("BASE_RPC", "https://mainnet.base.org")
|
|
39
41
|
CELO_RPC = os.environ.get("CELO_RPC", "https://forno.celo.org")
|
|
40
|
-
|
|
42
|
+
ETHEREUM_RPC = os.environ.get("ETHEREUM_RPC", "https://ethereum.publicnode.com")
|
|
43
|
+
GNOSIS_RPC = os.environ.get("GNOSIS_RPC", "https://rpc-gate.autonolas.tech/gnosis-rpc/")
|
|
41
44
|
MODE_RPC = os.environ.get("MODE_RPC", "https://mainnet.mode.network/")
|
|
45
|
+
OPTIMISM_RPC = os.environ.get("OPTIMISM_RPC", "https://mainnet.optimism.io")
|
|
46
|
+
POLYGON_RPC = os.environ.get("POLYGON_RPC", "https://polygon-rpc.com")
|
|
47
|
+
SOLANA_RPC = os.environ.get("SOLANA_RPC", "https://api.mainnet-beta.solana.com")
|
|
42
48
|
|
|
43
|
-
PUBLIC_RPCS = {
|
|
44
|
-
Chain.ETHEREUM: ETHEREUM_PUBLIC_RPC,
|
|
45
|
-
Chain.GNOSIS: GNOSIS_PUBLIC_RPC,
|
|
46
|
-
Chain.SOLANA: SOLANA_PUBLIC_RPC,
|
|
47
|
-
Chain.BASE: BASE_PUBLIC_RPC,
|
|
48
|
-
Chain.CELO: CELO_PUBLIC_RPC,
|
|
49
|
-
Chain.OPTIMISM: OPTIMISM_PUBLIC_RPC,
|
|
50
|
-
Chain.MODE: MODE_PUBLIC_RPC,
|
|
51
|
-
}
|
|
52
49
|
|
|
53
50
|
DEFAULT_RPCS = {
|
|
54
|
-
Chain.
|
|
55
|
-
Chain.GNOSIS: GNOSIS_RPC,
|
|
56
|
-
Chain.SOLANA: SOLANA_RPC,
|
|
51
|
+
Chain.ARBITRUM_ONE: ARBITRUM_ONE_RPC,
|
|
57
52
|
Chain.BASE: BASE_RPC,
|
|
58
53
|
Chain.CELO: CELO_RPC,
|
|
59
|
-
Chain.
|
|
54
|
+
Chain.ETHEREUM: ETHEREUM_RPC,
|
|
55
|
+
Chain.GNOSIS: GNOSIS_RPC,
|
|
60
56
|
Chain.MODE: MODE_RPC,
|
|
57
|
+
Chain.OPTIMISM: OPTIMISM_RPC,
|
|
58
|
+
Chain.POLYGON: POLYGON_RPC,
|
|
59
|
+
Chain.SOLANA: SOLANA_RPC,
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
# Base currency for each chain
|
|
64
63
|
CURRENCY_DENOMS = {
|
|
65
|
-
Chain.
|
|
66
|
-
Chain.GNOSIS: "xDAI",
|
|
67
|
-
Chain.SOLANA: "SOL",
|
|
64
|
+
Chain.ARBITRUM_ONE: "ETH",
|
|
68
65
|
Chain.BASE: "ETH",
|
|
69
66
|
Chain.CELO: "CELO",
|
|
70
|
-
Chain.
|
|
67
|
+
Chain.ETHEREUM: "ETH",
|
|
68
|
+
Chain.GNOSIS: "xDAI",
|
|
71
69
|
Chain.MODE: "ETH",
|
|
70
|
+
Chain.OPTIMISM: "ETH",
|
|
71
|
+
Chain.POLYGON: "POL",
|
|
72
|
+
Chain.SOLANA: "SOL",
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
# Smallest denomination for each chain
|
|
75
76
|
CURRENCY_SMALLEST_UNITS = {
|
|
76
|
-
Chain.
|
|
77
|
-
Chain.GNOSIS: "Wei",
|
|
78
|
-
Chain.SOLANA: "Lamport",
|
|
77
|
+
Chain.ARBITRUM_ONE: "Wei",
|
|
79
78
|
Chain.BASE: "Wei",
|
|
80
79
|
Chain.CELO: "Wei",
|
|
81
|
-
Chain.
|
|
80
|
+
Chain.ETHEREUM: "Wei",
|
|
81
|
+
Chain.GNOSIS: "Wei",
|
|
82
82
|
Chain.MODE: "Wei",
|
|
83
|
+
Chain.OPTIMISM: "Wei",
|
|
84
|
+
Chain.POLYGON: "Wei",
|
|
85
|
+
Chain.SOLANA: "Lamport",
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
|
operate/ledger/profiles.py
CHANGED
|
@@ -24,40 +24,33 @@ import typing as t
|
|
|
24
24
|
from autonomy.chain.constants import CHAIN_PROFILES, DEFAULT_MULTISEND
|
|
25
25
|
|
|
26
26
|
from operate.constants import NO_STAKING_PROGRAM_ID, ZERO_ADDRESS
|
|
27
|
+
from operate.ledger import CHAINS
|
|
27
28
|
from operate.operate_types import Chain, ContractAddresses
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
# TODO: Refactor, remove the usage of CONTRACTS and use CHAIN_PROFILES from Open Autonomy instead.
|
|
31
|
-
CHAINS = [
|
|
32
|
-
Chain.ARBITRUM_ONE,
|
|
33
|
-
Chain.BASE,
|
|
34
|
-
Chain.CELO,
|
|
35
|
-
Chain.ETHEREUM,
|
|
36
|
-
Chain.GNOSIS,
|
|
37
|
-
Chain.MODE,
|
|
38
|
-
Chain.OPTIMISM,
|
|
39
|
-
Chain.POLYGON,
|
|
40
|
-
]
|
|
41
|
-
|
|
42
32
|
CONTRACTS: t.Dict[Chain, ContractAddresses] = {}
|
|
43
33
|
for _chain in CHAINS:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
34
|
+
if _chain.value in CHAIN_PROFILES:
|
|
35
|
+
profile = CHAIN_PROFILES[_chain.value]
|
|
36
|
+
CONTRACTS[_chain] = ContractAddresses(
|
|
37
|
+
{
|
|
38
|
+
"service_registry": profile["service_registry"],
|
|
39
|
+
"service_registry_token_utility": profile[
|
|
40
|
+
"service_registry_token_utility"
|
|
41
|
+
],
|
|
42
|
+
"service_manager": profile["service_manager_token"],
|
|
43
|
+
"gnosis_safe_proxy_factory": profile["gnosis_safe_proxy_factory"],
|
|
44
|
+
"gnosis_safe_same_address_multisig": profile[
|
|
45
|
+
"gnosis_safe_same_address_multisig"
|
|
46
|
+
],
|
|
47
|
+
"safe_multisig_with_recovery_module": profile[
|
|
48
|
+
"safe_multisig_with_recovery_module"
|
|
49
|
+
],
|
|
50
|
+
"recovery_module": profile["recovery_module"],
|
|
51
|
+
"multisend": DEFAULT_MULTISEND,
|
|
52
|
+
}
|
|
53
|
+
)
|
|
61
54
|
|
|
62
55
|
STAKING: t.Dict[Chain, t.Dict[str, str]] = {
|
|
63
56
|
Chain.ARBITRUM_ONE: {},
|