iwa 0.0.1a6__py3-none-any.whl → 0.0.10__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.
- iwa/core/chain/interface.py +30 -23
- iwa/core/chain/models.py +21 -0
- iwa/core/contracts/contract.py +8 -2
- iwa/core/pricing.py +30 -21
- iwa/core/services/safe.py +13 -8
- iwa/core/services/transaction.py +15 -4
- iwa/core/utils.py +22 -0
- iwa/plugins/gnosis/safe.py +4 -3
- iwa/plugins/gnosis/tests/test_safe.py +9 -7
- iwa/plugins/olas/contracts/service.py +4 -4
- iwa/plugins/olas/contracts/staking.py +2 -3
- iwa/plugins/olas/plugin.py +14 -7
- iwa/plugins/olas/service_manager/lifecycle.py +109 -48
- iwa/plugins/olas/service_manager/mech.py +1 -1
- iwa/plugins/olas/service_manager/staking.py +92 -34
- iwa/plugins/olas/tests/test_plugin.py +6 -1
- iwa/plugins/olas/tests/test_plugin_full.py +12 -7
- iwa/plugins/olas/tests/test_service_manager_validation.py +16 -15
- iwa/tools/list_contracts.py +2 -2
- iwa/web/dependencies.py +1 -3
- iwa/web/routers/accounts.py +1 -2
- iwa/web/routers/olas/admin.py +1 -3
- iwa/web/routers/olas/funding.py +1 -3
- iwa/web/routers/olas/general.py +1 -3
- iwa/web/routers/olas/services.py +1 -2
- iwa/web/routers/olas/staking.py +19 -22
- iwa/web/routers/swap.py +1 -2
- iwa/web/routers/transactions.py +0 -2
- iwa/web/server.py +8 -6
- iwa/web/static/app.js +22 -0
- iwa/web/tests/test_web_endpoints.py +1 -1
- iwa/web/tests/test_web_olas.py +1 -1
- {iwa-0.0.1a6.dist-info → iwa-0.0.10.dist-info}/METADATA +1 -1
- {iwa-0.0.1a6.dist-info → iwa-0.0.10.dist-info}/RECORD +50 -49
- tests/test_chain_interface_coverage.py +3 -2
- tests/test_contract.py +165 -0
- tests/test_keys.py +2 -1
- tests/test_legacy_wallet.py +11 -0
- tests/test_pricing.py +32 -15
- tests/test_safe_coverage.py +3 -3
- tests/test_safe_service.py +3 -6
- tests/test_service_transaction.py +8 -3
- tests/test_staking_router.py +6 -3
- tests/test_transaction_service.py +4 -0
- tools/create_and_stake_service.py +103 -0
- tools/verify_drain.py +1 -4
- {iwa-0.0.1a6.dist-info → iwa-0.0.10.dist-info}/WHEEL +0 -0
- {iwa-0.0.1a6.dist-info → iwa-0.0.10.dist-info}/entry_points.txt +0 -0
- {iwa-0.0.1a6.dist-info → iwa-0.0.10.dist-info}/licenses/LICENSE +0 -0
- {iwa-0.0.1a6.dist-info → iwa-0.0.10.dist-info}/top_level.txt +0 -0
|
@@ -10,22 +10,22 @@ iwa/core/mnemonic.py,sha256=LiG1VmpydQoHQ0pHUJ1OIlrWJry47VSMnOqPM_Yk-O8,12930
|
|
|
10
10
|
iwa/core/models.py,sha256=kBQ0cBe6uFmL2QfW7mjKiMFeZxhT-FRN-RyK3Ko0vE8,12849
|
|
11
11
|
iwa/core/monitor.py,sha256=OmhKVMkfhvtxig3wDUL6iGwBIClTx0YUqMncCao4SqI,7953
|
|
12
12
|
iwa/core/plugins.py,sha256=FLvOG4S397fKi0aTH1fWBEtexn4yvGv_QzGWqFrhSKE,1102
|
|
13
|
-
iwa/core/pricing.py,sha256=
|
|
13
|
+
iwa/core/pricing.py,sha256=B8dAUszv9t5EqvXQ7gh9CNBqlMLR598-c6Pv3NqtpO4,3561
|
|
14
14
|
iwa/core/secrets.py,sha256=U7DZKrwKuSOFV00Ij3ISrrO1cWn_t1GBW_0PyAqjcD4,2588
|
|
15
15
|
iwa/core/tables.py,sha256=y7Cg67PAGHYVMVyAjbo_CQ9t2iz7UXE-OTuUHRyFRTo,2021
|
|
16
16
|
iwa/core/test.py,sha256=gey0dql5eajo1itOhgkSrgfyGWue2eSfpr0xzX3vc38,643
|
|
17
17
|
iwa/core/types.py,sha256=EfDfIwLajTNK-BP9K17QLOIsGCs8legplKI_bUD_NjM,1992
|
|
18
18
|
iwa/core/ui.py,sha256=DglmrI7XhUmOpLn9Nog9Cej4r-VT0JGFkuSNBx-XorQ,3131
|
|
19
|
-
iwa/core/utils.py,sha256=
|
|
19
|
+
iwa/core/utils.py,sha256=5fc8Ffd3F47iFb8QbnZmKh_d2Fsh1ZytVG2HFnhySEQ,2950
|
|
20
20
|
iwa/core/wallet.py,sha256=ZxztWOK8MzIih7nGT52BUvE6Z5a9AA056IV_gqMPQpA,12972
|
|
21
21
|
iwa/core/chain/__init__.py,sha256=XJMmn0ed-_aVkY2iEMKpuTxPgIKBd41dexSVmEZTa-o,1604
|
|
22
22
|
iwa/core/chain/errors.py,sha256=9SEbhxZ-qASPkzt-DoI51qq0GRJVqRgqgL720gO7a64,1275
|
|
23
|
-
iwa/core/chain/interface.py,sha256=
|
|
23
|
+
iwa/core/chain/interface.py,sha256=b6Nac4Wg4zygu9B81MTMswt96Ls_--GbPJuEOKu5loY,20497
|
|
24
24
|
iwa/core/chain/manager.py,sha256=cFEzh6pK5OyVhjhpeMAqhc9RnRDQR1DjIGiGKp-FXBI,1159
|
|
25
|
-
iwa/core/chain/models.py,sha256=
|
|
25
|
+
iwa/core/chain/models.py,sha256=0OgBo08FZEQisOdd00YUMXSAV7BC0CcWpqJ2y-gs0cI,4863
|
|
26
26
|
iwa/core/chain/rate_limiter.py,sha256=gU7TmWdH9D_wbXKT1X7mIgoIUCWVuebgvRhxiyLGAmI,6613
|
|
27
27
|
iwa/core/contracts/__init__.py,sha256=P5GFY_pnuI02teqVY2U0t98bn1_SSPAbcAzRMpCdTi4,34
|
|
28
|
-
iwa/core/contracts/contract.py,sha256=
|
|
28
|
+
iwa/core/contracts/contract.py,sha256=qHU1bvo1uhL2smY-dOrLSwVo6fb0ouWcpMsralGwJio,11440
|
|
29
29
|
iwa/core/contracts/erc20.py,sha256=VqriOdUXej0ilTgpukpm1FUF_9sSrVMAPuEpIvyZ2SQ,2646
|
|
30
30
|
iwa/core/contracts/multisend.py,sha256=tSdBCWe7LSdBoKZ7z2QebmRFK4M2ln7H3kmJBeEb4Ho,2431
|
|
31
31
|
iwa/core/contracts/abis/erc20.json,sha256=vrdExMWcIogg_nO59j1Pmipmpa2Ulj3oCCdcdrrFVCE,16995
|
|
@@ -35,8 +35,8 @@ iwa/core/services/__init__.py,sha256=ab5pYzmu3LrZLTO5N-plx6Rp4R0hBEnbbzsgz84zWGM
|
|
|
35
35
|
iwa/core/services/account.py,sha256=01MoEvl6FJlMnMB4fGwsPtnGa4kgA-d5hJeKu_ACg7Y,1982
|
|
36
36
|
iwa/core/services/balance.py,sha256=mPE12CuOFfCaJXaQXWOcQM1O03ZF3ghpy_-oOjNk_GE,4104
|
|
37
37
|
iwa/core/services/plugin.py,sha256=GNNlbtELyHl7MNVChrypF76GYphxXduxDog4kx1MLi8,3277
|
|
38
|
-
iwa/core/services/safe.py,sha256=
|
|
39
|
-
iwa/core/services/transaction.py,sha256=
|
|
38
|
+
iwa/core/services/safe.py,sha256=ytNJMndXrzTMHwhDZKYLIh4Q0UTWDBgQgTpof-UqIkA,14827
|
|
39
|
+
iwa/core/services/transaction.py,sha256=81jiGF2cxhyy0Oh1jw6uyl0q10oZxOCLnsE3dtOgHJ0,7375
|
|
40
40
|
iwa/core/services/transfer/__init__.py,sha256=ZJfshFxJRsp8rkOqfVvd1cqEzIJ9tqBJh8pc0l90GLk,5576
|
|
41
41
|
iwa/core/services/transfer/base.py,sha256=sohz-Ss2i-pGYGl4x9bD93cnYKcSvsXaXyvyRawvgQs,9043
|
|
42
42
|
iwa/core/services/transfer/erc20.py,sha256=0GvlfETgdJg15azs1apH1gI6Vm3gAv22o1saNbIfiAY,8685
|
|
@@ -48,25 +48,25 @@ iwa/plugins/__init__.py,sha256=zy-DjOZn8GSgIETN2X_GAb9O6yk71t6ZRzeUgoZ52KA,23
|
|
|
48
48
|
iwa/plugins/gnosis/__init__.py,sha256=dpx0mE84eV-g5iZaH5nKivZJnoKWyRFX5rhdjowBwuU,114
|
|
49
49
|
iwa/plugins/gnosis/cow_utils.py,sha256=iSvbfgTr2bCqRsUznKCWqmoTnyuX-WZX4oh0E-l3XBU,2263
|
|
50
50
|
iwa/plugins/gnosis/plugin.py,sha256=AgkgOGYfnrcjWrPUiAvySMj6ITnss0SFXiEi6Z6fnMs,1885
|
|
51
|
-
iwa/plugins/gnosis/safe.py,sha256=
|
|
51
|
+
iwa/plugins/gnosis/safe.py,sha256=aDcBz50Bliur90sVqxgjGoQJyCqsfZlm0PDsmWIDBak,5517
|
|
52
52
|
iwa/plugins/gnosis/cow/__init__.py,sha256=lZN5QpIYWL67rE8r7z7zS9dlr8OqFrYeD9T4-RwUghU,224
|
|
53
53
|
iwa/plugins/gnosis/cow/quotes.py,sha256=u2xFKgL7QTKqCkSPMv1RHaXvZ6WzID4haaZDMVS42Bs,5177
|
|
54
54
|
iwa/plugins/gnosis/cow/swap.py,sha256=XZdvJbTbh54hxer7cKkum7lNQ-03gddMK95K3MenaFE,15209
|
|
55
55
|
iwa/plugins/gnosis/cow/types.py,sha256=-9VRiFhAkmN1iIJ95Pg7zLFSeXtkkW00sl13usxi3o8,470
|
|
56
56
|
iwa/plugins/gnosis/tests/test_cow.py,sha256=iVy5ockMIcPZWsX4WGXU91DhBsYEZ5NOxtFzAQ2sK3o,8440
|
|
57
|
-
iwa/plugins/gnosis/tests/test_safe.py,sha256=
|
|
57
|
+
iwa/plugins/gnosis/tests/test_safe.py,sha256=pw1zrYvAiVtmPIU5k7BtOQpDNAQTSTrLIaeljCjSahc,3216
|
|
58
58
|
iwa/plugins/olas/__init__.py,sha256=_NhBczzM61fhGYwGhnWfEeL8Jywyy_730GASe2BxzeQ,106
|
|
59
59
|
iwa/plugins/olas/constants.py,sha256=mFUMDB46WbM9Y8sw7c-i9TRyJVrDNZXdCXa64M5tI6A,6608
|
|
60
60
|
iwa/plugins/olas/importer.py,sha256=f8KlZ9dGcNbpg8uoTYbO9sDvbluZoslhpWFLqPjPnLA,26717
|
|
61
61
|
iwa/plugins/olas/mech_reference.py,sha256=CaSCpQnQL4F7wOG6Ox6Zdoy-uNEQ78YBwVLILQZKL8Q,5782
|
|
62
62
|
iwa/plugins/olas/models.py,sha256=xC5hYakX53pBT6zZteM9cyiC7t6XRLLpobjQmDYueOo,3520
|
|
63
|
-
iwa/plugins/olas/plugin.py,sha256=
|
|
63
|
+
iwa/plugins/olas/plugin.py,sha256=S_vnvZ02VdVWD7N5kp7u5JIRQ2JLtfwGDZ7OHkAN0M8,9390
|
|
64
64
|
iwa/plugins/olas/contracts/activity_checker.py,sha256=UDbCgFOyuEhHf1qoUwCUdLc8oK8ksuAYE-ZgKrMBnwg,3415
|
|
65
65
|
iwa/plugins/olas/contracts/base.py,sha256=y73aQbDq6l4zUpz_eQAg4MsLkTAEqjjupXlcvxjfgCI,240
|
|
66
66
|
iwa/plugins/olas/contracts/mech.py,sha256=dXYtyORc-oiu9ga5PtTquOFkoakb6BLGKvlUsteygIg,2767
|
|
67
67
|
iwa/plugins/olas/contracts/mech_marketplace.py,sha256=hMADl5MQGvT2wLRKu4vHGe4RrAZVq8Y2M_EvXWWz528,1554
|
|
68
|
-
iwa/plugins/olas/contracts/service.py,sha256=
|
|
69
|
-
iwa/plugins/olas/contracts/staking.py,sha256=
|
|
68
|
+
iwa/plugins/olas/contracts/service.py,sha256=jGCA51qhpVhguRfVPRnfL08WNQBbruvxiCaiispRm3U,6739
|
|
69
|
+
iwa/plugins/olas/contracts/staking.py,sha256=vt2UZ6G0edg1EbPrzFK5AIse531bQTu4PLydQ6rgrAA,14494
|
|
70
70
|
iwa/plugins/olas/contracts/abis/activity_checker.json,sha256=HT0IMbyTLMO71ITBKwoS950rHe772suPP4b8eDAodJ0,2230
|
|
71
71
|
iwa/plugins/olas/contracts/abis/mech.json,sha256=bMMCXInjE_2PTPnc_sIyS_H8pod5Sm_e-xTbKgZppKc,16369
|
|
72
72
|
iwa/plugins/olas/contracts/abis/mech_marketplace.json,sha256=KPnF-H_UATb3wdb_7o6ky_hSp5xwgvckD-QqylsWJLg,32468
|
|
@@ -80,9 +80,9 @@ iwa/plugins/olas/scripts/test_simple_lifecycle.py,sha256=8T50tOZx3afeECSfCNAb0rA
|
|
|
80
80
|
iwa/plugins/olas/service_manager/__init__.py,sha256=GXiThMEY3nPgHUl1i-DLrF4h96z9jPxxI8Jepo2E1PM,1926
|
|
81
81
|
iwa/plugins/olas/service_manager/base.py,sha256=V4o71ZYUSc_GzM-RWocaAGsrqI-fa5V7CcyloVpcjwE,4666
|
|
82
82
|
iwa/plugins/olas/service_manager/drain.py,sha256=IS7YYKuQdkULcNdxfHVzjcq95pXKdpajolzLL78u4jc,12430
|
|
83
|
-
iwa/plugins/olas/service_manager/lifecycle.py,sha256
|
|
84
|
-
iwa/plugins/olas/service_manager/mech.py,sha256=
|
|
85
|
-
iwa/plugins/olas/service_manager/staking.py,sha256=
|
|
83
|
+
iwa/plugins/olas/service_manager/lifecycle.py,sha256=0jGLoqNLRuIqzpv2VVtAqVtnuKSwkzhWw8-m8qknjQU,36834
|
|
84
|
+
iwa/plugins/olas/service_manager/mech.py,sha256=72-tEap-aYd0gebcH6y_De1SNeL6OrXn7sWuv_Ok_-Y,12224
|
|
85
|
+
iwa/plugins/olas/service_manager/staking.py,sha256=zwI2IREh502DDjhgKDyHr7puXRi46sQ7xp7KgbcZf3w,23931
|
|
86
86
|
iwa/plugins/olas/tests/conftest.py,sha256=4vM7EI00SrTGyeP0hNzsGSQHEj2-iznVgzlNh2_OGfo,739
|
|
87
87
|
iwa/plugins/olas/tests/test_importer.py,sha256=i9LKov7kNRECB3hmRnhKBwcfx3uxtjWe4BB77bOOpeo,4282
|
|
88
88
|
iwa/plugins/olas/tests/test_importer_error_handling.py,sha256=X37TrvJ6-3-NuJ2megm0Cnx3KJdA1wke563pRf_EPJk,12084
|
|
@@ -93,15 +93,15 @@ iwa/plugins/olas/tests/test_olas_models.py,sha256=5scX-wvRLGH3G44S2okq_tyQ9Rk7Pd
|
|
|
93
93
|
iwa/plugins/olas/tests/test_olas_view.py,sha256=kh3crsriyoRiZC6l8vzGllocvQnYmqziv-csbmXih0E,10489
|
|
94
94
|
iwa/plugins/olas/tests/test_olas_view_actions.py,sha256=jAxr9bjFNAaxGf1btIrxdMaHgJ0PWX9aDwVU-oPGMpk,5109
|
|
95
95
|
iwa/plugins/olas/tests/test_olas_view_modals.py,sha256=8j0PNFjKqFC5V1kBdVFWNLMvqGt49H6fLSYGxn02c8o,5562
|
|
96
|
-
iwa/plugins/olas/tests/test_plugin.py,sha256=
|
|
97
|
-
iwa/plugins/olas/tests/test_plugin_full.py,sha256=
|
|
96
|
+
iwa/plugins/olas/tests/test_plugin.py,sha256=RVgU-Cq6t_3mOh90xFAGwlJOV7ZIgp0VNaK5ZAxisAQ,2565
|
|
97
|
+
iwa/plugins/olas/tests/test_plugin_full.py,sha256=xevLYmdU4zd5FyEEEn9gvzGimPztmt6vymybeZHXnq8,8507
|
|
98
98
|
iwa/plugins/olas/tests/test_service_lifecycle.py,sha256=dhIDzgcXh1VV-S6VGXtNqtdxxIdXY1F1hRcPL6RBxno,5649
|
|
99
99
|
iwa/plugins/olas/tests/test_service_manager.py,sha256=dsnj1DfSYptIZkiPaLHGM9utbX9XOAy0axuzu-3kofg,41818
|
|
100
100
|
iwa/plugins/olas/tests/test_service_manager_errors.py,sha256=BtN-89gwaFpy3RJIDt98U0TT0lksg9NHvoSpSBHSXag,8229
|
|
101
101
|
iwa/plugins/olas/tests/test_service_manager_flows.py,sha256=HsAD1axnfow1iTiRBqR7DT6sa3U-q8ApOlYN4Cd87Ao,20142
|
|
102
102
|
iwa/plugins/olas/tests/test_service_manager_mech.py,sha256=qG6qu5IPRNypXUsblU2OEkuiuwDJ0TH8RXZbibmTFcQ,4937
|
|
103
103
|
iwa/plugins/olas/tests/test_service_manager_rewards.py,sha256=hIVckGl5rEr-KHGNUxxVopal0Tpw4EZharBt0MbaCVA,11798
|
|
104
|
-
iwa/plugins/olas/tests/test_service_manager_validation.py,sha256=
|
|
104
|
+
iwa/plugins/olas/tests/test_service_manager_validation.py,sha256=ajlfH5uc4mAHf8A7GLE5cW7X8utM2vUilM0JdGDdlVg,5382
|
|
105
105
|
iwa/plugins/olas/tests/test_service_staking.py,sha256=XkC8H_qVumOQ1i4_vidEdXqkK5stInd2tgT50l0SRMk,12201
|
|
106
106
|
iwa/plugins/olas/tests/test_staking_integration.py,sha256=zQdhhSLz5h2OvQ6GOJ-Yl_Y17itPML4Pr9VPkjWoz9c,9420
|
|
107
107
|
iwa/plugins/olas/tests/test_staking_validation.py,sha256=J7DgDdIiVTkKv_7obtSHQ2lgfUGPmUwuPjTUkiT4cbs,4198
|
|
@@ -109,7 +109,7 @@ iwa/plugins/olas/tui/__init__.py,sha256=5ZRsbC7J3z1xfkZRiwr4bLEklf78rNVjdswe2p7S
|
|
|
109
109
|
iwa/plugins/olas/tui/olas_view.py,sha256=qcPxhurDPJjHWln6R64ZVAJ2h82IXzw48unhRvQVZqQ,36448
|
|
110
110
|
iwa/tools/__init__.py,sha256=jQyuwDQGRigSe7S9JMb4yK3CXPgZFJNffzt6N2v9PU0,21
|
|
111
111
|
iwa/tools/check_profile.py,sha256=0LAv9wx4wMM610mX88-6tIoDi2I5LDzh0W9nkprt42s,2177
|
|
112
|
-
iwa/tools/list_contracts.py,sha256=
|
|
112
|
+
iwa/tools/list_contracts.py,sha256=2w-LYB2RVS-eGil2kLiBIEm3tYYhYzT4lAuGO6VtLJU,4861
|
|
113
113
|
iwa/tools/release.py,sha256=-Z9GG6Y-K6KG32K0VUf_MruiUdJxG6W7ToOMzhyCH7Y,3963
|
|
114
114
|
iwa/tools/reset_env.py,sha256=FKN0wuh9Xq00c94B2kEFehHPKcWldxYqgU45yJwg5Cg,3140
|
|
115
115
|
iwa/tools/reset_tenderly.py,sha256=w1-KujdqRpimrgiREvoRwVXsGsGoYisCLmYTcsJ6Np8,11295
|
|
@@ -129,27 +129,27 @@ iwa/tui/tests/test_wallets_refactor.py,sha256=71G3HLbhTtgDy3ffVbYv0MFYRgdYd-NWGB
|
|
|
129
129
|
iwa/tui/tests/test_widgets.py,sha256=C9UgIGeWRaQ459JygFEQx-7hOi9mWrSUDDIMZH1ge50,3994
|
|
130
130
|
iwa/tui/widgets/__init__.py,sha256=UzD6nJbwv9hOtkWl9I7faXm1a-rcu4xFRxrf4KBwwY4,161
|
|
131
131
|
iwa/tui/widgets/base.py,sha256=G844GU61qSS6AgY5NxmOVHTghbgHlyTfo8hhE2VUrqQ,3379
|
|
132
|
-
iwa/web/dependencies.py,sha256=
|
|
132
|
+
iwa/web/dependencies.py,sha256=0_dAJlRh6gKrUDRPKUe92eshFsg572yx_H0lQgSqGDA,2103
|
|
133
133
|
iwa/web/models.py,sha256=MSD9WPy_Nz_amWgoo2KSDTn4ZLv_AV0o0amuNtSf-68,3035
|
|
134
|
-
iwa/web/server.py,sha256=
|
|
135
|
-
iwa/web/routers/accounts.py,sha256=
|
|
134
|
+
iwa/web/server.py,sha256=4ZLVFEKoGs_NoCcXMeyYzDNdxUXazjwHQaX7CR1pwHE,5239
|
|
135
|
+
iwa/web/routers/accounts.py,sha256=HP7DdUbbnRb1dFCs7zqyhx61_IBJwDMKtmXpvAPZodg,3916
|
|
136
136
|
iwa/web/routers/state.py,sha256=aEfeGFAOyaS6kWhXuWLrdvEbnQAeonKIWxJrQkqJ1GY,2198
|
|
137
|
-
iwa/web/routers/swap.py,sha256=
|
|
138
|
-
iwa/web/routers/transactions.py,sha256=
|
|
137
|
+
iwa/web/routers/swap.py,sha256=8xycAytquR29ELxW3vx428W8s9bI_w_x2kpRhhJ0KXY,22630
|
|
138
|
+
iwa/web/routers/transactions.py,sha256=bRjfD7zcuX3orVIHzOMVsIkEtcE_pM_KCom4cdAKV6Q,5602
|
|
139
139
|
iwa/web/routers/olas/__init__.py,sha256=Jo6Dm1e8fHafCD800fbxsxeFz3tvuEEKXEf5-9tj5Qg,947
|
|
140
|
-
iwa/web/routers/olas/admin.py,sha256=
|
|
141
|
-
iwa/web/routers/olas/funding.py,sha256=
|
|
142
|
-
iwa/web/routers/olas/general.py,sha256=
|
|
143
|
-
iwa/web/routers/olas/services.py,sha256=
|
|
144
|
-
iwa/web/routers/olas/staking.py,sha256=
|
|
145
|
-
iwa/web/static/app.js,sha256=
|
|
140
|
+
iwa/web/routers/olas/admin.py,sha256=PMRdNelqYgQ1xbqh3floFV5xrVtBRQiwZPd8J9_ffxg,5785
|
|
141
|
+
iwa/web/routers/olas/funding.py,sha256=f8fADNtbZEBFl-vuVKfas6os38Vot6K5tJBTenZmCD0,4832
|
|
142
|
+
iwa/web/routers/olas/general.py,sha256=dPsBQppTGoQY1RztliUhseOHOZGeeCR10lhThD9kyXo,803
|
|
143
|
+
iwa/web/routers/olas/services.py,sha256=fq_NBRlXlc-cHpAJYwZ7KsDqgcnB7vSCFqrHZnsLiYQ,14740
|
|
144
|
+
iwa/web/routers/olas/staking.py,sha256=3f5flyAb9uDdw6WKH3f68ERA6x_6geJGqOGeTMz-Rbs,12128
|
|
145
|
+
iwa/web/static/app.js,sha256=BCpAp1BcFVcfEfqXa-9gOtjb-8YlV5NIPWgSeCchD1E,113472
|
|
146
146
|
iwa/web/static/index.html,sha256=q7s7plnMbN1Nkzr5bRxZgvgOFerUChEGIZW7SpAVtPc,28514
|
|
147
147
|
iwa/web/static/style.css,sha256=aTtE42mmfYV6y7xfo9cUgUhT8x-KyNC1zmPjSdskxIk,24315
|
|
148
|
-
iwa/web/tests/test_web_endpoints.py,sha256=
|
|
149
|
-
iwa/web/tests/test_web_olas.py,sha256=
|
|
148
|
+
iwa/web/tests/test_web_endpoints.py,sha256=C264MH-CTyDW4GLUrTXBgLJKUk4-89pFAScBddd4Fvk,23995
|
|
149
|
+
iwa/web/tests/test_web_olas.py,sha256=0CVSsrncOeJ3x0ECV7mVLQV_CXZRrOqGiVjgLIi6hZ8,16308
|
|
150
150
|
iwa/web/tests/test_web_swap.py,sha256=7A4gBJFL01kIXPtW1E1J17SCsVc_0DmUn-R8kKrnnVA,2974
|
|
151
151
|
iwa/web/tests/test_web_swap_coverage.py,sha256=zGNrzlhZ_vWDCvWmLcoUwFgqxnrp_ACbo49AtWBS_Kw,5584
|
|
152
|
-
iwa-0.0.
|
|
152
|
+
iwa-0.0.10.dist-info/licenses/LICENSE,sha256=eIubm_IlBHPYRQlLNZKbBNKhJUUP3JH0A2miZUhAVfI,1078
|
|
153
153
|
tests/legacy_cow.py,sha256=oOkZvIxL70ReEoD9oHQbOD5GpjIr6AGNHcOCgfPlerU,8389
|
|
154
154
|
tests/legacy_safe.py,sha256=AssM2g13E74dNGODu_H0Q0y412lgqsrYnEzI97nm_Ts,2972
|
|
155
155
|
tests/legacy_transaction_retry_logic.py,sha256=D9RqZ7DBu61Xr2djBAodU2p9UE939LL-DnQXswX5iQk,1497
|
|
@@ -160,15 +160,15 @@ tests/test_account_service.py,sha256=g_AIVT2jhlvUtbFTaCd-d15x4CmXJQaV66tlAgnaXwY
|
|
|
160
160
|
tests/test_balance_service.py,sha256=86iEkPd2M1-UFy3qOxV1EguQOEYbboy2-2mAyS3ctGs,6549
|
|
161
161
|
tests/test_chain.py,sha256=uXbNl9wD4e2qV1xs_MO74Soj3iaH-5xoxge4rD3FypU,16986
|
|
162
162
|
tests/test_chain_interface.py,sha256=Wu0q0sREtmYBp7YvWrBIrrSTtqeQj18oJp2VmMUEMec,8312
|
|
163
|
-
tests/test_chain_interface_coverage.py,sha256=
|
|
163
|
+
tests/test_chain_interface_coverage.py,sha256=fvrVvw8-DMwdsSFKQHUhpbfutrVRxnnTc-tjB7Bb-jo,3327
|
|
164
164
|
tests/test_cli.py,sha256=WW6EDeHLws5-BqFNOy11pH_D5lttuyspD5hrDCFpR0Q,3968
|
|
165
|
-
tests/test_contract.py,sha256=
|
|
165
|
+
tests/test_contract.py,sha256=MuL9dI_EJXo7hNiapUPNqmP87LRh3_HluAtOEGEWyus,14116
|
|
166
166
|
tests/test_db.py,sha256=dmbrupj0qlUeiiycZ2mzMFjf7HrDa6tcqMPY8zpiKIk,5710
|
|
167
167
|
tests/test_drain_coverage.py,sha256=jtN5tIXzSTlS2IjwLS60azyMYsjFDlSTUa98JM1bMic,6786
|
|
168
168
|
tests/test_erc20.py,sha256=kNEw1afpm5EbXRNXkjpkBNZIy7Af1nqGlztKH5IWAwU,3074
|
|
169
169
|
tests/test_gnosis_plugin.py,sha256=XMoHBCTrnVBq9bXYPzMUIrhr95caucMVRxooCjKrzjg,3454
|
|
170
|
-
tests/test_keys.py,sha256=
|
|
171
|
-
tests/test_legacy_wallet.py,sha256=
|
|
170
|
+
tests/test_keys.py,sha256=pxlGlHB-NaTbC8qar093ttdHLLtkM8irVNmCbH2KVf4,17421
|
|
171
|
+
tests/test_legacy_wallet.py,sha256=j7AorUMvuChyxy18y9j8Ux8wpZa2_ZvKutp5obKLLI0,49895
|
|
172
172
|
tests/test_main.py,sha256=y2xr7HjCt4rHsxm8y6n24FKCteSHPyxC3DFuMcUgX1Y,475
|
|
173
173
|
tests/test_migration.py,sha256=fYoxzI3KqGh0cPV0bFcbvGrAnKcNlvnwjggG_uD0QGo,1789
|
|
174
174
|
tests/test_mnemonic.py,sha256=BFtXMMg17uHWh_H-ZwAOn0qzgbUCqL8BRLkgRjzfzxo,7379
|
|
@@ -177,19 +177,19 @@ tests/test_models.py,sha256=1bEfPiDVgEdtwFEzwecSPAHjCF8kjOPSMeQExJ7eCJ4,7107
|
|
|
177
177
|
tests/test_monitor.py,sha256=P_hF61VMlCX2rh9yu_a6aKhlgXOAcCMGOZRntjcqrd0,7255
|
|
178
178
|
tests/test_multisend.py,sha256=IvXpwnC5xSDRCyCDGcMdO3L-eQegvdjAzHZB0FoVFUI,2685
|
|
179
179
|
tests/test_plugin_service.py,sha256=ZEe37kV_sv4Eb04032O1hZIoo9yf5gJo83ks7Grzrng,3767
|
|
180
|
-
tests/test_pricing.py,sha256=
|
|
180
|
+
tests/test_pricing.py,sha256=ptu_2Csc6d64bIzMMw3TheJge2Kfn05Gs-twz_KmBzg,5276
|
|
181
181
|
tests/test_rate_limiter.py,sha256=DOIlrBP2AtVFHCpznIoFn2FjFc33emG7M_FffLh4MGE,7314
|
|
182
182
|
tests/test_reset_tenderly.py,sha256=GVoqbDT3n4_GnlKF5Lx-8ew15jT8I2hIPdTulQDb6dI,7215
|
|
183
183
|
tests/test_rpc_view.py,sha256=sgZ53KEHl8VGb7WKYa0VI7Cdxbf8JH1SdroHYbWHjfQ,2031
|
|
184
|
-
tests/test_safe_coverage.py,sha256=
|
|
185
|
-
tests/test_safe_service.py,sha256=
|
|
184
|
+
tests/test_safe_coverage.py,sha256=g9Bdrpkc-Mc8HBjk07lYNRkzxWZiF922uiVLZqhehBE,5093
|
|
185
|
+
tests/test_safe_service.py,sha256=nxDYmGd6p2gGe7BEeMxsqS8CgeJarPofV38HC6Cop44,5770
|
|
186
186
|
tests/test_service_manager_integration.py,sha256=I_BLUzEKrVTyg_8jqsUK0oFD3aQVPCRJ7z0gY8P-j04,2354
|
|
187
187
|
tests/test_service_manager_structure.py,sha256=zK506ucCXCBHcjPYKrKEuK1bgq0xsbawyL8Y-wahXf8,868
|
|
188
|
-
tests/test_service_transaction.py,sha256=
|
|
189
|
-
tests/test_staking_router.py,sha256=
|
|
188
|
+
tests/test_service_transaction.py,sha256=SecU2Fy32jLH4lg0CZHNrNkqM1pzPnYB0qZtxqaDnFE,6416
|
|
189
|
+
tests/test_staking_router.py,sha256=e8bI_u24SsORn9bkf4QWOtFoGxi86WTyu_Zl_QnUvpA,2769
|
|
190
190
|
tests/test_staking_simple.py,sha256=NHyZ1pcVQEJGFiGseC5m6Y9Y6FJGnRIFJUwhd1hAV9g,1138
|
|
191
191
|
tests/test_tables.py,sha256=1KQHgxuizoOrRxpubDdnzk9iaU5Lwyp3bcWP_hZD5uU,2686
|
|
192
|
-
tests/test_transaction_service.py,sha256=
|
|
192
|
+
tests/test_transaction_service.py,sha256=iuRMyOsMS4fRNIhudbW0ovSfA6vGzZpjIApVryOpO3U,5283
|
|
193
193
|
tests/test_transfer_multisend.py,sha256=PErjNqNwN66TMh4oVa307re64Ucccg1LkXqB0KlkmsI,6677
|
|
194
194
|
tests/test_transfer_native.py,sha256=cDbb4poV_veIw6eHpokrHe9yUndOjA6rQhrHd_IY3HQ,7445
|
|
195
195
|
tests/test_transfer_security.py,sha256=gdpC6ybdXQbQgILbAQ0GqjWdwn9AJRNR3B_7TYg0NxI,3617
|
|
@@ -198,9 +198,10 @@ tests/test_transfer_swap_unit.py,sha256=v_tld2oF2jFf_qdrCxIPQFlAymQqHT2dcw2Z05qW
|
|
|
198
198
|
tests/test_ui_coverage.py,sha256=N-7uhPlKOXCKyS32VjwG3JQRnAS1PoAJxR-InaOZtCQ,2423
|
|
199
199
|
tests/test_utils.py,sha256=vkP49rYNI8BRzLpWR3WnKdDr8upeZjZcs7Rx0pjbQMo,1292
|
|
200
200
|
tests/test_workers.py,sha256=MInwdkFY5LdmFB3o1odIaSD7AQZb3263hNafO1De5PE,2793
|
|
201
|
-
tools/
|
|
202
|
-
|
|
203
|
-
iwa-0.0.
|
|
204
|
-
iwa-0.0.
|
|
205
|
-
iwa-0.0.
|
|
206
|
-
iwa-0.0.
|
|
201
|
+
tools/create_and_stake_service.py,sha256=1xwy_bJQI1j9yIQ968Oc9Db_F6mk1659LuuZntTASDE,3742
|
|
202
|
+
tools/verify_drain.py,sha256=PkMjblyOOAuQge88FwfEzRtCYeEtJxXhPBmtQYCoQ-8,6743
|
|
203
|
+
iwa-0.0.10.dist-info/METADATA,sha256=GeRcXbxPd8-PCggPGpiLqM-ovGziAsHJp1SzHWMxMao,7295
|
|
204
|
+
iwa-0.0.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
205
|
+
iwa-0.0.10.dist-info/entry_points.txt,sha256=nwB6kscrfA7M00pYmL2j-sBH6eF6h2ga9IK1BZxdiyQ,241
|
|
206
|
+
iwa-0.0.10.dist-info/top_level.txt,sha256=kedS9cRUbm4JE2wYeabIXilhHjN8KCw0IGbqqqsw0Bs,16
|
|
207
|
+
iwa-0.0.10.dist-info/RECORD,,
|
|
@@ -31,8 +31,9 @@ def test_rpc_rotation_exhaustion(mock_web3):
|
|
|
31
31
|
# We also need to prevent rotation from working or make rotation also fail
|
|
32
32
|
# Since we only have 2 RPCs, eventually it will give up.
|
|
33
33
|
|
|
34
|
-
with
|
|
35
|
-
|
|
34
|
+
with patch("time.sleep"): # Avoid real exponential backoff delays
|
|
35
|
+
with pytest.raises(Exception, match="Connection Error"):
|
|
36
|
+
chain.with_retry(lambda: chain.web3.eth.get_block("latest"))
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
def test_rate_limiting_backoff(mock_web3):
|
tests/test_contract.py
CHANGED
|
@@ -195,3 +195,168 @@ def test_extract_events_edge_cases(mock_chain_interface):
|
|
|
195
195
|
|
|
196
196
|
events = contract.extract_events(receipt)
|
|
197
197
|
assert len(events) == 0
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# =============================================================================
|
|
201
|
+
# RPC ROTATION TESTS - Verify fix for contract.call() re-evaluating on retry
|
|
202
|
+
# =============================================================================
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_call_reevaluates_contract_on_retry(mock_chain_interface, mock_abi_file):
|
|
206
|
+
"""Verify that self.contract is re-evaluated on each retry attempt.
|
|
207
|
+
|
|
208
|
+
This test verifies the fix for the bug where the contract method was
|
|
209
|
+
captured once outside the retry lambda, causing retries to use the
|
|
210
|
+
stale provider after RPC rotation.
|
|
211
|
+
"""
|
|
212
|
+
contract = MockContract("0xAddress", "gnosis")
|
|
213
|
+
|
|
214
|
+
# Track how many times web3.eth.contract is called (proxy for contract property access)
|
|
215
|
+
contract_creation_count = [0]
|
|
216
|
+
|
|
217
|
+
def counting_contract_factory(address, abi):
|
|
218
|
+
contract_creation_count[0] += 1
|
|
219
|
+
mock = MagicMock()
|
|
220
|
+
# First call fails, second succeeds
|
|
221
|
+
if contract_creation_count[0] == 1:
|
|
222
|
+
mock.functions.testFunc.return_value.call.side_effect = Exception(
|
|
223
|
+
"429 Too Many Requests"
|
|
224
|
+
)
|
|
225
|
+
else:
|
|
226
|
+
mock.functions.testFunc.return_value.call.return_value = "success"
|
|
227
|
+
return mock
|
|
228
|
+
|
|
229
|
+
mock_chain_interface.web3.eth.contract.side_effect = counting_contract_factory
|
|
230
|
+
|
|
231
|
+
# Implement with_retry that actually retries on 429
|
|
232
|
+
def real_with_retry(fn, max_retries=6, operation_name="operation"):
|
|
233
|
+
for attempt in range(max_retries + 1):
|
|
234
|
+
try:
|
|
235
|
+
return fn()
|
|
236
|
+
except Exception as e:
|
|
237
|
+
if "429" in str(e) and attempt < max_retries:
|
|
238
|
+
continue
|
|
239
|
+
raise
|
|
240
|
+
|
|
241
|
+
mock_chain_interface.with_retry.side_effect = real_with_retry
|
|
242
|
+
|
|
243
|
+
# Execute - should fail first, then succeed
|
|
244
|
+
result = contract.call("testFunc")
|
|
245
|
+
|
|
246
|
+
assert result == "success"
|
|
247
|
+
# KEY ASSERTION: contract property (and thus web3.eth.contract) should be called
|
|
248
|
+
# once per attempt. With the fix, this should be 2. Before the fix, it would be 1.
|
|
249
|
+
assert contract_creation_count[0] == 2, (
|
|
250
|
+
f"Expected contract to be created 2 times (once per retry attempt), "
|
|
251
|
+
f"but was created {contract_creation_count[0]} times. "
|
|
252
|
+
"This suggests the fix for re-evaluating self.contract on retry is not working."
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def test_call_uses_fresh_provider_after_rotation(mock_chain_interface, mock_abi_file):
|
|
257
|
+
"""Verify that after RPC rotation, the contract uses the new provider.
|
|
258
|
+
|
|
259
|
+
This simulates the scenario where:
|
|
260
|
+
1. First call fails with 429
|
|
261
|
+
2. RPC rotates to new provider
|
|
262
|
+
3. Retry should use the NEW provider, not the old one
|
|
263
|
+
"""
|
|
264
|
+
contract = MockContract("0xAddress", "gnosis")
|
|
265
|
+
|
|
266
|
+
# Track which provider version is being used
|
|
267
|
+
provider_versions = []
|
|
268
|
+
current_provider_version = [1] # Mutable to track version changes
|
|
269
|
+
|
|
270
|
+
def mock_contract_factory(address, abi):
|
|
271
|
+
mock = MagicMock()
|
|
272
|
+
# Capture which provider version was used when this contract was created
|
|
273
|
+
mock._provider_version = current_provider_version[0]
|
|
274
|
+
provider_versions.append(current_provider_version[0])
|
|
275
|
+
return mock
|
|
276
|
+
|
|
277
|
+
mock_chain_interface.web3.eth.contract.side_effect = mock_contract_factory
|
|
278
|
+
|
|
279
|
+
# Simulate RPC rotation by incrementing provider version
|
|
280
|
+
def simulate_rotation():
|
|
281
|
+
current_provider_version[0] += 1
|
|
282
|
+
return True
|
|
283
|
+
|
|
284
|
+
mock_chain_interface.rotate_rpc = simulate_rotation
|
|
285
|
+
|
|
286
|
+
# Make with_retry call rotation between attempts
|
|
287
|
+
attempt_count = [0]
|
|
288
|
+
|
|
289
|
+
def mock_with_retry(fn, **kwargs):
|
|
290
|
+
attempt_count[0] += 1
|
|
291
|
+
if attempt_count[0] == 1:
|
|
292
|
+
# First attempt: simulate 429 then rotation
|
|
293
|
+
simulate_rotation()
|
|
294
|
+
# Call again after rotation
|
|
295
|
+
return fn()
|
|
296
|
+
return fn()
|
|
297
|
+
|
|
298
|
+
mock_chain_interface.with_retry.side_effect = mock_with_retry
|
|
299
|
+
|
|
300
|
+
# Execute call (this should access contract property, which creates new contract)
|
|
301
|
+
contract.call("testFunc")
|
|
302
|
+
|
|
303
|
+
# Verify: contract was created at least once with the rotated provider
|
|
304
|
+
# If the fix works, the provider_versions list should show provider version 2
|
|
305
|
+
# (because rotation happened before the successful call)
|
|
306
|
+
assert len(provider_versions) >= 1
|
|
307
|
+
# The last contract should have been created with the rotated provider
|
|
308
|
+
assert provider_versions[-1] == 2, f"Expected provider version 2, got {provider_versions}"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def test_call_with_429_triggers_retry_with_new_contract(mock_chain_interface, mock_abi_file):
|
|
312
|
+
"""Integration test: 429 error should trigger retry which uses fresh contract.
|
|
313
|
+
|
|
314
|
+
This test verifies the complete flow:
|
|
315
|
+
1. Call fails with 429
|
|
316
|
+
2. with_retry handles it and retries
|
|
317
|
+
3. The retry uses a fresh contract instance (new provider)
|
|
318
|
+
"""
|
|
319
|
+
contract = MockContract("0xAddress", "gnosis")
|
|
320
|
+
|
|
321
|
+
# Create distinct mock contracts for each call
|
|
322
|
+
mock_contract_1 = MagicMock()
|
|
323
|
+
mock_contract_1.functions.testFunc.return_value.call.side_effect = Exception(
|
|
324
|
+
"429 Too Many Requests"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
mock_contract_2 = MagicMock()
|
|
328
|
+
mock_contract_2.functions.testFunc.return_value.call.return_value = "success_from_rotated_rpc"
|
|
329
|
+
|
|
330
|
+
contracts_returned = [mock_contract_1, mock_contract_2]
|
|
331
|
+
contract_call_count = [0]
|
|
332
|
+
|
|
333
|
+
def mock_contract_factory(address, abi):
|
|
334
|
+
result = contracts_returned[min(contract_call_count[0], len(contracts_returned) - 1)]
|
|
335
|
+
contract_call_count[0] += 1
|
|
336
|
+
return result
|
|
337
|
+
|
|
338
|
+
mock_chain_interface.web3.eth.contract.side_effect = mock_contract_factory
|
|
339
|
+
|
|
340
|
+
# Implement with_retry that actually retries
|
|
341
|
+
def real_with_retry(fn, max_retries=6, operation_name="operation"):
|
|
342
|
+
last_error = None
|
|
343
|
+
for attempt in range(max_retries + 1):
|
|
344
|
+
try:
|
|
345
|
+
return fn()
|
|
346
|
+
except Exception as e:
|
|
347
|
+
last_error = e
|
|
348
|
+
if "429" in str(e) and attempt < max_retries:
|
|
349
|
+
continue # Retry
|
|
350
|
+
raise
|
|
351
|
+
raise last_error
|
|
352
|
+
|
|
353
|
+
mock_chain_interface.with_retry.side_effect = real_with_retry
|
|
354
|
+
|
|
355
|
+
# Execute - should succeed on second attempt with rotated RPC
|
|
356
|
+
result = contract.call("testFunc")
|
|
357
|
+
|
|
358
|
+
assert result == "success_from_rotated_rpc"
|
|
359
|
+
# Verify we created 2 contract instances (one per attempt)
|
|
360
|
+
assert contract_call_count[0] == 2, (
|
|
361
|
+
f"Expected 2 contract creations, got {contract_call_count[0]}"
|
|
362
|
+
)
|
tests/test_keys.py
CHANGED
|
@@ -67,7 +67,8 @@ def mock_account():
|
|
|
67
67
|
def from_key_side_effect(private_key):
|
|
68
68
|
# 1. Handle the master private key
|
|
69
69
|
if (
|
|
70
|
-
private_key
|
|
70
|
+
private_key
|
|
71
|
+
== "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" # gitleaks:allow
|
|
71
72
|
):
|
|
72
73
|
addr = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
|
|
73
74
|
# 2. Handle our mock key format
|
tests/test_legacy_wallet.py
CHANGED
|
@@ -39,6 +39,17 @@ def mock_transaction_service():
|
|
|
39
39
|
yield instance
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
@pytest.fixture(autouse=True)
|
|
43
|
+
def mock_chain_sleeps():
|
|
44
|
+
"""Mock time.sleep in chain modules to speed up tests."""
|
|
45
|
+
with (
|
|
46
|
+
patch("iwa.core.chain.interface.time.sleep"),
|
|
47
|
+
patch("iwa.core.chain.rate_limiter.time.sleep"),
|
|
48
|
+
patch("time.sleep"),
|
|
49
|
+
):
|
|
50
|
+
yield
|
|
51
|
+
|
|
52
|
+
|
|
42
53
|
@pytest.fixture
|
|
43
54
|
def mock_key_storage():
|
|
44
55
|
with patch("iwa.core.wallet.KeyStorage") as mock:
|
tests/test_pricing.py
CHANGED
|
@@ -18,6 +18,13 @@ def price_service(mock_secrets):
|
|
|
18
18
|
return PriceService()
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
@pytest.fixture(autouse=True)
|
|
22
|
+
def clear_cache():
|
|
23
|
+
"""Clear global cache before each test to ensure isolation."""
|
|
24
|
+
with patch("iwa.core.pricing._PRICE_CACHE", {}):
|
|
25
|
+
yield
|
|
26
|
+
|
|
27
|
+
|
|
21
28
|
def test_get_token_price_success(price_service):
|
|
22
29
|
with patch("iwa.core.pricing.requests.get") as mock_get:
|
|
23
30
|
mock_get.return_value.status_code = 200
|
|
@@ -36,30 +43,36 @@ def test_get_token_price_cached(price_service):
|
|
|
36
43
|
# Pre-populate cache
|
|
37
44
|
from datetime import datetime
|
|
38
45
|
|
|
39
|
-
|
|
46
|
+
cache_data = {
|
|
47
|
+
"ethereum_eur": {"price": 100.0, "timestamp": datetime.now()}
|
|
48
|
+
}
|
|
40
49
|
|
|
41
|
-
with patch("iwa.core.pricing.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
with patch.dict("iwa.core.pricing._PRICE_CACHE", cache_data):
|
|
51
|
+
with patch("iwa.core.pricing.requests.get") as mock_get:
|
|
52
|
+
price = price_service.get_token_price("ethereum", "eur")
|
|
53
|
+
assert price == 100.0
|
|
54
|
+
mock_get.assert_not_called()
|
|
45
55
|
|
|
46
56
|
|
|
47
57
|
def test_get_token_price_cache_expired(price_service):
|
|
48
58
|
# Pre-populate expired cache
|
|
49
59
|
from datetime import datetime
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
|
|
61
|
+
cache_data = {
|
|
62
|
+
"ethereum_eur": {
|
|
63
|
+
"price": 100.0,
|
|
64
|
+
"timestamp": datetime.now() - timedelta(minutes=60), # > 30 min TTL
|
|
65
|
+
}
|
|
54
66
|
}
|
|
55
67
|
|
|
56
|
-
with patch("iwa.core.pricing.
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
with patch.dict("iwa.core.pricing._PRICE_CACHE", cache_data):
|
|
69
|
+
with patch("iwa.core.pricing.requests.get") as mock_get:
|
|
70
|
+
mock_get.return_value.status_code = 200
|
|
71
|
+
mock_get.return_value.json.return_value = {"ethereum": {"eur": 200.0}}
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
73
|
+
price = price_service.get_token_price("ethereum", "eur")
|
|
74
|
+
assert price == 200.0
|
|
75
|
+
mock_get.assert_called_once()
|
|
63
76
|
|
|
64
77
|
|
|
65
78
|
def test_get_token_price_api_error(price_service):
|
|
@@ -84,7 +97,9 @@ def test_get_token_price_rate_limit():
|
|
|
84
97
|
with patch("iwa.core.pricing.secrets") as mock_secrets:
|
|
85
98
|
mock_secrets.coingecko_api_key = None
|
|
86
99
|
|
|
100
|
+
# Need to re-instantiate or patch secrets on instance since it's read in __init__
|
|
87
101
|
service = PriceService()
|
|
102
|
+
service.api_key = None
|
|
88
103
|
|
|
89
104
|
with patch("iwa.core.pricing.requests.get") as mock_get, patch("time.sleep"):
|
|
90
105
|
# Return 429 for all attempts
|
|
@@ -94,7 +109,7 @@ def test_get_token_price_rate_limit():
|
|
|
94
109
|
price = service.get_token_price("ethereum", "eur")
|
|
95
110
|
|
|
96
111
|
assert price is None
|
|
97
|
-
# Should have tried max_retries + 1 times
|
|
112
|
+
# Should have tried max_retries + 1 times (3 total)
|
|
98
113
|
assert mock_get.call_count == 3
|
|
99
114
|
|
|
100
115
|
|
|
@@ -106,6 +121,7 @@ def test_get_token_price_rate_limit_then_success():
|
|
|
106
121
|
mock_secrets.coingecko_api_key = None
|
|
107
122
|
|
|
108
123
|
service = PriceService()
|
|
124
|
+
service.api_key = None
|
|
109
125
|
|
|
110
126
|
with patch("iwa.core.pricing.requests.get") as mock_get, patch("time.sleep"):
|
|
111
127
|
# First call returns 429, second succeeds
|
|
@@ -130,6 +146,7 @@ def test_get_token_price_no_api_key():
|
|
|
130
146
|
mock_secrets.coingecko_api_key = None
|
|
131
147
|
|
|
132
148
|
service = PriceService()
|
|
149
|
+
service.api_key = None
|
|
133
150
|
|
|
134
151
|
with patch("iwa.core.pricing.requests.get") as mock_get:
|
|
135
152
|
mock_get.return_value.status_code = 200
|
tests/test_safe_coverage.py
CHANGED
|
@@ -56,7 +56,7 @@ def test_execute_safe_transaction_success(safe_service, mock_deps):
|
|
|
56
56
|
mock_safe_instance = mock_safe_multisig_cls.return_value
|
|
57
57
|
mock_safe_tx = MagicMock()
|
|
58
58
|
mock_safe_instance.build_tx.return_value = mock_safe_tx
|
|
59
|
-
mock_safe_tx.tx_hash.hex.return_value = "
|
|
59
|
+
mock_safe_tx.tx_hash.hex.return_value = "TxHash"
|
|
60
60
|
|
|
61
61
|
# Execute
|
|
62
62
|
tx_hash = safe_service.execute_safe_transaction(safe_address, to_address, value, chain_name)
|
|
@@ -90,7 +90,7 @@ def test_get_sign_and_execute_callback(safe_service, mock_deps):
|
|
|
90
90
|
|
|
91
91
|
# Test executing callback
|
|
92
92
|
mock_safe_tx = MagicMock()
|
|
93
|
-
mock_safe_tx.tx_hash.hex.return_value = "
|
|
93
|
+
mock_safe_tx.tx_hash.hex.return_value = "TxHash"
|
|
94
94
|
|
|
95
95
|
result = callback(mock_safe_tx)
|
|
96
96
|
|
|
@@ -119,7 +119,7 @@ def test_redeploy_safes(safe_service, mock_deps):
|
|
|
119
119
|
|
|
120
120
|
mock_deps["key_storage"].accounts = {"0xSafe1": account1}
|
|
121
121
|
|
|
122
|
-
with patch("iwa.core.
|
|
122
|
+
with patch("iwa.core.chain.models.secrets") as mock_settings:
|
|
123
123
|
mock_settings.gnosis_rpc.get_secret_value.return_value = "http://rpc"
|
|
124
124
|
|
|
125
125
|
with patch("iwa.core.services.safe.EthereumClient") as mock_eth_client:
|
tests/test_safe_service.py
CHANGED
|
@@ -53,18 +53,16 @@ def mock_dependencies():
|
|
|
53
53
|
patch("iwa.core.services.safe.EthereumClient") as mock_client,
|
|
54
54
|
patch("iwa.core.services.safe.Safe") as mock_safe,
|
|
55
55
|
patch("iwa.core.services.safe.ProxyFactory") as mock_proxy_factory,
|
|
56
|
-
patch("iwa.core.services.safe.secrets") as mock_secrets,
|
|
57
56
|
patch("iwa.core.services.safe.log_transaction") as mock_log,
|
|
58
57
|
patch("iwa.core.services.safe.get_safe_master_copy_address") as mock_master,
|
|
59
58
|
patch("iwa.core.services.safe.get_safe_proxy_factory_address") as mock_factory,
|
|
59
|
+
patch("time.sleep"), # Avoid any retry delays
|
|
60
60
|
):
|
|
61
|
-
mock_secrets.gnosis_rpc.get_secret_value.return_value = "http://rpc"
|
|
62
|
-
|
|
63
61
|
# Setup Safe creation return
|
|
64
62
|
mock_create_tx = MagicMock()
|
|
65
63
|
# Valid Checksum Address - New Safe (Matches Pydantic output)
|
|
66
64
|
mock_create_tx.contract_address = "0xbEC49fa140ACaa83533f900357DCD37866d50618"
|
|
67
|
-
mock_create_tx.tx_hash.hex.return_value = "
|
|
65
|
+
mock_create_tx.tx_hash.hex.return_value = "TxHash"
|
|
68
66
|
|
|
69
67
|
mock_safe.create.return_value = mock_create_tx
|
|
70
68
|
|
|
@@ -72,7 +70,7 @@ def mock_dependencies():
|
|
|
72
70
|
mock_deploy_tx = MagicMock()
|
|
73
71
|
# Valid checksum address - Salted Safe
|
|
74
72
|
mock_deploy_tx.contract_address = "0xDAFEA492D9c6733ae3d56b7Ed1ADB60692c98Bc5"
|
|
75
|
-
mock_deploy_tx.tx_hash.hex.return_value = "
|
|
73
|
+
mock_deploy_tx.tx_hash.hex.return_value = "TxHashSalted"
|
|
76
74
|
|
|
77
75
|
mock_proxy_factory.return_value.deploy_proxy_contract_with_nonce.return_value = (
|
|
78
76
|
mock_deploy_tx
|
|
@@ -104,7 +102,6 @@ def mock_dependencies():
|
|
|
104
102
|
"client": mock_client,
|
|
105
103
|
"safe": mock_safe,
|
|
106
104
|
"proxy_factory": mock_proxy_factory,
|
|
107
|
-
"secrets": mock_secrets,
|
|
108
105
|
"log": mock_log,
|
|
109
106
|
"master": mock_master,
|
|
110
107
|
"factory": mock_factory,
|
|
@@ -17,6 +17,10 @@ def mock_chain_interfaces():
|
|
|
17
17
|
gnosis_interface.chain = mock_chain
|
|
18
18
|
gnosis_interface.web3 = MagicMock()
|
|
19
19
|
instance.get.return_value = gnosis_interface
|
|
20
|
+
|
|
21
|
+
# Mock with_retry to execute the operation
|
|
22
|
+
gnosis_interface.with_retry.side_effect = lambda op, **kwargs: op()
|
|
23
|
+
|
|
20
24
|
yield instance
|
|
21
25
|
|
|
22
26
|
|
|
@@ -115,8 +119,8 @@ def test_sign_and_send_max_retries_exhausted(
|
|
|
115
119
|
# Should fail after max retries
|
|
116
120
|
assert success is False
|
|
117
121
|
assert receipt == {} # Returns empty dict on failure
|
|
118
|
-
# Should have tried
|
|
119
|
-
assert chain_interface.web3.eth.send_raw_transaction.call_count ==
|
|
122
|
+
# Should have tried 10 times (max_retries)
|
|
123
|
+
assert chain_interface.web3.eth.send_raw_transaction.call_count == 10
|
|
120
124
|
|
|
121
125
|
|
|
122
126
|
def test_sign_and_send_transaction_reverted(
|
|
@@ -170,7 +174,8 @@ def test_sign_and_send_signer_not_found(
|
|
|
170
174
|
# Signing raises ValueError for unknown account
|
|
171
175
|
mock_key_storage.sign_transaction.side_effect = ValueError("Account not found")
|
|
172
176
|
|
|
173
|
-
|
|
177
|
+
with patch("time.sleep"): # Avoid real retry delays
|
|
178
|
+
success, receipt = transaction_service.sign_and_send(tx, "unknown_signer")
|
|
174
179
|
|
|
175
180
|
assert success is False
|
|
176
181
|
assert receipt == {} # Returns empty dict on failure
|