iwa 0.0.2__tar.gz → 0.0.11__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 (214) hide show
  1. {iwa-0.0.2/src/iwa.egg-info → iwa-0.0.11}/PKG-INFO +1 -1
  2. {iwa-0.0.2 → iwa-0.0.11}/pyproject.toml +2 -2
  3. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/chain/interface.py +51 -30
  4. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/chain/models.py +9 -15
  5. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/contracts/contract.py +8 -2
  6. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/pricing.py +10 -8
  7. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/safe.py +13 -8
  8. iwa-0.0.11/src/iwa/core/services/transaction.py +376 -0
  9. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/utils.py +22 -0
  10. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/wallet.py +2 -1
  11. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/safe.py +4 -3
  12. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/tests/test_safe.py +9 -7
  13. iwa-0.0.11/src/iwa/plugins/olas/contracts/abis/service_registry_token_utility.json +926 -0
  14. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/service.py +54 -4
  15. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/staking.py +2 -3
  16. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/plugin.py +14 -7
  17. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/lifecycle.py +382 -85
  18. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/mech.py +1 -1
  19. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/staking.py +229 -82
  20. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_contracts.py +6 -2
  21. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_plugin.py +6 -1
  22. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_plugin_full.py +12 -7
  23. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_lifecycle.py +1 -4
  24. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager.py +59 -89
  25. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_errors.py +1 -2
  26. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_flows.py +5 -15
  27. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_validation.py +16 -15
  28. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tools/list_contracts.py +2 -2
  29. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/dependencies.py +1 -3
  30. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/accounts.py +1 -2
  31. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/olas/admin.py +1 -3
  32. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/olas/funding.py +1 -3
  33. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/olas/general.py +1 -3
  34. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/olas/services.py +53 -21
  35. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/olas/staking.py +27 -24
  36. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/swap.py +1 -2
  37. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/transactions.py +0 -2
  38. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/server.py +8 -6
  39. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/static/app.js +22 -0
  40. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/tests/test_web_endpoints.py +1 -1
  41. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/tests/test_web_olas.py +1 -1
  42. {iwa-0.0.2 → iwa-0.0.11/src/iwa.egg-info}/PKG-INFO +1 -1
  43. {iwa-0.0.2 → iwa-0.0.11}/src/iwa.egg-info/SOURCES.txt +2 -0
  44. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_chain.py +12 -7
  45. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_chain_interface_coverage.py +3 -2
  46. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_contract.py +165 -0
  47. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_keys.py +2 -1
  48. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_legacy_wallet.py +11 -0
  49. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_pricing.py +32 -15
  50. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_safe_coverage.py +3 -3
  51. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_safe_service.py +3 -6
  52. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_service_transaction.py +8 -3
  53. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_staking_router.py +6 -3
  54. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_transaction_service.py +4 -0
  55. iwa-0.0.11/src/tools/create_and_stake_service.py +103 -0
  56. {iwa-0.0.2 → iwa-0.0.11}/src/tools/verify_drain.py +1 -4
  57. iwa-0.0.2/src/iwa/core/services/transaction.py +0 -172
  58. {iwa-0.0.2 → iwa-0.0.11}/LICENSE +0 -0
  59. {iwa-0.0.2 → iwa-0.0.11}/README.md +0 -0
  60. {iwa-0.0.2 → iwa-0.0.11}/setup.cfg +0 -0
  61. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/__init__.py +0 -0
  62. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/__main__.py +0 -0
  63. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/__init__.py +0 -0
  64. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/chain/__init__.py +0 -0
  65. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/chain/errors.py +0 -0
  66. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/chain/manager.py +0 -0
  67. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/chain/rate_limiter.py +0 -0
  68. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/cli.py +0 -0
  69. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/constants.py +0 -0
  70. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/contracts/__init__.py +0 -0
  71. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/contracts/abis/erc20.json +0 -0
  72. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/contracts/abis/multisend.json +0 -0
  73. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/contracts/abis/multisend_call_only.json +0 -0
  74. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/contracts/erc20.py +0 -0
  75. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/contracts/multisend.py +0 -0
  76. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/db.py +0 -0
  77. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/ipfs.py +0 -0
  78. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/keys.py +0 -0
  79. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/mnemonic.py +0 -0
  80. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/models.py +0 -0
  81. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/monitor.py +0 -0
  82. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/plugins.py +0 -0
  83. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/secrets.py +0 -0
  84. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/__init__.py +0 -0
  85. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/account.py +0 -0
  86. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/balance.py +0 -0
  87. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/plugin.py +0 -0
  88. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/transfer/__init__.py +0 -0
  89. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/transfer/base.py +0 -0
  90. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/transfer/erc20.py +0 -0
  91. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/transfer/multisend.py +0 -0
  92. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/transfer/native.py +0 -0
  93. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/services/transfer/swap.py +0 -0
  94. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/tables.py +0 -0
  95. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/test.py +0 -0
  96. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/tests/test_wallet.py +0 -0
  97. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/types.py +0 -0
  98. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/core/ui.py +0 -0
  99. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/__init__.py +0 -0
  100. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/__init__.py +0 -0
  101. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow/__init__.py +0 -0
  102. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow/quotes.py +0 -0
  103. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow/swap.py +0 -0
  104. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow/types.py +0 -0
  105. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/cow_utils.py +0 -0
  106. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/plugin.py +0 -0
  107. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/gnosis/tests/test_cow.py +0 -0
  108. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/__init__.py +0 -0
  109. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/constants.py +0 -0
  110. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/activity_checker.json +0 -0
  111. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/mech.json +0 -0
  112. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/mech_marketplace.json +0 -0
  113. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/mech_new.json +0 -0
  114. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/service_manager.json +0 -0
  115. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/service_registry.json +0 -0
  116. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/staking.json +0 -0
  117. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/abis/staking_token.json +0 -0
  118. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/activity_checker.py +0 -0
  119. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/base.py +0 -0
  120. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/mech.py +0 -0
  121. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/contracts/mech_marketplace.py +0 -0
  122. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/importer.py +0 -0
  123. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/mech_reference.py +0 -0
  124. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/models.py +0 -0
  125. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/scripts/test_full_mech_flow.py +0 -0
  126. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/scripts/test_simple_lifecycle.py +0 -0
  127. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/__init__.py +0 -0
  128. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/base.py +0 -0
  129. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/service_manager/drain.py +0 -0
  130. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/conftest.py +0 -0
  131. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_importer.py +0 -0
  132. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_importer_error_handling.py +0 -0
  133. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_mech_contracts.py +0 -0
  134. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_integration.py +0 -0
  135. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_models.py +0 -0
  136. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_view.py +0 -0
  137. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_view_actions.py +0 -0
  138. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_olas_view_modals.py +0 -0
  139. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_mech.py +0 -0
  140. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_manager_rewards.py +0 -0
  141. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_service_staking.py +0 -0
  142. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_staking_integration.py +0 -0
  143. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tests/test_staking_validation.py +0 -0
  144. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tui/__init__.py +0 -0
  145. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/plugins/olas/tui/olas_view.py +0 -0
  146. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tools/__init__.py +0 -0
  147. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tools/check_profile.py +0 -0
  148. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tools/release.py +0 -0
  149. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tools/reset_env.py +0 -0
  150. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tools/reset_tenderly.py +0 -0
  151. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tools/restore_backup.py +0 -0
  152. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tools/wallet_check.py +0 -0
  153. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/__init__.py +0 -0
  154. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/app.py +0 -0
  155. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/modals/__init__.py +0 -0
  156. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/modals/base.py +0 -0
  157. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/rpc.py +0 -0
  158. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/screens/__init__.py +0 -0
  159. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/screens/wallets.py +0 -0
  160. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/tests/test_app.py +0 -0
  161. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/tests/test_rpc.py +0 -0
  162. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/tests/test_wallets_refactor.py +0 -0
  163. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/tests/test_widgets.py +0 -0
  164. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/widgets/__init__.py +0 -0
  165. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/widgets/base.py +0 -0
  166. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/tui/workers.py +0 -0
  167. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/models.py +0 -0
  168. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/olas/__init__.py +0 -0
  169. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/routers/state.py +0 -0
  170. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/static/index.html +0 -0
  171. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/static/style.css +0 -0
  172. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/tests/test_web_swap.py +0 -0
  173. {iwa-0.0.2 → iwa-0.0.11}/src/iwa/web/tests/test_web_swap_coverage.py +0 -0
  174. {iwa-0.0.2 → iwa-0.0.11}/src/iwa.egg-info/dependency_links.txt +0 -0
  175. {iwa-0.0.2 → iwa-0.0.11}/src/iwa.egg-info/entry_points.txt +0 -0
  176. {iwa-0.0.2 → iwa-0.0.11}/src/iwa.egg-info/requires.txt +0 -0
  177. {iwa-0.0.2 → iwa-0.0.11}/src/iwa.egg-info/top_level.txt +0 -0
  178. {iwa-0.0.2 → iwa-0.0.11}/src/tests/legacy_cow.py +0 -0
  179. {iwa-0.0.2 → iwa-0.0.11}/src/tests/legacy_safe.py +0 -0
  180. {iwa-0.0.2 → iwa-0.0.11}/src/tests/legacy_transaction_retry_logic.py +0 -0
  181. {iwa-0.0.2 → iwa-0.0.11}/src/tests/legacy_tui.py +0 -0
  182. {iwa-0.0.2 → iwa-0.0.11}/src/tests/legacy_wallets_screen.py +0 -0
  183. {iwa-0.0.2 → iwa-0.0.11}/src/tests/legacy_web.py +0 -0
  184. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_account_service.py +0 -0
  185. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_balance_service.py +0 -0
  186. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_chain_interface.py +0 -0
  187. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_cli.py +0 -0
  188. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_db.py +0 -0
  189. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_drain_coverage.py +0 -0
  190. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_erc20.py +0 -0
  191. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_gnosis_plugin.py +0 -0
  192. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_main.py +0 -0
  193. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_migration.py +0 -0
  194. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_mnemonic.py +0 -0
  195. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_modals.py +0 -0
  196. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_models.py +0 -0
  197. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_monitor.py +0 -0
  198. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_multisend.py +0 -0
  199. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_plugin_service.py +0 -0
  200. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_rate_limiter.py +0 -0
  201. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_reset_tenderly.py +0 -0
  202. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_rpc_view.py +0 -0
  203. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_service_manager_integration.py +0 -0
  204. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_service_manager_structure.py +0 -0
  205. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_staking_simple.py +0 -0
  206. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_tables.py +0 -0
  207. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_transfer_multisend.py +0 -0
  208. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_transfer_native.py +0 -0
  209. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_transfer_security.py +0 -0
  210. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_transfer_structure.py +0 -0
  211. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_transfer_swap_unit.py +0 -0
  212. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_ui_coverage.py +0 -0
  213. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_utils.py +0 -0
  214. {iwa-0.0.2 → iwa-0.0.11}/src/tests/test_workers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iwa
3
- Version: 0.0.2
3
+ Version: 0.0.11
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.0.2"
3
+ version = "0.0.11"
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"
@@ -71,7 +71,7 @@ where = ["src"]
71
71
 
72
72
  [tool.ruff]
73
73
  line-length = 100
74
- target-version = "0.0.2"
74
+ target-version = "0.0.11"
75
75
  fix = true
76
76
 
77
77
  [tool.ruff.lint]
@@ -1,5 +1,6 @@
1
1
  """ChainInterface class for blockchain interactions."""
2
2
 
3
+ import threading
3
4
  import time
4
5
  from typing import Callable, Dict, Optional, Tuple, TypeVar, Union
5
6
 
@@ -45,6 +46,7 @@ class ChainInterface:
45
46
  )
46
47
 
47
48
  self._initial_block = 0
49
+ self._rotation_lock = threading.Lock()
48
50
  self._init_web3()
49
51
 
50
52
  @property
@@ -154,18 +156,6 @@ class ChainInterface:
154
156
  print("╚══════════════════════════════════════════════════╝")
155
157
  print("")
156
158
 
157
- def _init_web3(self):
158
- """Initialize Web3 with current RPC."""
159
- rpc_url = self.chain.rpcs[self._current_rpc_index] if self.chain.rpcs else ""
160
- raw_web3 = Web3(Web3.HTTPProvider(rpc_url, request_kwargs={"timeout": DEFAULT_RPC_TIMEOUT}))
161
-
162
- # Use duck typing to check if current web3 is a RateLimitedWeb3 wrapper
163
- # (isinstance check fails when RateLimitedWeb3 is mocked in tests)
164
- if hasattr(self, "web3") and hasattr(self.web3, "set_backend"):
165
- self.web3.set_backend(raw_web3)
166
- else:
167
- self.web3 = RateLimitedWeb3(raw_web3, self._rate_limiter, self)
168
-
169
159
  def _is_rate_limit_error(self, error: Exception) -> bool:
170
160
  """Check if error is a rate limit (429) error."""
171
161
  err_text = str(error).lower()
@@ -269,19 +259,36 @@ class ChainInterface:
269
259
 
270
260
  def rotate_rpc(self) -> bool:
271
261
  """Rotate to the next available RPC."""
272
- if not self.chain.rpcs or len(self.chain.rpcs) <= 1:
273
- return False
262
+ with self._rotation_lock:
263
+ if not self.chain.rpcs or len(self.chain.rpcs) <= 1:
264
+ return False
265
+
266
+ # Simple Round Robin rotation
267
+ self._current_rpc_index = (self._current_rpc_index + 1) % len(self.chain.rpcs)
268
+ # Internal call to _init_web3 already expects to be under lock if called from here,
269
+ # but _init_web3 itself doesn't have a lock. Let's make it consistent.
270
+ self._init_web3_under_lock()
271
+
272
+ logger.info(
273
+ f"Rotated RPC for {self.chain.name} to index {self._current_rpc_index}: {self.chain.rpcs[self._current_rpc_index]}"
274
+ )
275
+ return True
274
276
 
275
- # Simple Round Robin rotation
276
- # We don't check health here because the health check itself might consume rate limits
277
- # or fail flakily. Better to just switch and try the operation.
278
- self._current_rpc_index = (self._current_rpc_index + 1) % len(self.chain.rpcs)
279
- self._init_web3()
277
+ def _init_web3(self):
278
+ """Initialize Web3 with current RPC (thread-safe)."""
279
+ with self._rotation_lock:
280
+ self._init_web3_under_lock()
280
281
 
281
- logger.info(
282
- f"Rotated RPC for {self.chain.name} to index {self._current_rpc_index}: {self.chain.rpcs[self._current_rpc_index]}"
283
- )
284
- return True
282
+ def _init_web3_under_lock(self):
283
+ """Internal non-thread-safe web3 initialization."""
284
+ rpc_url = self.chain.rpcs[self._current_rpc_index] if self.chain.rpcs else ""
285
+ raw_web3 = Web3(Web3.HTTPProvider(rpc_url, request_kwargs={"timeout": DEFAULT_RPC_TIMEOUT}))
286
+
287
+ # Use duck typing to check if current web3 is a RateLimitedWeb3 wrapper
288
+ if hasattr(self, "web3") and hasattr(self.web3, "set_backend"):
289
+ self.web3.set_backend(raw_web3)
290
+ else:
291
+ self.web3 = RateLimitedWeb3(raw_web3, self._rate_limiter, self)
285
292
 
286
293
  def check_rpc_health(self) -> bool:
287
294
  """Check if the current RPC is healthy."""
@@ -358,15 +365,29 @@ class ChainInterface:
358
365
  except Exception:
359
366
  return address[:6] + "..." + address[-4:]
360
367
 
361
- def get_token_decimals(self, address: EthereumAddress) -> int:
362
- """Get token decimals for an address."""
363
- try:
364
- from iwa.core.contracts.erc20 import ERC20Contract
368
+ def get_token_decimals(self, address: EthereumAddress, fallback_to_18: bool = True) -> Optional[int]:
369
+ """Get token decimals for an address.
365
370
 
366
- erc20 = ERC20Contract(address, self.chain.name.lower())
367
- return erc20.decimals if erc20.decimals is not None else 18
371
+ Args:
372
+ address: Token contract address.
373
+ fallback_to_18: If True, return 18 on error (default).
374
+ If False, return None on error (useful for detecting NFTs).
375
+
376
+ Returns:
377
+ Decimals as int, or None if error and fallback_to_18 is False.
378
+
379
+ """
380
+ try:
381
+ # Call decimals() directly without with_retry to avoid error logging
382
+ contract = self.web3.eth.contract(
383
+ address=self.web3.to_checksum_address(address),
384
+ abi=[{"constant": True, "inputs": [], "name": "decimals", "outputs": [{"type": "uint8"}], "type": "function"}]
385
+ )
386
+ return contract.functions.decimals().call()
368
387
  except Exception:
369
- return 18
388
+ if fallback_to_18:
389
+ return 18
390
+ return None
370
391
 
371
392
  def get_native_balance_wei(self, address: EthereumAddress):
372
393
  """Get the native balance in wei"""
@@ -78,13 +78,11 @@ class Gnosis(SupportedChain):
78
78
  if not self.rpcs and secrets.gnosis_rpc:
79
79
  self.rpcs = secrets.gnosis_rpc.get_secret_value().split(",")
80
80
 
81
- # Defensive: ensure no comma-separated strings in list
81
+ # Defensive: ensure no comma-separated strings and NO quotes in list
82
82
  new_rpcs = []
83
83
  for rpc in self.rpcs:
84
- if "," in rpc:
85
- new_rpcs.extend([r.strip() for r in rpc.split(",") if r.strip()])
86
- else:
87
- new_rpcs.append(rpc)
84
+ parts = [r.strip().strip("'\"") for r in rpc.split(",") if r.strip()]
85
+ new_rpcs.extend(parts)
88
86
  self.rpcs = new_rpcs
89
87
 
90
88
 
@@ -107,13 +105,11 @@ class Ethereum(SupportedChain):
107
105
  if not self.rpcs and secrets.ethereum_rpc:
108
106
  self.rpcs = secrets.ethereum_rpc.get_secret_value().split(",")
109
107
 
110
- # Defensive: ensure no comma-separated strings in list
108
+ # Defensive: ensure no comma-separated strings and NO quotes in list
111
109
  new_rpcs = []
112
110
  for rpc in self.rpcs:
113
- if "," in rpc:
114
- new_rpcs.extend([r.strip() for r in rpc.split(",") if r.strip()])
115
- else:
116
- new_rpcs.append(rpc)
111
+ parts = [r.strip().strip("'\"") for r in rpc.split(",") if r.strip()]
112
+ new_rpcs.extend(parts)
117
113
  self.rpcs = new_rpcs
118
114
 
119
115
 
@@ -136,13 +132,11 @@ class Base(SupportedChain):
136
132
  if not self.rpcs and secrets.base_rpc:
137
133
  self.rpcs = secrets.base_rpc.get_secret_value().split(",")
138
134
 
139
- # Defensive: ensure no comma-separated strings in list
135
+ # Defensive: ensure no comma-separated strings and NO quotes in list
140
136
  new_rpcs = []
141
137
  for rpc in self.rpcs:
142
- if "," in rpc:
143
- new_rpcs.extend([r.strip() for r in rpc.split(",") if r.strip()])
144
- else:
145
- new_rpcs.append(rpc)
138
+ parts = [r.strip().strip("'\"") for r in rpc.split(",") if r.strip()]
139
+ new_rpcs.extend(parts)
146
140
  self.rpcs = new_rpcs
147
141
 
148
142
 
@@ -190,10 +190,16 @@ class ContractInstance:
190
190
  Exception: If the call fails, with decoded error information.
191
191
 
192
192
  """
193
- method = getattr(self.contract.functions, method_name)
194
193
  try:
194
+
195
+ def do_call():
196
+ # Re-evaluate self.contract on each retry to get current provider
197
+ # This is critical for RPC rotation to work correctly
198
+ method = getattr(self.contract.functions, method_name)
199
+ return method(*args).call()
200
+
195
201
  return self.chain_interface.with_retry(
196
- lambda: method(*args).call(),
202
+ do_call,
197
203
  operation_name=f"call {method_name} on {self.name}",
198
204
  )
199
205
  except Exception as e:
@@ -9,17 +9,19 @@ from loguru import logger
9
9
 
10
10
  from iwa.core.secrets import secrets
11
11
 
12
+ # Global cache shared across all PriceService instances
13
+ _PRICE_CACHE: Dict[str, Dict] = {}
14
+ _CACHE_TTL = timedelta(minutes=30)
15
+
12
16
 
13
17
  class PriceService:
14
18
  """Service to fetch token prices from CoinGecko."""
15
19
 
16
20
  BASE_URL = "https://api.coingecko.com/api/v3"
17
21
 
18
- def __init__(self, cache_ttl_minutes: int = 5):
22
+ def __init__(self):
19
23
  """Initialize PriceService."""
20
24
  self.secrets = secrets
21
- self.cache: Dict[str, Dict] = {} # {id_currency: {"price": float, "timestamp": datetime}}
22
- self.cache_ttl = timedelta(minutes=cache_ttl_minutes)
23
25
  self.api_key = (
24
26
  self.secrets.coingecko_api_key.get_secret_value()
25
27
  if self.secrets.coingecko_api_key
@@ -39,15 +41,15 @@ class PriceService:
39
41
  """
40
42
  cache_key = f"{token_id}_{vs_currency}"
41
43
 
42
- # Check cache
43
- if cache_key in self.cache:
44
- entry = self.cache[cache_key]
45
- if datetime.now() - entry["timestamp"] < self.cache_ttl:
44
+ # Check global cache
45
+ if cache_key in _PRICE_CACHE:
46
+ entry = _PRICE_CACHE[cache_key]
47
+ if datetime.now() - entry["timestamp"] < _CACHE_TTL:
46
48
  return entry["price"]
47
49
 
48
50
  price = self._fetch_price_from_api(token_id, vs_currency)
49
51
  if price is not None:
50
- self.cache[cache_key] = {"price": price, "timestamp": datetime.now()}
52
+ _PRICE_CACHE[cache_key] = {"price": price, "timestamp": datetime.now()}
51
53
  return price
52
54
 
53
55
  def _fetch_price_from_api(self, token_id: str, vs_currency: str) -> Optional[float]:
@@ -11,7 +11,6 @@ from safe_eth.safe.safe_tx import SafeTx
11
11
  from iwa.core.constants import ZERO_ADDRESS
12
12
  from iwa.core.db import log_transaction
13
13
  from iwa.core.models import StoredSafeAccount
14
- from iwa.core.secrets import secrets
15
14
  from iwa.core.utils import (
16
15
  get_safe_master_copy_address,
17
16
  get_safe_proxy_factory_address,
@@ -99,8 +98,11 @@ class SafeService:
99
98
  return owner_addresses
100
99
 
101
100
  def _get_ethereum_client(self, chain_name: str) -> EthereumClient:
102
- rpc_secret = getattr(secrets, f"{chain_name}_rpc")
103
- return EthereumClient(rpc_secret.get_secret_value())
101
+ from iwa.core.chain import ChainInterfaces
102
+
103
+ # Use ChainInterface which has proper RPC rotation and parsing
104
+ chain_interface = ChainInterfaces().get(chain_name)
105
+ return EthereumClient(chain_interface.chain.rpc)
104
106
 
105
107
  def _deploy_safe_contract(
106
108
  self,
@@ -141,7 +143,7 @@ class SafeService:
141
143
  gas=5_000_000,
142
144
  gas_price=gas_price,
143
145
  )
144
- return tx_sent.contract_address, tx_sent.tx_hash.hex()
146
+ return tx_sent.contract_address, f"0x{tx_sent.tx_hash.hex()}"
145
147
 
146
148
  else:
147
149
  # Standard random salt via Safe.create
@@ -153,7 +155,7 @@ class SafeService:
153
155
  threshold=threshold,
154
156
  proxy_factory_address=proxy_factory_address,
155
157
  )
156
- return create_tx.contract_address, create_tx.tx_hash.hex()
158
+ return create_tx.contract_address, f"0x{create_tx.tx_hash.hex()}"
157
159
 
158
160
  def _log_safe_deployment(
159
161
  self,
@@ -248,8 +250,11 @@ class SafeService:
248
250
  continue
249
251
 
250
252
  for chain in account.chains:
251
- rpc_secret = getattr(secrets, f"{chain}_rpc")
252
- ethereum_client = EthereumClient(rpc_secret.get_secret_value())
253
+ from iwa.core.chain import ChainInterfaces
254
+
255
+ # Use ChainInterface which has proper RPC rotation and parsing
256
+ chain_interface = ChainInterfaces().get(chain)
257
+ ethereum_client = EthereumClient(chain_interface.chain.rpc)
253
258
 
254
259
  code = ethereum_client.w3.eth.get_code(account.address)
255
260
 
@@ -304,7 +309,7 @@ class SafeService:
304
309
  # Execute using the first signer
305
310
  safe_tx.execute(signer_keys[0])
306
311
 
307
- return safe_tx.tx_hash.hex()
312
+ return f"0x{safe_tx.tx_hash.hex()}"
308
313
  finally:
309
314
  # SECURITY: Overwrite keys with zeros before clearing (best effort)
310
315
  for i in range(len(signer_keys)):