ipor-fusion 4.0.0.dev240__tar.gz → 4.0.0.dev249__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 (342) hide show
  1. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/AGENTS.md +39 -20
  2. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/MIGRATION_4.md +45 -27
  3. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/PKG-INFO +15 -8
  4. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/README.md +14 -7
  5. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/pyproject.toml +1 -1
  6. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/market_cmd.py +6 -5
  7. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/mcp/server.py +16 -9
  8. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_mcp_server.py +76 -0
  9. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.claude-plugin/marketplace.json +0 -0
  10. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.claude-plugin/plugin.json +0 -0
  11. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.env.example +0 -0
  12. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.gemini/settings.json +0 -0
  13. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.git-blame-ignore-revs +0 -0
  14. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.github/workflows/cd.yml +0 -0
  15. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.github/workflows/ci.yml +0 -0
  16. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.github/workflows/commit-lint.yml +0 -0
  17. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.github/workflows/notify-slack.yml +0 -0
  18. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.github/workflows/python-build.yml +0 -0
  19. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.github/workflows/python-publish.yml +0 -0
  20. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.github/workflows/release.yml +0 -0
  21. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.github/workflows/report-slack.yml +0 -0
  22. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.gitignore +0 -0
  23. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.mcp.json +0 -0
  24. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.pre-commit-config.yaml +0 -0
  25. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/.python-version +0 -0
  26. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/CHANGELOG.md +0 -0
  27. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/CLAUDE.md +0 -0
  28. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/CODEOWNERS +0 -0
  29. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/LICENSE +0 -0
  30. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/build.sh +0 -0
  31. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/examples/CONTRIBUTING.md +0 -0
  32. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/examples/README.md +0 -0
  33. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  34. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  35. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  36. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  37. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/examples/external_state_margin_leg_base.py +0 -0
  38. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/examples/simple_aave_v3_supply_base.py +0 -0
  39. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/pytest.ini +0 -0
  40. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/scripts/commit-check.sh +0 -0
  41. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/scripts/crosschain_readiness.py +0 -0
  42. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/skills/ipor-deploy-vault/SKILL.md +0 -0
  43. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/__init__.py +0 -0
  44. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/about.py +0 -0
  45. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cache.py +0 -0
  46. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/chains.py +0 -0
  47. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/__init__.py +0 -0
  48. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  49. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/config_cmd.py +0 -0
  50. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/config_store.py +0 -0
  51. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/main.py +0 -0
  52. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/options.py +0 -0
  53. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  54. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  55. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/vault_health.py +0 -0
  56. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  57. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  58. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  59. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/config/__init__.py +0 -0
  60. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/config/roles.py +0 -0
  61. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/__init__.py +0 -0
  62. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/access.py +0 -0
  63. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/address.py +0 -0
  64. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/context.py +0 -0
  65. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/contract.py +0 -0
  66. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/erc20.py +0 -0
  67. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/external_state_executor.py +0 -0
  68. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/fee_manager.py +0 -0
  69. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/fuse.py +0 -0
  70. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/fuse_action.py +0 -0
  71. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/fusion_factory.py +0 -0
  72. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/logs.py +0 -0
  73. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/multicall.py +0 -0
  74. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/oracle.py +0 -0
  75. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/plasma_vault.py +0 -0
  76. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/rewards_manager.py +0 -0
  77. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/simulation.py +0 -0
  78. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/simulation_result.py +0 -0
  79. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  80. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/__init__.py +0 -0
  81. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  82. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  83. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  84. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  85. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  86. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  87. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  88. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  89. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  90. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/contracts.py +0 -0
  91. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/discovery.py +0 -0
  92. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/errors.py +0 -0
  93. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  94. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  95. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  96. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  97. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/lane.py +0 -0
  98. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/logs.py +0 -0
  99. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/messages.py +0 -0
  100. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/simulation.py +0 -0
  101. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  102. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  103. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  104. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  105. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  106. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/crosschain/transport.py +0 -0
  107. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/dependency_graph.py +0 -0
  108. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/entry_cli.py +0 -0
  109. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/entry_mcp.py +0 -0
  110. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/errors.py +0 -0
  111. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/explorer.py +0 -0
  112. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/field_docs.py +0 -0
  113. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/__init__.py +0 -0
  114. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  115. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/async_action.py +0 -0
  116. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/base.py +0 -0
  117. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  118. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/erc4626.py +0 -0
  119. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  120. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/events.py +0 -0
  121. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/external_state.py +0 -0
  122. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
  123. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  124. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  125. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/hypercore.py +0 -0
  126. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
  127. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/merkl.py +0 -0
  128. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/morpho.py +0 -0
  129. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  130. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  131. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/fuses/universal.py +0 -0
  132. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/guide/__init__.py +0 -0
  133. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/guide/architecture.md +0 -0
  134. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/guide/glossary.md +0 -0
  135. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/guide/invariants.md +0 -0
  136. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/guide/quickstart.md +0 -0
  137. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/hypercore/__init__.py +0 -0
  138. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/market_ids.py +0 -0
  139. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/market_info.py +0 -0
  140. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/mcp/__init__.py +0 -0
  141. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/mcp/guide.py +0 -0
  142. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/mcp/models.py +0 -0
  143. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/morpho_api.py +0 -0
  144. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/py.typed +0 -0
  145. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/__init__.py +0 -0
  146. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/aave_v3.py +0 -0
  147. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/aave_v4.py +0 -0
  148. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/compound_v3.py +0 -0
  149. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/hypercore.py +0 -0
  150. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  151. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/lending_health.py +0 -0
  152. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/morpho.py +0 -0
  153. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  154. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  155. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  156. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/position_manager.py +0 -0
  157. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  158. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  159. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/readers/unpriceable.py +0 -0
  160. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/substrates.py +0 -0
  161. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/types.py +0 -0
  162. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/validation.py +0 -0
  163. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/vault_data.py +0 -0
  164. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/vault_erc20.py +0 -0
  165. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/vault_info.py +0 -0
  166. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/vault_managers.py +0 -0
  167. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/vault_positions.py +0 -0
  168. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/src/ipor_fusion/vault_reads.py +0 -0
  169. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/UniswapV3UniversalRouter.py +0 -0
  170. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_crosschain.py +0 -0
  171. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_crosschain_lifecycle.py +0 -0
  172. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_crosschain_recovery.py +0 -0
  173. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_euler_v2.py +0 -0
  174. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_event_args.py +0 -0
  175. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_fake_rpc.py +0 -0
  176. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_foundry.py +0 -0
  177. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_golden.py +0 -0
  178. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_guide.py +0 -0
  179. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_hypercore_model.py +0 -0
  180. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_hypercore_shadow.py +0 -0
  181. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_live.py +0 -0
  182. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_multicall.py +0 -0
  183. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_oracle_mapping_fake.py +0 -0
  184. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_simulate.py +0 -0
  185. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/_validation.py +0 -0
  186. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/addresses.py +0 -0
  187. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/conftest.py +0 -0
  188. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/constants.py +0 -0
  189. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  190. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/hypercore_market55.json +0 -0
  191. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
  192. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
  193. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
  194. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
  195. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
  196. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
  197. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
  198. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
  199. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_blue.json +0 -0
  200. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
  201. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
  202. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
  203. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
  204. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
  205. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
  206. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
  207. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/simulate_v1_request.json +0 -0
  208. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/clean.json +0 -0
  209. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
  210. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  211. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
  212. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
  213. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
  214. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
  215. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/fetched.json +0 -0
  216. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
  217. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/full.json +0 -0
  218. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
  219. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/full.txt +0 -0
  220. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/rich.json +0 -0
  221. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
  222. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  223. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_about.py +0 -0
  224. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_access_manager.py +0 -0
  225. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cache.py +0 -0
  226. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_chains.py +0 -0
  227. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_changelog.py +0 -0
  228. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_commands.py +0 -0
  229. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_config_store.py +0 -0
  230. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_helpers.py +0 -0
  231. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_hypercore.py +0 -0
  232. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_lending_health.py +0 -0
  233. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_market_golden.py +0 -0
  234. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_market_morpho_blue.py +0 -0
  235. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_mcp_parity.py +0 -0
  236. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_oracle_mapping.py +0 -0
  237. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_oracle_mapping_golden.py +0 -0
  238. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_pending_requests_scan.py +0 -0
  239. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_role_accounts.py +0 -0
  240. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_vault_info_golden.py +0 -0
  241. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_vault_predates_getters.py +0 -0
  242. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_cli_vault_validate.py +0 -0
  243. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_contract_call.py +0 -0
  244. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_contract_encoder.py +0 -0
  245. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_ccip_events.py +0 -0
  246. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_ccip_indexer.py +0 -0
  247. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_ccip_quote.py +0 -0
  248. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_chainlink.py +0 -0
  249. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_contracts.py +0 -0
  250. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_encoding.py +0 -0
  251. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_errors.py +0 -0
  252. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_lane.py +0 -0
  253. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_layering.py +0 -0
  254. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_readiness.py +0 -0
  255. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_readiness_report.py +0 -0
  256. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_crosschain_transport.py +0 -0
  257. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_docs_typing.py +0 -0
  258. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_erc20.py +0 -0
  259. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_error_hierarchy.py +0 -0
  260. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_examples_composed_hypercore.py +0 -0
  261. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_examples_crosschain.py +0 -0
  262. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  263. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_examples_vaults.py +0 -0
  264. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_explorer.py +0 -0
  265. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_external_state_events.py +0 -0
  266. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_external_state_executor.py +0 -0
  267. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_fee_manager.py +0 -0
  268. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_fuse_encoding.py +0 -0
  269. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_fusion_instance.py +0 -0
  270. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_guide.py +0 -0
  271. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_hypercore_deployment.py +0 -0
  272. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_hypercore_events.py +0 -0
  273. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_hypercore_flow.py +0 -0
  274. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_hypercore_live.py +0 -0
  275. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_hypercore_outcome.py +0 -0
  276. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_hypercore_readers.py +0 -0
  277. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_hypercore_simulation.py +0 -0
  278. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_logs.py +0 -0
  279. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_mcp_hypercore.py +0 -0
  280. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_mcp_models.py +0 -0
  281. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_mcp_vault_validate.py +0 -0
  282. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_multicall.py +0 -0
  283. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_oracle.py +0 -0
  284. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_oracle_mapping.py +0 -0
  285. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_plasma_vault.py +0 -0
  286. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_public_api.py +0 -0
  287. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_public_type_reachability.py +0 -0
  288. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_reader_encoding.py +0 -0
  289. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_readiness_selection.py +0 -0
  290. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_revert_decoding.py +0 -0
  291. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_rewards_manager.py +0 -0
  292. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_roles.py +0 -0
  293. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_sdk_layering.py +0 -0
  294. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  295. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  296. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  297. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_crosschain_canary.py +0 -0
  298. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_crosschain_hypercore.py +0 -0
  299. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  300. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  301. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  302. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  303. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  304. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  305. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  306. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_examples_arbitrum.py +0 -0
  307. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_examples_base.py +0 -0
  308. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_examples_crosschain.py +0 -0
  309. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_external_state_executor_base.py +0 -0
  310. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  311. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  312. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  313. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_morpho_blue.py +0 -0
  314. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_pilot_v3.py +0 -0
  315. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  316. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  317. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  318. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  319. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_readers_integration.py +0 -0
  320. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_release_funds.py +0 -0
  321. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_swap_on_base.py +0 -0
  322. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  323. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulation_errors.py +0 -0
  324. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_simulation_observation.py +0 -0
  325. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_solidity_compiler.py +0 -0
  326. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_solidity_mirrors.py +0 -0
  327. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_substrates.py +0 -0
  328. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_types_and_events.py +0 -0
  329. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_unpriceable_tokens.py +0 -0
  330. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_validation.py +0 -0
  331. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_vault_data_block.py +0 -0
  332. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_vault_dep_graph.py +0 -0
  333. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_vault_info_fetched_golden.py +0 -0
  334. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_vault_info_onchain_base.py +0 -0
  335. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  336. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_vault_reconciliation_onchain_base.py +0 -0
  337. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_vault_simulator_blocks.py +0 -0
  338. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_web3_context.py +0 -0
  339. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/test_withdraw_manager.py +0 -0
  340. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/typed_client/consumer.py +0 -0
  341. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/tests/typed_client/rejected.py +0 -0
  342. {ipor_fusion-4.0.0.dev240 → ipor_fusion-4.0.0.dev249}/uv.lock +0 -0
@@ -28,7 +28,7 @@ uv run ruff format ./ # format (CI runs
28
28
  uv run ruff check ./ # lint: bandit S, C901 max-complexity 10, PLR limits
29
29
  uv run pyright # types, basic mode, src + tests + examples + scripts
30
30
  uv run pytest # all tests + coverage gate (fail_under = 98, mcp/ included)
31
- uv run pytest -m "cli or mcp" --no-cov # fast offline subset
31
+ uv run pytest -m "cli or mcp" --no-cov # CLI/MCP subset (the full suite is mostly offline too)
32
32
  uv run pytest --run-readiness -m readiness --no-cov -rA # opt-in live Ethereum-hub POC checks
33
33
  uv run python scripts/crosschain_readiness.py --output /tmp/crosschain-readiness.json # live Arbitrum/HyperEVM report
34
34
  uv run pytest tests/test_fuse_encoding.py -k aave --no-cov # single file / test
@@ -59,9 +59,9 @@ everything else is `sdk`). Most `sdk` tests are offline too: the suite with no
59
59
  provider set still clears the coverage gate.
60
60
  Integration tests (`test_simulate_*`, `test_vault_info_onchain_*`) run against
61
61
  live RPCs via `eth_simulateV1`, no Docker or Anvil (`ANVIL_WALLET` is just a
62
- named address constant). They auto-skip unless `ETHEREUM_PROVIDER_URL`,
63
- `BASE_PROVIDER_URL` and `ARBITRUM_PROVIDER_URL` are set (`.env` is loaded via
64
- python-dotenv) and the provider supports `eth_simulateV1`. Every such gate goes
62
+ named address constant). Each skips unless the provider it needs
63
+ (`ETHEREUM_PROVIDER_URL`, `BASE_PROVIDER_URL` or `ARBITRUM_PROVIDER_URL`; `.env` is
64
+ loaded via python-dotenv) is set and supports `eth_simulateV1`. Every such gate goes
65
65
  through `tests/_live.py` (`provider_url`, `archive_context`, `unavailable`): it
66
66
  skips locally and fails when the source is named in `IPOR_FUSION_REQUIRE_LIVE`
67
67
  (comma-separated env var names, `GITHUB` for the Solidity mirrors). CI
@@ -84,9 +84,11 @@ executors from the creation code they store; `source` builds a factory pair from
84
84
  that revision (the fuses are compiled from it); a checkout without `node_modules` also
85
85
  needs `IPOR_FUSION_FOUNDRY_REMAPPINGS` for OpenZeppelin. `test_crosschain_ccip_events.py`
86
86
  mirrors the CCIP event registry from a checkout at its own `CONTRACTS_REVISION`.
87
- `test_solidity_compiler.py` uses `IPOR_FUSION_CONTRACTS_DIR` for a local contracts
88
- checkout and optional `IPOR_FUSION_FOUNDRY_REMAPPINGS`. These tests skip when their
89
- external prerequisites are unavailable.
87
+ `test_solidity_compiler.py` unit-tests that compiler helper (`tests/_foundry.py`)
88
+ offline, with `forge` and `git` mocked. The checkout-based tests above skip when their
89
+ external prerequisites are unavailable. `test_hypercore_live.py`,
90
+ `test_hypercore_deployment.py` and `test_hypercore_simulation.py` need
91
+ `HYPEREVM_PROVIDER_URL` as well.
90
92
  `test_simulate_crosschain_lifecycle.py` drives the mainnet POC deployment from
91
93
  Ethereum to every spoke on every transport through `CrosschainLane` and
92
94
  `CrosschainSimulator`; the fixtures (`Chain`, `Deployment`, `Spoke`, pinned
@@ -110,8 +112,9 @@ the deployed runtimes, and `tests/_hypercore_model.py` answers those addresses t
110
112
  tests prove EVM validation under supplied Core state. `test_hypercore_simulation.py`
111
113
  runs one vault cycle; `test_simulate_crosschain_hypercore.py` runs the composed flow
112
114
  (v3 canary hub → CCIP → the market-55 vault → a trade → recall → claim) and holds every
113
- CCIP delivery to its route gas limit. Both skip without the source directory and both
114
- RPCs.
115
+ CCIP delivery to its route gas limit. Both skip without the source directory;
116
+ `test_hypercore_simulation.py` also needs `HYPEREVM_PROVIDER_URL`, the composed flow
117
+ `ARBITRUM_PROVIDER_URL` as well.
115
118
  `test_crosschain_readiness.py` checks the Ethereum-hub POC's live bridge and IPOR
116
119
  preconditions. Its `readiness` marker requires `--run-readiness`: ordinary runs
117
120
  exclude it even with `-m sdk`. Known HyperEVM gaps are non-strict xfails restricted
@@ -243,8 +246,10 @@ the lifecycle matrix follows that.
243
246
  snapshots), `vault_positions.py` (Morpho/Aave position breakdowns and their token
244
247
  prices) and `vault_erc20.py` (the ERC20_VAULT_BALANCE holdings);
245
248
  `vault_info.py` — `build_vault_info_json(data)`, the `fusion vault info --json`
246
- document rendered from the snapshot alone (contract names via an injected
247
- `resolve_name`) and typed block by block (`VaultInfoJson` and its nested
249
+ document rendered from the snapshot, plus three inputs that are not chain state at
250
+ its block: contract names through the injected `resolve_name` (Etherscan I/O on a
251
+ pool), the deployment fields the caller writes into the snapshot from
252
+ `fetch_deployment_info`, and `age_days` from the wall clock; typed block by block (`VaultInfoJson` and its nested
248
253
  TypedDicts), and `health_check_lists`; `explorer.py` Etherscan V2 (single endpoint, needs API key);
249
254
  `cache.py` the `ContractCache` protocol (`get`/`put` of immutable facts keyed
250
255
  `CacheKey(chain_id, address, field)`), `FileContractCache` (one JSON file, atomic
@@ -284,13 +289,16 @@ the lifecycle matrix follows that.
284
289
  uniswap_v3, universal); `events.py` (`extract_events`, `abi_value` and the raw-log
285
290
  event decoder both event families share), `external_state_event_decoding.py` and
286
291
  `hypercore_event_decoding.py` (the ExternalState and HyperCore event specs, classes
287
- and decoders; internal modules, the names are exported from `ipor_fusion.fuses`);
292
+ and decoders; internal modules, the ExternalState names are exported from
293
+ `ipor_fusion.fuses`, the HyperCore ones from `ipor_fusion.hypercore`), `hypercore.py`
294
+ (the HyperCore fuses, market 55);
288
295
  `base.py` re-exports the `core/fuse.py` base and validators,
289
296
  `FuseAction` and `ZERO_ADDRESS` (from `types`). A fuse is a value: equality and
290
297
  hash cover its class and every attribute it stores
291
298
  - `crosschain/` — crosschain Plasma Vaults (executor on the hub chain, dispatcher at
292
299
  the same address on each spoke chain), split like the contracts: shared `messages`
293
- (`Command`, `MsgType`, envelopes), `contracts` (wrapper bases), `transport`
300
+ (`Command`, `MsgType`, envelopes), `logs` (`log_bytes` and the raw-log field
301
+ helpers the decoders share), `contracts` (wrapper bases), `transport`
294
302
  (`CrosschainTransport` seam), `lane` (`CrosschainLane`: one executor/dispatcher pair
295
303
  driven without knowing its transport; it carries its transport's fuse classes and
296
304
  the keeper's attestation helpers), `discovery` (`discover_deployment` reads the
@@ -304,6 +312,8 @@ the lifecycle matrix follows that.
304
312
  `ccip/`, each with its wire codecs, executor/dispatcher/factory wrappers, transport
305
313
  and lane; `ccip/chainlink` reads Chainlink's Router, OnRamp, TokenAdminRegistry and
306
314
  token pool (`ccip_token_lane`: is there a lane, does the token travel on it);
315
+ `ccip/quote` (`quote_ccip_native_fee`: the exact native fee of a send, from a
316
+ simulation with the payer's balance at zero);
307
317
  `ccip/events` decodes the executor/dispatcher/factory events by topic (one
308
318
  registry, mirrored from the v3 source; the v2 pair emits a subset of it);
309
319
  `ccip/chainlink` also wraps the 2.0 OffRamp, verifier resolver and committee verifier
@@ -314,19 +324,27 @@ the lifecycle matrix follows that.
314
324
  mirroring `contracts/fuses/crosschain/`; they build on `core/fuse.py`, so the
315
325
  dependency direction is `core` <- `crosschain` and `fuses/` holds no crosschain code
316
326
  (neither package imports the other). Names mirror the Solidity contracts and
317
- libraries. Adding a transport: a new subpackage, its encoders in `crosschain/fuses/`
318
- and one `LANES` entry. Layering: the encoders import the wire codecs and wrappers;
327
+ libraries. Adding a transport: a new subpackage, its encoders in `crosschain/fuses/`,
328
+ one `LANES` entry and its probe in `discovery.detect_transport` (CCIP answers
329
+ `transportKind()`, Stargate a non-zero `STARGATE_POOL()`). Layering: the encoders import the wire codecs and wrappers;
319
330
  lanes, discovery, transports and the simulator import the encoders.
320
331
  `ipor_fusion.crosschain` exports the whole surface (encoders, lanes, discovery,
321
332
  transports, simulator). A package `__init__` runs before its modules and the
322
333
  encoders import `ccip.contracts` / `stargate.layerzero`, so `.ccip` and `.stargate`
323
- export only their codecs and wrappers. `tests/test_crosschain_layering.py` pins this:
334
+ export only what sits below the encoders: their codecs and wrappers, and for
335
+ `.ccip` also the Chainlink readers (`ccip_token_lane`), the fee quote
336
+ (`quote_ccip_native_fee`) and the indexer client (`fetch_verifier_result`,
337
+ `manual_execution`). `tests/test_crosschain_layering.py` pins this:
324
338
  the SDK's module-level import graph, with the implicit package-`__init__` edges, is
325
339
  acyclic, and the crosschain package has no function-level or `TYPE_CHECKING`
326
340
  imports; never work around a cycle with a lazy import, move the module instead.
327
- - `readers/` — read side: lending_health, oracle_mapping, position_manager, aave_v3,
328
- compound_v3, morpho, ramses_v2, uniswap_v3, unpriceable (middleware-priced tokens of
329
- granted substrates the oracle cannot price)
341
+ - `readers/` — read side: lending_health, oracle_mapping (with oracle_mapping_nodes
342
+ and oracle_mapping_reader), position_manager, aave_v3, aave_v4, compound_v3, morpho,
343
+ ramses_v2, uniswap_v3, unpriceable (middleware-priced tokens of granted substrates
344
+ the oracle cannot price), hypercore and hypercore_outcome (HyperCore reads and
345
+ transfer outcomes)
346
+ - `hypercore/` — the preview HyperCore names re-exported from `fuses/hypercore*.py`
347
+ and `readers/hypercore*.py`; no code of its own
330
348
  - `cli/` — `main.py` root group; `changelog_cmd.py`, `config_cmd.py`, `market_cmd.py`,
331
349
  `vault_cmd.py` command orchestration over `vault_data` / `vault_info`; `options.py`
332
350
  the shared `--chain-id` / address param types and chain/provider resolution;
@@ -387,7 +405,8 @@ or examples.
387
405
  - Fuses (`fuses/`) are stateless encoders: a method returns an immutable
388
406
  `FuseAction(fuse_address, calldata)` and touches no chain. `Fuse` builds it
389
407
  via `self._action_raw(solidity_signature, values)`; the shared `_validate_*`
390
- helpers raise before encoding. `PlasmaVault.execute([actions])` runs the
408
+ helpers raise before encoding (the HyperCore encoders add their own integer-width
409
+ checks, `_validate_uint` / `_validate_positive_uint` in `fuses/hypercore.py`). `PlasmaVault.execute([actions])` runs the
391
410
  batch atomically.
392
411
  - Pin reads, never mutate a shared context: `ctx.at_block(n)` is a copy whose
393
412
  reads (and `get_logs` upper bound) default to block ``n``; `Call.call(ctx, block=n)`
@@ -20,8 +20,9 @@ the details, and each change is described once, in its section.
20
20
  > checker cannot see the calls made through them;
21
21
  > - run the reads against a provider, not only the test suite.
22
22
 
23
- Work through these in order; each is what breaks first at run time after the one
24
- before it is fixed.
23
+ Work through these in order. Removed imports fail first, at import; the rest is
24
+ the recommended order, roughly what fails next at run time (a positional option
25
+ such as `Web3Context.from_url(url, key)` can fail before any wrapper is built).
25
26
 
26
27
  1. **Removed and renamed imports fail first, at import.** The preview features
27
28
  left the top level: crosschain names import from `ipor_fusion.crosschain`,
@@ -47,8 +48,9 @@ before it is fixed.
47
48
  raises `AttributeError`. See [Vault reads and validation](#vault-reads-and-validation).
48
49
  5. **The vault read is one snapshot.** `fetch_vault_data(ctx, vault, *, block=None,
49
50
  lending=True, metadata=True)` takes the vault address and the chain from `ctx`;
50
- `validate_vault_state(state)` and `build_vault_info_json(data, *,
51
- resolve_name=None)` are pure. See [Vault reads and validation](#vault-reads-and-validation).
51
+ `validate_vault_state(state)` reads nothing, and `build_vault_info_json(data, *,
52
+ resolve_name=None)` reads no vault state (contract names come from
53
+ `resolve_name`, the deployment age from the wall clock). See [Vault reads and validation](#vault-reads-and-validation).
52
54
  6. **Simulations return typed observations.** `sim.observe("label", call)` raises
53
55
  `TypeError`: `read = sim.observe(call, label="label")` returns a
54
56
  `SimulationObservation`, read as `read.value` after `run()`;
@@ -282,7 +284,8 @@ The vault read is one pinned snapshot, and the chain is always the context's:
282
284
  `VaultState` also provides `vault`, `chain_id`, `market_balances`, `erc20` and the
283
285
  queue's `shares_to_release_assets`.
284
286
  - `build_vault_info_json(data, *, resolve_name=None)` renders from the snapshot and
285
- reads nothing; the chain and its label come from `data.chain_id`. Without
287
+ reads no vault state (`resolve_name`, which may look names up, and the deployment
288
+ age, computed from the wall clock, are its only other inputs); the chain and its label come from `data.chain_id`. Without
286
289
  `resolve_name` it names no contract (see
287
290
  [Contract cache and request limit](#contract-cache-and-request-limit)).
288
291
  - `fetch_deployment_info(ctx, vault, *, api_key=None)` and
@@ -301,14 +304,16 @@ The vault read is one pinned snapshot, and the chain is always the context's:
301
304
 
302
305
  **Public API.** 4.0 declares it: `ipor_fusion.__all__`, which now also exports the
303
306
  names earlier reachable only by module path (`FusionFactory`, `FusionInstance`,
304
- `fetch_market`, `resolve_token_symbol`, the crosschain codecs, ...). Import from
307
+ `fetch_market`, `resolve_token_symbol`, ...); the preview features are exported only
308
+ from their subpackages (see below). Import from
305
309
  `ipor_fusion`: a module path outside the README's list (*Public API and stability*)
306
310
  still works but is internal. Every project type a public signature or field uses is
307
311
  exported too, among them `AaveV3PositionBreakdown`, `AaveV3ReserveTokens`,
308
- `AccountRequest`, `StateOverrideProvider`, `TransferDirection` and the validation
312
+ `AccountRequest`, `StateOverrideProvider` and the validation
309
313
  vocabulary `Severity`, `Verdict` and `Check`. Also new: `InFlightLimit` (see
310
- [Contract cache and request limit](#contract-cache-and-request-limit)) and
311
- `log_bytes`, the hex-or-bytes log field helper the crosschain decoders use.
314
+ [Contract cache and request limit](#contract-cache-and-request-limit)), and in
315
+ `ipor_fusion.crosschain` `log_bytes`, the hex-or-bytes log field helper the
316
+ crosschain decoders use.
312
317
 
313
318
  **Crosschain fuse encoders moved.** The crosschain market encoders moved from
314
319
  `ipor_fusion.fuses.crosschain` into the crosschain package, and
@@ -391,9 +396,9 @@ the modules are internal.
391
396
  - `MORPHO_BLUE_ADDRESS`: use `morpho_blue_address(chain_id)`. `BLOCK_EXPLORER_URLS`,
392
397
  `MORPHO_BLUE_ADDRESSES`, `AAVE_V3_POOL` and `explorer.SUPPORTED_CHAINS` are
393
398
  internal. Import `CHAIN_NAMES`, `CHAIN_NAME_TO_ID`, `SUPPORTED_CHAIN_IDS`,
394
- `GET_LOGS_RANGE_HINTS`, `PUBLIC_ALLOCATOR_ADDRESSES`, `HYPERCORE_PENDING_READERS`,
395
- `HYPEREVM_CHAIN_ID` and `HYPERCORE_MARKET` from `ipor_fusion` (the HyperCore ones
396
- also from `ipor_fusion.readers`).
399
+ `GET_LOGS_RANGE_HINTS`, `PUBLIC_ALLOCATOR_ADDRESSES` and `HYPEREVM_CHAIN_ID` from
400
+ `ipor_fusion` (`HYPEREVM_CHAIN_ID` also from `ipor_fusion.readers`), and
401
+ `HYPERCORE_PENDING_READERS` and `HYPERCORE_MARKET` from `ipor_fusion.hypercore`.
397
402
  - `read_erc20_holdings`: the holdings are `VaultData.erc20`.
398
403
  - `ExternalStateExecutor.mark_nav` and `NavMark`: send `propose_balance` and
399
404
  `confirm_balance` from separate custodians, then
@@ -626,7 +631,10 @@ typed client with pyright's `reportArgumentType`. Other fields keep their types
626
631
  the SDK returns already carry their type and go back in unwrapped.
627
632
 
628
633
  The SDK's own types are importable from `ipor_fusion` (and the public module
629
- `ipor_fusion.types`), as are `USDC_TOKEN_INDEX` and `hip3_action_asset`. Third-party
634
+ `ipor_fusion.types`), except the preview features' ids, which come from their
635
+ subpackages: the crosschain ids (`CommandId`, ...) from `ipor_fusion.crosschain`, the
636
+ HyperCore ids (`HyperCoreAsset`, ...), `USDC_TOKEN_INDEX` and `hip3_action_asset`
637
+ from `ipor_fusion.hypercore`. Third-party
630
638
  types are not re-exported: `ChecksumAddress` and `HexAddress` come from `eth_typing`,
631
639
  and `BlockIdentifier`, `Timestamp` and `TxParams` from `web3.types`. The wrappers
632
640
  label values; they do not validate byte widths or address spelling.
@@ -890,8 +898,10 @@ oracle node's `source_detail`, the Morpho `asset`, `adapters`, `caps`,
890
898
  ## Typed JSON documents
891
899
 
892
900
  The JSON document builders return `TypedDict`s instead of `dict[str, Any]`; the
893
- documents themselves (keys, values, order, the MCP schemas) are unchanged, so
894
- untyped code needs none of this section.
901
+ typing itself changes no document (keys, values, order); the documents' own 4.0
902
+ changes are listed under [Behavior changes](#behavior-changes) (the validation
903
+ document, reconciliation and lending-health fields, MCP null keys, addresses,
904
+ supply cap).
895
905
 
896
906
  | Builder | Returns |
897
907
  |---|---|
@@ -968,8 +978,9 @@ class. An event's args TypedDict is its class's `args` annotation:
968
978
  `typing.get_type_hints(EXTERNAL_STATE_EVENT_CLASSES[name])["args"]`. Values carry
969
979
  nominal types (`Amount`, `MarketId`, `FuseAddress`, `HyperCoreTokenIndex`,
970
980
  `PayloadHash`, web3's `Timestamp`, ...), and `HyperCoreEvent.fuse` is a
971
- `FuseAddress | None` on every event. Every name is importable from `ipor_fusion` and
972
- from `ipor_fusion.fuses`.
981
+ `FuseAddress | None` on every event. The ExternalState names are importable from
982
+ `ipor_fusion` and `ipor_fusion.fuses`; the HyperCore ones (preview) only from
983
+ `ipor_fusion.hypercore`.
973
984
 
974
985
  The two families share one input contract. `decode_external_state_event(log)` and
975
986
  `decode_hypercore_event(log)` take a web3 receipt log or a raw JSON-RPC / indexer
@@ -991,15 +1002,20 @@ These apply to every caller, type-checked or not. Changes described in full abov
991
1002
  are listed with a link.
992
1003
 
993
1004
  - **The CLI and the MCP tools take the same arguments.**
994
- - **MCP inputs:** `chain_id` and `block_number` are optional and omitted means
995
- auto-detected / latest. The `0` sentinel is gone, and `0` is now rejected with
996
- an error naming the argument to omit (it is never read as chain or block 0);
997
- a client that passed `0` must omit the argument instead. The CLI refuses
998
- `--block-number 0` and `--chain-id 0` the same way. `server_info(changelog_since=)` is `server_info(since=)` as in
1005
+ - **MCP inputs:** the vault tools' `chain_id` and every `block_number` are
1006
+ optional and omitted means auto-detected / latest; `market_morpho_blue` and
1007
+ `market_meta_morpho` still require a positive `chain_id`. The `0` sentinel is
1008
+ gone: an optional `0` is rejected with an error naming the argument to omit (it
1009
+ is never read as chain or block 0), so a client that passed `0` omits the
1010
+ argument instead; the market tools' required `chain_id` rejects `0` too and
1011
+ needs the real chain id. The CLI refuses `--block-number 0` and `--chain-id 0`
1012
+ the same way. `server_info(changelog_since=)` is `server_info(since=)` as in
999
1013
  `fusion changelog --since`, and `vault_oracle_mapping` takes `max_depth` like
1000
1014
  `--max-depth`.
1001
- - **Chain auto-detection:** both surfaces use the saved vault's chain, else the
1002
- chain of the only configured provider. The CLI used only the saved vault.
1015
+ - **Chain auto-detection:** the vault commands and tools use the saved vault's
1016
+ chain, else the chain of the only configured provider (the CLI used only the
1017
+ saved vault); `vault_add` uses the only configured provider, and
1018
+ `config_set_provider` asks the provider (`eth_chainId`).
1003
1019
  - **CLI options:** every `--block-number` rejects a negative block, and
1004
1020
  `fusion market morpho-blue --block` is `--block-number` (`--block` still
1005
1021
  works).
@@ -1104,8 +1120,8 @@ are listed with a link.
1104
1120
  and small or empty markets could be off by orders of magnitude: expect materially
1105
1121
  different numbers where they were wrong.
1106
1122
 
1107
- - **MCP `outputSchema` describes every nested block.** The tool output is
1108
- byte-identical; the schemas of `vault_info`, `vault_oracle_mapping`,
1123
+ - **MCP `outputSchema` describes every nested block.** This change alone keeps the
1124
+ tool output byte-identical (the other changes in this list still apply); the schemas of `vault_info`, `vault_oracle_mapping`,
1109
1125
  `market_morpho_blue` and `market_meta_morpho` replace each free-form object with
1110
1126
  the block's own definition: `links` (`VaultLinks`), `deployment`
1111
1127
  (`DeploymentModel` or `DeploymentErrorModel`), `share_price` (`SharePriceModel`),
@@ -1192,4 +1208,6 @@ are listed with a link.
1192
1208
  Both stay unhashable (`__hash__` is `None`, as before). The freeze is shallow:
1193
1209
  the `logs` / `calls` lists themselves can still be mutated.
1194
1210
  - Saved config entries are checksummed when loaded; the MCP `vault_add` tool rejects
1195
- a value that is not an address.
1211
+ a value that is not an address. The MCP vault tools accept a vault address as 3.x
1212
+ did (any letter case, with or without `0x`, surrounding whitespace) and normalize
1213
+ it before the strict SDK parser; only the SDK's `parse_*` functions are strict.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev240
3
+ Version: 4.0.0.dev249
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
@@ -412,7 +412,8 @@ release; adding one does not.
412
412
  - **Third-party types** are not re-exported: import `ChecksumAddress` and
413
413
  `HexAddress` from `eth_typing`, and `BlockIdentifier`, `Timestamp` and `TxParams`
414
414
  from `web3.types`. The SDK's own types (`ChainId`, `Amount`, `PlasmaVaultAddress`,
415
- ...) come from `ipor_fusion` or `ipor_fusion.types`.
415
+ ...) come from `ipor_fusion` or `ipor_fusion.types`; a preview feature's ids
416
+ (`CommandId`, `HyperCoreAsset`, ...) come from its subpackage.
416
417
 
417
418
  `tests/test_public_api.py` snapshots every `__all__` and the names each public
418
419
  module keeps by path, so a change to the surface is a deliberate, reviewed edit.
@@ -489,9 +490,12 @@ import these from their subpackage, and expect changes in a minor release.
489
490
  | Crosschain fuses (market 54): `StargateCrosschainSupplyFuse`, `StargateCrosschainCommandFuse`, `CcipCrosschainSupplyFuse`, `CcipCrosschainCommandFuse`, `CrosschainClaimFuse`; transport-agnostic bases `CrosschainSupplyFuse` / `CrosschainCommandFuse` with `StargateSendParams` / `CcipSendParams`, plus `Command`, `CrosschainSubstrateLib`, `OptionsBuilder` | `ipor_fusion.crosschain` |
490
491
  | HyperCore fuses (market 55): `HyperCoreDepositFuse`, `HyperCoreMarginFuse`, `HyperCoreSendFuse`, `HyperCoreOrderFuse`, `HyperCoreCancelFuse`, `HyperCoreBuilderFeeFuse`, `HyperCoreSubstrates` | `ipor_fusion.hypercore`, with `HyperCoreReader`, `read_hypercore_vault_state` and the event decoders |
491
492
 
492
- The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Use
493
- `crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` for that deployment;
494
- `open_lane` discovers the vault's actual market grants rather than assuming 54.
493
+ The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Pass
494
+ `market_id=crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` to `open_lane`
495
+ / `discover_deployment` for that deployment. Both require it explicitly:
496
+ `discover_deployment` reads the market's executor grants; `open_lane` takes an executor
497
+ address and uses the market ID to find the vault's matching fuses unless they are
498
+ passed.
495
499
 
496
500
  ### Readers (`ipor_fusion.readers`)
497
501
 
@@ -542,15 +546,18 @@ takes the same object.
542
546
  ## Development
543
547
 
544
548
  ```bash
545
- uv sync --all-extras # Install dependencies
549
+ uv sync --all-extras --locked # Install dependencies (as CI)
546
550
  uv run pytest tests/test_fuse_encoding.py -n auto -v # Unit tests (fast, offline)
547
- uv run pytest -v -s # All tests (needs `.*_PROVIDER_URL` variables in `.env`)
551
+ uv run pytest -v -s # All tests; live ones skip without their provider
548
552
  uv run ruff format ./ # Format
549
553
  uv run ruff check ./ # Lint
550
554
  uv run pyright # Type check
551
555
  ```
552
556
 
553
- Integration tests need provider URLs in `.env` (eth_simulateV1 at a pinned fork block — no local node):
557
+ Each integration test runs when its provider URL is in `.env` (eth_simulateV1 at a
558
+ pinned fork block, no local node) and skips otherwise; HyperEVM tests also need
559
+ `HYPEREVM_PROVIDER_URL`, and the source-backed rehearsals a contracts checkout (see
560
+ AGENTS.md):
554
561
 
555
562
  ```bash
556
563
  cp .env.example .env
@@ -364,7 +364,8 @@ release; adding one does not.
364
364
  - **Third-party types** are not re-exported: import `ChecksumAddress` and
365
365
  `HexAddress` from `eth_typing`, and `BlockIdentifier`, `Timestamp` and `TxParams`
366
366
  from `web3.types`. The SDK's own types (`ChainId`, `Amount`, `PlasmaVaultAddress`,
367
- ...) come from `ipor_fusion` or `ipor_fusion.types`.
367
+ ...) come from `ipor_fusion` or `ipor_fusion.types`; a preview feature's ids
368
+ (`CommandId`, `HyperCoreAsset`, ...) come from its subpackage.
368
369
 
369
370
  `tests/test_public_api.py` snapshots every `__all__` and the names each public
370
371
  module keeps by path, so a change to the surface is a deliberate, reviewed edit.
@@ -441,9 +442,12 @@ import these from their subpackage, and expect changes in a minor release.
441
442
  | Crosschain fuses (market 54): `StargateCrosschainSupplyFuse`, `StargateCrosschainCommandFuse`, `CcipCrosschainSupplyFuse`, `CcipCrosschainCommandFuse`, `CrosschainClaimFuse`; transport-agnostic bases `CrosschainSupplyFuse` / `CrosschainCommandFuse` with `StargateSendParams` / `CcipSendParams`, plus `Command`, `CrosschainSubstrateLib`, `OptionsBuilder` | `ipor_fusion.crosschain` |
442
443
  | HyperCore fuses (market 55): `HyperCoreDepositFuse`, `HyperCoreMarginFuse`, `HyperCoreSendFuse`, `HyperCoreOrderFuse`, `HyperCoreCancelFuse`, `HyperCoreBuilderFeeFuse`, `HyperCoreSubstrates` | `ipor_fusion.hypercore`, with `HyperCoreReader`, `read_hypercore_vault_state` and the event decoders |
443
444
 
444
- The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Use
445
- `crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` for that deployment;
446
- `open_lane` discovers the vault's actual market grants rather than assuming 54.
445
+ The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Pass
446
+ `market_id=crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` to `open_lane`
447
+ / `discover_deployment` for that deployment. Both require it explicitly:
448
+ `discover_deployment` reads the market's executor grants; `open_lane` takes an executor
449
+ address and uses the market ID to find the vault's matching fuses unless they are
450
+ passed.
447
451
 
448
452
  ### Readers (`ipor_fusion.readers`)
449
453
 
@@ -494,15 +498,18 @@ takes the same object.
494
498
  ## Development
495
499
 
496
500
  ```bash
497
- uv sync --all-extras # Install dependencies
501
+ uv sync --all-extras --locked # Install dependencies (as CI)
498
502
  uv run pytest tests/test_fuse_encoding.py -n auto -v # Unit tests (fast, offline)
499
- uv run pytest -v -s # All tests (needs `.*_PROVIDER_URL` variables in `.env`)
503
+ uv run pytest -v -s # All tests; live ones skip without their provider
500
504
  uv run ruff format ./ # Format
501
505
  uv run ruff check ./ # Lint
502
506
  uv run pyright # Type check
503
507
  ```
504
508
 
505
- Integration tests need provider URLs in `.env` (eth_simulateV1 at a pinned fork block — no local node):
509
+ Each integration test runs when its provider URL is in `.env` (eth_simulateV1 at a
510
+ pinned fork block, no local node) and skips otherwise; HyperEVM tests also need
511
+ `HYPEREVM_PROVIDER_URL`, and the source-backed rehearsals a contracts checkout (see
512
+ AGENTS.md):
506
513
 
507
514
  ```bash
508
515
  cp .env.example .env
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev240"
3
+ version = "4.0.0.dev249"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -1,10 +1,11 @@
1
1
  """`fusion market` command group — protocol-level market inspection.
2
2
 
3
- Currently exposes `morpho-blue <marketId>`: fetches a Morpho Blue market's
4
- parameters and state (loan/collateral/oracle/IRM/LLTV, totals, liquidity, APYs)
5
- plus the MetaMorpho vaults supplying it (current allocation, supply caps, and
6
- PublicAllocator flow caps + fees). Combines on-chain reads via `MorphoReader`
7
- with the Morpho public GraphQL API for vault discovery.
3
+ `morpho-blue <marketId>` fetches a Morpho Blue market's parameters and state
4
+ (loan/collateral/oracle/IRM/LLTV, totals, liquidity, APYs) plus the MetaMorpho
5
+ vaults supplying it (current allocation, supply caps, and PublicAllocator flow
6
+ caps + fees), combining on-chain reads via `MorphoReader` with the Morpho public
7
+ GraphQL API. `meta-morpho <vault>` inspects a MetaMorpho V1 or Vault V2 from the
8
+ API: roles, fees, adapters or allocations and caps.
8
9
  """
9
10
 
10
11
  from __future__ import annotations
@@ -59,6 +59,7 @@ from ipor_fusion.readers.morpho import MorphoReader, morpho_blue_address
59
59
  from ipor_fusion.readers.oracle_mapping import build_oracle_mapping
60
60
  from ipor_fusion.types import (
61
61
  ChainId,
62
+ PlasmaVaultAddress,
62
63
  parse_morpho_market_id,
63
64
  parse_plasma_vault_address,
64
65
  )
@@ -100,6 +101,12 @@ def _resolve_provider(cfg: FusionConfig, chain_id: ChainId) -> str:
100
101
  )
101
102
 
102
103
 
104
+ def _vault_arg(raw: str) -> PlasmaVaultAddress:
105
+ """A vault address as a tool caller sends it: surrounding whitespace, a
106
+ missing ``0x`` and any letter case are accepted, as in 3.x."""
107
+ return parse_plasma_vault_address(Web3.to_checksum_address(raw.strip()))
108
+
109
+
103
110
  def _resolve_chain_id(
104
111
  cfg: FusionConfig, vault_address: str, chain_id: ChainId | None
105
112
  ) -> ChainId:
@@ -210,12 +217,12 @@ def vault_info(
210
217
  etc.).
211
218
  """
212
219
  cfg = load_config()
213
- chain_id = _resolve_chain_id(cfg, vault_address, chain_id)
220
+ checksum = _vault_arg(vault_address)
221
+ chain_id = _resolve_chain_id(cfg, checksum, chain_id)
214
222
  # Fail fast (before any RPC) on chains the vault tooling can't handle;
215
223
  # UnsupportedChainError doubles as ValueError so FastMCP surfaces it.
216
224
  ensure_supported_chain(chain_id)
217
225
  ctx, effective_block = _build_ctx(cfg, chain_id, block_number)
218
- checksum = parse_plasma_vault_address(vault_address)
219
226
 
220
227
  # Cheap single-call probe: raises the typed ContractNotFoundError /
221
228
  # NotPlasmaVaultError ("not a vault" in both revert and empty-return
@@ -257,10 +264,10 @@ def vault_validate(
257
264
  consistent. It reads about as much as vault_info and takes about as long.
258
265
  """
259
266
  cfg = load_config()
260
- chain_id = _resolve_chain_id(cfg, vault_address, chain_id)
267
+ checksum = _vault_arg(vault_address)
268
+ chain_id = _resolve_chain_id(cfg, checksum, chain_id)
261
269
  ensure_supported_chain(chain_id)
262
270
  ctx, effective_block = _build_ctx(cfg, chain_id, block_number)
263
- checksum = parse_plasma_vault_address(vault_address)
264
271
 
265
272
  # Same cheap probe as vault_info: "not a vault" fails before the fetch.
266
273
  resolve_access_manager(ctx, checksum)
@@ -323,12 +330,12 @@ def vault_role_accounts(
323
330
  """
324
331
  cfg = load_config()
325
332
  role_id = None if role is None else Roles.resolve(role)
326
- chain_id = _resolve_chain_id(cfg, vault_address, chain_id)
333
+ checksum = _vault_arg(vault_address)
334
+ chain_id = _resolve_chain_id(cfg, checksum, chain_id)
327
335
  # Fail fast (before any RPC) on chains the vault tooling is not
328
336
  # validated on.
329
337
  ensure_supported_chain(chain_id)
330
338
  ctx, _ = _build_ctx(cfg, chain_id, block_number)
331
- checksum = parse_plasma_vault_address(vault_address)
332
339
 
333
340
  access_manager = resolve_access_manager(ctx, checksum)
334
341
  accounts = (
@@ -408,12 +415,12 @@ def vault_oracle_mapping(
408
415
  'events'). Historical blocks require an archive node.
409
416
  """
410
417
  cfg = load_config()
411
- chain_id = _resolve_chain_id(cfg, vault_address, chain_id)
418
+ checksum = _vault_arg(vault_address)
419
+ chain_id = _resolve_chain_id(cfg, checksum, chain_id)
412
420
  # The asset-source event replay scans logs from block 0 — fail fast
413
421
  # (before any RPC) on chains the vault tooling is not validated on.
414
422
  ensure_supported_chain(chain_id)
415
423
  ctx, _ = _build_ctx(cfg, chain_id, block_number)
416
- checksum = parse_plasma_vault_address(vault_address)
417
424
 
418
425
  # The event-replay fallback and the output both need a concrete block
419
426
  # number, so resolve "latest" up front and pin the context to it.
@@ -464,7 +471,7 @@ def vault_add(
464
471
  address: Vault address (required).
465
472
  label: Human-readable label (fetched from on-chain name() if empty).
466
473
  """
467
- checksum = parse_plasma_vault_address(address)
474
+ checksum = _vault_arg(address)
468
475
  cfg = load_config()
469
476
 
470
477
  if chain_id is None:
@@ -26,6 +26,7 @@ from ipor_fusion.mcp.models import (
26
26
  WithdrawManagerDetails,
27
27
  )
28
28
  from ipor_fusion.mcp.server import (
29
+ _vault_arg,
29
30
  config_set_etherscan_key,
30
31
  config_set_provider,
31
32
  config_show,
@@ -39,6 +40,7 @@ from ipor_fusion.mcp.server import (
39
40
  vault_oracle_mapping,
40
41
  vault_remove,
41
42
  vault_role_accounts,
43
+ vault_validate,
42
44
  )
43
45
  from ipor_fusion.morpho_api import (
44
46
  MorphoApiError,
@@ -1082,3 +1084,77 @@ class TestMarketMetaMorpho:
1082
1084
  def test_unknown_vault_errors(self, _fetch):
1083
1085
  with pytest.raises(MorphoApiError, match="not found"):
1084
1086
  market_meta_morpho(vault_address=_VAULT, chain_id=1)
1087
+
1088
+
1089
+ class TestVaultAddressArgument:
1090
+ """Tool callers send addresses as 3.x accepted them; the server normalizes
1091
+ before the strict SDK parser."""
1092
+
1093
+ VAULT = "0x43ee0243ea8cf02f7087d8b16c8d2007cc9c7ca2"
1094
+
1095
+ @pytest.mark.parametrize(
1096
+ "raw",
1097
+ [
1098
+ VAULT,
1099
+ VAULT.upper().replace("0X", "0x"),
1100
+ VAULT[2:],
1101
+ f" {VAULT} ",
1102
+ Web3.to_checksum_address(VAULT),
1103
+ ],
1104
+ )
1105
+ def test_accepts_any_spelling(self, raw: str):
1106
+ assert _vault_arg(raw) == Web3.to_checksum_address(self.VAULT)
1107
+
1108
+ @pytest.mark.parametrize("raw", ["", "0x1234", "not an address", "0x" + "g" * 40])
1109
+ def test_rejects_non_addresses(self, raw: str):
1110
+ with pytest.raises(ValueError):
1111
+ _vault_arg(raw)
1112
+
1113
+
1114
+ class TestVaultAddressResolvesSavedChain:
1115
+ """Every spelling the tools accept finds the saved vault's chain."""
1116
+
1117
+ @staticmethod
1118
+ def _config(providers: list[int]):
1119
+ from ipor_fusion.cli.config_store import FusionConfig, VaultEntry
1120
+
1121
+ return FusionConfig(
1122
+ providers={str(c): "https://rpc.example.com" for c in providers},
1123
+ vaults=[VaultEntry(address=_SAVED_VAULT, label="Test", chain_id=1)],
1124
+ )
1125
+
1126
+ @pytest.mark.parametrize(
1127
+ "spelling", [f" {_SAVED_VAULT} ", _SAVED_VAULT[2:], _SAVED_VAULT.upper()]
1128
+ )
1129
+ @pytest.mark.parametrize(
1130
+ "tool", [vault_info, vault_validate, vault_role_accounts, vault_oracle_mapping]
1131
+ )
1132
+ def test_saved_chain_wins_over_other_providers(self, tool, spelling: str):
1133
+ seen: list[int] = []
1134
+
1135
+ def stop(_cfg, chain_id, _block):
1136
+ seen.append(chain_id)
1137
+ raise RuntimeError("stop")
1138
+
1139
+ with (
1140
+ patch(
1141
+ "ipor_fusion.mcp.server.load_config",
1142
+ autospec=True,
1143
+ return_value=self._config([1, 8453]),
1144
+ ),
1145
+ patch("ipor_fusion.mcp.server._build_ctx", autospec=True, side_effect=stop),
1146
+ pytest.raises(RuntimeError, match="stop"),
1147
+ ):
1148
+ tool(spelling)
1149
+ assert seen == [1]
1150
+
1151
+ def test_missing_saved_chain_provider_is_reported_not_substituted(self):
1152
+ with (
1153
+ patch(
1154
+ "ipor_fusion.mcp.server.load_config",
1155
+ autospec=True,
1156
+ return_value=self._config([8453]),
1157
+ ),
1158
+ pytest.raises(ValueError, match="No provider for chain 1"),
1159
+ ):
1160
+ vault_info(f" {_SAVED_VAULT[2:]} ")