olas-operate-middleware 0.13.1__py3-none-any.whl → 0.13.3__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 (42) hide show
  1. {olas_operate_middleware-0.13.1.dist-info → olas_operate_middleware-0.13.3.dist-info}/METADATA +8 -27
  2. {olas_operate_middleware-0.13.1.dist-info → olas_operate_middleware-0.13.3.dist-info}/RECORD +42 -42
  3. operate/bridge/providers/provider.py +23 -31
  4. operate/cli.py +5 -18
  5. operate/constants.py +1 -0
  6. operate/data/contracts/dual_staking_token/contract.py +3 -3
  7. operate/data/contracts/dual_staking_token/contract.yaml +2 -2
  8. operate/data/contracts/foreign_omnibridge/contract.yaml +1 -1
  9. operate/data/contracts/home_omnibridge/contract.py +2 -2
  10. operate/data/contracts/home_omnibridge/contract.yaml +2 -2
  11. operate/data/contracts/l1_standard_bridge/contract.yaml +1 -1
  12. operate/data/contracts/l2_standard_bridge/contract.py +4 -4
  13. operate/data/contracts/l2_standard_bridge/contract.yaml +2 -2
  14. operate/data/contracts/mech_activity/contract.yaml +1 -1
  15. operate/data/contracts/optimism_mintable_erc20/contract.yaml +1 -1
  16. operate/data/contracts/recovery_module/contract.yaml +1 -1
  17. operate/data/contracts/requester_activity_checker/contract.yaml +1 -1
  18. operate/data/contracts/staking_token/contract.py +3 -3
  19. operate/data/contracts/staking_token/contract.yaml +2 -2
  20. operate/data/contracts/uniswap_v2_erc20/contract.yaml +3 -3
  21. operate/data/contracts/uniswap_v2_erc20/tests/test_contract.py +5 -5
  22. operate/keys.py +5 -3
  23. operate/ledger/__init__.py +1 -7
  24. operate/ledger/profiles.py +0 -1
  25. operate/operate_http/__init__.py +0 -2
  26. operate/operate_types.py +3 -93
  27. operate/quickstart/run_service.py +63 -6
  28. operate/quickstart/utils.py +8 -4
  29. operate/resource.py +2 -2
  30. operate/services/agent_runner.py +3 -3
  31. operate/services/deployment_runner.py +107 -82
  32. operate/services/health_checker.py +38 -2
  33. operate/services/manage.py +14 -17
  34. operate/services/protocol.py +122 -141
  35. operate/services/utils/mech.py +3 -3
  36. operate/services/utils/tendermint.py +5 -3
  37. operate/utils/gnosis.py +76 -101
  38. operate/wallet/master.py +53 -50
  39. operate/wallet/wallet_recovery_manager.py +110 -56
  40. {olas_operate_middleware-0.13.1.dist-info → olas_operate_middleware-0.13.3.dist-info}/WHEEL +0 -0
  41. {olas_operate_middleware-0.13.1.dist-info → olas_operate_middleware-0.13.3.dist-info}/entry_points.txt +0 -0
  42. {olas_operate_middleware-0.13.1.dist-info → olas_operate_middleware-0.13.3.dist-info}/licenses/LICENSE +0 -0
@@ -19,6 +19,7 @@
19
19
 
20
20
  """Wallet recovery manager"""
21
21
 
22
+ import enum
22
23
  import shutil
23
24
  import typing as t
24
25
  import uuid
@@ -49,6 +50,19 @@ RECOVERY_NEW_OBJECTS_DIR = "new"
49
50
  RECOVERY_OLD_OBJECTS_DIR = "old"
50
51
 
51
52
 
53
+ class WalletRecoveryStatus(str, enum.Enum):
54
+ """ProviderRequestStatus"""
55
+
56
+ NOT_PREPARED = "NOT_PREPARED"
57
+ PREPARED = "PREPARED"
58
+ IN_PROGRESS = "IN_PROGRESS"
59
+ COMPLETED = "COMPLETED"
60
+
61
+ def __str__(self) -> str:
62
+ """__str__"""
63
+ return self.value
64
+
65
+
52
66
  class WalletRecoveryError(Exception):
53
67
  """WalletRecoveryError"""
54
68
 
@@ -125,20 +139,16 @@ class WalletRecoveryManager:
125
139
  )
126
140
 
127
141
  last_prepared_bundle_id = self.data.last_prepared_bundle_id
128
- if last_prepared_bundle_id is not None:
129
- (
130
- _,
131
- num_safes_with_new_wallet,
132
- _,
133
- _,
134
- ) = self._get_swap_status(last_prepared_bundle_id)
135
- if num_safes_with_new_wallet > 0:
136
- self.logger.info(
137
- f"[WALLET RECOVERY MANAGER] Uncompleted bundle {last_prepared_bundle_id} has Safes with new wallet."
138
- )
139
- return self._load_bundle(
140
- bundle_id=last_prepared_bundle_id, new_password=new_password
141
- )
142
+ if (
143
+ last_prepared_bundle_id is not None
144
+ and self.status()["num_safes_with_new_wallet"] > 0
145
+ ):
146
+ self.logger.info(
147
+ f"[WALLET RECOVERY MANAGER] Uncompleted bundle {last_prepared_bundle_id} has Safes with new wallet."
148
+ )
149
+ return self._load_bundle(
150
+ bundle_id=last_prepared_bundle_id, new_password=new_password
151
+ )
142
152
 
143
153
  # Create new recovery bundle
144
154
  bundle_id = f"{RECOVERY_BUNDLE_PREFIX}{str(uuid.uuid4())}"
@@ -178,23 +188,45 @@ class WalletRecoveryManager:
178
188
  )
179
189
  return self._load_bundle(bundle_id=bundle_id, new_password=new_password)
180
190
 
181
- def _get_swap_status(self, bundle_id: str) -> t.Tuple[int, int, int, int]:
191
+ def _load_bundle( # pylint: disable=too-many-locals
192
+ self, bundle_id: str, new_password: t.Optional[str] = None
193
+ ) -> t.Dict:
182
194
  new_root = self.path / bundle_id / RECOVERY_NEW_OBJECTS_DIR
195
+
196
+ new_user_account = UserAccount.load(new_root / USER_JSON)
197
+ if new_password is not None and not new_user_account.is_valid(
198
+ password=new_password
199
+ ):
200
+ raise ValueError(MSG_INVALID_PASSWORD)
201
+
183
202
  new_wallets_path = new_root / WALLETS_DIR
184
- new_wallet_manager = MasterWalletManager(path=new_wallets_path, password=None)
203
+ new_wallet_manager = MasterWalletManager(
204
+ path=new_wallets_path, password=new_password
205
+ )
185
206
 
186
207
  num_safes = 0
187
208
  num_safes_with_new_wallet = 0
188
209
  num_safes_with_old_wallet = 0
189
210
  num_safes_with_both_wallets = 0
211
+ backup_owner_sets = set()
190
212
 
213
+ wallets = []
191
214
  for wallet in self.wallet_manager:
192
215
  new_wallet = next(
193
216
  (w for w in new_wallet_manager if w.ledger_type == wallet.ledger_type)
194
217
  )
218
+ new_mnemonic = None
219
+ if new_password:
220
+ new_mnemonic = new_wallet.decrypt_mnemonic(password=new_password)
221
+
222
+ wallet_json = wallet.json
223
+
195
224
  for chain, safe in wallet.safes.items():
225
+ chain_str = chain.value
196
226
  ledger_api = get_default_ledger_api(chain)
197
227
  owners = get_owners(ledger_api=ledger_api, safe=safe)
228
+ backup_owners = list(set(owners) - {wallet.address, new_wallet.address})
229
+ backup_owner_sets.add(frozenset(backup_owners))
198
230
 
199
231
  num_safes += 1
200
232
  if new_wallet.address in owners and wallet.address in owners:
@@ -204,42 +236,54 @@ class WalletRecoveryManager:
204
236
  if wallet.address in owners:
205
237
  num_safes_with_old_wallet += 1
206
238
 
207
- return (
208
- num_safes,
209
- num_safes_with_new_wallet,
210
- num_safes_with_old_wallet,
211
- num_safes_with_both_wallets,
212
- )
213
-
214
- def _load_bundle(self, bundle_id: str, new_password: str) -> t.Dict:
215
- new_root = self.path / bundle_id / RECOVERY_NEW_OBJECTS_DIR
216
-
217
- new_user_account = UserAccount.load(new_root / USER_JSON)
218
- if not new_user_account.is_valid(password=new_password):
219
- raise ValueError(MSG_INVALID_PASSWORD)
220
-
221
- new_wallets_path = new_root / WALLETS_DIR
222
- new_wallet_manager = MasterWalletManager(
223
- path=new_wallets_path, password=new_password
224
- )
239
+ wallet_json["safes"][chain_str] = {
240
+ safe: {
241
+ "owners": owners,
242
+ "backup_owners": backup_owners,
243
+ "owner_to_remove": (
244
+ wallet.address if wallet.address in owners else None
245
+ ),
246
+ "owner_to_add": (
247
+ new_wallet.address
248
+ if new_wallet.address not in owners
249
+ else None
250
+ ),
251
+ }
252
+ }
225
253
 
226
- wallets = []
227
- for wallet in self.wallet_manager:
228
- ledger_type = wallet.ledger_type
229
- new_wallet = new_wallet_manager.load(ledger_type=ledger_type)
230
- new_mnemonic = None
231
- if new_password:
232
- new_mnemonic = new_wallet.decrypt_mnemonic(password=new_password)
233
254
  wallets.append(
234
255
  {
235
- "current_wallet": wallet.json,
256
+ "current_wallet": wallet_json,
236
257
  "new_wallet": new_wallet.json,
237
258
  "new_mnemonic": new_mnemonic,
238
259
  }
239
260
  )
261
+
262
+ if num_safes_with_new_wallet == 0:
263
+ status = WalletRecoveryStatus.PREPARED
264
+ elif num_safes_with_new_wallet < num_safes:
265
+ status = WalletRecoveryStatus.IN_PROGRESS
266
+ else:
267
+ status = WalletRecoveryStatus.COMPLETED
268
+
240
269
  return {
241
270
  "id": bundle_id,
242
271
  "wallets": wallets,
272
+ "status": status,
273
+ "all_safes_have_backup_owner": all(
274
+ len(owners) >= 1 for owners in backup_owner_sets
275
+ ),
276
+ "consistent_backup_owner": len(backup_owner_sets) == 1,
277
+ "consistent_backup_owner_count": all(
278
+ len(owners) == 1 for owners in backup_owner_sets
279
+ ),
280
+ "prepared": bundle_id is not None,
281
+ "has_swaps": num_safes_with_new_wallet > 0,
282
+ "has_pending_swaps": num_safes_with_new_wallet < num_safes,
283
+ "num_safes": num_safes,
284
+ "num_safes_with_new_wallet": num_safes_with_new_wallet,
285
+ "num_safes_with_old_wallet": num_safes_with_old_wallet,
286
+ "num_safes_with_both_wallets": num_safes_with_both_wallets,
243
287
  }
244
288
 
245
289
  def recovery_requirements( # pylint: disable=too-many-locals
@@ -317,27 +361,37 @@ class WalletRecoveryManager:
317
361
  def status(self) -> t.Dict[str, t.Any]:
318
362
  """Get recovery status."""
319
363
  bundle_id = self.data.last_prepared_bundle_id
320
- if not bundle_id:
364
+ if bundle_id is None:
365
+ backup_owner_sets = set()
366
+ for wallet in self.wallet_manager:
367
+ for chain, safe in wallet.safes.items():
368
+ ledger_api = get_default_ledger_api(chain)
369
+ owners = get_owners(ledger_api=ledger_api, safe=safe)
370
+ backup_owners = list(set(owners) - {wallet.address})
371
+ backup_owner_sets.add(frozenset(backup_owners))
372
+
321
373
  return {
374
+ "id": None,
375
+ "wallets": [],
376
+ "status": WalletRecoveryStatus.NOT_PREPARED,
377
+ "all_safes_have_backup_owner": all(
378
+ len(owners) >= 1 for owners in backup_owner_sets
379
+ ),
380
+ "consistent_backup_owner": len(backup_owner_sets) == 1,
381
+ "consistent_backup_owner_count": all(
382
+ len(owners) == 1 for owners in backup_owner_sets
383
+ ),
322
384
  "prepared": False,
323
385
  "bundle_id": bundle_id,
324
386
  "has_swaps": False,
325
387
  "has_pending_swaps": False,
388
+ "num_safes": 0,
389
+ "num_safes_with_new_wallet": 0,
390
+ "num_safes_with_old_wallet": 0,
391
+ "num_safes_with_both_wallets": 0,
326
392
  }
327
393
 
328
- (
329
- num_safes,
330
- num_safes_with_new_wallet,
331
- _,
332
- _,
333
- ) = self._get_swap_status(bundle_id)
334
-
335
- return {
336
- "prepared": bundle_id is not None,
337
- "bundle_id": bundle_id,
338
- "has_swaps": num_safes_with_new_wallet > 0,
339
- "has_pending_swaps": num_safes_with_new_wallet < num_safes,
340
- }
394
+ return self._load_bundle(bundle_id=bundle_id)
341
395
 
342
396
  def complete_recovery( # pylint: disable=too-many-locals,too-many-statements
343
397
  self, raise_if_inconsistent_owners: bool = True