iwa 0.1.0__tar.gz → 0.1.2__tar.gz

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 (236) hide show
  1. {iwa-0.1.0/src/iwa.egg-info → iwa-0.1.2}/PKG-INFO +1 -1
  2. {iwa-0.1.0 → iwa-0.1.2}/pyproject.toml +2 -2
  3. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/chain/interface.py +3 -0
  4. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/transaction.py +77 -44
  5. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/service_manager/staking.py +20 -19
  6. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_service_staking.py +54 -0
  7. {iwa-0.1.0 → iwa-0.1.2/src/iwa.egg-info}/PKG-INFO +1 -1
  8. {iwa-0.1.0 → iwa-0.1.2}/LICENSE +0 -0
  9. {iwa-0.1.0 → iwa-0.1.2}/README.md +0 -0
  10. {iwa-0.1.0 → iwa-0.1.2}/setup.cfg +0 -0
  11. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/__init__.py +0 -0
  12. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/__main__.py +0 -0
  13. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/__init__.py +0 -0
  14. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/chain/__init__.py +0 -0
  15. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/chain/errors.py +0 -0
  16. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/chain/manager.py +0 -0
  17. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/chain/models.py +0 -0
  18. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/chain/rate_limiter.py +0 -0
  19. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/chainlist.py +0 -0
  20. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/cli.py +0 -0
  21. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/constants.py +0 -0
  22. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/contracts/__init__.py +0 -0
  23. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/contracts/abis/erc20.json +0 -0
  24. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/contracts/abis/multisend.json +0 -0
  25. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/contracts/abis/multisend_call_only.json +0 -0
  26. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/contracts/cache.py +0 -0
  27. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/contracts/contract.py +0 -0
  28. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/contracts/decoder.py +0 -0
  29. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/contracts/erc20.py +0 -0
  30. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/contracts/multisend.py +0 -0
  31. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/db.py +0 -0
  32. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/http.py +0 -0
  33. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/ipfs.py +0 -0
  34. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/keys.py +0 -0
  35. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/mnemonic.py +0 -0
  36. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/models.py +0 -0
  37. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/monitor.py +0 -0
  38. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/plugins.py +0 -0
  39. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/pricing.py +0 -0
  40. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/rpc_monitor.py +0 -0
  41. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/secrets.py +0 -0
  42. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/__init__.py +0 -0
  43. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/account.py +0 -0
  44. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/balance.py +0 -0
  45. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/plugin.py +0 -0
  46. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/safe.py +0 -0
  47. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/safe_executor.py +0 -0
  48. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/transfer/__init__.py +0 -0
  49. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/transfer/base.py +0 -0
  50. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/transfer/erc20.py +0 -0
  51. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/transfer/multisend.py +0 -0
  52. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/transfer/native.py +0 -0
  53. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/services/transfer/swap.py +0 -0
  54. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/tables.py +0 -0
  55. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/test.py +0 -0
  56. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/tests/test_gnosis_fee.py +0 -0
  57. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/tests/test_ipfs.py +0 -0
  58. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/tests/test_pricing.py +0 -0
  59. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/tests/test_regression_fixes.py +0 -0
  60. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/tests/test_wallet.py +0 -0
  61. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/types.py +0 -0
  62. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/ui.py +0 -0
  63. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/utils.py +0 -0
  64. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/core/wallet.py +0 -0
  65. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/__init__.py +0 -0
  66. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/__init__.py +0 -0
  67. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/cow/__init__.py +0 -0
  68. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/cow/quotes.py +0 -0
  69. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/cow/swap.py +0 -0
  70. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/cow/types.py +0 -0
  71. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/cow_utils.py +0 -0
  72. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/plugin.py +0 -0
  73. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/safe.py +0 -0
  74. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/tests/test_cow.py +0 -0
  75. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/gnosis/tests/test_safe.py +0 -0
  76. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/__init__.py +0 -0
  77. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/constants.py +0 -0
  78. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/activity_checker.json +0 -0
  79. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/mech.json +0 -0
  80. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/mech_marketplace.json +0 -0
  81. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/mech_marketplace_v1.json +0 -0
  82. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/mech_new.json +0 -0
  83. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/service_manager.json +0 -0
  84. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/service_registry.json +0 -0
  85. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/service_registry_token_utility.json +0 -0
  86. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/staking.json +0 -0
  87. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/abis/staking_token.json +0 -0
  88. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/activity_checker.py +0 -0
  89. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/base.py +0 -0
  90. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/mech.py +0 -0
  91. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/mech_marketplace.py +0 -0
  92. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/mech_marketplace_v1.py +0 -0
  93. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/service.py +0 -0
  94. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/contracts/staking.py +0 -0
  95. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/events.py +0 -0
  96. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/importer.py +0 -0
  97. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/mech_reference.py +0 -0
  98. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/models.py +0 -0
  99. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/plugin.py +0 -0
  100. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/scripts/test_full_mech_flow.py +0 -0
  101. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/scripts/test_simple_lifecycle.py +0 -0
  102. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/service_manager/__init__.py +0 -0
  103. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/service_manager/base.py +0 -0
  104. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/service_manager/drain.py +0 -0
  105. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/service_manager/lifecycle.py +0 -0
  106. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/service_manager/mech.py +0 -0
  107. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/conftest.py +0 -0
  108. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_importer.py +0 -0
  109. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_importer_error_handling.py +0 -0
  110. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_mech_contracts.py +0 -0
  111. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_olas_archiving.py +0 -0
  112. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_olas_contracts.py +0 -0
  113. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_olas_integration.py +0 -0
  114. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_olas_models.py +0 -0
  115. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_olas_view.py +0 -0
  116. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_olas_view_actions.py +0 -0
  117. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_olas_view_modals.py +0 -0
  118. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_plugin.py +0 -0
  119. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_plugin_full.py +0 -0
  120. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_service_lifecycle.py +0 -0
  121. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_service_manager.py +0 -0
  122. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_service_manager_errors.py +0 -0
  123. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_service_manager_flows.py +0 -0
  124. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_service_manager_mech.py +0 -0
  125. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_service_manager_rewards.py +0 -0
  126. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_service_manager_validation.py +0 -0
  127. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_staking_integration.py +0 -0
  128. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tests/test_staking_validation.py +0 -0
  129. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tui/__init__.py +0 -0
  130. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/plugins/olas/tui/olas_view.py +0 -0
  131. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/__init__.py +0 -0
  132. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/check_profile.py +0 -0
  133. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/drain_accounts.py +0 -0
  134. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/list_contracts.py +0 -0
  135. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/release.py +0 -0
  136. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/reset_env.py +0 -0
  137. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/reset_tenderly.py +0 -0
  138. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/restore_backup.py +0 -0
  139. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/test_chainlist.py +0 -0
  140. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tools/wallet_check.py +0 -0
  141. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/__init__.py +0 -0
  142. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/app.py +0 -0
  143. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/modals/__init__.py +0 -0
  144. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/modals/base.py +0 -0
  145. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/rpc.py +0 -0
  146. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/screens/__init__.py +0 -0
  147. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/screens/wallets.py +0 -0
  148. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/tests/test_app.py +0 -0
  149. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/tests/test_rpc.py +0 -0
  150. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/tests/test_wallets_refactor.py +0 -0
  151. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/tests/test_widgets.py +0 -0
  152. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/widgets/__init__.py +0 -0
  153. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/widgets/base.py +0 -0
  154. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/tui/workers.py +0 -0
  155. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/dependencies.py +0 -0
  156. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/models.py +0 -0
  157. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/accounts.py +0 -0
  158. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/olas/__init__.py +0 -0
  159. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/olas/admin.py +0 -0
  160. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/olas/funding.py +0 -0
  161. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/olas/general.py +0 -0
  162. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/olas/services.py +0 -0
  163. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/olas/staking.py +0 -0
  164. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/state.py +0 -0
  165. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/swap.py +0 -0
  166. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/routers/transactions.py +0 -0
  167. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/server.py +0 -0
  168. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/static/app.js +0 -0
  169. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/static/index.html +0 -0
  170. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/static/style.css +0 -0
  171. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/tests/test_web_endpoints.py +0 -0
  172. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/tests/test_web_olas.py +0 -0
  173. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/tests/test_web_swap.py +0 -0
  174. {iwa-0.1.0 → iwa-0.1.2}/src/iwa/web/tests/test_web_swap_coverage.py +0 -0
  175. {iwa-0.1.0 → iwa-0.1.2}/src/iwa.egg-info/SOURCES.txt +0 -0
  176. {iwa-0.1.0 → iwa-0.1.2}/src/iwa.egg-info/dependency_links.txt +0 -0
  177. {iwa-0.1.0 → iwa-0.1.2}/src/iwa.egg-info/entry_points.txt +0 -0
  178. {iwa-0.1.0 → iwa-0.1.2}/src/iwa.egg-info/requires.txt +0 -0
  179. {iwa-0.1.0 → iwa-0.1.2}/src/iwa.egg-info/top_level.txt +0 -0
  180. {iwa-0.1.0 → iwa-0.1.2}/src/tests/legacy_cow.py +0 -0
  181. {iwa-0.1.0 → iwa-0.1.2}/src/tests/legacy_safe.py +0 -0
  182. {iwa-0.1.0 → iwa-0.1.2}/src/tests/legacy_transaction_retry_logic.py +0 -0
  183. {iwa-0.1.0 → iwa-0.1.2}/src/tests/legacy_tui.py +0 -0
  184. {iwa-0.1.0 → iwa-0.1.2}/src/tests/legacy_wallets_screen.py +0 -0
  185. {iwa-0.1.0 → iwa-0.1.2}/src/tests/legacy_web.py +0 -0
  186. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_account_service.py +0 -0
  187. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_balance_service.py +0 -0
  188. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_chain.py +0 -0
  189. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_chain_interface.py +0 -0
  190. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_chain_interface_coverage.py +0 -0
  191. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_chainlist_enrichment.py +0 -0
  192. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_cli.py +0 -0
  193. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_contract.py +0 -0
  194. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_contract_cache.py +0 -0
  195. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_db.py +0 -0
  196. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_drain_coverage.py +0 -0
  197. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_erc20.py +0 -0
  198. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_gnosis_plugin.py +0 -0
  199. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_keys.py +0 -0
  200. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_legacy_wallet.py +0 -0
  201. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_main.py +0 -0
  202. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_migration.py +0 -0
  203. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_mnemonic.py +0 -0
  204. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_modals.py +0 -0
  205. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_models.py +0 -0
  206. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_monitor.py +0 -0
  207. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_multisend.py +0 -0
  208. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_plugin_service.py +0 -0
  209. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_rate_limiter.py +0 -0
  210. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_rate_limiter_retry.py +0 -0
  211. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_reset_tenderly.py +0 -0
  212. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_rpc_efficiency.py +0 -0
  213. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_rpc_rate_limit.py +0 -0
  214. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_rpc_rotation.py +0 -0
  215. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_rpc_view.py +0 -0
  216. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_safe_coverage.py +0 -0
  217. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_safe_executor.py +0 -0
  218. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_safe_integration.py +0 -0
  219. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_safe_service.py +0 -0
  220. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_service_manager_integration.py +0 -0
  221. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_service_manager_structure.py +0 -0
  222. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_service_transaction.py +0 -0
  223. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_staking_router.py +0 -0
  224. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_staking_simple.py +0 -0
  225. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_tables.py +0 -0
  226. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_transaction_service.py +0 -0
  227. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_transfer_multisend.py +0 -0
  228. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_transfer_native.py +0 -0
  229. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_transfer_security.py +0 -0
  230. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_transfer_structure.py +0 -0
  231. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_transfer_swap_unit.py +0 -0
  232. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_ui_coverage.py +0 -0
  233. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_utils.py +0 -0
  234. {iwa-0.1.0 → iwa-0.1.2}/src/tests/test_workers.py +0 -0
  235. {iwa-0.1.0 → iwa-0.1.2}/src/tools/create_and_stake_service.py +0 -0
  236. {iwa-0.1.0 → iwa-0.1.2}/src/tools/verify_drain.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iwa
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: A secure, modular, and plugin-based framework for crypto agents and ops
5
5
  Requires-Python: <4.0,>=3.12
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "iwa"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "A secure, modular, and plugin-based framework for crypto agents and ops"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12,<4.0"
@@ -72,7 +72,7 @@ where = ["src"]
72
72
 
73
73
  [tool.ruff]
74
74
  line-length = 100
75
- target-version = "0.1.0"
75
+ target-version = "0.1.2"
76
76
  fix = true
77
77
 
78
78
  [tool.ruff.lint]
@@ -94,6 +94,9 @@ class ChainInterface:
94
94
  """Get the current active RPC URL."""
95
95
  if not self.chain.rpcs:
96
96
  return ""
97
+ # Ensure index is valid (could be stale after rpcs list changes or singleton reuse)
98
+ if self._current_rpc_index >= len(self.chain.rpcs):
99
+ self._current_rpc_index = 0
97
100
  return self.chain.rpcs[self._current_rpc_index]
98
101
 
99
102
  @property
@@ -1,5 +1,6 @@
1
1
  """Transaction service module."""
2
2
 
3
+ import threading
3
4
  from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
4
5
 
5
6
  from loguru import logger
@@ -220,12 +221,29 @@ class TransferLogger:
220
221
  class TransactionService:
221
222
  """Manages transaction lifecycle: signing, sending, retrying."""
222
223
 
224
+ # Class-level lock for managing per-address locks
225
+ _locks_lock = threading.Lock()
226
+ _address_locks: Dict[str, threading.Lock] = {}
227
+
223
228
  def __init__(self, key_storage: KeyStorage, account_service: AccountService, safe_service=None):
224
229
  """Initialize TransactionService."""
225
230
  self.key_storage = key_storage
226
231
  self.account_service = account_service
227
232
  self.safe_service = safe_service
228
233
 
234
+ @classmethod
235
+ def _get_address_lock(cls, address: str) -> threading.Lock:
236
+ """Get or create a lock for a specific address.
237
+
238
+ This prevents nonce collisions when sending multiple transactions
239
+ from the same address concurrently.
240
+ """
241
+ address_lower = address.lower()
242
+ with cls._locks_lock:
243
+ if address_lower not in cls._address_locks:
244
+ cls._address_locks[address_lower] = threading.Lock()
245
+ return cls._address_locks[address_lower]
246
+
229
247
  def _resolve_label(self, address: str, chain_name: str = "gnosis") -> str:
230
248
  """Resolve address to human-readable label."""
231
249
  if not address:
@@ -255,69 +273,84 @@ class TransactionService:
255
273
 
256
274
  Uses ChainInterface.with_retry() for consistent RPC rotation and retry logic.
257
275
  Gas errors are handled by increasing gas and retrying within the same mechanism.
276
+
277
+ Note: Uses per-address locking to prevent nonce collisions when multiple
278
+ transactions are sent from the same address concurrently.
258
279
  """
259
280
  chain_interface = ChainInterfaces().get(chain_name)
260
281
  tx = dict(transaction)
261
282
 
262
- if not self._prepare_transaction(tx, signer_address_or_tag, chain_interface):
283
+ # Resolve signer first to get the address for locking
284
+ signer_account = self.account_service.resolve_account(signer_address_or_tag)
285
+ if not signer_account:
286
+ logger.error(f"Signer {signer_address_or_tag} not found")
263
287
  return False, {}
264
288
 
265
- # CHECK FOR SAFE TRANSACTION
266
- signer_account = self.account_service.resolve_account(signer_address_or_tag)
289
+ # CHECK FOR SAFE TRANSACTION (Safe has its own nonce management)
267
290
  if isinstance(signer_account, StoredSafeAccount):
268
291
  if not self.safe_service:
269
292
  logger.error("Attempted Safe transaction but SafeService is not initialized.")
270
293
  return False, {}
294
+ # Safe transactions don't need EOA nonce locking
295
+ if not self._prepare_transaction(tx, signer_address_or_tag, chain_interface):
296
+ return False, {}
271
297
  return self._execute_via_safe(tx, signer_account, chain_interface, chain_name, tags)
272
298
 
273
- # Mutable state for retry attempts
274
- state = {"gas_retries": 0, "max_gas_retries": 5}
299
+ # Acquire lock for this address to prevent nonce collisions
300
+ address_lock = self._get_address_lock(signer_account.address)
301
+ with address_lock:
302
+ if not self._prepare_transaction(tx, signer_address_or_tag, chain_interface):
303
+ return False, {}
275
304
 
276
- def _do_sign_send_wait() -> Tuple[bool, Dict, bytes]:
277
- """Inner operation wrapped by with_retry."""
278
- try:
279
- signed_txn = self.key_storage.sign_transaction(tx, signer_address_or_tag)
280
- txn_hash = chain_interface.web3.eth.send_raw_transaction(signed_txn.raw_transaction)
281
- receipt = chain_interface.web3.eth.wait_for_transaction_receipt(txn_hash)
305
+ # Mutable state for retry attempts
306
+ state = {"gas_retries": 0, "max_gas_retries": 5}
282
307
 
283
- status = getattr(receipt, "status", None)
284
- if status is None and isinstance(receipt, dict):
285
- status = receipt.get("status")
308
+ def _do_sign_send_wait() -> Tuple[bool, Dict, bytes]:
309
+ """Inner operation wrapped by with_retry."""
310
+ try:
311
+ signed_txn = self.key_storage.sign_transaction(tx, signer_address_or_tag)
312
+ txn_hash = chain_interface.web3.eth.send_raw_transaction(
313
+ signed_txn.raw_transaction
314
+ )
315
+ receipt = chain_interface.web3.eth.wait_for_transaction_receipt(txn_hash)
286
316
 
287
- if receipt and status == 1:
288
- return True, receipt, txn_hash
289
- # Transaction mined but reverted - don't retry
290
- logger.error("Transaction failed (status 0).")
291
- raise ValueError("Transaction reverted")
317
+ status = getattr(receipt, "status", None)
318
+ if status is None and isinstance(receipt, dict):
319
+ status = receipt.get("status")
292
320
 
293
- except web3_exceptions.Web3RPCError as e:
294
- # Handle gas errors by increasing gas and re-raising
295
- self._handle_gas_retry(e, tx, state)
296
- raise # Re-raise to trigger with_retry's retry mechanism
321
+ if receipt and status == 1:
322
+ return True, receipt, txn_hash
323
+ # Transaction mined but reverted - don't retry
324
+ logger.error("Transaction failed (status 0).")
325
+ raise ValueError("Transaction reverted")
297
326
 
298
- try:
299
- success, receipt, txn_hash = chain_interface.with_retry(
300
- _do_sign_send_wait,
301
- operation_name=f"sign_and_send to {tx.get('to', 'unknown')[:10]}...",
302
- )
303
- if success:
304
- signer_account = self.account_service.resolve_account(signer_address_or_tag)
305
- chain_interface.wait_for_no_pending_tx(signer_account.address)
306
- logger.info(f"Transaction sent successfully. Tx Hash: {txn_hash.hex()}")
307
- self._log_successful_transaction(
308
- receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
327
+ except web3_exceptions.Web3RPCError as e:
328
+ # Handle gas errors by increasing gas and re-raising
329
+ self._handle_gas_retry(e, tx, state)
330
+ raise # Re-raise to trigger with_retry's retry mechanism
331
+
332
+ try:
333
+ success, receipt, txn_hash = chain_interface.with_retry(
334
+ _do_sign_send_wait,
335
+ operation_name=f"sign_and_send to {tx.get('to', 'unknown')[:10]}...",
309
336
  )
310
- return True, receipt
311
- return False, {}
312
- except ValueError as e:
313
- # Transaction reverted - already logged
314
- if "reverted" in str(e).lower():
337
+ if success:
338
+ chain_interface.wait_for_no_pending_tx(signer_account.address)
339
+ logger.info(f"Transaction sent successfully. Tx Hash: {txn_hash.hex()}")
340
+ self._log_successful_transaction(
341
+ receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
342
+ )
343
+ return True, receipt
344
+ return False, {}
345
+ except ValueError as e:
346
+ # Transaction reverted - already logged
347
+ if "reverted" in str(e).lower():
348
+ return False, {}
349
+ logger.exception(f"Transaction failed: {e}")
350
+ return False, {}
351
+ except Exception as e:
352
+ logger.exception(f"Transaction failed after retries: {e}")
315
353
  return False, {}
316
- logger.exception(f"Transaction failed: {e}")
317
- return False, {}
318
- except Exception as e:
319
- logger.exception(f"Transaction failed after retries: {e}")
320
- return False, {}
321
354
 
322
355
  def _prepare_transaction(self, tx: dict, signer_tag: str, chain_interface) -> bool:
323
356
  """Ensure nonce and chainId are set."""
@@ -547,12 +547,12 @@ class StakingManagerMixin:
547
547
  f"Preparing to unstake service {self.service.service_id} from {self._get_label(staking_contract.address)}"
548
548
  )
549
549
 
550
- # Check that the service is staked
550
+ # Check that the service is staked or evicted (both can be unstaked)
551
551
  try:
552
552
  staking_state = staking_contract.get_staking_state(self.service.service_id)
553
553
  logger.info(f"Current staking state: {staking_state}")
554
554
 
555
- if staking_state != StakingState.STAKED:
555
+ if staking_state not in (StakingState.STAKED, StakingState.EVICTED):
556
556
  logger.error(
557
557
  f"Service {self.service.service_id} is not staked (state={staking_state}), cannot unstake"
558
558
  )
@@ -561,23 +561,24 @@ class StakingManagerMixin:
561
561
  logger.error(f"Failed to get staking state: {e}")
562
562
  return False
563
563
 
564
- # Check that enough time has passed since staking
565
- try:
566
- service_info = staking_contract.get_service_info(self.service.service_id)
567
- ts_start = service_info.get("ts_start", 0)
568
- if ts_start > 0:
569
- min_duration = staking_contract.min_staking_duration
570
- unlock_ts = ts_start + min_duration
571
- now_ts = datetime.now(timezone.utc).timestamp()
572
-
573
- if now_ts < unlock_ts:
574
- diff = int(unlock_ts - now_ts)
575
- logger.error(
576
- f"Cannot unstake yet. Minimum staking duration not met. Unlocks in {diff} seconds."
577
- )
578
- return False
579
- except Exception as e:
580
- logger.warning(f"Could not verify staking duration: {e}. Proceeding with caution.")
564
+ # Check that enough time has passed since staking (skip for EVICTED - already expired)
565
+ if staking_state == StakingState.STAKED:
566
+ try:
567
+ service_info = staking_contract.get_service_info(self.service.service_id)
568
+ ts_start = service_info.get("ts_start", 0)
569
+ if ts_start > 0:
570
+ min_duration = staking_contract.min_staking_duration
571
+ unlock_ts = ts_start + min_duration
572
+ now_ts = datetime.now(timezone.utc).timestamp()
573
+
574
+ if now_ts < unlock_ts:
575
+ diff = int(unlock_ts - now_ts)
576
+ logger.error(
577
+ f"Cannot unstake yet. Minimum staking duration not met. Unlocks in {diff} seconds."
578
+ )
579
+ return False
580
+ except Exception as e:
581
+ logger.warning(f"Could not verify staking duration: {e}. Proceeding with caution.")
581
582
 
582
583
  # Use service owner which holds the NFT (not necessarily master)
583
584
  owner_address = self.service.service_owner_address or self.wallet.master_account.address
@@ -51,6 +51,60 @@ def test_sm_unstake_not_staked(mock_wallet):
51
51
  assert result is False
52
52
 
53
53
 
54
+ def test_sm_unstake_evicted_succeeds(mock_wallet):
55
+ """Cover unstake when service is EVICTED - should succeed."""
56
+ with (
57
+ patch("iwa.core.models.Config"),
58
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
59
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
60
+ ):
61
+ mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
62
+ sm = ServiceManager(mock_wallet)
63
+ sm.service = Service(
64
+ service_name="t", chain_name="gnosis", service_id=1, multisig_address=VALID_ADDR
65
+ )
66
+
67
+ mock_staking = MagicMock()
68
+ mock_staking.get_staking_state.return_value = StakingState.EVICTED
69
+ mock_staking.prepare_unstake_tx.return_value = {"to": VALID_ADDR}
70
+ mock_staking.extract_events.return_value = [{"name": "ServiceUnstaked"}]
71
+
72
+ mock_wallet.sign_and_send_transaction.return_value = (True, {"status": 1})
73
+
74
+ result = sm.unstake(mock_staking)
75
+ assert result is True
76
+ # Verify staking_contract_address is cleared
77
+ assert sm.service.staking_contract_address is None
78
+
79
+
80
+ def test_sm_unstake_evicted_skips_duration_check(mock_wallet):
81
+ """Cover that EVICTED services skip the min_staking_duration check."""
82
+ with (
83
+ patch("iwa.core.models.Config"),
84
+ patch("iwa.plugins.olas.service_manager.base.ContractCache") as mock_cache,
85
+ patch("iwa.plugins.olas.service_manager.staking.ContractCache", mock_cache),
86
+ ):
87
+ mock_cache.return_value.get_contract.side_effect = lambda cls, *a, **k: cls(*a, **k)
88
+ sm = ServiceManager(mock_wallet)
89
+ sm.service = Service(
90
+ service_name="t", chain_name="gnosis", service_id=1, multisig_address=VALID_ADDR
91
+ )
92
+
93
+ mock_staking = MagicMock()
94
+ mock_staking.get_staking_state.return_value = StakingState.EVICTED
95
+ mock_staking.prepare_unstake_tx.return_value = {"to": VALID_ADDR}
96
+ mock_staking.extract_events.return_value = [{"name": "ServiceUnstaked"}]
97
+ # get_service_info should NOT be called for EVICTED services
98
+ mock_staking.get_service_info.side_effect = AssertionError(
99
+ "get_service_info should not be called for EVICTED"
100
+ )
101
+
102
+ mock_wallet.sign_and_send_transaction.return_value = (True, {"status": 1})
103
+
104
+ result = sm.unstake(mock_staking)
105
+ assert result is True
106
+
107
+
54
108
  def test_sm_unstake_tx_fails(mock_wallet):
55
109
  """Cover unstake transaction failure (lines 766-768)."""
56
110
  with (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iwa
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: A secure, modular, and plugin-based framework for crypto agents and ops
5
5
  Requires-Python: <4.0,>=3.12
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes