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
@@ -42,6 +42,8 @@ import requests
42
42
  from flask import Flask, Response, jsonify, request
43
43
  from werkzeug.exceptions import InternalServerError, NotFound
44
44
 
45
+ from operate.constants import DEFAULT_TIMEOUT
46
+
45
47
 
46
48
  ENCODING = "utf-8"
47
49
  DEFAULT_LOG_FILE = "com.log"
@@ -474,7 +476,7 @@ class PeriodDumper:
474
476
  os.chmod(path, stat.S_IWRITE)
475
477
  func(path)
476
478
  except (FileNotFoundError, OSError):
477
- return
479
+ pass
478
480
 
479
481
  def dump_period(self) -> None:
480
482
  """Dump tendermint run data for replay"""
@@ -532,7 +534,7 @@ def create_app( # pylint: disable=too-many-statements
532
534
  )
533
535
  priv_key_data = json.loads(priv_key_file.read_text(encoding=ENCODING))
534
536
  del priv_key_data["priv_key"]
535
- status = requests.get(TM_STATUS_ENDPOINT).json()
537
+ status = requests.get(TM_STATUS_ENDPOINT, timeout=DEFAULT_TIMEOUT).json()
536
538
  priv_key_data["peer_id"] = status["result"]["node_info"]["id"]
537
539
  return {
538
540
  "params": priv_key_data,
@@ -600,7 +602,7 @@ def create_app( # pylint: disable=too-many-statements
600
602
  endpoint = f"{tendermint_params.rpc_laddr.replace('tcp', 'http').replace(non_routable, loopback)}/block"
601
603
  height = request.args.get("height")
602
604
  params = {"height": height} if height is not None else None
603
- res = requests.get(endpoint, params)
605
+ res = requests.get(endpoint, params, timeout=DEFAULT_TIMEOUT)
604
606
  app_hash_ = res.json()["result"]["block"]["header"]["app_hash"]
605
607
  return jsonify({"app_hash": app_hash_}), res.status_code
606
608
  except Exception as e: # pylint: disable=W0703
operate/utils/gnosis.py CHANGED
@@ -170,9 +170,7 @@ def create_safe(
170
170
  """Create gnosis safe."""
171
171
  salt_nonce = salt_nonce or _get_nonce()
172
172
 
173
- def _build( # pylint: disable=unused-argument
174
- *args: t.Any, **kwargs: t.Any
175
- ) -> t.Dict:
173
+ def _build() -> t.Dict:
176
174
  tx = registry_contracts.gnosis_safe.get_deploy_transaction(
177
175
  ledger_api=ledger_api,
178
176
  deployer_address=crypto.address,
@@ -183,30 +181,26 @@ def create_safe(
183
181
  del tx["contract_address"]
184
182
  return tx
185
183
 
186
- tx_settler = TxSettler(
187
- ledger_api=ledger_api,
188
- crypto=crypto,
189
- chain_type=ChainProfile.CUSTOM,
190
- timeout=ON_CHAIN_INTERACT_TIMEOUT,
191
- retries=ON_CHAIN_INTERACT_RETRIES,
192
- sleep=ON_CHAIN_INTERACT_SLEEP,
193
- )
194
- setattr( # noqa: B010
195
- tx_settler,
196
- "build",
197
- _build,
198
- )
199
- receipt = tx_settler.transact(
200
- method=lambda: {},
201
- contract="",
202
- kwargs={},
184
+ tx_settler = (
185
+ TxSettler(
186
+ ledger_api=ledger_api,
187
+ crypto=crypto,
188
+ chain_type=ChainProfile.CUSTOM,
189
+ timeout=ON_CHAIN_INTERACT_TIMEOUT,
190
+ retries=ON_CHAIN_INTERACT_RETRIES,
191
+ sleep=ON_CHAIN_INTERACT_SLEEP,
192
+ tx_builder=_build,
193
+ )
194
+ .transact()
195
+ .settle()
203
196
  )
204
- tx_hash = receipt.get("transactionHash", "").hex()
205
- instance = registry_contracts.gnosis_safe_proxy_factory.get_instance(
206
- ledger_api=ledger_api,
207
- contract_address="0xa6b71e26c5e0845f74c812102ca7114b6a896ab2",
197
+ (event,) = tx_settler.get_events(
198
+ contract=registry_contracts.gnosis_safe_proxy_factory.get_instance(
199
+ ledger_api=ledger_api,
200
+ contract_address="0xa6b71e26c5e0845f74c812102ca7114b6a896ab2",
201
+ ),
202
+ event_name="ProxyCreation",
208
203
  )
209
- (event,) = instance.events.ProxyCreation().process_receipt(receipt)
210
204
  safe_address = event["args"]["proxy"]
211
205
 
212
206
  if backup_owner is not None:
@@ -231,7 +225,7 @@ def create_safe(
231
225
  )
232
226
  time.sleep(next_delay)
233
227
 
234
- return safe_address, salt_nonce, tx_hash
228
+ return safe_address, salt_nonce, tx_settler.tx_hash
235
229
 
236
230
 
237
231
  def get_owners(ledger_api: LedgerApi, safe: str) -> t.List[str]:
@@ -248,16 +242,14 @@ def send_safe_txs(
248
242
  ledger_api: LedgerApi,
249
243
  crypto: Crypto,
250
244
  to: t.Optional[str] = None,
251
- ) -> t.Optional[str]:
245
+ ) -> str:
252
246
  """Send internal safe transaction."""
253
247
  owner = ledger_api.api.to_checksum_address(
254
248
  crypto.address,
255
249
  )
256
250
  to_address = to or safe
257
251
 
258
- def _build_tx( # pylint: disable=unused-argument
259
- *args: t.Any, **kwargs: t.Any
260
- ) -> t.Optional[str]:
252
+ def _build_tx() -> t.Optional[str]:
261
253
  safe_tx_hash = registry_contracts.gnosis_safe.get_raw_safe_transaction_hash(
262
254
  ledger_api=ledger_api,
263
255
  contract_address=safe,
@@ -290,25 +282,22 @@ def send_safe_txs(
290
282
  nonce=ledger_api.api.eth.get_transaction_count(owner),
291
283
  )
292
284
 
293
- tx_settler = TxSettler(
294
- ledger_api=ledger_api,
295
- crypto=crypto,
296
- chain_type=Chain.from_id(
297
- ledger_api._chain_id # pylint: disable=protected-access
298
- ),
299
- timeout=ON_CHAIN_INTERACT_TIMEOUT,
300
- retries=ON_CHAIN_INTERACT_RETRIES,
301
- sleep=ON_CHAIN_INTERACT_SLEEP,
302
- )
303
- setattr(tx_settler, "build", _build_tx) # noqa: B010
304
- tx_receipt = tx_settler.transact(
305
- method=lambda: {},
306
- contract="",
307
- kwargs={},
308
- dry_run=False,
285
+ return (
286
+ TxSettler(
287
+ ledger_api=ledger_api,
288
+ crypto=crypto,
289
+ chain_type=Chain.from_id(
290
+ ledger_api._chain_id # pylint: disable=protected-access
291
+ ),
292
+ tx_builder=_build_tx,
293
+ timeout=ON_CHAIN_INTERACT_TIMEOUT,
294
+ retries=ON_CHAIN_INTERACT_RETRIES,
295
+ sleep=ON_CHAIN_INTERACT_SLEEP,
296
+ )
297
+ .transact()
298
+ .settle()
299
+ .tx_hash
309
300
  )
310
- tx_hash = tx_receipt.get("transactionHash", "").hex()
311
- return tx_hash
312
301
 
313
302
 
314
303
  def add_owner(
@@ -322,8 +311,8 @@ def add_owner(
322
311
  ledger_api=ledger_api,
323
312
  contract_address=safe,
324
313
  )
325
- txd = instance.encodeABI(
326
- fn_name="addOwnerWithThreshold",
314
+ txd = instance.encode_abi(
315
+ abi_element_identifier="addOwnerWithThreshold",
327
316
  args=[
328
317
  owner,
329
318
  1,
@@ -368,8 +357,8 @@ def swap_owner(
368
357
  ledger_api=ledger_api,
369
358
  contract_address=safe,
370
359
  )
371
- txd = instance.encodeABI(
372
- fn_name="swapOwner",
360
+ txd = instance.encode_abi(
361
+ abi_element_identifier="swapOwner",
373
362
  args=[
374
363
  prev_owner,
375
364
  old_owner,
@@ -398,8 +387,8 @@ def remove_owner(
398
387
  ledger_api=ledger_api,
399
388
  contract_address=safe,
400
389
  )
401
- txd = instance.encodeABI(
402
- fn_name="removeOwner",
390
+ txd = instance.encode_abi(
391
+ abi_element_identifier="removeOwner",
403
392
  args=[
404
393
  prev_owner,
405
394
  owner,
@@ -420,16 +409,14 @@ def transfer(
420
409
  safe: str,
421
410
  to: str,
422
411
  amount: t.Union[float, int],
423
- ) -> t.Optional[str]:
412
+ ) -> str:
424
413
  """Transfer assets from safe to given address."""
425
414
  amount = int(amount)
426
415
  owner = ledger_api.api.to_checksum_address(
427
416
  crypto.address,
428
417
  )
429
418
 
430
- def _build_tx( # pylint: disable=unused-argument
431
- *args: t.Any, **kwargs: t.Any
432
- ) -> t.Optional[str]:
419
+ def _build_tx() -> t.Optional[str]:
433
420
  safe_tx_hash = registry_contracts.gnosis_safe.get_raw_safe_transaction_hash(
434
421
  ledger_api=ledger_api,
435
422
  contract_address=safe,
@@ -462,25 +449,22 @@ def transfer(
462
449
  nonce=ledger_api.api.eth.get_transaction_count(owner),
463
450
  )
464
451
 
465
- tx_settler = TxSettler(
466
- ledger_api=ledger_api,
467
- crypto=crypto,
468
- chain_type=Chain.from_id(
469
- ledger_api._chain_id # pylint: disable=protected-access
470
- ),
471
- timeout=ON_CHAIN_INTERACT_TIMEOUT,
472
- retries=ON_CHAIN_INTERACT_RETRIES,
473
- sleep=ON_CHAIN_INTERACT_SLEEP,
474
- )
475
- setattr(tx_settler, "build", _build_tx) # noqa: B010
476
- tx_receipt = tx_settler.transact(
477
- method=lambda: {},
478
- contract="",
479
- kwargs={},
480
- dry_run=False,
452
+ return (
453
+ TxSettler(
454
+ ledger_api=ledger_api,
455
+ crypto=crypto,
456
+ chain_type=Chain.from_id(
457
+ ledger_api._chain_id # pylint: disable=protected-access
458
+ ),
459
+ tx_builder=_build_tx,
460
+ timeout=ON_CHAIN_INTERACT_TIMEOUT,
461
+ retries=ON_CHAIN_INTERACT_RETRIES,
462
+ sleep=ON_CHAIN_INTERACT_SLEEP,
463
+ )
464
+ .transact()
465
+ .settle()
466
+ .tx_hash
481
467
  )
482
- tx_hash = tx_receipt.get("transactionHash", "").hex()
483
- return tx_hash
484
468
 
485
469
 
486
470
  def transfer_erc20_from_safe(
@@ -497,8 +481,8 @@ def transfer_erc20_from_safe(
497
481
  ledger_api=ledger_api,
498
482
  contract_address=token,
499
483
  )
500
- txd = instance.encodeABI(
501
- fn_name="transfer",
484
+ txd = instance.encode_abi(
485
+ abi_element_identifier="transfer",
502
486
  args=[
503
487
  to,
504
488
  amount,
@@ -547,18 +531,8 @@ def drain_eoa(
547
531
  chain_id: int,
548
532
  ) -> t.Optional[str]:
549
533
  """Drain all the native tokens from the crypto wallet."""
550
- tx_helper = TxSettler(
551
- ledger_api=ledger_api,
552
- crypto=crypto,
553
- chain_type=ChainProfile.CUSTOM,
554
- timeout=ON_CHAIN_INTERACT_TIMEOUT,
555
- retries=ON_CHAIN_INTERACT_RETRIES,
556
- sleep=ON_CHAIN_INTERACT_SLEEP,
557
- )
558
534
 
559
- def _build_tx( # pylint: disable=unused-argument
560
- *args: t.Any, **kwargs: t.Any
561
- ) -> t.Dict:
535
+ def _build_tx() -> t.Dict:
562
536
  """Build transaction"""
563
537
  chain_fee = estimate_transfer_tx_fee(
564
538
  chain=Chain.from_id(chain_id),
@@ -595,13 +569,20 @@ def drain_eoa(
595
569
 
596
570
  return tx
597
571
 
598
- setattr(tx_helper, "build", _build_tx) # noqa: B010
599
572
  try:
600
- tx_receipt = tx_helper.transact(
601
- method=lambda: {},
602
- contract="",
603
- kwargs={},
604
- dry_run=False,
573
+ return (
574
+ TxSettler(
575
+ ledger_api=ledger_api,
576
+ crypto=crypto,
577
+ chain_type=ChainProfile.CUSTOM,
578
+ timeout=ON_CHAIN_INTERACT_TIMEOUT,
579
+ retries=ON_CHAIN_INTERACT_RETRIES,
580
+ sleep=ON_CHAIN_INTERACT_SLEEP,
581
+ tx_builder=_build_tx,
582
+ )
583
+ .transact()
584
+ .settle()
585
+ .tx_hash
605
586
  )
606
587
  except ChainInteractionError as e:
607
588
  if "No balance to drain from wallet" in str(e):
@@ -610,12 +591,6 @@ def drain_eoa(
610
591
 
611
592
  raise e
612
593
 
613
- tx_hash = tx_receipt.get("transactionHash", None)
614
- if tx_hash is not None:
615
- return tx_hash.hex()
616
-
617
- return None
618
-
619
594
 
620
595
  def get_asset_balance(
621
596
  ledger_api: LedgerApi,
operate/wallet/master.py CHANGED
@@ -75,14 +75,12 @@ class MasterWallet(LocalResource):
75
75
 
76
76
  path: Path
77
77
  address: str
78
-
79
- safes: t.Dict[Chain, str] = field(default_factory=dict)
80
- safe_chains: t.List[Chain] = field(default_factory=list)
78
+ safes: t.Dict[Chain, str]
79
+ safe_chains: t.List[Chain]
81
80
  ledger_type: LedgerType
82
81
  safe_nonce: t.Optional[int] = None
83
82
 
84
83
  _key: str
85
- _mnemonic: str
86
84
  _crypto: t.Optional[Crypto] = None
87
85
  _password: t.Optional[str] = None
88
86
  _crypto_cls: t.Type[Crypto]
@@ -111,10 +109,15 @@ class MasterWallet(LocalResource):
111
109
  """Key path."""
112
110
  return self.path / self._key
113
111
 
112
+ @classmethod
113
+ def mnemonic_filename(cls) -> str:
114
+ """Return deterministic mnemonic filename per ledger type."""
115
+ return f"{cls.ledger_type.value.lower()}.mnemonic.json"
116
+
114
117
  @property
115
118
  def mnemonic_path(self) -> Path:
116
119
  """Mnemonic path."""
117
- return self.path / self._mnemonic
120
+ return self.path / self.__class__.mnemonic_filename()
118
121
 
119
122
  @staticmethod
120
123
  def ledger_api(
@@ -250,7 +253,6 @@ class EthereumMasterWallet(MasterWallet):
250
253
 
251
254
  _file = ledger_type.config_file
252
255
  _key = ledger_type.key_file
253
- _mnemonic = ledger_type.mnemonic_file
254
256
  _crypto_cls = EthereumCrypto
255
257
 
256
258
  def _pre_transfer_checks(
@@ -284,7 +286,7 @@ class EthereumMasterWallet(MasterWallet):
284
286
 
285
287
  def _transfer_from_eoa(
286
288
  self, to: str, amount: int, chain: Chain, rpc: t.Optional[str] = None
287
- ) -> t.Optional[str]:
289
+ ) -> str:
288
290
  """Transfer funds from EOA wallet."""
289
291
  balance = self.get_balance(chain=chain, from_safe=False)
290
292
  tx_fee = estimate_transfer_tx_fee(
@@ -306,18 +308,8 @@ class EthereumMasterWallet(MasterWallet):
306
308
  )
307
309
 
308
310
  ledger_api = t.cast(EthereumApi, self.ledger_api(chain=chain, rpc=rpc))
309
- tx_helper = TxSettler(
310
- ledger_api=ledger_api,
311
- crypto=self.crypto,
312
- chain_type=ChainProfile.CUSTOM,
313
- timeout=ON_CHAIN_INTERACT_TIMEOUT,
314
- retries=ON_CHAIN_INTERACT_RETRIES,
315
- sleep=ON_CHAIN_INTERACT_SLEEP,
316
- )
317
311
 
318
- def _build_tx( # pylint: disable=unused-argument
319
- *args: t.Any, **kwargs: t.Any
320
- ) -> t.Dict:
312
+ def _build_tx() -> t.Dict:
321
313
  """Build transaction"""
322
314
  max_priority_fee_per_gas = os.getenv("MAX_PRIORITY_FEE_PER_GAS", None)
323
315
  max_fee_per_gas = os.getenv("MAX_FEE_PER_GAS", None)
@@ -339,10 +331,20 @@ class EthereumMasterWallet(MasterWallet):
339
331
  raise_on_try=True,
340
332
  )
341
333
 
342
- setattr(tx_helper, "build", _build_tx) # noqa: B010
343
- tx_receipt = tx_helper.transact(lambda x: x, "", kwargs={})
344
- tx_hash = tx_receipt.get("transactionHash", "").hex()
345
- return tx_hash
334
+ return (
335
+ TxSettler(
336
+ ledger_api=ledger_api,
337
+ crypto=self.crypto,
338
+ chain_type=ChainProfile.CUSTOM,
339
+ timeout=ON_CHAIN_INTERACT_TIMEOUT,
340
+ retries=ON_CHAIN_INTERACT_RETRIES,
341
+ sleep=ON_CHAIN_INTERACT_SLEEP,
342
+ tx_builder=_build_tx,
343
+ )
344
+ .transact()
345
+ .settle()
346
+ .tx_hash
347
+ )
346
348
 
347
349
  def _transfer_from_safe(
348
350
  self, to: str, amount: int, chain: Chain, rpc: t.Optional[str] = None
@@ -389,7 +391,7 @@ class EthereumMasterWallet(MasterWallet):
389
391
  amount: int,
390
392
  chain: Chain,
391
393
  rpc: t.Optional[str] = None,
392
- ) -> t.Optional[str]:
394
+ ) -> str:
393
395
  """Transfer erc20 from EOA wallet."""
394
396
  to = self._pre_transfer_checks(
395
397
  to=to, amount=amount, chain=chain, from_safe=False, asset=token
@@ -397,18 +399,8 @@ class EthereumMasterWallet(MasterWallet):
397
399
 
398
400
  wallet_address = self.address
399
401
  ledger_api = t.cast(EthereumApi, self.ledger_api(chain=chain, rpc=rpc))
400
- tx_settler = TxSettler(
401
- ledger_api=ledger_api,
402
- crypto=self.crypto,
403
- chain_type=ChainProfile.CUSTOM,
404
- timeout=ON_CHAIN_INTERACT_TIMEOUT,
405
- retries=ON_CHAIN_INTERACT_RETRIES,
406
- sleep=ON_CHAIN_INTERACT_SLEEP,
407
- )
408
402
 
409
- def _build_transfer_tx( # pylint: disable=unused-argument
410
- *args: t.Any, **kargs: t.Any
411
- ) -> t.Dict:
403
+ def _build_transfer_tx() -> t.Dict:
412
404
  # TODO Backport to OpenAEA
413
405
  instance = registry_contracts.erc20.get_instance(
414
406
  ledger_api=ledger_api,
@@ -427,15 +419,20 @@ class EthereumMasterWallet(MasterWallet):
427
419
  update_tx_with_gas_estimate(tx, ledger_api)
428
420
  return tx
429
421
 
430
- setattr(tx_settler, "build", _build_transfer_tx) # noqa: B010
431
- tx_receipt = tx_settler.transact(
432
- method=lambda: {},
433
- contract="",
434
- kwargs={},
435
- dry_run=False,
422
+ return (
423
+ TxSettler(
424
+ ledger_api=ledger_api,
425
+ crypto=self.crypto,
426
+ chain_type=ChainProfile.CUSTOM,
427
+ timeout=ON_CHAIN_INTERACT_TIMEOUT,
428
+ retries=ON_CHAIN_INTERACT_RETRIES,
429
+ sleep=ON_CHAIN_INTERACT_SLEEP,
430
+ tx_builder=_build_transfer_tx,
431
+ )
432
+ .transact()
433
+ .settle()
434
+ .tx_hash
436
435
  )
437
- tx_hash = tx_receipt.get("transactionHash", "").hex()
438
- return tx_hash
439
436
 
440
437
  def transfer( # pylint: disable=too-many-arguments
441
438
  self,
@@ -572,7 +569,7 @@ class EthereumMasterWallet(MasterWallet):
572
569
  # Backport support on aea
573
570
 
574
571
  eoa_wallet_path = path / cls._key
575
- eoa_mnemonic_path = path / cls._mnemonic
572
+ eoa_mnemonic_path = path / cls.mnemonic_filename()
576
573
 
577
574
  if eoa_wallet_path.exists():
578
575
  raise FileExistsError(f"Wallet file already exists at {eoa_wallet_path}.")
@@ -611,12 +608,10 @@ class EthereumMasterWallet(MasterWallet):
611
608
 
612
609
  def decrypt_mnemonic(self, password: str) -> t.Optional[t.List[str]]:
613
610
  """Retrieve the mnemonic"""
614
- eoa_mnemonic_path = self.path / self.ledger_type.mnemonic_file
615
-
616
- if not eoa_mnemonic_path.exists():
611
+ if not self.mnemonic_path.exists():
617
612
  return None
618
613
 
619
- encrypted_mnemonic = EncryptedData.load(eoa_mnemonic_path)
614
+ encrypted_mnemonic = EncryptedData.load(self.mnemonic_path)
620
615
  mnemonic = encrypted_mnemonic.decrypt(password).decode("utf-8")
621
616
  return mnemonic.split()
622
617
 
@@ -722,6 +717,9 @@ class EthereumMasterWallet(MasterWallet):
722
717
  "The master wallet cannot be set as the Safe backup owner."
723
718
  )
724
719
 
720
+ if self.address not in owners:
721
+ return False
722
+
725
723
  owners.remove(self.address)
726
724
  old_backup_owner = owners[0] if owners else None
727
725
 
@@ -769,7 +767,9 @@ class EthereumMasterWallet(MasterWallet):
769
767
  chain_str = chain.value
770
768
  ledger_api = self.ledger_api(chain=chain, rpc=rpc)
771
769
  owners = get_owners(ledger_api=ledger_api, safe=safe)
772
- owners.remove(self.address)
770
+
771
+ if self.address in owners:
772
+ owners.remove(self.address)
773
773
 
774
774
  balances[chain_str] = {self.address: {}, safe: {}}
775
775
 
@@ -781,8 +781,8 @@ class EthereumMasterWallet(MasterWallet):
781
781
  balances[chain_str][safe][asset] = self.get_balance(
782
782
  chain=chain, asset=asset, from_safe=True
783
783
  )
784
- wallet_json["safes"][chain.value] = {
785
- wallet_json["safes"][chain.value]: {
784
+ wallet_json["safes"][chain_str] = {
785
+ safe: {
786
786
  "backup_owners": owners,
787
787
  "balances": balances[chain_str][safe],
788
788
  }
@@ -791,6 +791,9 @@ class EthereumMasterWallet(MasterWallet):
791
791
 
792
792
  wallet_json["balances"] = balances
793
793
  wallet_json["extended_json"] = True
794
+ wallet_json["all_safes_have_backup_owner"] = all(
795
+ len(owners) > 0 for owners in owner_sets
796
+ )
794
797
  wallet_json["consistent_safe_address"] = len(set(self.safes.values())) == 1
795
798
  wallet_json["consistent_backup_owner"] = len(owner_sets) == 1
796
799
  wallet_json["consistent_backup_owner_count"] = all(