iwa 0.0.16__tar.gz → 0.0.18__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.16/src/iwa.egg-info → iwa-0.0.18}/PKG-INFO +1 -1
  2. {iwa-0.0.16 → iwa-0.0.18}/pyproject.toml +2 -2
  3. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/chain/interface.py +1 -65
  4. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/pricing.py +1 -4
  5. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/transaction.py +72 -70
  6. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/transfer/erc20.py +8 -0
  7. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/transfer/native.py +47 -13
  8. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/mech.py +43 -25
  9. {iwa-0.0.16 → iwa-0.0.18/src/iwa.egg-info}/PKG-INFO +1 -1
  10. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_chain.py +2 -97
  11. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_legacy_wallet.py +6 -6
  12. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_service_transaction.py +23 -8
  13. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_staking_router.py +14 -6
  14. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_transaction_service.py +20 -6
  15. {iwa-0.0.16 → iwa-0.0.18}/LICENSE +0 -0
  16. {iwa-0.0.16 → iwa-0.0.18}/README.md +0 -0
  17. {iwa-0.0.16 → iwa-0.0.18}/setup.cfg +0 -0
  18. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/__init__.py +0 -0
  19. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/__main__.py +0 -0
  20. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/__init__.py +0 -0
  21. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/chain/__init__.py +0 -0
  22. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/chain/errors.py +0 -0
  23. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/chain/manager.py +0 -0
  24. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/chain/models.py +0 -0
  25. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/chain/rate_limiter.py +0 -0
  26. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/cli.py +0 -0
  27. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/constants.py +0 -0
  28. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/contracts/__init__.py +0 -0
  29. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/contracts/abis/erc20.json +0 -0
  30. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/contracts/abis/multisend.json +0 -0
  31. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/contracts/abis/multisend_call_only.json +0 -0
  32. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/contracts/contract.py +0 -0
  33. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/contracts/erc20.py +0 -0
  34. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/contracts/multisend.py +0 -0
  35. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/db.py +0 -0
  36. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/ipfs.py +0 -0
  37. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/keys.py +0 -0
  38. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/mnemonic.py +0 -0
  39. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/models.py +0 -0
  40. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/monitor.py +0 -0
  41. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/plugins.py +0 -0
  42. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/secrets.py +0 -0
  43. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/__init__.py +0 -0
  44. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/account.py +0 -0
  45. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/balance.py +0 -0
  46. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/plugin.py +0 -0
  47. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/safe.py +0 -0
  48. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/transfer/__init__.py +0 -0
  49. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/transfer/base.py +0 -0
  50. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/transfer/multisend.py +0 -0
  51. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/services/transfer/swap.py +0 -0
  52. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/tables.py +0 -0
  53. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/test.py +0 -0
  54. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/tests/test_wallet.py +0 -0
  55. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/types.py +0 -0
  56. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/ui.py +0 -0
  57. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/utils.py +0 -0
  58. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/core/wallet.py +0 -0
  59. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/__init__.py +0 -0
  60. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/__init__.py +0 -0
  61. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow/__init__.py +0 -0
  62. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow/quotes.py +0 -0
  63. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow/swap.py +0 -0
  64. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow/types.py +0 -0
  65. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/cow_utils.py +0 -0
  66. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/plugin.py +0 -0
  67. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/safe.py +0 -0
  68. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/tests/test_cow.py +0 -0
  69. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/gnosis/tests/test_safe.py +0 -0
  70. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/__init__.py +0 -0
  71. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/constants.py +0 -0
  72. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/activity_checker.json +0 -0
  73. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/mech.json +0 -0
  74. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/mech_marketplace.json +0 -0
  75. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/mech_new.json +0 -0
  76. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/service_manager.json +0 -0
  77. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/service_registry.json +0 -0
  78. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/service_registry_token_utility.json +0 -0
  79. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/staking.json +0 -0
  80. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/abis/staking_token.json +0 -0
  81. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/activity_checker.py +0 -0
  82. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/base.py +0 -0
  83. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/mech.py +0 -0
  84. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/mech_marketplace.py +0 -0
  85. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/service.py +0 -0
  86. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/contracts/staking.py +0 -0
  87. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/importer.py +0 -0
  88. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/mech_reference.py +0 -0
  89. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/models.py +0 -0
  90. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/plugin.py +0 -0
  91. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/scripts/test_full_mech_flow.py +0 -0
  92. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/scripts/test_simple_lifecycle.py +0 -0
  93. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/__init__.py +0 -0
  94. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/base.py +0 -0
  95. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/drain.py +0 -0
  96. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/lifecycle.py +0 -0
  97. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/service_manager/staking.py +0 -0
  98. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/conftest.py +0 -0
  99. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_importer.py +0 -0
  100. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_importer_error_handling.py +0 -0
  101. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_mech_contracts.py +0 -0
  102. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_contracts.py +0 -0
  103. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_integration.py +0 -0
  104. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_models.py +0 -0
  105. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_view.py +0 -0
  106. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_view_actions.py +0 -0
  107. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_olas_view_modals.py +0 -0
  108. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_plugin.py +0 -0
  109. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_plugin_full.py +0 -0
  110. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_lifecycle.py +0 -0
  111. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager.py +0 -0
  112. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_errors.py +0 -0
  113. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_flows.py +0 -0
  114. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_mech.py +0 -0
  115. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_rewards.py +0 -0
  116. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_manager_validation.py +0 -0
  117. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_service_staking.py +0 -0
  118. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_staking_integration.py +0 -0
  119. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tests/test_staking_validation.py +0 -0
  120. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tui/__init__.py +0 -0
  121. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/plugins/olas/tui/olas_view.py +0 -0
  122. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tools/__init__.py +0 -0
  123. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tools/check_profile.py +0 -0
  124. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tools/list_contracts.py +0 -0
  125. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tools/release.py +0 -0
  126. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tools/reset_env.py +0 -0
  127. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tools/reset_tenderly.py +0 -0
  128. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tools/restore_backup.py +0 -0
  129. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tools/wallet_check.py +0 -0
  130. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/__init__.py +0 -0
  131. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/app.py +0 -0
  132. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/modals/__init__.py +0 -0
  133. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/modals/base.py +0 -0
  134. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/rpc.py +0 -0
  135. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/screens/__init__.py +0 -0
  136. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/screens/wallets.py +0 -0
  137. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/tests/test_app.py +0 -0
  138. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/tests/test_rpc.py +0 -0
  139. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/tests/test_wallets_refactor.py +0 -0
  140. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/tests/test_widgets.py +0 -0
  141. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/widgets/__init__.py +0 -0
  142. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/widgets/base.py +0 -0
  143. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/tui/workers.py +0 -0
  144. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/dependencies.py +0 -0
  145. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/models.py +0 -0
  146. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/accounts.py +0 -0
  147. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/olas/__init__.py +0 -0
  148. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/olas/admin.py +0 -0
  149. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/olas/funding.py +0 -0
  150. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/olas/general.py +0 -0
  151. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/olas/services.py +0 -0
  152. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/olas/staking.py +0 -0
  153. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/state.py +0 -0
  154. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/swap.py +0 -0
  155. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/routers/transactions.py +0 -0
  156. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/server.py +0 -0
  157. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/static/app.js +0 -0
  158. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/static/index.html +0 -0
  159. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/static/style.css +0 -0
  160. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/tests/test_web_endpoints.py +0 -0
  161. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/tests/test_web_olas.py +0 -0
  162. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/tests/test_web_swap.py +0 -0
  163. {iwa-0.0.16 → iwa-0.0.18}/src/iwa/web/tests/test_web_swap_coverage.py +0 -0
  164. {iwa-0.0.16 → iwa-0.0.18}/src/iwa.egg-info/SOURCES.txt +0 -0
  165. {iwa-0.0.16 → iwa-0.0.18}/src/iwa.egg-info/dependency_links.txt +0 -0
  166. {iwa-0.0.16 → iwa-0.0.18}/src/iwa.egg-info/entry_points.txt +0 -0
  167. {iwa-0.0.16 → iwa-0.0.18}/src/iwa.egg-info/requires.txt +0 -0
  168. {iwa-0.0.16 → iwa-0.0.18}/src/iwa.egg-info/top_level.txt +0 -0
  169. {iwa-0.0.16 → iwa-0.0.18}/src/tests/legacy_cow.py +0 -0
  170. {iwa-0.0.16 → iwa-0.0.18}/src/tests/legacy_safe.py +0 -0
  171. {iwa-0.0.16 → iwa-0.0.18}/src/tests/legacy_transaction_retry_logic.py +0 -0
  172. {iwa-0.0.16 → iwa-0.0.18}/src/tests/legacy_tui.py +0 -0
  173. {iwa-0.0.16 → iwa-0.0.18}/src/tests/legacy_wallets_screen.py +0 -0
  174. {iwa-0.0.16 → iwa-0.0.18}/src/tests/legacy_web.py +0 -0
  175. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_account_service.py +0 -0
  176. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_balance_service.py +0 -0
  177. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_chain_interface.py +0 -0
  178. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_chain_interface_coverage.py +0 -0
  179. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_cli.py +0 -0
  180. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_contract.py +0 -0
  181. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_db.py +0 -0
  182. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_drain_coverage.py +0 -0
  183. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_erc20.py +0 -0
  184. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_gnosis_plugin.py +0 -0
  185. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_keys.py +0 -0
  186. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_main.py +0 -0
  187. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_migration.py +0 -0
  188. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_mnemonic.py +0 -0
  189. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_modals.py +0 -0
  190. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_models.py +0 -0
  191. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_monitor.py +0 -0
  192. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_multisend.py +0 -0
  193. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_plugin_service.py +0 -0
  194. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_pricing.py +0 -0
  195. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_rate_limiter.py +0 -0
  196. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_reset_tenderly.py +0 -0
  197. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_rpc_rotation.py +0 -0
  198. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_rpc_view.py +0 -0
  199. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_safe_coverage.py +0 -0
  200. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_safe_service.py +0 -0
  201. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_service_manager_integration.py +0 -0
  202. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_service_manager_structure.py +0 -0
  203. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_staking_simple.py +0 -0
  204. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_tables.py +0 -0
  205. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_transfer_multisend.py +0 -0
  206. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_transfer_native.py +0 -0
  207. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_transfer_security.py +0 -0
  208. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_transfer_structure.py +0 -0
  209. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_transfer_swap_unit.py +0 -0
  210. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_ui_coverage.py +0 -0
  211. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_utils.py +0 -0
  212. {iwa-0.0.16 → iwa-0.0.18}/src/tests/test_workers.py +0 -0
  213. {iwa-0.0.16 → iwa-0.0.18}/src/tools/create_and_stake_service.py +0 -0
  214. {iwa-0.0.16 → iwa-0.0.18}/src/tools/verify_drain.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iwa
3
- Version: 0.0.16
3
+ Version: 0.0.18
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.16"
3
+ version = "0.0.18"
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.16"
74
+ target-version = "0.0.18"
75
75
  fix = true
76
76
 
77
77
  [tool.ruff.lint]
@@ -2,9 +2,8 @@
2
2
 
3
3
  import threading
4
4
  import time
5
- from typing import Callable, Dict, Optional, Tuple, TypeVar, Union
5
+ from typing import Callable, Dict, Optional, TypeVar, Union
6
6
 
7
- from eth_account.datastructures import SignedTransaction
8
7
  from web3 import Web3
9
8
 
10
9
  from iwa.core.chain.errors import TenderlyQuotaExceededError, sanitize_rpc_url
@@ -449,69 +448,6 @@ class ChainInterface:
449
448
 
450
449
  return False
451
450
 
452
- def send_native_transfer(
453
- self,
454
- from_address: EthereumAddress,
455
- to_address: EthereumAddress,
456
- value_wei: int,
457
- sign_callback: Callable[[dict], SignedTransaction],
458
- ) -> Tuple[bool, Optional[str]]:
459
- """Send native currency transaction with retry logic."""
460
-
461
- def _do_transfer() -> Tuple[bool, Optional[str]]:
462
- tx = {
463
- "from": from_address,
464
- "to": to_address,
465
- "value": value_wei,
466
- "nonce": self.web3.eth.get_transaction_count(from_address),
467
- "chainId": self.chain.chain_id,
468
- }
469
-
470
- balance_wei = self.get_native_balance_wei(from_address)
471
- gas_price = self.web3.eth.gas_price
472
- gas_estimate = self.web3.eth.estimate_gas(tx)
473
- required_wei = value_wei + (gas_estimate * gas_price)
474
-
475
- if balance_wei < required_wei:
476
- logger.error(
477
- f"Insufficient balance. "
478
- f"Balance: {self.web3.from_wei(balance_wei, 'ether'):.4f} "
479
- f"{self.chain.native_currency}, "
480
- f"Required: {self.web3.from_wei(required_wei, 'ether'):.4f} "
481
- f"{self.chain.native_currency}"
482
- )
483
- return False, None
484
-
485
- tx["gas"] = gas_estimate
486
- tx["gasPrice"] = gas_price
487
-
488
- signed_tx = sign_callback(tx)
489
- txn_hash = self.web3.eth.send_raw_transaction(signed_tx.raw_transaction)
490
- receipt = self.web3.eth.wait_for_transaction_receipt(txn_hash)
491
-
492
- status = getattr(receipt, "status", None)
493
- if status is None and isinstance(receipt, dict):
494
- status = receipt.get("status")
495
-
496
- if receipt and status == 1:
497
- self.wait_for_no_pending_tx(from_address)
498
- logger.info(f"Transaction sent successfully. Tx Hash: {txn_hash.hex()}")
499
- # Check Tenderly block limit after each successful transaction
500
- self.check_block_limit()
501
- return True, receipt["transactionHash"].hex()
502
-
503
- logger.error("Transaction failed (status != 1)")
504
- return False, None
505
-
506
- try:
507
- return self.with_retry(
508
- _do_transfer,
509
- operation_name=f"native_transfer to {str(to_address)[:10]}...",
510
- )
511
- except Exception as e:
512
- logger.exception(f"Native transfer failed: {e}")
513
- return False, None
514
-
515
451
  def get_token_address(self, token_name: str) -> Optional[EthereumAddress]:
516
452
  """Get token address by name"""
517
453
  return self.chain.get_token_address(token_name)
@@ -19,7 +19,6 @@ class PriceService:
19
19
  """Service to fetch token prices from CoinGecko."""
20
20
 
21
21
  BASE_URL = "https://api.coingecko.com/api/v3"
22
- DEMO_URL = "https://demo-api.coingecko.com/api/v3"
23
22
 
24
23
  def __init__(self):
25
24
  """Initialize PriceService."""
@@ -60,9 +59,7 @@ class PriceService:
60
59
  max_retries = 2
61
60
  for attempt in range(max_retries + 1):
62
61
  try:
63
- # Use demo URL if API key is present, otherwise standard URL
64
- # NOTE: Demo URL is significantly more reliable for demo keys
65
- base_url = self.DEMO_URL if self.api_key else self.BASE_URL
62
+ base_url = self.BASE_URL
66
63
  url = f"{base_url}/simple/price"
67
64
  params = {"ids": token_id, "vs_currencies": vs_currency}
68
65
  headers = {}
@@ -1,6 +1,5 @@
1
1
  """Transaction service module."""
2
2
 
3
- import time
4
3
  from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
5
4
 
6
5
  from loguru import logger
@@ -31,32 +30,37 @@ class TransferLogger:
31
30
  self.account_service = account_service
32
31
  self.chain_interface = chain_interface
33
32
 
34
- def log_transfers(self, receipt: Dict, tx: Dict) -> None:
33
+ def log_transfers(self, receipt: Dict) -> None:
35
34
  """Log all transfers (ERC20 and native) from a transaction receipt.
36
35
 
37
36
  Args:
38
37
  receipt: Transaction receipt containing logs.
39
- tx: Original transaction dict.
40
38
 
41
39
  """
42
- # Log native value transfer if present
43
- native_value = tx.get("value", 0)
44
- if native_value and int(native_value) > 0:
45
- self._log_native_transfer(tx, native_value)
40
+ # Get the original transaction to check for native value transfer
41
+ tx_hash = receipt.get("transactionHash") or getattr(receipt, "transactionHash", None)
42
+ if tx_hash:
43
+ try:
44
+ tx = self.chain_interface.web3.eth.get_transaction(tx_hash)
45
+ native_value = getattr(tx, "value", 0) or tx.get("value", 0) if isinstance(tx, dict) else getattr(tx, "value", 0)
46
+ if native_value and int(native_value) > 0:
47
+ from_addr = getattr(tx, "from", "") if hasattr(tx, "from") else tx.get("from", "")
48
+ # Handle AttributeDict's special 'from' attribute
49
+ if not from_addr and hasattr(tx, "__getitem__"):
50
+ from_addr = tx["from"]
51
+ to_addr = getattr(tx, "to", "") or (tx.get("to", "") if isinstance(tx, dict) else "")
52
+ self._log_native_transfer(from_addr, to_addr, native_value)
53
+ except Exception as e:
54
+ logger.debug(f"Could not get tx for native transfer logging: {e}")
46
55
 
47
56
  # Log ERC20 transfers from event logs
48
- logs = receipt.get("logs", [])
49
- if hasattr(receipt, "logs"):
50
- logs = receipt.logs
57
+ logs = receipt.get("logs", []) if isinstance(receipt, dict) else getattr(receipt, "logs", [])
51
58
 
52
59
  for log in logs:
53
60
  self._process_log(log)
54
61
 
55
- def _log_native_transfer(self, tx: Dict, value_wei: int) -> None:
62
+ def _log_native_transfer(self, from_addr: str, to_addr: str, value_wei: int) -> None:
56
63
  """Log a native currency transfer."""
57
- from_addr = tx.get("from", "")
58
- to_addr = tx.get("to", "")
59
-
60
64
  from_label = self._resolve_address_label(from_addr)
61
65
  to_label = self._resolve_address_label(to_addr)
62
66
 
@@ -213,53 +217,67 @@ class TransactionService:
213
217
  chain_name: str = "gnosis",
214
218
  tags: Optional[List[str]] = None,
215
219
  ) -> Tuple[bool, Dict]:
216
- """Sign and send a transaction with retry logic for gas."""
220
+ """Sign and send a transaction using unified retry mechanism.
221
+
222
+ Uses ChainInterface.with_retry() for consistent RPC rotation and retry logic.
223
+ Gas errors are handled by increasing gas and retrying within the same mechanism.
224
+ """
217
225
  chain_interface = ChainInterfaces().get(chain_name)
218
226
  tx = dict(transaction)
219
- max_retries = 10
220
227
 
221
228
  if not self._prepare_transaction(tx, signer_address_or_tag, chain_interface):
222
229
  return False, {}
223
230
 
224
- for attempt in range(1, max_retries + 1):
231
+ # Mutable state for retry attempts
232
+ state = {"gas_retries": 0, "max_gas_retries": 5}
233
+
234
+ def _do_sign_send_wait() -> Tuple[bool, Dict, bytes]:
235
+ """Inner operation wrapped by with_retry."""
225
236
  try:
226
237
  signed_txn = self.key_storage.sign_transaction(tx, signer_address_or_tag)
227
- txn_hash = chain_interface.web3.eth.send_raw_transaction(signed_txn.raw_transaction)
228
-
229
- # Use chain_interface.with_retry for waiting for receipt to handle timeouts/RPC errors
230
- def wait_for_receipt(tx_h=txn_hash):
231
- return chain_interface.web3.eth.wait_for_transaction_receipt(tx_h)
232
-
233
- receipt = chain_interface.with_retry(
234
- wait_for_receipt, operation_name="wait_for_receipt"
238
+ txn_hash = chain_interface.web3.eth.send_raw_transaction(
239
+ signed_txn.raw_transaction
235
240
  )
241
+ receipt = chain_interface.web3.eth.wait_for_transaction_receipt(txn_hash)
236
242
 
237
- if receipt and getattr(receipt, "status", None) == 1:
238
- signer_account = self.account_service.resolve_account(signer_address_or_tag)
239
- chain_interface.wait_for_no_pending_tx(signer_account.address)
240
- logger.info(f"Transaction sent successfully. Tx Hash: {txn_hash.hex()}")
241
-
242
- self._log_successful_transaction(
243
- receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
244
- )
245
- return True, receipt
243
+ status = getattr(receipt, "status", None)
244
+ if status is None and isinstance(receipt, dict):
245
+ status = receipt.get("status")
246
246
 
247
- # Transaction reverted
247
+ if receipt and status == 1:
248
+ return True, receipt, txn_hash
249
+ # Transaction mined but reverted - don't retry
248
250
  logger.error("Transaction failed (status 0).")
249
- return False, {}
251
+ raise ValueError("Transaction reverted")
250
252
 
251
253
  except web3_exceptions.Web3RPCError as e:
252
- if self._handle_gas_error(e, tx, attempt, max_retries):
253
- continue
254
- return False, {}
254
+ # Handle gas errors by increasing gas and re-raising
255
+ self._handle_gas_retry(e, tx, state)
256
+ raise # Re-raise to trigger with_retry's retry mechanism
255
257
 
256
- except Exception as e:
257
- # Attempt RPC rotation
258
- if self._handle_generic_error(e, chain_interface, attempt, max_retries):
259
- continue
258
+ try:
259
+ success, receipt, txn_hash = chain_interface.with_retry(
260
+ _do_sign_send_wait,
261
+ operation_name=f"sign_and_send to {tx.get('to', 'unknown')[:10]}...",
262
+ )
263
+ if success:
264
+ signer_account = self.account_service.resolve_account(signer_address_or_tag)
265
+ chain_interface.wait_for_no_pending_tx(signer_account.address)
266
+ logger.info(f"Transaction sent successfully. Tx Hash: {txn_hash.hex()}")
267
+ self._log_successful_transaction(
268
+ receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
269
+ )
270
+ return True, receipt
271
+ return False, {}
272
+ except ValueError as e:
273
+ # Transaction reverted - already logged
274
+ if "reverted" in str(e).lower():
260
275
  return False, {}
261
-
262
- return False, {}
276
+ logger.exception(f"Transaction failed: {e}")
277
+ return False, {}
278
+ except Exception as e:
279
+ logger.exception(f"Transaction failed after retries: {e}")
280
+ return False, {}
263
281
 
264
282
  def _prepare_transaction(self, tx: dict, signer_tag: str, chain_interface) -> bool:
265
283
  """Ensure nonce and chainId are set."""
@@ -274,32 +292,16 @@ class TransactionService:
274
292
  tx["chainId"] = chain_interface.chain.chain_id
275
293
  return True
276
294
 
277
- def _handle_gas_error(self, e, tx, attempt, max_retries) -> bool:
278
- err_text = str(e)
279
- if self._is_gas_too_low_error(err_text) and attempt < max_retries:
280
- logger.warning(
281
- f"Gas too low error detected. Retrying with increased gas (Attempt {attempt}/{max_retries})..."
282
- )
295
+ def _handle_gas_retry(self, e: Exception, tx: dict, state: dict) -> None:
296
+ """Increase gas if error is gas-related and retries remaining."""
297
+ if self._is_gas_too_low_error(str(e)) and state["gas_retries"] < state["max_gas_retries"]:
283
298
  current_gas = int(tx.get("gas", 30_000))
284
299
  tx["gas"] = int(current_gas * 1.5)
285
- tx["gas"] = int(current_gas * 1.5)
286
- # Exponential backoff for gas errors
287
- time.sleep(min(2**attempt, 30))
288
- return True
289
- logger.exception(f"Error sending transaction: {e}")
290
- return False
291
-
292
- def _handle_generic_error(self, e, chain_interface, attempt, max_retries) -> bool:
293
- if attempt < max_retries:
294
- logger.warning(f"Error encountered: {e}. Attempting to rotate RPC...")
295
-
296
- if chain_interface.rotate_rpc():
297
- logger.info("Retrying with new RPC...")
298
- # Exponential backoff
299
- time.sleep(min(2**attempt, 30))
300
- return True
301
- logger.exception(f"Unexpected error sending transaction: {e}")
302
- return False
300
+ state["gas_retries"] += 1
301
+ logger.warning(
302
+ f"Gas too low, increasing to {tx['gas']} "
303
+ f"(attempt {state['gas_retries']}/{state['max_gas_retries']})"
304
+ )
303
305
 
304
306
  def _log_successful_transaction(
305
307
  self, receipt, tx, signer_account, chain_name, txn_hash, tags, chain_interface
@@ -323,7 +325,7 @@ class TransactionService:
323
325
 
324
326
  # Log transfer events (ERC20 and native value)
325
327
  transfer_logger = TransferLogger(self.account_service, chain_interface)
326
- transfer_logger.log_transfers(receipt, tx)
328
+ transfer_logger.log_transfers(receipt)
327
329
 
328
330
  except Exception as log_err:
329
331
  logger.warning(f"Failed to log transaction: {log_err}")
@@ -64,6 +64,14 @@ class ERC20TransferMixin:
64
64
  value_eur=v_eur,
65
65
  tags=["erc20-transfer", "safe-transaction"],
66
66
  )
67
+
68
+ # Log transfers extracted from receipt events
69
+ if receipt:
70
+ from iwa.core.services.transaction import TransferLogger
71
+
72
+ transfer_logger = TransferLogger(self.account_service, interface)
73
+ transfer_logger.log_transfers(receipt)
74
+
67
75
  return tx_hash
68
76
 
69
77
  def _send_erc20_via_eoa(
@@ -61,6 +61,15 @@ class NativeTransferMixin:
61
61
  value_eur=v_eur,
62
62
  tags=["native-transfer", "safe-transaction"],
63
63
  )
64
+
65
+ # Log transfers extracted from receipt events
66
+ if receipt:
67
+ from iwa.core.services.transaction import TransferLogger
68
+
69
+ interface = ChainInterfaces().get(chain_name)
70
+ transfer_logger = TransferLogger(self.account_service, interface)
71
+ transfer_logger.log_transfers(receipt)
72
+
64
73
  return tx_hash
65
74
 
66
75
  def _send_native_via_eoa(
@@ -74,20 +83,38 @@ class NativeTransferMixin:
74
83
  to_tag: Optional[str],
75
84
  token_symbol: str,
76
85
  ) -> Optional[str]:
77
- """Send native currency via EOA (externally owned account)."""
78
- success, tx_hash = chain_interface.send_native_transfer(
79
- from_address=from_account.address,
80
- to_address=to_address,
81
- value_wei=amount_wei,
82
- sign_callback=lambda tx: self.key_storage.sign_transaction(tx, from_account.address),
86
+ """Send native currency via EOA using unified TransactionService."""
87
+ # Build transaction dict
88
+ try:
89
+ gas_price = chain_interface.web3.eth.gas_price
90
+ gas_estimate = chain_interface.web3.eth.estimate_gas({
91
+ "from": from_account.address,
92
+ "to": to_address,
93
+ "value": amount_wei,
94
+ })
95
+ except Exception as e:
96
+ logger.error(f"Failed to estimate gas for native transfer: {e}")
97
+ return None
98
+
99
+ tx = {
100
+ "from": from_account.address,
101
+ "to": to_address,
102
+ "value": amount_wei,
103
+ "gas": gas_estimate,
104
+ "gasPrice": gas_price,
105
+ }
106
+
107
+ # Use unified TransactionService
108
+ success, receipt = self.transaction_service.sign_and_send(
109
+ tx, from_account.address, chain_name, tags=["native-transfer"]
83
110
  )
84
- if success and tx_hash:
85
- # Get receipt for gas calculation
86
- receipt = None
87
- try:
88
- receipt = chain_interface.web3.eth.get_transaction_receipt(tx_hash)
89
- except Exception as e:
90
- logger.warning(f"Could not get receipt for {tx_hash}: {e}")
111
+
112
+ if success and receipt:
113
+ tx_hash = receipt.get("transactionHash", b"")
114
+ if hasattr(tx_hash, "hex"):
115
+ tx_hash = tx_hash.hex()
116
+ elif isinstance(tx_hash, bytes):
117
+ tx_hash = tx_hash.hex()
91
118
 
92
119
  gas_cost, gas_value_eur = self._calculate_gas_info(receipt, chain_name)
93
120
  p_eur, v_eur = self._get_token_price_info(token_symbol, amount_wei, chain_name)
@@ -106,6 +133,13 @@ class NativeTransferMixin:
106
133
  value_eur=v_eur,
107
134
  tags=["native-transfer"],
108
135
  )
136
+
137
+ # Log transfers extracted from receipt events
138
+ from iwa.core.services.transaction import TransferLogger
139
+
140
+ transfer_logger = TransferLogger(self.account_service, chain_interface)
141
+ transfer_logger.log_transfers(receipt)
142
+
109
143
  return tx_hash
110
144
  return None
111
145
 
@@ -140,11 +140,13 @@ class MechManagerMixin:
140
140
  )
141
141
  if use_marketplace:
142
142
  priority_mech = priority_mech or detected_priority_mech
143
+ mech_address = mech_address or detected_marketplace
143
144
 
144
145
  if use_marketplace:
145
146
  return self._send_marketplace_mech_request(
146
147
  data=data,
147
148
  value=value,
149
+ marketplace_address=mech_address,
148
150
  priority_mech=priority_mech,
149
151
  max_delivery_rate=max_delivery_rate,
150
152
  payment_type=payment_type,
@@ -260,10 +262,39 @@ class MechManagerMixin:
260
262
 
261
263
  return True
262
264
 
265
+ def _resolve_marketplace_config(
266
+ self, marketplace_addr: Optional[str], priority_addr: Optional[str]
267
+ ) -> tuple[str, str]:
268
+ """Resolve marketplace and priority mech addresses. Returns (marketplace, priority)."""
269
+ chain_name = self.chain_name if self.service else getattr(self, "chain_name", "gnosis")
270
+ protocol_contracts = OLAS_CONTRACTS.get(chain_name, {})
271
+
272
+ resolved_mp = marketplace_addr or protocol_contracts.get("OLAS_MECH_MARKETPLACE")
273
+ if not resolved_mp:
274
+ raise ValueError(f"Mech Marketplace address not found for chain {chain_name}")
275
+
276
+ if not priority_addr:
277
+ raise ValueError("priority_mech is required for marketplace requests")
278
+
279
+ return str(resolved_mp), Web3.to_checksum_address(priority_addr)
280
+
281
+ def _prepare_marketplace_params(
282
+ self,
283
+ value: Optional[int],
284
+ max_delivery_rate: Optional[int],
285
+ payment_type: Optional[bytes],
286
+ ) -> tuple[int, int, bytes]:
287
+ """Prepare default values for marketplace parameters."""
288
+ p_type = payment_type or bytes.fromhex(PAYMENT_TYPE_NATIVE)
289
+ val = value if value is not None else 10_000_000_000_000_000
290
+ rate = max_delivery_rate if max_delivery_rate is not None else val
291
+ return val, rate, p_type
292
+
263
293
  def _send_marketplace_mech_request(
264
294
  self,
265
295
  data: bytes,
266
296
  value: Optional[int] = None,
297
+ marketplace_address: Optional[str] = None,
267
298
  priority_mech: Optional[str] = None,
268
299
  max_delivery_rate: Optional[int] = None,
269
300
  payment_type: Optional[bytes] = None,
@@ -275,43 +306,30 @@ class MechManagerMixin:
275
306
  logger.error("No active service")
276
307
  return None
277
308
 
278
- multisig_address = self.service.multisig_address
279
- chain_name = self.chain_name if self.service else getattr(self, "chain_name", "gnosis")
280
- protocol_contracts = OLAS_CONTRACTS.get(chain_name, {})
281
- marketplace_address = protocol_contracts.get("OLAS_MECH_MARKETPLACE")
282
-
283
- if not marketplace_address:
284
- logger.error(f"Mech Marketplace address not found for chain {chain_name}")
285
- return None
286
-
287
- if not priority_mech:
288
- logger.error("priority_mech is required for marketplace requests")
309
+ try:
310
+ marketplace_address, priority_mech = self._resolve_marketplace_config(
311
+ marketplace_address, priority_mech
312
+ )
313
+ except ValueError as e:
314
+ logger.error(e)
289
315
  return None
290
316
 
291
- priority_mech = Web3.to_checksum_address(priority_mech)
292
- marketplace = MechMarketplaceContract(str(marketplace_address), chain_name=chain_name)
317
+ marketplace = MechMarketplaceContract(marketplace_address, chain_name=self.chain_name)
293
318
 
294
319
  if not self._validate_priority_mech(marketplace, priority_mech):
295
320
  return None
296
321
 
297
- # Set defaults for payment
298
- if payment_type is None:
299
- payment_type = bytes.fromhex(PAYMENT_TYPE_NATIVE)
300
-
301
- if value is None:
302
- value = 10_000_000_000_000_000
303
- logger.info(f"Using default value: {value} wei (0.01 xDAI)")
304
-
305
- if max_delivery_rate is None:
306
- max_delivery_rate = value
307
- logger.info(f"Using value as max_delivery_rate: {max_delivery_rate}")
322
+ # Set defaults for payment and delivery
323
+ value, max_delivery_rate, payment_type = self._prepare_marketplace_params(
324
+ value, max_delivery_rate, payment_type
325
+ )
308
326
 
309
327
  if not self._validate_marketplace_params(marketplace, response_timeout, payment_type):
310
328
  return None
311
329
 
312
330
  # Prepare transaction
313
331
  tx_data = marketplace.prepare_request_tx(
314
- from_address=multisig_address,
332
+ from_address=self.service.multisig_address,
315
333
  request_data=data,
316
334
  priority_mech=priority_mech,
317
335
  response_timeout=response_timeout,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iwa
3
- Version: 0.0.16
3
+ Version: 0.0.18
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
@@ -195,43 +195,8 @@ def test_wait_for_no_pending_tx(mock_web3):
195
195
  assert ci.wait_for_no_pending_tx("0xSender") is False
196
196
 
197
197
 
198
- def test_send_native_transfer(mock_web3):
199
- chain = MagicMock(spec=SupportedChain, rpcs=["https://rpc"], chain_id=1, native_currency="ETH")
200
- chain.name = "TestChain"
201
- type(chain).rpc = PropertyMock(return_value="https://rpc")
202
- ci = ChainInterface(chain)
203
- account = MagicMock(address="0xSender", key="key")
204
-
205
- ci.web3.eth.get_transaction_count.return_value = 0
206
- ci.web3.eth.gas_price = 10
207
- ci.web3.eth.estimate_gas.return_value = 21000
208
-
209
- # Sufficient balance
210
- ci.web3.eth.get_balance.return_value = 10**18 # plenty
211
- ci.web3.eth.get_balance.return_value = 10**18 # plenty
212
- # Valid mock return for success: (True, dict_receipt)
213
- # The actual method returns tx_hash.hex().
214
- mock_signed_tx = MagicMock()
215
- mock_signed_tx.raw_transaction = b"raw"
216
- mock_receipt = {"transactionHash": b"hash", "status": 1}
217
-
218
- with (
219
- patch.object(ci.web3.eth, "send_raw_transaction", return_value=b"hash"),
220
- patch.object(ci.web3.eth, "wait_for_transaction_receipt", return_value=mock_receipt),
221
- patch.object(ci, "wait_for_no_pending_tx", return_value=True),
222
- ):
223
- success, tx_hash = ci.send_native_transfer(
224
- account.address, "0xReceiver", 1000, sign_callback=lambda tx: mock_signed_tx
225
- )
226
- assert success is True
227
- assert tx_hash == "68617368"
228
-
229
- # Insufficient balance
230
- ci.web3.eth.get_balance.return_value = 0
231
- ci.web3.from_wei.return_value = 0.0
232
- assert ci.send_native_transfer(
233
- account.address, "0xReceiver", 1000, sign_callback=lambda tx: mock_signed_tx
234
- ) == (False, None)
198
+ # NOTE: test_send_native_transfer was removed because the method was removed
199
+ # from ChainInterface. Native transfers now go through TransactionService.
235
200
 
236
201
 
237
202
  def test_chain_interfaces_get():
@@ -352,66 +317,6 @@ def test_chain_interface_with_real_chains():
352
317
  # --- Negative Tests ---
353
318
 
354
319
 
355
- def test_send_native_transfer_insufficient_balance(mock_web3):
356
- """Test send_native_transfer fails with insufficient balance."""
357
- chain = MagicMock(spec=SupportedChain)
358
- chain.name = "TestChain"
359
- chain.rpcs = ["https://rpc"]
360
- chain.chain_id = 1
361
- chain.native_currency = "ETH"
362
- type(chain).rpc = PropertyMock(return_value="https://rpc")
363
-
364
- ci = ChainInterface(chain)
365
- ci.web3.eth.get_transaction_count.return_value = 0
366
- ci.web3.eth.gas_price = 1000000000 # 1 gwei
367
- ci.web3.eth.estimate_gas.return_value = 21000
368
- ci.web3.eth.get_balance.return_value = 1000 # Very low balance
369
- ci.web3.from_wei.return_value = 0.000001
370
-
371
- sign_callback = MagicMock()
372
-
373
- success, tx_hash = ci.send_native_transfer(
374
- from_address="0x1111111111111111111111111111111111111111",
375
- to_address="0x2222222222222222222222222222222222222222",
376
- value_wei=10**18, # 1 ETH - more than available
377
- sign_callback=sign_callback,
378
- )
379
-
380
- assert success is False
381
- assert tx_hash is None
382
- sign_callback.assert_not_called()
383
-
384
-
385
- def test_send_native_transfer_rpc_error(mock_web3):
386
- """Test send_native_transfer handles RPC errors."""
387
- chain = MagicMock(spec=SupportedChain)
388
- chain.name = "TestChain"
389
- chain.rpcs = ["https://rpc"]
390
- chain.chain_id = 1
391
- chain.native_currency = "ETH"
392
- type(chain).rpc = PropertyMock(return_value="https://rpc")
393
-
394
- ci = ChainInterface(chain)
395
- ci.web3.eth.get_transaction_count.return_value = 0
396
- ci.web3.eth.gas_price = 1000000000
397
- ci.web3.eth.estimate_gas.return_value = 21000
398
- ci.web3.eth.get_balance.return_value = 10**19 # Enough balance
399
- ci.web3.from_wei.return_value = 10.0
400
- ci.web3.eth.send_raw_transaction.side_effect = Exception("Connection refused")
401
-
402
- sign_callback = MagicMock()
403
- sign_callback.return_value = MagicMock(raw_transaction=b"signed")
404
-
405
- success, tx_hash = ci.send_native_transfer(
406
- from_address="0x1111111111111111111111111111111111111111",
407
- to_address="0x2222222222222222222222222222222222222222",
408
- value_wei=10**17,
409
- sign_callback=sign_callback,
410
- )
411
-
412
- assert success is False
413
- assert tx_hash is None
414
-
415
320
 
416
321
  def test_get_token_symbol_fallback_on_error(mock_web3):
417
322
  """Test get_token_symbol returns truncated address on error."""