iwa 0.0.58__py3-none-any.whl → 0.0.60__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 (60) hide show
  1. iwa/core/chain/interface.py +118 -53
  2. iwa/core/chain/rate_limiter.py +35 -12
  3. iwa/core/chainlist.py +15 -10
  4. iwa/core/cli.py +3 -0
  5. iwa/core/contracts/cache.py +1 -1
  6. iwa/core/contracts/contract.py +1 -0
  7. iwa/core/contracts/decoder.py +10 -4
  8. iwa/core/http.py +31 -0
  9. iwa/core/ipfs.py +11 -19
  10. iwa/core/keys.py +10 -4
  11. iwa/core/models.py +1 -3
  12. iwa/core/pricing.py +3 -21
  13. iwa/core/rpc_monitor.py +1 -0
  14. iwa/core/services/balance.py +0 -1
  15. iwa/core/services/safe.py +8 -2
  16. iwa/core/services/safe_executor.py +52 -18
  17. iwa/core/services/transaction.py +32 -12
  18. iwa/core/services/transfer/erc20.py +0 -1
  19. iwa/core/services/transfer/native.py +1 -1
  20. iwa/core/tests/test_gnosis_fee.py +6 -2
  21. iwa/core/tests/test_ipfs.py +1 -1
  22. iwa/core/tests/test_regression_fixes.py +3 -6
  23. iwa/core/utils.py +2 -0
  24. iwa/core/wallet.py +3 -1
  25. iwa/plugins/olas/constants.py +15 -5
  26. iwa/plugins/olas/contracts/activity_checker.py +3 -3
  27. iwa/plugins/olas/contracts/staking.py +0 -1
  28. iwa/plugins/olas/events.py +15 -13
  29. iwa/plugins/olas/importer.py +26 -20
  30. iwa/plugins/olas/plugin.py +16 -14
  31. iwa/plugins/olas/service_manager/drain.py +1 -3
  32. iwa/plugins/olas/service_manager/lifecycle.py +9 -9
  33. iwa/plugins/olas/service_manager/staking.py +11 -6
  34. iwa/plugins/olas/tests/test_olas_archiving.py +25 -15
  35. iwa/plugins/olas/tests/test_olas_integration.py +49 -29
  36. iwa/plugins/olas/tests/test_service_manager.py +8 -10
  37. iwa/plugins/olas/tests/test_service_manager_errors.py +5 -4
  38. iwa/plugins/olas/tests/test_service_manager_flows.py +6 -5
  39. iwa/plugins/olas/tests/test_service_staking.py +64 -38
  40. iwa/tools/drain_accounts.py +2 -1
  41. iwa/tools/reset_env.py +2 -1
  42. iwa/tools/test_chainlist.py +5 -1
  43. iwa/tui/screens/wallets.py +1 -3
  44. iwa/web/routers/olas/services.py +10 -5
  45. {iwa-0.0.58.dist-info → iwa-0.0.60.dist-info}/METADATA +1 -1
  46. {iwa-0.0.58.dist-info → iwa-0.0.60.dist-info}/RECORD +60 -59
  47. tests/test_balance_service.py +0 -2
  48. tests/test_chain.py +1 -2
  49. tests/test_chain_interface.py +3 -3
  50. tests/test_rate_limiter.py +7 -5
  51. tests/test_rate_limiter_retry.py +34 -33
  52. tests/test_rpc_efficiency.py +4 -1
  53. tests/test_rpc_rate_limit.py +4 -3
  54. tests/test_rpc_rotation.py +4 -4
  55. tests/test_safe_executor.py +76 -50
  56. tests/test_safe_integration.py +11 -6
  57. {iwa-0.0.58.dist-info → iwa-0.0.60.dist-info}/WHEEL +0 -0
  58. {iwa-0.0.58.dist-info → iwa-0.0.60.dist-info}/entry_points.txt +0 -0
  59. {iwa-0.0.58.dist-info → iwa-0.0.60.dist-info}/licenses/LICENSE +0 -0
  60. {iwa-0.0.58.dist-info → iwa-0.0.60.dist-info}/top_level.txt +0 -0
@@ -181,9 +181,7 @@ class OlasServiceImporter:
181
181
  if service.service_id:
182
182
  key = f"{service.chain_name}:{service.service_id}"
183
183
  if key in seen_keys:
184
- logger.debug(
185
- f"Skipping duplicate service {key} from {service.source_folder}"
186
- )
184
+ logger.debug(f"Skipping duplicate service {key} from {service.source_folder}")
187
185
  duplicates += 1
188
186
  continue
189
187
  seen_keys.add(key)
@@ -482,9 +480,7 @@ class OlasServiceImporter:
482
480
  staking_program_id, chain_name
483
481
  )
484
482
 
485
- def _resolve_staking_contract(
486
- self, staking_program_id: str, chain_name: str
487
- ) -> Optional[str]:
483
+ def _resolve_staking_contract(self, staking_program_id: str, chain_name: str) -> Optional[str]:
488
484
  """Resolve a staking program ID to a contract address."""
489
485
  address = STAKING_PROGRAM_MAP.get(staking_program_id)
490
486
  if address:
@@ -540,8 +536,10 @@ class OlasServiceImporter:
540
536
 
541
537
  # Check for "safes" entry which indicates the owner is a Safe
542
538
  # Structure: "safes": { "gnosis": "0x..." }
543
- if "safes" in data and FLAGS_OWNER_SAFE in data["safes"]: # Need to detect chain dynamically or iterate
544
- pass
539
+ if (
540
+ "safes" in data and FLAGS_OWNER_SAFE in data["safes"]
541
+ ): # Need to detect chain dynamically or iterate
542
+ pass
545
543
 
546
544
  # Logic update:
547
545
  # 1. Capture EOA address always (it's the signer)
@@ -557,9 +555,13 @@ class OlasServiceImporter:
557
555
  if safe_owner_address:
558
556
  # CASE: Owner is Safe
559
557
  service.service_owner_multisig_address = safe_owner_address
560
- service.service_owner_eoa_address = eoa_address # The EOA is the signer/controller
558
+ service.service_owner_eoa_address = (
559
+ eoa_address # The EOA is the signer/controller
560
+ )
561
561
 
562
- logger.debug(f"Extracted Safe owner address: {safe_owner_address} (Signer: {eoa_address})")
562
+ logger.debug(
563
+ f"Extracted Safe owner address: {safe_owner_address} (Signer: {eoa_address})"
564
+ )
563
565
  elif eoa_address:
564
566
  # CASE: Owner is EOA
565
567
  service.service_owner_eoa_address = eoa_address
@@ -767,8 +769,8 @@ class OlasServiceImporter:
767
769
  safe_result = self._import_safe(
768
770
  address=service.safe_address,
769
771
  signers=self._get_agent_signers(service),
770
- tag_suffix="multisig", # e.g. trader_zeta_safe
771
- service_name=service.service_name
772
+ tag_suffix="multisig", # e.g. trader_zeta_safe
773
+ service_name=service.service_name,
772
774
  )
773
775
  if safe_result[0]:
774
776
  result.imported_safes.append(service.safe_address)
@@ -778,19 +780,22 @@ class OlasServiceImporter:
778
780
  result.errors.append(f"Safe {service.safe_address}: {safe_result[1]}")
779
781
 
780
782
  # 2. Import Owner Safe (if it exists and is different)
781
- if service.service_owner_multisig_address and service.service_owner_multisig_address != service.safe_address:
782
- # Signer for Owner Safe is the EOA owner key
783
+ if (
784
+ service.service_owner_multisig_address
785
+ and service.service_owner_multisig_address != service.safe_address
786
+ ):
787
+ # Signer for Owner Safe is the EOA owner key
783
788
  owner_signers = self._get_owner_signers(service)
784
789
 
785
790
  safe_result = self._import_safe(
786
791
  address=service.service_owner_multisig_address,
787
792
  signers=owner_signers,
788
- tag_suffix="owner_multisig", # e.g. trader_zeta_owner_safe
789
- service_name=service.service_name
793
+ tag_suffix="owner_multisig", # e.g. trader_zeta_owner_safe
794
+ service_name=service.service_name,
790
795
  )
791
796
  if safe_result[0]:
792
- result.imported_safes.append(service.service_owner_multisig_address)
793
- logger.info(f"Imported Owner Safe {service.service_owner_multisig_address}")
797
+ result.imported_safes.append(service.service_owner_multisig_address)
798
+ logger.info(f"Imported Owner Safe {service.service_owner_multisig_address}")
794
799
 
795
800
  def _get_agent_signers(self, service: DiscoveredService) -> List[str]:
796
801
  """Get list of signers for the agent safe."""
@@ -926,7 +931,7 @@ class OlasServiceImporter:
926
931
  address: str,
927
932
  signers: List[str] = None,
928
933
  tag_suffix: str = "multisig",
929
- service_name: Optional[str] = None
934
+ service_name: Optional[str] = None,
930
935
  ) -> Tuple[bool, str]:
931
936
  """Import a generic Safe."""
932
937
  if not address:
@@ -1062,4 +1067,5 @@ class OlasServiceImporter:
1062
1067
  key.signature_failed = True
1063
1068
  logger.warning(f"Error verifying signature for key {key.address}: {e}")
1064
1069
 
1065
- FLAGS_OWNER_SAFE="deprecated"
1070
+
1071
+ FLAGS_OWNER_SAFE = "deprecated"
@@ -151,7 +151,9 @@ class OlasPlugin(Plugin):
151
151
 
152
152
  is_signer = key_addr in [s.lower() for s in on_chain_signers]
153
153
  if not is_signer:
154
- safe_text += f"\n[bold red]⚠ Agent {agent_key.address} - NOT A SIGNER![/bold red]"
154
+ safe_text += (
155
+ f"\n[bold red]⚠ Agent {agent_key.address} - NOT A SIGNER![/bold red]"
156
+ )
155
157
  else:
156
158
  safe_text += f" (Signer: {agent_key.address[:6]}...)"
157
159
 
@@ -182,21 +184,21 @@ class OlasPlugin(Plugin):
182
184
  # 1. Display Signer/EOA Owner
183
185
  owner_key = next((k for k in service.keys if k.role == "owner"), None)
184
186
  if owner_key:
185
- val = owner_key.address
186
- if not val.startswith("0x"):
187
- val = "0x" + val
188
-
189
- if owner_key.signature_verified:
190
- val = f"[green]{val}[/green]"
191
- elif not owner_key.is_encrypted:
192
- val = f"[red]{val}[/red]"
193
- status = "🔒 encrypted" if owner_key.is_encrypted else "🔓 plaintext"
194
- table.add_row("Owner (EOA)", f"{val} {status}")
187
+ val = owner_key.address
188
+ if not val.startswith("0x"):
189
+ val = "0x" + val
190
+
191
+ if owner_key.signature_verified:
192
+ val = f"[green]{val}[/green]"
193
+ elif not owner_key.is_encrypted:
194
+ val = f"[red]{val}[/red]"
195
+ status = "🔒 encrypted" if owner_key.is_encrypted else "🔓 plaintext"
196
+ table.add_row("Owner (EOA)", f"{val} {status}")
195
197
  elif service.service_owner_eoa_address:
196
- # Fallback if we have an address but no key object
197
- table.add_row("Owner (EOA)", service.service_owner_eoa_address)
198
+ # Fallback if we have an address but no key object
199
+ table.add_row("Owner (EOA)", service.service_owner_eoa_address)
198
200
  else:
199
- table.add_row("Owner (EOA)", "[yellow]N/A[/yellow]")
201
+ table.add_row("Owner (EOA)", "[yellow]N/A[/yellow]")
200
202
 
201
203
  # 2. Display Safe Owner
202
204
  if service.service_owner_multisig_address:
@@ -135,9 +135,7 @@ class DrainManagerMixin:
135
135
  withdrawal_tag = self.wallet.get_tag_by_address(withdrawal_address) or withdrawal_address
136
136
  multisig_tag = self.wallet.get_tag_by_address(multisig_address) or multisig_address
137
137
 
138
- logger.info(
139
- f"Withdrawing {olas_amount:.4f} OLAS from {multisig_tag} to {withdrawal_tag}"
140
- )
138
+ logger.info(f"Withdrawing {olas_amount:.4f} OLAS from {multisig_tag} to {withdrawal_tag}")
141
139
 
142
140
  # Transfer from Safe to withdrawal address
143
141
  tx_hash = self.wallet.send(
@@ -429,9 +429,7 @@ class LifecycleManagerMixin:
429
429
  )
430
430
 
431
431
  if balance < bond_amount:
432
- logger.error(
433
- f"[ACTIVATE] FAIL: Owner balance {balance} < required {bond_amount}"
434
- )
432
+ logger.error(f"[ACTIVATE] FAIL: Owner balance {balance} < required {bond_amount}")
435
433
  return False
436
434
 
437
435
  protocol_contracts = OLAS_CONTRACTS.get(self.chain_name.lower(), {})
@@ -682,7 +680,9 @@ class LifecycleManagerMixin:
682
680
  )
683
681
  return True
684
682
 
685
- logger.info(f"[REGISTER] Service Owner approving Token Utility for bond: {bond_amount_wei} wei")
683
+ logger.info(
684
+ f"[REGISTER] Service Owner approving Token Utility for bond: {bond_amount_wei} wei"
685
+ )
686
686
 
687
687
  utility_address = str(
688
688
  OLAS_CONTRACTS[self.chain_name]["OLAS_SERVICE_REGISTRY_TOKEN_UTILITY"]
@@ -697,9 +697,7 @@ class LifecycleManagerMixin:
697
697
  )
698
698
 
699
699
  if allowance >= bond_amount_wei:
700
- logger.debug(
701
- f"[REGISTER] Sufficient allowance ({allowance} >= {bond_amount_wei})"
702
- )
700
+ logger.debug(f"[REGISTER] Sufficient allowance ({allowance} >= {bond_amount_wei})")
703
701
  return True
704
702
 
705
703
  # Use service owner which holds the OLAS tokens (not necessarily master)
@@ -800,7 +798,9 @@ class LifecycleManagerMixin:
800
798
  )
801
799
  return False
802
800
 
803
- logger.debug(f"[DEPLOY] Preparing deploy tx for owner {self._get_label(self.service.service_owner_address)}")
801
+ logger.debug(
802
+ f"[DEPLOY] Preparing deploy tx for owner {self._get_label(self.service.service_owner_address)}"
803
+ )
804
804
  deploy_tx = self.manager.prepare_deploy_tx(
805
805
  from_address=self.service.service_owner_address,
806
806
  service_id=self.service.service_id,
@@ -855,7 +855,7 @@ class LifecycleManagerMixin:
855
855
  _, agent_instances = self.registry.call("getAgentInstances", self.service.service_id)
856
856
  service_info = self.registry.get_service(self.service.service_id)
857
857
  threshold = service_info["threshold"]
858
- # Store the multisig in the wallet with tag
858
+ # Store the multisig in the wallet with tag
859
859
  multisig_tag = f"{self.service.service_name}_multisig"
860
860
 
861
861
  # ARCHIVING LOGIC: If tag is already taken by a different address, rename the old one
@@ -90,6 +90,7 @@ class StakingManagerMixin:
90
90
  # Try token/contract names
91
91
  try:
92
92
  from iwa.core.chain import ChainInterfaces
93
+
93
94
  chain_interface = ChainInterfaces().get(self.chain_name)
94
95
  token_name = chain_interface.chain.get_token_name(address)
95
96
  if token_name:
@@ -407,7 +408,9 @@ class StakingManagerMixin:
407
408
  # NOTE: We don't check OLAS balance here because OLAS was already
408
409
  # deposited to the Token Utility during activation (min_staking_deposit)
409
410
  # and registration (agent_bond). The staking contract pulls from there.
410
- logger.debug("[STAKE] OLAS already deposited to Token Utility during activation/registration")
411
+ logger.debug(
412
+ "[STAKE] OLAS already deposited to Token Utility during activation/registration"
413
+ )
411
414
 
412
415
  return {"min_deposit": min_deposit, "staking_token": staking_token}
413
416
 
@@ -437,7 +440,9 @@ class StakingManagerMixin:
437
440
  owner_address = self.service.service_owner_address or self.wallet.master_account.address
438
441
 
439
442
  # Approve service NFT - this is an ERC-721 approval, not ERC-20
440
- logger.debug(f"[STAKE] Approving service NFT for staking contract from {self._get_label(owner_address)}...")
443
+ logger.debug(
444
+ f"[STAKE] Approving service NFT for staking contract from {self._get_label(owner_address)}..."
445
+ )
441
446
  approve_tx = self.registry.prepare_approve_tx(
442
447
  from_address=owner_address,
443
448
  spender=staking_contract.address,
@@ -483,7 +488,9 @@ class StakingManagerMixin:
483
488
  # Use service owner which holds the NFT (not necessarily master)
484
489
  owner_address = self.service.service_owner_address or self.wallet.master_account.address
485
490
 
486
- logger.debug(f"[STAKE] Preparing stake transaction from {self._get_label(owner_address)}...")
491
+ logger.debug(
492
+ f"[STAKE] Preparing stake transaction from {self._get_label(owner_address)}..."
493
+ )
487
494
  stake_tx = staking_contract.prepare_stake_tx(
488
495
  from_address=owner_address,
489
496
  service_id=self.service.service_id,
@@ -601,9 +608,7 @@ class StakingManagerMixin:
601
608
  return False
602
609
 
603
610
  tx_hash = get_tx_hash(receipt)
604
- logger.info(
605
- f"Unstake transaction sent: {tx_hash if receipt else 'No Receipt'}"
606
- )
611
+ logger.info(f"Unstake transaction sent: {tx_hash if receipt else 'No Receipt'}")
607
612
 
608
613
  events = staking_contract.extract_events(receipt)
609
614
 
@@ -20,10 +20,12 @@ class TestOlasArchiving(unittest.TestCase):
20
20
  self.mock_wallet.key_storage = self.mock_key_storage
21
21
 
22
22
  # Initialize ServiceManager
23
- with patch("iwa.core.models.Config"), \
24
- patch("iwa.plugins.olas.service_manager.base.ChainInterfaces"), \
25
- patch("iwa.plugins.olas.service_manager.base.ContractCache"), \
26
- patch("iwa.plugins.olas.service_manager.base.ServiceRegistryContract"):
23
+ with (
24
+ patch("iwa.core.models.Config"),
25
+ patch("iwa.plugins.olas.service_manager.base.ChainInterfaces"),
26
+ patch("iwa.plugins.olas.service_manager.base.ContractCache"),
27
+ patch("iwa.plugins.olas.service_manager.base.ServiceRegistryContract"),
28
+ ):
27
29
  self.sm = ServiceManager(self.mock_wallet)
28
30
  self.sm.service = Service(service_name="trader_psi", chain_name="gnosis", service_id=1)
29
31
  self.sm.chain_name = "gnosis"
@@ -40,23 +42,30 @@ class TestOlasArchiving(unittest.TestCase):
40
42
 
41
43
  # 2. Mock required contract calls for deploy()
42
44
  self.sm.registry.get_service.return_value = {
43
- "state": ServiceState.FINISHED_REGISTRATION, # DEPLOYED
45
+ "state": ServiceState.FINISHED_REGISTRATION, # DEPLOYED
44
46
  "security_deposit": 0,
45
47
  "multisig": new_address,
46
48
  "threshold": 1,
47
- "configHash": b"\x00" * 32
49
+ "configHash": b"\x00" * 32,
48
50
  }
49
- self.sm.registry.call.return_value = (None, []) # getAgentInstances
51
+ self.sm.registry.call.return_value = (None, []) # getAgentInstances
50
52
 
51
53
  # 3. Trigger deploy (archiving happens here)
52
- with patch.object(self.mock_wallet, "sign_and_send_transaction", return_value=(True, {"status": 1})), \
53
- patch.object(self.sm.registry, "extract_events", return_value=[
54
- {"name": "DeployService", "args": {}},
55
- {"name": "CreateMultisigWithAgents", "args": {"multisig": new_address}}
56
- ]), \
57
- patch("iwa.plugins.olas.service_manager.lifecycle.get_tx_hash", return_value="0xhash"), \
58
- patch("iwa.core.models.StoredSafeAccount") as mock_safe_cls:
59
-
54
+ with (
55
+ patch.object(
56
+ self.mock_wallet, "sign_and_send_transaction", return_value=(True, {"status": 1})
57
+ ),
58
+ patch.object(
59
+ self.sm.registry,
60
+ "extract_events",
61
+ return_value=[
62
+ {"name": "DeployService", "args": {}},
63
+ {"name": "CreateMultisigWithAgents", "args": {"multisig": new_address}},
64
+ ],
65
+ ),
66
+ patch("iwa.plugins.olas.service_manager.lifecycle.get_tx_hash", return_value="0xhash"),
67
+ patch("iwa.core.models.StoredSafeAccount") as mock_safe_cls,
68
+ ):
60
69
  self.sm.deploy(fund_multisig=False)
61
70
 
62
71
  # 4. Verify rename_account was called for the old address
@@ -69,5 +78,6 @@ class TestOlasArchiving(unittest.TestCase):
69
78
  self.assertEqual(kwargs["address"], new_address)
70
79
  self.assertEqual(kwargs["tag"], multisig_tag)
71
80
 
81
+
72
82
  if __name__ == "__main__":
73
83
  unittest.main()
@@ -309,9 +309,11 @@ def test_importer_import_service_config_duplicate(mock_wallet):
309
309
 
310
310
  def test_sm_create_token_utility_missing(mock_wallet):
311
311
  """Cover create() with missing token utility (lines 204-206)."""
312
- with patch("iwa.core.models.Config"), \
313
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
314
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
312
+ with (
313
+ patch("iwa.core.models.Config"),
314
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
315
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
316
+ ):
315
317
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
316
318
  sm = ServiceManager(mock_wallet)
317
319
 
@@ -323,9 +325,11 @@ def test_sm_create_token_utility_missing(mock_wallet):
323
325
 
324
326
  def test_sm_get_staking_status_staked_info_fail(mock_wallet):
325
327
  """Cover get_staking_status with STAKED but get_service_info fails (lines 843-854)."""
326
- with patch("iwa.core.models.Config"), \
327
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
328
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
328
+ with (
329
+ patch("iwa.core.models.Config"),
330
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
331
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
332
+ ):
329
333
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
330
334
  sm = ServiceManager(mock_wallet)
331
335
  sm.service = Service(
@@ -347,9 +351,11 @@ def test_sm_get_staking_status_staked_info_fail(mock_wallet):
347
351
 
348
352
  def test_sm_call_checkpoint_prepare_fail(mock_wallet):
349
353
  """Cover call_checkpoint prepare failure (lines 1100-1102)."""
350
- with patch("iwa.core.models.Config"), \
351
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
352
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
354
+ with (
355
+ patch("iwa.core.models.Config"),
356
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
357
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
358
+ ):
353
359
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
354
360
  sm = ServiceManager(mock_wallet)
355
361
  sm.service = Service(
@@ -369,9 +375,11 @@ def test_sm_call_checkpoint_prepare_fail(mock_wallet):
369
375
 
370
376
  def test_sm_spin_up_no_service(mock_wallet):
371
377
  """Cover spin_up with no service (lines 1167-1170)."""
372
- with patch("iwa.core.models.Config"), \
373
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
374
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
378
+ with (
379
+ patch("iwa.core.models.Config"),
380
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
381
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
382
+ ):
375
383
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
376
384
  sm = ServiceManager(mock_wallet)
377
385
  sm.service = None
@@ -382,9 +390,11 @@ def test_sm_spin_up_no_service(mock_wallet):
382
390
 
383
391
  def test_sm_spin_up_activation_fail(mock_wallet):
384
392
  """Cover spin_up activation failure (lines 1181-1183)."""
385
- with patch("iwa.core.models.Config"), \
386
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
387
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
393
+ with (
394
+ patch("iwa.core.models.Config"),
395
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
396
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
397
+ ):
388
398
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
389
399
  sm = ServiceManager(mock_wallet)
390
400
  sm.service = Service(service_name="t", chain_name="gnosis", service_id=1)
@@ -402,9 +412,11 @@ def test_sm_spin_up_activation_fail(mock_wallet):
402
412
 
403
413
  def test_sm_wind_down_no_service(mock_wallet):
404
414
  """Cover wind_down with no service (lines 1264-1266)."""
405
- with patch("iwa.core.models.Config"), \
406
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
407
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
415
+ with (
416
+ patch("iwa.core.models.Config"),
417
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
418
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
419
+ ):
408
420
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
409
421
  sm = ServiceManager(mock_wallet)
410
422
  sm.service = None
@@ -415,9 +427,11 @@ def test_sm_wind_down_no_service(mock_wallet):
415
427
 
416
428
  def test_sm_wind_down_nonexistent(mock_wallet):
417
429
  """Cover wind_down with non-existent service (lines 1274-1276)."""
418
- with patch("iwa.core.models.Config"), \
419
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
420
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
430
+ with (
431
+ patch("iwa.core.models.Config"),
432
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
433
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
434
+ ):
421
435
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
422
436
  sm = ServiceManager(mock_wallet)
423
437
  sm.service = Service(service_name="t", chain_name="gnosis", service_id=1)
@@ -432,9 +446,11 @@ def test_sm_wind_down_nonexistent(mock_wallet):
432
446
 
433
447
  def test_sm_mech_request_no_service(mock_wallet):
434
448
  """Cover _send_legacy_mech_request with no service (lines 1502-1504)."""
435
- with patch("iwa.core.models.Config"), \
436
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
437
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
449
+ with (
450
+ patch("iwa.core.models.Config"),
451
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
452
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
453
+ ):
438
454
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
439
455
  sm = ServiceManager(mock_wallet)
440
456
  sm.service = None
@@ -445,9 +461,11 @@ def test_sm_mech_request_no_service(mock_wallet):
445
461
 
446
462
  def test_sm_mech_request_no_address(mock_wallet):
447
463
  """Cover _send_legacy_mech_request missing mech address (lines 1510-1512)."""
448
- with patch("iwa.core.models.Config"), \
449
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
450
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
464
+ with (
465
+ patch("iwa.core.models.Config"),
466
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
467
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
468
+ ):
451
469
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
452
470
  sm = ServiceManager(mock_wallet)
453
471
  sm.service = Service(service_name="t", chain_name="unknown", service_id=1)
@@ -458,8 +476,10 @@ def test_sm_mech_request_no_address(mock_wallet):
458
476
 
459
477
  def test_sm_marketplace_mech_no_service(mock_wallet):
460
478
  """Cover _send_marketplace_mech_request with no service (lines 1549-1551)."""
461
- with patch("iwa.core.models.Config"), \
462
- patch("iwa.plugins.olas.service_manager.base.ContractCache"):
479
+ with (
480
+ patch("iwa.core.models.Config"),
481
+ patch("iwa.plugins.olas.service_manager.base.ContractCache"),
482
+ ):
463
483
  sm = ServiceManager(mock_wallet)
464
484
  sm.service = None
465
485
 
@@ -104,8 +104,6 @@ def mock_chain_interfaces():
104
104
  yield mock
105
105
 
106
106
 
107
-
108
-
109
107
  @pytest.fixture
110
108
  def service_manager(
111
109
  mock_config,
@@ -113,13 +111,14 @@ def service_manager(
113
111
  mock_registry,
114
112
  mock_manager_contract,
115
113
  mock_chain_interfaces,
116
-
117
114
  mock_olas_config,
118
115
  mock_service,
119
116
  ):
120
117
  """ServiceManager fixture with mocked dependencies."""
121
- with patch("iwa.plugins.olas.service_manager.base.Config") as local_mock_config, \
122
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache:
118
+ with (
119
+ patch("iwa.plugins.olas.service_manager.base.Config") as local_mock_config,
120
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
121
+ ):
123
122
  instance = local_mock_config.return_value
124
123
  instance.plugins = {"olas": mock_olas_config}
125
124
  instance.save_config = MagicMock()
@@ -136,7 +135,6 @@ def service_manager(
136
135
  yield sm
137
136
 
138
137
 
139
-
140
138
  def test_init(service_manager):
141
139
  """Test initialization."""
142
140
  assert service_manager.registry is not None
@@ -179,9 +177,7 @@ def test_create_no_event(service_manager, mock_wallet):
179
177
  mock_wallet.sign_and_send_transaction.return_value = (True, {})
180
178
  service_manager.registry.extract_events.return_value = []
181
179
 
182
- res = service_manager.create(
183
- token_address_or_tag="0x1111111111111111111111111111111111111111"
184
- )
180
+ res = service_manager.create(token_address_or_tag="0x1111111111111111111111111111111111111111")
185
181
  # create() finds no ID, logs error, returns None for service_id.
186
182
  assert res is None
187
183
 
@@ -911,7 +907,9 @@ def test_activate_registration_token_service_sends_security_deposit_as_value(
911
907
 
912
908
  # Mock balance check to pass
913
909
  mock_wallet.balance_service = MagicMock()
914
- mock_wallet.balance_service.get_erc20_balance_wei.return_value = 100 * 10**18 # Plenty of balance
910
+ mock_wallet.balance_service.get_erc20_balance_wei.return_value = (
911
+ 100 * 10**18
912
+ ) # Plenty of balance
915
913
 
916
914
  # Mock allowance to pass check (return an int)
917
915
  mock_wallet.transfer_service.get_erc20_allowance.return_value = 10**20 # Plenty of allowance
@@ -26,10 +26,11 @@ def mock_wallet():
26
26
  @pytest.fixture
27
27
  def mock_manager(mock_wallet):
28
28
  """Setup a ServiceManager with mocked dependencies."""
29
- with patch("iwa.plugins.olas.service_manager.base.Config") as mock_cfg_cls, \
30
- patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache, \
31
- patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache):
32
-
29
+ with (
30
+ patch("iwa.plugins.olas.service_manager.base.Config") as mock_cfg_cls,
31
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
32
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
33
+ ):
33
34
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
34
35
 
35
36
  mock_cfg = mock_cfg_cls.return_value
@@ -237,7 +237,9 @@ def test_activate_registration(
237
237
  @patch("iwa.plugins.olas.service_manager.base.ServiceRegistryContract")
238
238
  @patch("iwa.plugins.olas.service_manager.base.ServiceManagerContract")
239
239
  @patch("iwa.plugins.olas.service_manager.base.ContractCache")
240
- def test_register_agent(mock_cache, mock_sm_contract, mock_registry_contract, mock_config_cls, mock_wallet):
240
+ def test_register_agent(
241
+ mock_cache, mock_sm_contract, mock_registry_contract, mock_config_cls, mock_wallet
242
+ ):
241
243
  """Test agent registration flow."""
242
244
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
243
245
  mock_config_inst = mock_config_cls.return_value
@@ -354,7 +356,9 @@ def test_deploy(mock_cache, mock_sm_contract, mock_registry_contract, mock_confi
354
356
  @patch("iwa.plugins.olas.service_manager.base.ServiceRegistryContract")
355
357
  @patch("iwa.plugins.olas.service_manager.base.ServiceManagerContract")
356
358
  @patch("iwa.plugins.olas.service_manager.base.ContractCache")
357
- def test_terminate(mock_cache, mock_sm_contract, mock_registry_contract, mock_config_cls, mock_wallet):
359
+ def test_terminate(
360
+ mock_cache, mock_sm_contract, mock_registry_contract, mock_config_cls, mock_wallet
361
+ ):
358
362
  """Test service termination."""
359
363
  mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
360
364
  # Setup mock service
@@ -457,7 +461,6 @@ def test_stake(mock_cache, mock_sm_contract, mock_registry_contract, mock_config
457
461
  "required_agent_bond": 50000000000000000000,
458
462
  }
459
463
 
460
-
461
464
  success = manager.stake(mock_staking)
462
465
  assert success is True
463
466
  assert manager.service.staking_contract_address == "0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd"
@@ -480,8 +483,6 @@ def test_stake(mock_cache, mock_sm_contract, mock_registry_contract, mock_config
480
483
  assert manager.stake(mock_staking) is False
481
484
  mock_staking.get_service_ids.return_value = []
482
485
 
483
-
484
-
485
486
  # 4. Approve fail
486
487
  mock_wallet.sign_and_send_transaction.return_value = (False, {})
487
488
  assert manager.stake(mock_staking) is False