iwa 0.0.18__py3-none-any.whl → 0.0.20__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 (32) hide show
  1. iwa/core/chainlist.py +116 -0
  2. iwa/core/constants.py +1 -0
  3. iwa/core/contracts/cache.py +131 -0
  4. iwa/core/contracts/contract.py +7 -0
  5. iwa/core/rpc_monitor.py +60 -0
  6. iwa/plugins/olas/constants.py +41 -39
  7. iwa/plugins/olas/contracts/abis/mech_marketplace_v1.json +828 -0
  8. iwa/plugins/olas/contracts/activity_checker.py +63 -25
  9. iwa/plugins/olas/contracts/mech_marketplace_v1.py +68 -0
  10. iwa/plugins/olas/contracts/staking.py +115 -19
  11. iwa/plugins/olas/events.py +141 -0
  12. iwa/plugins/olas/scripts/test_full_mech_flow.py +1 -1
  13. iwa/plugins/olas/service_manager/base.py +7 -2
  14. iwa/plugins/olas/service_manager/lifecycle.py +30 -5
  15. iwa/plugins/olas/service_manager/mech.py +251 -42
  16. iwa/plugins/olas/service_manager/staking.py +6 -2
  17. iwa/plugins/olas/tests/test_olas_integration.py +38 -10
  18. iwa/plugins/olas/tests/test_service_manager.py +7 -1
  19. iwa/plugins/olas/tests/test_service_manager_errors.py +22 -11
  20. iwa/plugins/olas/tests/test_service_manager_flows.py +24 -8
  21. iwa/plugins/olas/tests/test_service_staking.py +59 -15
  22. iwa/plugins/olas/tests/test_staking_validation.py +8 -14
  23. iwa/tools/reset_tenderly.py +2 -2
  24. iwa/tools/test_chainlist.py +38 -0
  25. iwa/web/routers/olas/staking.py +9 -4
  26. {iwa-0.0.18.dist-info → iwa-0.0.20.dist-info}/METADATA +1 -1
  27. {iwa-0.0.18.dist-info → iwa-0.0.20.dist-info}/RECORD +32 -24
  28. tests/test_rpc_efficiency.py +103 -0
  29. {iwa-0.0.18.dist-info → iwa-0.0.20.dist-info}/WHEEL +0 -0
  30. {iwa-0.0.18.dist-info → iwa-0.0.20.dist-info}/entry_points.txt +0 -0
  31. {iwa-0.0.18.dist-info → iwa-0.0.20.dist-info}/licenses/LICENSE +0 -0
  32. {iwa-0.0.18.dist-info → iwa-0.0.20.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,103 @@
1
+ from unittest.mock import MagicMock, patch
2
+
3
+ import pytest
4
+
5
+ from iwa.core.contracts.cache import ContractCache
6
+ from iwa.plugins.olas.contracts.staking import StakingContract
7
+
8
+
9
+ @pytest.fixture
10
+ def mock_chain_interface():
11
+ with patch("iwa.core.contracts.contract.ChainInterfaces") as mock_chains:
12
+ mock_interface = MagicMock()
13
+ mock_chains.return_value.get.return_value = mock_interface
14
+
15
+ # Mock web3 and contract
16
+ mock_web3_backend = MagicMock()
17
+ mock_interface.web3._web3 = mock_web3_backend
18
+
19
+ mock_contract = MagicMock()
20
+ mock_web3_backend.eth.contract.return_value = mock_contract
21
+
22
+ # Mock with_retry to execute the function
23
+ mock_interface.with_retry.side_effect = lambda func, **kwargs: func()
24
+
25
+ # Yield both interface and contract mock
26
+ yield mock_interface, mock_contract
27
+
28
+ def test_staking_contract_lazy_loading(mock_chain_interface):
29
+ """Verify StakingContract init does NOT make RPC calls."""
30
+ mock_interface, mock_contract = mock_chain_interface
31
+
32
+ # Reset ContractCache
33
+ ContractCache().clear()
34
+
35
+ # Instantiate logic
36
+ contract = StakingContract(address="0x123", chain_name="gnosis")
37
+
38
+ # Assert NO calls to call yet (since we mock with_retry to execute immediately, 0 calls means 0 executions)
39
+ assert mock_interface.with_retry.call_count == 0
40
+
41
+ # Setup return value
42
+ # livenessPeriod is a property that calls "livenessPeriod" on contract
43
+ mock_contract.functions.livenessPeriod.return_value.call.return_value = 3600
44
+
45
+ # Access property
46
+ val = contract.liveness_period
47
+ assert val == 3600
48
+
49
+ # Assert 1 call
50
+ assert mock_contract.functions.livenessPeriod.return_value.call.call_count == 1
51
+
52
+ # Access again
53
+ val = contract.liveness_period
54
+
55
+ # Assert still 1 call (cached)
56
+ assert mock_contract.functions.livenessPeriod.return_value.call.call_count == 1
57
+
58
+ def test_contract_cache_singleton(mock_chain_interface):
59
+ """Verify ContractCache returns same instance and reuses property cache."""
60
+ mock_interface, mock_contract = mock_chain_interface
61
+ ContractCache().clear()
62
+
63
+ c1 = ContractCache().get_contract(StakingContract, "0xABC", "gnosis")
64
+ c2 = ContractCache().get_contract(StakingContract, "0xabc", "gnosis") # Check ignore case
65
+
66
+ assert c1 is c2
67
+
68
+ # populate cache on c1
69
+ mock_contract.functions.maxNumServices.return_value.call.return_value = 500
70
+
71
+ val1 = c1.max_num_services
72
+ assert val1 == 500
73
+ assert mock_contract.functions.maxNumServices.return_value.call.call_count == 1
74
+
75
+ # access on c2
76
+ val2 = c2.max_num_services
77
+ assert val2 == 500
78
+ # Call count should NOT increase
79
+ assert mock_contract.functions.maxNumServices.return_value.call.call_count == 1
80
+
81
+ def test_epoch_aware_caching(mock_chain_interface):
82
+ """Verify ts_checkpoint caching logic."""
83
+ mock_interface, mock_contract = mock_chain_interface
84
+ ContractCache().clear()
85
+ contract = StakingContract(address="0xEpoch", chain_name="gnosis")
86
+
87
+ # Mock return values for tsCheckpoint
88
+ # We use side_effect on the call() method to simulate changing return values if needed
89
+ # But here we just want it to return 1000 once
90
+ mock_contract.functions.tsCheckpoint.return_value.call.return_value = 1000
91
+
92
+ # Set liveness period in cache to avoid RPC call for it
93
+ contract._contract_params_cache["livenessPeriod"] = 600
94
+
95
+ # 1. Fetch ts_checkpoint
96
+ ts = contract.ts_checkpoint()
97
+ assert ts == 1000
98
+ assert mock_contract.functions.tsCheckpoint.return_value.call.call_count == 1
99
+
100
+ # 2. Call again - should be cached
101
+ ts2 = contract.ts_checkpoint()
102
+ assert ts2 == 1000
103
+ assert mock_contract.functions.tsCheckpoint.return_value.call.call_count == 1
File without changes