ipor-fusion 4.0.0.dev68__tar.gz → 4.0.0.dev71__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 (292) hide show
  1. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/MIGRATION_4.md +9 -0
  2. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/PKG-INFO +1 -1
  3. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/pyproject.toml +1 -1
  4. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/__init__.py +2 -0
  5. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/market_cmd.py +1 -1
  6. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/options.py +2 -2
  7. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/vault_info_view.py +4 -4
  8. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/context.py +16 -17
  9. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/contract.py +3 -3
  10. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/logs.py +13 -3
  11. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/stargate/contracts.py +16 -9
  12. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/stargate/layerzero.py +10 -10
  13. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/stargate/transport.py +7 -4
  14. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/errors.py +3 -3
  15. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/explorer.py +1 -1
  16. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/base.py +5 -1
  17. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/crosschain/ccip.py +4 -4
  18. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/crosschain/stargate.py +4 -3
  19. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/universal.py +5 -1
  20. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/aave_v3.py +2 -4
  21. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/morpho.py +9 -3
  22. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/oracle_mapping_nodes.py +1 -3
  23. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/oracle_mapping_reader.py +2 -2
  24. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/types.py +3 -1
  25. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/vault_info.py +1 -1
  26. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_encoding.py +3 -1
  27. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_web3_context.py +1 -1
  28. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/typed_client/consumer.py +8 -0
  29. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.claude-plugin/marketplace.json +0 -0
  30. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.claude-plugin/plugin.json +0 -0
  31. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.env.example +0 -0
  32. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.gemini/settings.json +0 -0
  33. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.git-blame-ignore-revs +0 -0
  34. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.github/workflows/cd.yml +0 -0
  35. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.github/workflows/ci.yml +0 -0
  36. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.github/workflows/commit-lint.yml +0 -0
  37. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.github/workflows/notify-slack.yml +0 -0
  38. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.github/workflows/python-build.yml +0 -0
  39. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.github/workflows/python-publish.yml +0 -0
  40. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.github/workflows/release.yml +0 -0
  41. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.github/workflows/report-slack.yml +0 -0
  42. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.gitignore +0 -0
  43. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.mcp.json +0 -0
  44. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.pre-commit-config.yaml +0 -0
  45. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/.python-version +0 -0
  46. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/AGENTS.md +0 -0
  47. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/CHANGELOG.md +0 -0
  48. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/CLAUDE.md +0 -0
  49. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/CODEOWNERS +0 -0
  50. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/LICENSE +0 -0
  51. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/README.md +0 -0
  52. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/build.sh +0 -0
  53. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/examples/CONTRIBUTING.md +0 -0
  54. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/examples/README.md +0 -0
  55. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  56. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  57. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  58. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  59. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/examples/external_state_margin_leg_base.py +0 -0
  60. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/examples/simple_aave_v3_supply_base.py +0 -0
  61. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/pytest.ini +0 -0
  62. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/scripts/commit-check.sh +0 -0
  63. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/scripts/crosschain_readiness.py +0 -0
  64. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/skills/ipor-deploy-vault/SKILL.md +0 -0
  65. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/about.py +0 -0
  66. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cache.py +0 -0
  67. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/chains.py +0 -0
  68. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/__init__.py +0 -0
  69. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  70. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/config_cmd.py +0 -0
  71. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/config_store.py +0 -0
  72. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/main.py +0 -0
  73. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  74. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  75. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/vault_health.py +0 -0
  76. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  77. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  78. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/config/__init__.py +0 -0
  79. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/config/roles.py +0 -0
  80. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/__init__.py +0 -0
  81. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/access.py +0 -0
  82. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/address.py +0 -0
  83. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/erc20.py +0 -0
  84. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/external_state_executor.py +0 -0
  85. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/fee_manager.py +0 -0
  86. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/fuse_action.py +0 -0
  87. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/fusion_factory.py +0 -0
  88. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/multicall.py +0 -0
  89. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/oracle.py +0 -0
  90. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/plasma_vault.py +0 -0
  91. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/rewards_manager.py +0 -0
  92. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/simulation.py +0 -0
  93. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  94. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/__init__.py +0 -0
  95. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  96. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  97. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  98. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  99. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  100. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  101. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  102. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  103. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  104. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/contracts.py +0 -0
  105. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/discovery.py +0 -0
  106. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/errors.py +0 -0
  107. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/lane.py +0 -0
  108. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/logs.py +0 -0
  109. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/messages.py +0 -0
  110. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/simulation.py +0 -0
  111. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  112. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  113. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/crosschain/transport.py +0 -0
  114. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/dependency_graph.py +0 -0
  115. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/entry_cli.py +0 -0
  116. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/entry_mcp.py +0 -0
  117. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/field_docs.py +0 -0
  118. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/__init__.py +0 -0
  119. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  120. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/async_action.py +0 -0
  121. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  122. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
  123. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
  124. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/erc4626.py +0 -0
  125. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  126. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/events.py +0 -0
  127. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/external_state.py +0 -0
  128. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  129. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  130. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  131. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/hypercore.py +0 -0
  132. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  133. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/merkl.py +0 -0
  134. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/morpho.py +0 -0
  135. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  136. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  137. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/guide/__init__.py +0 -0
  138. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/guide/architecture.md +0 -0
  139. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/guide/glossary.md +0 -0
  140. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/guide/invariants.md +0 -0
  141. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/guide/quickstart.md +0 -0
  142. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/market_ids.py +0 -0
  143. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/market_info.py +0 -0
  144. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/mcp/__init__.py +0 -0
  145. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/mcp/guide.py +0 -0
  146. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/mcp/models.py +0 -0
  147. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/mcp/server.py +0 -0
  148. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/morpho_api.py +0 -0
  149. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/py.typed +0 -0
  150. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/__init__.py +0 -0
  151. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/aave_v4.py +0 -0
  152. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/compound_v3.py +0 -0
  153. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/hypercore.py +0 -0
  154. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  155. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/lending_health.py +0 -0
  156. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  157. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/position_manager.py +0 -0
  158. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  159. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  160. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/readers/unpriceable.py +0 -0
  161. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/substrates.py +0 -0
  162. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/validation.py +0 -0
  163. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/vault_data.py +0 -0
  164. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/vault_managers.py +0 -0
  165. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/vault_positions.py +0 -0
  166. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/src/ipor_fusion/vault_reads.py +0 -0
  167. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/UniswapV3UniversalRouter.py +0 -0
  168. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_crosschain.py +0 -0
  169. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_crosschain_lifecycle.py +0 -0
  170. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_crosschain_recovery.py +0 -0
  171. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_euler_v2.py +0 -0
  172. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_foundry.py +0 -0
  173. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_guide.py +0 -0
  174. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_hypercore_model.py +0 -0
  175. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_hypercore_shadow.py +0 -0
  176. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_multicall.py +0 -0
  177. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_simulate.py +0 -0
  178. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/_validation.py +0 -0
  179. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/addresses.py +0 -0
  180. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/conftest.py +0 -0
  181. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/constants.py +0 -0
  182. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  183. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/fixtures/hypercore_market55.json +0 -0
  184. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/fixtures/vault_info_golden/clean.json +0 -0
  185. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  186. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/fixtures/vault_info_golden/rich.json +0 -0
  187. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  188. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_about.py +0 -0
  189. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_access_manager.py +0 -0
  190. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cache.py +0 -0
  191. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_chains.py +0 -0
  192. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_changelog.py +0 -0
  193. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_commands.py +0 -0
  194. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_config_store.py +0 -0
  195. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_helpers.py +0 -0
  196. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_hypercore.py +0 -0
  197. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_lending_health.py +0 -0
  198. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_market_morpho_blue.py +0 -0
  199. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_oracle_mapping.py +0 -0
  200. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_pending_requests_scan.py +0 -0
  201. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_role_accounts.py +0 -0
  202. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_vault_info_golden.py +0 -0
  203. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_vault_predates_getters.py +0 -0
  204. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_cli_vault_validate.py +0 -0
  205. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_contract_call.py +0 -0
  206. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_contract_encoder.py +0 -0
  207. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_ccip_events.py +0 -0
  208. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_ccip_indexer.py +0 -0
  209. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_ccip_quote.py +0 -0
  210. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_chainlink.py +0 -0
  211. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_contracts.py +0 -0
  212. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_errors.py +0 -0
  213. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_lane.py +0 -0
  214. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_layering.py +0 -0
  215. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_readiness.py +0 -0
  216. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_readiness_report.py +0 -0
  217. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_crosschain_transport.py +0 -0
  218. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_erc20.py +0 -0
  219. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_examples_composed_hypercore.py +0 -0
  220. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_examples_crosschain.py +0 -0
  221. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  222. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_examples_vaults.py +0 -0
  223. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_explorer.py +0 -0
  224. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_external_state_events.py +0 -0
  225. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_external_state_executor.py +0 -0
  226. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_fee_manager.py +0 -0
  227. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_fuse_encoding.py +0 -0
  228. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_fusion_instance.py +0 -0
  229. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_guide.py +0 -0
  230. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_hypercore_deployment.py +0 -0
  231. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_hypercore_events.py +0 -0
  232. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_hypercore_flow.py +0 -0
  233. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_hypercore_live.py +0 -0
  234. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_hypercore_outcome.py +0 -0
  235. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_hypercore_readers.py +0 -0
  236. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_hypercore_simulation.py +0 -0
  237. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_logs.py +0 -0
  238. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_mcp_hypercore.py +0 -0
  239. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_mcp_models.py +0 -0
  240. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_mcp_server.py +0 -0
  241. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_mcp_vault_validate.py +0 -0
  242. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_multicall.py +0 -0
  243. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_oracle.py +0 -0
  244. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_oracle_mapping.py +0 -0
  245. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_plasma_vault.py +0 -0
  246. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_reader_encoding.py +0 -0
  247. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_readiness_selection.py +0 -0
  248. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_revert_decoding.py +0 -0
  249. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_rewards_manager.py +0 -0
  250. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_roles.py +0 -0
  251. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_sdk_layering.py +0 -0
  252. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  253. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  254. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  255. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_crosschain_canary.py +0 -0
  256. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_crosschain_hypercore.py +0 -0
  257. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  258. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  259. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  260. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  261. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  262. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  263. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  264. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_examples_arbitrum.py +0 -0
  265. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_examples_base.py +0 -0
  266. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_examples_crosschain.py +0 -0
  267. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_external_state_executor_base.py +0 -0
  268. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  269. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  270. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  271. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_morpho_blue.py +0 -0
  272. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_pilot_v3.py +0 -0
  273. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  274. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  275. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  276. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  277. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_readers_integration.py +0 -0
  278. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_release_funds.py +0 -0
  279. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_swap_on_base.py +0 -0
  280. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  281. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_solidity_compiler.py +0 -0
  282. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_solidity_mirrors.py +0 -0
  283. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_substrates.py +0 -0
  284. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_types_and_events.py +0 -0
  285. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_unpriceable_tokens.py +0 -0
  286. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_validation.py +0 -0
  287. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_vault_dep_graph.py +0 -0
  288. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_vault_info_onchain_base.py +0 -0
  289. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  290. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_vault_simulator_blocks.py +0 -0
  291. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/tests/test_withdraw_manager.py +0 -0
  292. {ipor_fusion-4.0.0.dev68 → ipor_fusion-4.0.0.dev71}/uv.lock +0 -0
@@ -119,6 +119,15 @@ from_=...)` needs neither, so drop a zero-address placeholder. `execute()` witho
119
119
  `dataclasses.asdict` form) with every field parsed; use it instead of
120
120
  `FusionInstance(**data)`, which passes raw strings into the role-typed fields.
121
121
 
122
+ **Transactions, LayerZero and constants.** `Web3Context.build_transaction` and
123
+ `Call.build_transaction` return web3's `TxParams` (the same keys; `data` is `0x` hex,
124
+ the fee fields are `Wei`). LayerZero endpoint ids are `LayerZeroEid`:
125
+ `StargateChain.eid`, `Packet.src_eid` / `dst_eid`, `EnforcedOptionParam.eid`,
126
+ `executor_eid()` and `eid_of()` return it and `chain_id_of`, `peers`, `lz_receive_calldata(src_eid=)`
127
+ and the Stargate fuse's `eid=` take it (`LayerZeroEid(30101)`).
128
+ `encode_oft_compose_msg(*, nonce=, src_eid=, amount_ld=, compose_msg=)` takes
129
+ keyword arguments only. `ZERO_ADDRESS` is a `ChecksumAddress` (same value).
130
+
122
131
  **Markets and substrates.** Every `IporFusionMarkets` member is a `MarketId`.
123
132
  `decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
124
133
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev68
3
+ Version: 4.0.0.dev71
4
4
  Summary: The IPOR Fusion SDK for Python
5
5
  Project-URL: homepage, https://ipor.io
6
6
  Project-URL: repository, https://github.com/IPOR-Labs/ipor-fusion.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev68"
3
+ version = "4.0.0.dev71"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -325,6 +325,7 @@ from ipor_fusion.types import (
325
325
  HyperCoreClientOrderId,
326
326
  HyperCoreOrderId,
327
327
  HyperCoreTokenIndex,
328
+ LayerZeroEid,
328
329
  Liquidity,
329
330
  MappingStatus,
330
331
  MarginTableId,
@@ -650,6 +651,7 @@ __all__ = [
650
651
  "ProposalId",
651
652
  "ReturnRequestId",
652
653
  "StargateAssetId",
654
+ "LayerZeroEid",
653
655
  "TransferId",
654
656
  "Price",
655
657
  "Period",
@@ -51,7 +51,7 @@ class MarketIdType(click.ParamType):
51
51
 
52
52
  name = "marketId"
53
53
 
54
- def convert(self, value, param, ctx): # type: ignore[override]
54
+ def convert(self, value, param, ctx):
55
55
  if not isinstance(value, str):
56
56
  self.fail(f"expected string, got {type(value).__name__}", param, ctx)
57
57
  try:
@@ -51,7 +51,7 @@ class VaultAddressType(AddressType):
51
51
  """An address the command treats as a Plasma Vault; the command's probe
52
52
  (`resolve_access_manager`) is what checks it on chain."""
53
53
 
54
- def convert(self, value, param, ctx) -> PlasmaVaultAddress: # type: ignore[override]
54
+ def convert(self, value, param, ctx) -> PlasmaVaultAddress:
55
55
  return PlasmaVaultAddress(super().convert(value, param, ctx))
56
56
 
57
57
 
@@ -63,7 +63,7 @@ class ChainType(click.ParamType):
63
63
 
64
64
  name = "chain"
65
65
 
66
- def convert(self, value, param, ctx) -> ChainId: # type: ignore[override]
66
+ def convert(self, value, param, ctx) -> ChainId:
67
67
  if isinstance(value, int):
68
68
  return ChainId(value)
69
69
  try:
@@ -32,7 +32,7 @@ from ipor_fusion.readers.hypercore import HyperCorePendingState
32
32
  from ipor_fusion.substrates import (
33
33
  format_market_label,
34
34
  )
35
- from ipor_fusion.types import ZERO_ADDRESS, ChainId, PlasmaVaultAddress
35
+ from ipor_fusion.types import ZERO_ADDRESS, ChainId, PlasmaVaultAddress, Shares
36
36
  from ipor_fusion.validation import (
37
37
  validate_vault_state,
38
38
  )
@@ -388,16 +388,16 @@ def _print_pending_requests(data: VaultData, plasma_vault: PlasmaVault) -> None:
388
388
  click.echo(" Pending requests: (none)")
389
389
  return
390
390
 
391
- total_shares = sum((r.shares for r in requests), 0)
391
+ total_shares = Shares(sum(r.shares for r in requests))
392
392
  total_assets: int | None = _safe_call(
393
- lambda ts=total_shares: plasma_vault.convert_to_assets(ts).call() # type: ignore[misc]
393
+ lambda ts=total_shares: plasma_vault.convert_to_assets(ts).call()
394
394
  )
395
395
 
396
396
  click.echo(f" Pending requests ({len(requests)}):")
397
397
  rows: list[tuple[str, ...]] = []
398
398
  for req in requests:
399
399
  assets: int | None = _safe_call(
400
- lambda s=req.shares: plasma_vault.convert_to_assets(s).call() # type: ignore[misc]
400
+ lambda s=req.shares: plasma_vault.convert_to_assets(s).call()
401
401
  )
402
402
  assets_str = (
403
403
  f"{_format_amount(assets, data.asset_decimals)} {data.asset_symbol}"
@@ -3,7 +3,8 @@ from __future__ import annotations
3
3
  import logging
4
4
 
5
5
  from eth_account import Account
6
- from eth_typing import ChecksumAddress
6
+ from eth_typing import ChecksumAddress, HexStr
7
+ from eth_utils import encode_hex
7
8
  from hexbytes import HexBytes
8
9
  from requests import ConnectionError as RequestsConnectionError
9
10
  from requests import HTTPError, Timeout
@@ -14,7 +15,7 @@ from web3.providers.rpc.utils import (
14
15
  REQUEST_RETRY_ALLOWLIST,
15
16
  ExceptionRetryConfiguration,
16
17
  )
17
- from web3.types import BlockIdentifier, LogReceipt, TxReceipt
18
+ from web3.types import BlockIdentifier, LogReceipt, TxParams, TxReceipt, Wei
18
19
 
19
20
  from ipor_fusion.chains import GET_LOGS_RANGE_HINTS
20
21
  from ipor_fusion.core.logs import (
@@ -182,7 +183,7 @@ class Web3Context:
182
183
  data: bytes,
183
184
  *,
184
185
  block: BlockIdentifier = "latest",
185
- ) -> dict:
186
+ ) -> TxParams:
186
187
  """Build the transaction dict for ``data`` against ``to`` without signing
187
188
  or broadcasting — the same dict `send` would submit, ready to hand to an
188
189
  external signer or to inspect for a gas/cost preview (`gas` and
@@ -211,7 +212,7 @@ class Web3Context:
211
212
  max_fee_per_gas = self._calculate_max_fee_per_gas(
212
213
  gas_price, base_fee, max_priority_fee_per_gas
213
214
  )
214
- data_hex = self._data_hex(data)
215
+ data_hex = encode_hex(data)
215
216
  estimated_gas = self._estimate_gas(to, data_hex, signer, block)
216
217
  return {
217
218
  "chainId": self.chain_id,
@@ -262,7 +263,7 @@ class Web3Context:
262
263
  `build_transaction`.
263
264
  """
264
265
  signer = self._require_signer("estimate gas")
265
- return self._estimate_gas(to, self._data_hex(data), signer, block)
266
+ return self._estimate_gas(to, encode_hex(data), signer, block)
266
267
 
267
268
  def get_logs(
268
269
  self,
@@ -323,12 +324,12 @@ class Web3Context:
323
324
  def _estimate_gas(
324
325
  self,
325
326
  to: ChecksumAddress,
326
- data: str,
327
- from_address: str,
327
+ data: HexStr,
328
+ from_address: ChecksumAddress,
328
329
  block: BlockIdentifier = "latest",
329
330
  ) -> int:
330
331
  estimated = self.web3.eth.estimate_gas(
331
- {"to": to, "from": from_address, "data": data}, # type: ignore[typeddict-item]
332
+ {"to": to, "from": from_address, "data": data},
332
333
  block_identifier=block,
333
334
  )
334
335
  # State can shift between estimate and execution, so pad the limit; it is
@@ -337,25 +338,23 @@ class Web3Context:
337
338
 
338
339
  def _calculate_max_fee_per_gas(
339
340
  self, gas_price: int, base_fee: int | None, max_priority_fee_per_gas: int
340
- ) -> int:
341
+ ) -> Wei:
341
342
  # Prefer a base-fee-relative ceiling on EIP-1559 chains; fall back to a
342
343
  # gas-price margin where the block carries no base fee.
343
344
  if base_fee is None:
344
- return gas_price + self._percent_of(gas_price, self.GAS_PRICE_MARGIN)
345
- return base_fee * self.BASE_FEE_HEADROOM_MULTIPLIER + max_priority_fee_per_gas
345
+ return Wei(gas_price + self._percent_of(gas_price, self.GAS_PRICE_MARGIN))
346
+ return Wei(
347
+ base_fee * self.BASE_FEE_HEADROOM_MULTIPLIER + max_priority_fee_per_gas
348
+ )
346
349
 
347
- def _get_max_priority_fee(self, gas_price: int) -> int:
348
- return min(self.DEFAULT_TRANSACTION_MAX_PRIORITY_FEE, gas_price // 10)
350
+ def _get_max_priority_fee(self, gas_price: int) -> Wei:
351
+ return Wei(min(self.DEFAULT_TRANSACTION_MAX_PRIORITY_FEE, gas_price // 10))
349
352
 
350
353
  def _require_signer(self, action: str) -> ChecksumAddress:
351
354
  if self.signer is None:
352
355
  raise ValueError(f"Signer required to {action}")
353
356
  return self.signer
354
357
 
355
- @staticmethod
356
- def _data_hex(data: bytes) -> str:
357
- return f"0x{data.hex()}"
358
-
359
358
  @staticmethod
360
359
  def _percent_of(value: int, percentage: int) -> int:
361
360
  return value * percentage // 100
@@ -8,7 +8,7 @@ from eth_abi import decode, encode
8
8
  from eth_typing import ChecksumAddress
9
9
  from eth_utils import function_signature_to_4byte_selector
10
10
  from web3 import Web3
11
- from web3.types import BlockIdentifier, TxReceipt
11
+ from web3.types import BlockIdentifier, TxParams, TxReceipt
12
12
 
13
13
  from ipor_fusion.core.context import Web3Context
14
14
  from ipor_fusion.errors import EmptyCallResultError
@@ -105,7 +105,7 @@ class Call(Generic[T]):
105
105
 
106
106
  def build_transaction(
107
107
  self, ctx: Web3Context | None = None, *, block: BlockIdentifier = "latest"
108
- ) -> dict:
108
+ ) -> TxParams:
109
109
  """Build the transaction dict for this Call without signing or sending —
110
110
  mirrors `.send()`, delegating to `Web3Context.build_transaction`. Returns
111
111
  the dict a signer would submit; needs a signer address, no key."""
@@ -165,7 +165,7 @@ class ContractWrapper:
165
165
  and so rejects input that is genuinely the caller's.
166
166
  """
167
167
  instance = cls.__new__(cls)
168
- instance._ctx = None # type: ignore[assignment]
168
+ instance._ctx = None
169
169
  instance._address = (
170
170
  Web3.to_checksum_address(address)
171
171
  if address is not None
@@ -19,7 +19,9 @@ from concurrent.futures import ThreadPoolExecutor
19
19
  from typing import Protocol
20
20
 
21
21
  import requests
22
- from eth_typing import ChecksumAddress
22
+ from eth_typing import ChecksumAddress, HexStr
23
+ from eth_utils import encode_hex
24
+ from hexbytes import HexBytes
23
25
  from web3 import Web3
24
26
  from web3.exceptions import Web3Exception
25
27
  from web3.types import BlockIdentifier, FilterParams, LogReceipt
@@ -158,6 +160,14 @@ class _Rejected(Exception):
158
160
  self.size = size
159
161
 
160
162
 
163
+ def _topic_filter(topic: str | list[str]) -> HexStr | list[HexStr]:
164
+ """A topic (or an OR-list of them) as the canonical hex `eth_getLogs`
165
+ filters by; raises on text that is not hex."""
166
+ if isinstance(topic, str):
167
+ return encode_hex(HexBytes(topic))
168
+ return [encode_hex(HexBytes(alternative)) for alternative in topic]
169
+
170
+
161
171
  class _Scan:
162
172
  def __init__(
163
173
  self,
@@ -169,7 +179,7 @@ class _Scan:
169
179
  ):
170
180
  self._web3 = web3
171
181
  self._address = address
172
- self._topics = topics
182
+ self._topics = [_topic_filter(topic) for topic in topics]
173
183
  self._timeout_s = timeout_s
174
184
  self._deadline = time.monotonic() + timeout_s
175
185
  self._stopped = threading.Event()
@@ -219,7 +229,7 @@ class _Scan:
219
229
  self._check(low, high)
220
230
  params: FilterParams = {
221
231
  "address": self._address,
222
- "topics": self._topics, # type: ignore[typeddict-item]
232
+ "topics": self._topics,
223
233
  "fromBlock": low,
224
234
  "toBlock": high,
225
235
  }
@@ -23,6 +23,7 @@ from ipor_fusion.types import (
23
23
  ChainId,
24
24
  CommandId,
25
25
  DeploymentRequestId,
26
+ LayerZeroEid,
26
27
  PlasmaVaultAddress,
27
28
  PositionSetHash,
28
29
  ProposalId,
@@ -122,10 +123,12 @@ class StargateCrosschainExecutor(CrosschainExecutor):
122
123
  def proposal_count(self) -> Call[int]:
123
124
  return self._view("proposalCount()", output_types=["uint256"])
124
125
 
125
- def eid_of(self, chain_id: ChainId) -> Call[int]:
126
- return self._view("eidOf(uint256)", chain_id, output_types=["uint32"])
126
+ def eid_of(self, chain_id: ChainId) -> Call[LayerZeroEid]:
127
+ return self._view(
128
+ "eidOf(uint256)", chain_id, output_types=["uint32"], decoder=LayerZeroEid
129
+ )
127
130
 
128
- def chain_id_of(self, eid: int) -> Call[ChainId]:
131
+ def chain_id_of(self, eid: LayerZeroEid) -> Call[ChainId]:
129
132
  return self._view(
130
133
  "chainIdOf(uint32)", eid, output_types=["uint256"], decoder=ChainId
131
134
  )
@@ -252,8 +255,10 @@ class StargateCrosschainDispatcher(CrosschainDispatcher):
252
255
  def stargate_pool(self) -> Call[ChecksumAddress]:
253
256
  return self._view("STARGATE_POOL()", output_types=["address"], decoder=_address)
254
257
 
255
- def executor_eid(self) -> Call[int]:
256
- return self._view("EXECUTOR_EID()", output_types=["uint32"])
258
+ def executor_eid(self) -> Call[LayerZeroEid]:
259
+ return self._view(
260
+ "EXECUTOR_EID()", output_types=["uint32"], decoder=LayerZeroEid
261
+ )
257
262
 
258
263
  def observation(self) -> Call[Observation]:
259
264
  """``trackedIdle`` in local decimals plus ``convertToAssets`` of every
@@ -299,7 +304,7 @@ class StargateTokenMessaging(ContractWrapper):
299
304
  """Stargate V2 ``TokenMessaging``, the OApp that carries taxi packets:
300
305
  ``peers`` tells whether a route to an endpoint id exists."""
301
306
 
302
- def peers(self, eid: int) -> Call[bytes]:
307
+ def peers(self, eid: LayerZeroEid) -> Call[bytes]:
303
308
  """The peer OApp on ``eid`` as bytes32; zero when no route exists."""
304
309
  return self._view("peers(uint32)", eid, output_types=["bytes32"])
305
310
 
@@ -332,10 +337,12 @@ class StargateCrosschainFactory(CrosschainFactory):
332
337
  decoder=_address,
333
338
  )
334
339
 
335
- def eid_of(self, chain_id: ChainId) -> Call[int]:
336
- return self._view("eidOf(uint256)", chain_id, output_types=["uint32"])
340
+ def eid_of(self, chain_id: ChainId) -> Call[LayerZeroEid]:
341
+ return self._view(
342
+ "eidOf(uint256)", chain_id, output_types=["uint32"], decoder=LayerZeroEid
343
+ )
337
344
 
338
- def chain_id_of(self, eid: int) -> Call[ChainId]:
345
+ def chain_id_of(self, eid: LayerZeroEid) -> Call[ChainId]:
339
346
  return self._view(
340
347
  "chainIdOf(uint32)", eid, output_types=["uint256"], decoder=ChainId
341
348
  )
@@ -30,7 +30,7 @@ from web3 import Web3
30
30
  from ipor_fusion.core.address import _validate_not_zero_address
31
31
  from ipor_fusion.core.contract import _encode_calldata
32
32
  from ipor_fusion.crosschain.logs import log_bytes
33
- from ipor_fusion.types import ZERO_ADDRESS, MessageId, StargateAssetId
33
+ from ipor_fusion.types import ZERO_ADDRESS, LayerZeroEid, MessageId, StargateAssetId
34
34
 
35
35
  _TYPE_3 = b"\x00\x03"
36
36
  _EXECUTOR_WORKER_ID = 1
@@ -129,7 +129,7 @@ class EnforcedOptionParam:
129
129
  ``msg_type`` 1 and must carry a non-zero ``lzReceive`` gas; the compose
130
130
  (asset) lane is 2."""
131
131
 
132
- eid: int
132
+ eid: LayerZeroEid
133
133
  msg_type: int
134
134
  options: bytes | OptionsBuilder
135
135
 
@@ -149,9 +149,9 @@ class Packet:
149
149
  """One ``PacketV1Codec`` packet: the payload of a ``PacketSent`` event."""
150
150
 
151
151
  nonce: int
152
- src_eid: int
152
+ src_eid: LayerZeroEid
153
153
  sender: ChecksumAddress
154
- dst_eid: int
154
+ dst_eid: LayerZeroEid
155
155
  receiver: ChecksumAddress
156
156
  guid: MessageId
157
157
  message: bytes
@@ -162,9 +162,9 @@ class Packet:
162
162
  raise ValueError("not a version-1 LayerZero packet")
163
163
  return cls(
164
164
  nonce=int.from_bytes(encoded[1:9], "big"),
165
- src_eid=int.from_bytes(encoded[9:13], "big"),
165
+ src_eid=LayerZeroEid(int.from_bytes(encoded[9:13], "big")),
166
166
  sender=bytes32_to_address(encoded[13:45]),
167
- dst_eid=int.from_bytes(encoded[45:49], "big"),
167
+ dst_eid=LayerZeroEid(int.from_bytes(encoded[45:49], "big")),
168
168
  receiver=bytes32_to_address(encoded[49:81]),
169
169
  guid=MessageId(encoded[81:113]),
170
170
  message=encoded[113:],
@@ -213,7 +213,7 @@ class TaxiMessage:
213
213
 
214
214
 
215
215
  def encode_oft_compose_msg(
216
- nonce: int, src_eid: int, amount_ld: int, compose_msg: bytes
216
+ *, nonce: int, src_eid: LayerZeroEid, amount_ld: int, compose_msg: bytes
217
217
  ) -> bytes:
218
218
  """``OFTComposeMsgCodec.encode(nonce, srcEid, amountLD, composeMsg)``: the
219
219
  message the endpoint passes to ``lzCompose`` after a Stargate delivery.
@@ -228,12 +228,12 @@ def encode_oft_compose_msg(
228
228
 
229
229
  def lz_receive_calldata(
230
230
  *,
231
- src_eid: int,
231
+ src_eid: LayerZeroEid,
232
232
  sender: ChecksumAddress,
233
233
  nonce: int,
234
234
  guid: MessageId,
235
235
  message: bytes,
236
- executor: ChecksumAddress = ZERO_ADDRESS, # type: ignore[assignment]
236
+ executor: ChecksumAddress = ZERO_ADDRESS,
237
237
  extra_data: bytes = b"",
238
238
  ) -> bytes:
239
239
  """``lzReceive(Origin, guid, message, executor, extraData)`` as the endpoint calls it."""
@@ -252,7 +252,7 @@ def lz_compose_calldata(
252
252
  from_: ChecksumAddress,
253
253
  guid: MessageId,
254
254
  message: bytes,
255
- executor: ChecksumAddress = ZERO_ADDRESS, # type: ignore[assignment]
255
+ executor: ChecksumAddress = ZERO_ADDRESS,
256
256
  extra_data: bytes = b"",
257
257
  ) -> bytes:
258
258
  """``lzCompose(from, guid, message, executor, extraData)`` as the endpoint calls it."""
@@ -29,7 +29,7 @@ from ipor_fusion.crosschain.transport import (
29
29
  OutboundMessage,
30
30
  transfer_call,
31
31
  )
32
- from ipor_fusion.types import ChainId, StargateAssetId
32
+ from ipor_fusion.types import ChainId, LayerZeroEid, StargateAssetId
33
33
 
34
34
 
35
35
  @dataclass(frozen=True, slots=True)
@@ -45,7 +45,7 @@ class StargateChain:
45
45
  """
46
46
 
47
47
  chain_id: ChainId
48
- eid: int
48
+ eid: LayerZeroEid
49
49
  endpoint: ChecksumAddress
50
50
  token_messaging: ChecksumAddress
51
51
  stargate_pool: ChecksumAddress
@@ -87,7 +87,7 @@ class StargateTransport(CrosschainTransport):
87
87
 
88
88
  def __init__(self, chains: Iterable[StargateChain]) -> None:
89
89
  self._chains: dict[ChainId, StargateChain] = {}
90
- self._by_eid: dict[int, StargateChain] = {}
90
+ self._by_eid: dict[LayerZeroEid, StargateChain] = {}
91
91
  for chain in chains:
92
92
  if chain.chain_id in self._chains:
93
93
  raise ValueError(f"duplicate Stargate chain_id {chain.chain_id}")
@@ -194,7 +194,10 @@ class StargateTransport(CrosschainTransport):
194
194
  ]
195
195
  taxi: TaxiMessage = message.raw[1]
196
196
  compose = encode_oft_compose_msg(
197
- packet.nonce, packet.src_eid, message.token_amount, taxi.compose_msg
197
+ nonce=packet.nonce,
198
+ src_eid=packet.src_eid,
199
+ amount_ld=message.token_amount,
200
+ compose_msg=taxi.compose_msg,
198
201
  )
199
202
  return [
200
203
  transfer_call(
@@ -9,7 +9,7 @@ from eth_abi.exceptions import InsufficientDataBytes
9
9
  from eth_typing import ChecksumAddress
10
10
  from eth_utils import function_signature_to_4byte_selector
11
11
  from web3 import Web3
12
- from web3.types import TxReceipt
12
+ from web3.types import TxParams, TxReceipt
13
13
 
14
14
  from ipor_fusion.types import TxHash
15
15
 
@@ -119,7 +119,7 @@ def get_revert_reason(web3: Web3, tx_hash: TxHash, receipt: TxReceipt) -> str |
119
119
  """Replay a failed tx as eth_call to capture revert data."""
120
120
  try:
121
121
  tx = web3.eth.get_transaction(tx_hash)
122
- call_params = {
122
+ call_params: TxParams = {
123
123
  "from": tx["from"],
124
124
  "to": tx["to"],
125
125
  "data": tx["input"],
@@ -129,7 +129,7 @@ def get_revert_reason(web3: Web3, tx_hash: TxHash, receipt: TxReceipt) -> str |
129
129
  call_params["gas"] = tx["gas"]
130
130
 
131
131
  block_number = receipt["blockNumber"]
132
- web3.eth.call(call_params, block_identifier=block_number) # type: ignore[arg-type]
132
+ web3.eth.call(call_params, block_identifier=block_number)
133
133
  return None # replay succeeded — can't determine reason
134
134
  except Exception as exc:
135
135
  exc_data = getattr(exc, "data", None)
@@ -141,7 +141,7 @@ def _fetch_getsourcecode(
141
141
  with urlopen(url, timeout=10) as resp: # noqa: S310
142
142
  data = json.loads(resp.read().decode())
143
143
  if data.get("status") == "1" and data.get("result"):
144
- return data["result"][0] # type: ignore[no-any-return]
144
+ return data["result"][0]
145
145
  if "rate limit" in str(data.get("result", "")).lower():
146
146
  time.sleep(_RETRY_DELAY * (attempt + 1))
147
147
  continue
@@ -42,7 +42,11 @@ class Fuse(ABC): # noqa: B024 # ABC marks intent; no shared abstract method
42
42
  raise ValueError(f"{name} must not be negative, got {value}")
43
43
 
44
44
  def __eq__(self, other: object) -> bool:
45
- return type(self) is type(other) and self._address == other._address # type: ignore[attr-defined]
45
+ return (
46
+ isinstance(other, Fuse)
47
+ and type(self) is type(other)
48
+ and self._address == other._address
49
+ )
46
50
 
47
51
  def __hash__(self) -> int:
48
52
  return hash((type(self), self._address))
@@ -42,7 +42,7 @@ class CcipSendParams(SendParams):
42
42
 
43
43
  max_fee: int
44
44
  gas_limit: int
45
- fee_token: ChecksumAddress = ZERO_ADDRESS # type: ignore[assignment]
45
+ fee_token: ChecksumAddress = ZERO_ADDRESS
46
46
 
47
47
  @classmethod
48
48
  def from_route(cls, route: CcipRouteConfig, *, token: bool) -> CcipSendParams:
@@ -148,8 +148,8 @@ class CcipCommandType(IntEnum):
148
148
 
149
149
  _EMPTY_ROUTE = CcipRouteConfig(
150
150
  chain_selector=0,
151
- peer=ZERO_ADDRESS, # type: ignore[arg-type]
152
- fee_token=ZERO_ADDRESS, # type: ignore[arg-type]
151
+ peer=ZERO_ADDRESS,
152
+ fee_token=ZERO_ADDRESS,
153
153
  message_gas_limit=0,
154
154
  token_gas_limit=0,
155
155
  max_fee=0,
@@ -255,7 +255,7 @@ class CcipCrosschainCommandFuse(CrosschainCommandFuse):
255
255
  command: Command = EMPTY_COMMAND,
256
256
  max_fee: int = 0,
257
257
  gas_limit: int = 0,
258
- fee_token: ChecksumAddress = ZERO_ADDRESS, # type: ignore[assignment]
258
+ fee_token: ChecksumAddress = ZERO_ADDRESS,
259
259
  ) -> FuseAction:
260
260
  """Forward one operation to the executor (or its factory):
261
261
 
@@ -33,9 +33,10 @@ from ipor_fusion.fuses.crosschain.base import (
33
33
  CrosschainSupplyFuse,
34
34
  SendParams,
35
35
  )
36
- from ipor_fusion.types import Amount, ChainId, DeploymentRequestId
36
+ from ipor_fusion.types import Amount, ChainId, DeploymentRequestId, LayerZeroEid
37
37
 
38
38
  _NO_CHAIN = ChainId(0)
39
+ _NO_EID = LayerZeroEid(0)
39
40
  _NO_REQUEST = DeploymentRequestId(0)
40
41
  _ZERO_AMOUNT = Amount(0)
41
42
 
@@ -172,7 +173,7 @@ class StargateCrosschainCommandFuse(CrosschainCommandFuse):
172
173
  *,
173
174
  executor: ChecksumAddress,
174
175
  chain_id: ChainId = _NO_CHAIN,
175
- eid: int = 0,
176
+ eid: LayerZeroEid = _NO_EID,
176
177
  enforced_options: bytes = b"",
177
178
  plasma_vaults: Sequence[ChecksumAddress] = (),
178
179
  request_id: DeploymentRequestId = _NO_REQUEST,
@@ -264,7 +265,7 @@ class StargateCrosschainCommandFuse(CrosschainCommandFuse):
264
265
  self,
265
266
  command_type: StargateCrosschainCommandType,
266
267
  chain_id: ChainId,
267
- eid: int,
268
+ eid: LayerZeroEid,
268
269
  request_id: DeploymentRequestId,
269
270
  command: Command,
270
271
  ) -> None:
@@ -65,7 +65,11 @@ class UniversalTokenSwapperFuse(Fuse):
65
65
  self._abi = abi
66
66
 
67
67
  def __eq__(self, other: object) -> bool:
68
- return super().__eq__(other) and self._abi is other._abi # type: ignore[attr-defined]
68
+ return (
69
+ isinstance(other, UniversalTokenSwapperFuse)
70
+ and super().__eq__(other)
71
+ and self._abi is other._abi
72
+ )
69
73
 
70
74
  def __hash__(self) -> int:
71
75
  return hash((type(self), self._address, self._abi))
@@ -6,9 +6,7 @@ from web3 import Web3
6
6
  from ipor_fusion.core.contract import Call, ContractWrapper
7
7
  from ipor_fusion.core.erc20 import ERC20
8
8
  from ipor_fusion.core.multicall import Multicall3
9
- from ipor_fusion.types import Amount
10
-
11
- _ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
9
+ from ipor_fusion.types import ZERO_ADDRESS, Amount
12
10
 
13
11
  # DataTypes.ReserveDataLegacy returned by Pool.getReserveData(address).
14
12
  # All fields are static, so the struct is encoded inline as a flat sequence.
@@ -163,7 +161,7 @@ class AaveV3Reader(ContractWrapper):
163
161
  tokens.variable_debt_token,
164
162
  tokens.stable_debt_token,
165
163
  )
166
- listed = [token for token in reserve_tokens if token.lower() != _ZERO_ADDRESS]
164
+ listed = [token for token in reserve_tokens if token.lower() != ZERO_ADDRESS]
167
165
  balances = iter(
168
166
  Multicall3(self._ctx).aggregate(
169
167
  [ERC20(self._ctx, token).balance_of(user) for token in listed]
@@ -13,12 +13,18 @@ from ipor_fusion.core.context import Web3Context
13
13
  from ipor_fusion.core.contract import Call, ContractWrapper
14
14
  from ipor_fusion.core.multicall import Multicall3
15
15
  from ipor_fusion.errors import MorphoMarketNotFoundError, UnsupportedChainError
16
- from ipor_fusion.types import Amount, ChainId, Fee, MorphoBlueMarketId, Shares
16
+ from ipor_fusion.types import (
17
+ ZERO_ADDRESS,
18
+ Amount,
19
+ ChainId,
20
+ Fee,
21
+ MorphoBlueMarketId,
22
+ Shares,
23
+ )
17
24
 
18
25
  WAD = 10**18
19
26
  SECONDS_PER_YEAR = 365 * 24 * 60 * 60 # matches Morpho IRM YEAR constant
20
27
 
21
- _ZERO_ADDRESS = Web3.to_checksum_address("0x" + "00" * 20)
22
28
  _addr = Web3.to_checksum_address
23
29
 
24
30
  # Morpho Blue sits at its CREATE2 address (0xBBBB…EEFFCb) on Ethereum and Base
@@ -257,7 +263,7 @@ class MorphoReader(ContractWrapper):
257
263
  Use this when the caller has already read `market()` and `market_params()`
258
264
  to avoid two redundant RPC roundtrips.
259
265
  """
260
- if params.irm == _ZERO_ADDRESS:
266
+ if params.irm == ZERO_ADDRESS:
261
267
  # Morpho skips the IRM when irm == address(0) (MetaMorpho "idle"
262
268
  # markets): no interest accrues, so every rate is zero.
263
269
  rate_wad = 0
@@ -14,6 +14,7 @@ from eth_typing import ChecksumAddress
14
14
 
15
15
  from ipor_fusion.readers.oracle_mapping_reader import OracleMappingReader
16
16
  from ipor_fusion.types import (
17
+ ZERO_ADDRESS,
17
18
  AssetSource,
18
19
  MappingStatus,
19
20
  NodeStatus,
@@ -21,9 +22,6 @@ from ipor_fusion.types import (
21
22
  Price,
22
23
  )
23
24
 
24
- ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
25
-
26
-
27
25
  # source_type values (also the human label in the path / output). Adding a
28
26
  # type? Follow the "Adding a feed type" checklist in readers/oracle_mapping.py.
29
27
  TYPE_CHAINLINK = "ChainlinkAggregator"
@@ -33,8 +33,8 @@ ASSET_PRICE_SOURCE_UPDATED_TOPIC = (
33
33
  )
34
34
 
35
35
 
36
- def _addr(value: object) -> ChecksumAddress:
37
- return Web3.to_checksum_address(value) # type: ignore[arg-type]
36
+ def _addr(value: str) -> ChecksumAddress:
37
+ return Web3.to_checksum_address(value)
38
38
 
39
39
 
40
40
  class _Erc4626Feed(ContractWrapper):