ipor-fusion 4.0.0.dev234__tar.gz → 4.0.0.dev244__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.dev234 → ipor_fusion-4.0.0.dev244}/AGENTS.md +43 -22
  2. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/MIGRATION_4.md +19 -0
  3. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/PKG-INFO +13 -7
  4. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/README.md +12 -6
  5. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/pyproject.toml +1 -1
  6. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/__init__.py +4 -0
  7. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/config_cmd.py +1 -1
  8. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/config_store.py +18 -0
  9. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/market_cmd.py +13 -10
  10. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/options.py +43 -10
  11. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_cmd.py +21 -18
  12. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/__init__.py +4 -0
  13. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/access.py +40 -2
  14. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/mcp/models.py +2 -35
  15. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/mcp/server.py +104 -67
  16. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_helpers.py +8 -2
  17. ipor_fusion-4.0.0.dev244/tests/test_cli_mcp_parity.py +157 -0
  18. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_oracle_mapping.py +6 -0
  19. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_role_accounts.py +6 -0
  20. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_mcp_models.py +21 -12
  21. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_mcp_server.py +10 -3
  22. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_mcp_vault_validate.py +8 -2
  23. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_public_api.py +24 -20
  24. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.claude-plugin/marketplace.json +0 -0
  25. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.claude-plugin/plugin.json +0 -0
  26. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.env.example +0 -0
  27. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.gemini/settings.json +0 -0
  28. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.git-blame-ignore-revs +0 -0
  29. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/cd.yml +0 -0
  30. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/ci.yml +0 -0
  31. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/commit-lint.yml +0 -0
  32. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/notify-slack.yml +0 -0
  33. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/python-build.yml +0 -0
  34. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/python-publish.yml +0 -0
  35. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/release.yml +0 -0
  36. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/report-slack.yml +0 -0
  37. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.gitignore +0 -0
  38. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.mcp.json +0 -0
  39. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.pre-commit-config.yaml +0 -0
  40. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.python-version +0 -0
  41. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/CHANGELOG.md +0 -0
  42. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/CLAUDE.md +0 -0
  43. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/CODEOWNERS +0 -0
  44. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/LICENSE +0 -0
  45. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/build.sh +0 -0
  46. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/CONTRIBUTING.md +0 -0
  47. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/README.md +0 -0
  48. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  49. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  50. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  51. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  52. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/external_state_margin_leg_base.py +0 -0
  53. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/simple_aave_v3_supply_base.py +0 -0
  54. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/pytest.ini +0 -0
  55. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/scripts/commit-check.sh +0 -0
  56. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/scripts/crosschain_readiness.py +0 -0
  57. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/skills/ipor-deploy-vault/SKILL.md +0 -0
  58. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/about.py +0 -0
  59. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cache.py +0 -0
  60. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/chains.py +0 -0
  61. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/__init__.py +0 -0
  62. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  63. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/main.py +0 -0
  64. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  65. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_health.py +0 -0
  66. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  67. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  68. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  69. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/config/__init__.py +0 -0
  70. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/config/roles.py +0 -0
  71. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/address.py +0 -0
  72. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/context.py +0 -0
  73. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/contract.py +0 -0
  74. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/erc20.py +0 -0
  75. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/external_state_executor.py +0 -0
  76. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/fee_manager.py +0 -0
  77. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/fuse.py +0 -0
  78. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/fuse_action.py +0 -0
  79. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/fusion_factory.py +0 -0
  80. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/logs.py +0 -0
  81. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/multicall.py +0 -0
  82. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/oracle.py +0 -0
  83. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/plasma_vault.py +0 -0
  84. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/rewards_manager.py +0 -0
  85. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/simulation.py +0 -0
  86. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/simulation_result.py +0 -0
  87. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  88. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/__init__.py +0 -0
  89. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  90. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  91. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  92. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  93. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  94. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  95. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  96. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  97. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  98. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/contracts.py +0 -0
  99. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/discovery.py +0 -0
  100. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/errors.py +0 -0
  101. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  102. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  103. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  104. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  105. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/lane.py +0 -0
  106. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/logs.py +0 -0
  107. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/messages.py +0 -0
  108. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/simulation.py +0 -0
  109. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  110. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  111. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  112. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  113. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  114. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/transport.py +0 -0
  115. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/dependency_graph.py +0 -0
  116. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/entry_cli.py +0 -0
  117. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/entry_mcp.py +0 -0
  118. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/errors.py +0 -0
  119. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/explorer.py +0 -0
  120. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/field_docs.py +0 -0
  121. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/__init__.py +0 -0
  122. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  123. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/async_action.py +0 -0
  124. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/base.py +0 -0
  125. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  126. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/erc4626.py +0 -0
  127. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  128. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/events.py +0 -0
  129. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/external_state.py +0 -0
  130. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
  131. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  132. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  133. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/hypercore.py +0 -0
  134. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
  135. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/merkl.py +0 -0
  136. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/morpho.py +0 -0
  137. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  138. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  139. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/universal.py +0 -0
  140. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/__init__.py +0 -0
  141. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/architecture.md +0 -0
  142. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/glossary.md +0 -0
  143. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/invariants.md +0 -0
  144. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/quickstart.md +0 -0
  145. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/hypercore/__init__.py +0 -0
  146. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/market_ids.py +0 -0
  147. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/market_info.py +0 -0
  148. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/mcp/__init__.py +0 -0
  149. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/mcp/guide.py +0 -0
  150. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/morpho_api.py +0 -0
  151. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/py.typed +0 -0
  152. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/__init__.py +0 -0
  153. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/aave_v3.py +0 -0
  154. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/aave_v4.py +0 -0
  155. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/compound_v3.py +0 -0
  156. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/hypercore.py +0 -0
  157. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  158. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/lending_health.py +0 -0
  159. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/morpho.py +0 -0
  160. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  161. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  162. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  163. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/position_manager.py +0 -0
  164. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  165. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  166. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/unpriceable.py +0 -0
  167. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/substrates.py +0 -0
  168. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/types.py +0 -0
  169. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/validation.py +0 -0
  170. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_data.py +0 -0
  171. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_erc20.py +0 -0
  172. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_info.py +0 -0
  173. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_managers.py +0 -0
  174. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_positions.py +0 -0
  175. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_reads.py +0 -0
  176. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/UniswapV3UniversalRouter.py +0 -0
  177. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_crosschain.py +0 -0
  178. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_crosschain_lifecycle.py +0 -0
  179. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_crosschain_recovery.py +0 -0
  180. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_euler_v2.py +0 -0
  181. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_event_args.py +0 -0
  182. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_fake_rpc.py +0 -0
  183. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_foundry.py +0 -0
  184. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_golden.py +0 -0
  185. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_guide.py +0 -0
  186. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_hypercore_model.py +0 -0
  187. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_hypercore_shadow.py +0 -0
  188. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_live.py +0 -0
  189. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_multicall.py +0 -0
  190. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_oracle_mapping_fake.py +0 -0
  191. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_simulate.py +0 -0
  192. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_validation.py +0 -0
  193. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/addresses.py +0 -0
  194. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/conftest.py +0 -0
  195. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/constants.py +0 -0
  196. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  197. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/hypercore_market55.json +0 -0
  198. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
  199. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
  200. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
  201. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
  202. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
  203. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
  204. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
  205. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
  206. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue.json +0 -0
  207. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
  208. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
  209. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
  210. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
  211. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
  212. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
  213. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
  214. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/simulate_v1_request.json +0 -0
  215. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/clean.json +0 -0
  216. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
  217. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  218. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
  219. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
  220. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
  221. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
  222. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/fetched.json +0 -0
  223. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
  224. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/full.json +0 -0
  225. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
  226. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/full.txt +0 -0
  227. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/rich.json +0 -0
  228. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
  229. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  230. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_about.py +0 -0
  231. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_access_manager.py +0 -0
  232. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cache.py +0 -0
  233. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_chains.py +0 -0
  234. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_changelog.py +0 -0
  235. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_commands.py +0 -0
  236. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_config_store.py +0 -0
  237. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_hypercore.py +0 -0
  238. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_lending_health.py +0 -0
  239. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_market_golden.py +0 -0
  240. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_market_morpho_blue.py +0 -0
  241. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_oracle_mapping_golden.py +0 -0
  242. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_pending_requests_scan.py +0 -0
  243. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_vault_info_golden.py +0 -0
  244. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_vault_predates_getters.py +0 -0
  245. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_vault_validate.py +0 -0
  246. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_contract_call.py +0 -0
  247. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_contract_encoder.py +0 -0
  248. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_ccip_events.py +0 -0
  249. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_ccip_indexer.py +0 -0
  250. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_ccip_quote.py +0 -0
  251. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_chainlink.py +0 -0
  252. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_contracts.py +0 -0
  253. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_encoding.py +0 -0
  254. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_errors.py +0 -0
  255. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_lane.py +0 -0
  256. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_layering.py +0 -0
  257. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_readiness.py +0 -0
  258. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_readiness_report.py +0 -0
  259. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_transport.py +0 -0
  260. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_docs_typing.py +0 -0
  261. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_erc20.py +0 -0
  262. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_error_hierarchy.py +0 -0
  263. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_examples_composed_hypercore.py +0 -0
  264. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_examples_crosschain.py +0 -0
  265. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  266. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_examples_vaults.py +0 -0
  267. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_explorer.py +0 -0
  268. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_external_state_events.py +0 -0
  269. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_external_state_executor.py +0 -0
  270. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_fee_manager.py +0 -0
  271. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_fuse_encoding.py +0 -0
  272. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_fusion_instance.py +0 -0
  273. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_guide.py +0 -0
  274. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_deployment.py +0 -0
  275. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_events.py +0 -0
  276. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_flow.py +0 -0
  277. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_live.py +0 -0
  278. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_outcome.py +0 -0
  279. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_readers.py +0 -0
  280. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_simulation.py +0 -0
  281. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_logs.py +0 -0
  282. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_mcp_hypercore.py +0 -0
  283. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_multicall.py +0 -0
  284. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_oracle.py +0 -0
  285. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_oracle_mapping.py +0 -0
  286. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_plasma_vault.py +0 -0
  287. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_public_type_reachability.py +0 -0
  288. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_reader_encoding.py +0 -0
  289. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_readiness_selection.py +0 -0
  290. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_revert_decoding.py +0 -0
  291. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_rewards_manager.py +0 -0
  292. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_roles.py +0 -0
  293. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_sdk_layering.py +0 -0
  294. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  295. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  296. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  297. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_crosschain_canary.py +0 -0
  298. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_crosschain_hypercore.py +0 -0
  299. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  300. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  301. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  302. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  303. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  304. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  305. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  306. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_examples_arbitrum.py +0 -0
  307. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_examples_base.py +0 -0
  308. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_examples_crosschain.py +0 -0
  309. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_external_state_executor_base.py +0 -0
  310. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  311. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  312. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  313. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_morpho_blue.py +0 -0
  314. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_pilot_v3.py +0 -0
  315. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  316. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  317. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  318. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  319. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_readers_integration.py +0 -0
  320. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_release_funds.py +0 -0
  321. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_swap_on_base.py +0 -0
  322. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  323. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulation_errors.py +0 -0
  324. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulation_observation.py +0 -0
  325. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_solidity_compiler.py +0 -0
  326. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_solidity_mirrors.py +0 -0
  327. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_substrates.py +0 -0
  328. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_types_and_events.py +0 -0
  329. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_unpriceable_tokens.py +0 -0
  330. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_validation.py +0 -0
  331. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_data_block.py +0 -0
  332. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_dep_graph.py +0 -0
  333. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_info_fetched_golden.py +0 -0
  334. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_info_onchain_base.py +0 -0
  335. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  336. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_reconciliation_onchain_base.py +0 -0
  337. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_simulator_blocks.py +0 -0
  338. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_web3_context.py +0 -0
  339. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_withdraw_manager.py +0 -0
  340. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/typed_client/consumer.py +0 -0
  341. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/typed_client/rejected.py +0 -0
  342. {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/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
@@ -218,7 +221,7 @@ the lifecycle matrix follows that.
218
221
  | `IporFusionMarkets`, `Roles` | `market_ids.py`, `config/roles.py` mirror `IporFusionMarkets.sol`, `Roles.sol` in `ipor-fusion/contracts/libraries/`; drift-gated by `tests/test_solidity_mirrors.py` — bump its pinned ref in the same change that syncs the mirrors An id sourced from a contracts feature branch ahead of the pinned ref goes in the test's `_AHEAD_OF_UPSTREAM` allowlist and is removed when the ref catches up |
219
222
  | substrate decoders | `substrates.py` registry mirrors each market's `contracts/fuses/<protocol>/*SubstrateLib.sol` or `*FuseLib.sol` |
220
223
  | `vault_info` / `fusion market` JSON shapes | the builders and their TypedDicts (`VaultInfoJson`, `MorphoBlueMarketJson`, `MetaMorphoV1Json` / `MetaMorphoV2Json`) in `vault_info.py` / `market_info.py`, models in `mcp/models.py` (`extra="forbid"`, the published schema), the `VaultInfoJson`-typed `_full_vault_info_dict` fixture in `test_mcp_models.py` |
221
- | CLI command set | every CLI command has a matching tool in `mcp/server.py` (`changelog` maps to `server_info`) |
224
+ | CLI command set | every CLI command has a matching tool in `mcp/server.py` with the same arguments (`changelog` maps to `server_info`; `--json` and display filters are CLI-only); `tests/test_cli_mcp_parity.py` enforces it, and both resolve chain and provider through `cli/config_store.resolve_chain_id` |
222
225
 
223
226
  ## Layout (`src/ipor_fusion/`)
224
227
 
@@ -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)`
@@ -479,7 +498,9 @@ the rest share names). Read-only: `vault_info`, `vault_validate`,
479
498
  `vault_list`, `vault_role_accounts`, `vault_oracle_mapping`, `market_morpho_blue`,
480
499
  `market_meta_morpho`, `config_show`, `server_info`. Mutating: `vault_add`,
481
500
  `vault_remove`, `config_set_provider`, `config_set_etherscan_key`. Every new
482
- CLI command gets a matching MCP tool. Put every fact in its structured home:
501
+ CLI command gets a matching MCP tool with the same argument names; an optional
502
+ chain or block is `None` when omitted (`ChainArg`, `BlockArg`), never a `0`
503
+ sentinel. Put every fact in its structured home:
483
504
  the return shape is the Pydantic return type (it becomes `outputSchema`),
484
505
  per-argument semantics go in `Annotated[..., Field(description=...)]`, closed
485
506
  value sets are `Literal` enums. The docstring keeps only what has no
@@ -990,6 +990,25 @@ and `log_index` was passed through as given (a hex string from a raw log); the e
990
990
  These apply to every caller, type-checked or not. Changes described in full above
991
991
  are listed with a link.
992
992
 
993
+ - **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
999
+ `fusion changelog --since`, and `vault_oracle_mapping` takes `max_depth` like
1000
+ `--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.
1003
+ - **CLI options:** every `--block-number` rejects a negative block, and
1004
+ `fusion market morpho-blue --block` is `--block-number` (`--block` still
1005
+ works).
1006
+ - **One role-accounts document:** `fusion vault role-accounts --json` and
1007
+ `vault_role_accounts` publish `RoleAccountsJson`, built by
1008
+ `build_role_accounts_json` (exported).
1009
+ - **Parity test:** `tests/test_cli_mcp_parity.py` holds every command to a
1010
+ tool with the same arguments.
1011
+
993
1012
  - **`vault validate` publishes one document.** In 3.x the CLI's `--json` output
994
1013
  and the MCP `vault_validate` tool returned two different shapes; both now
995
1014
  return `VaultValidationJson`, built by `build_vault_validation_json(validation,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev234
3
+ Version: 4.0.0.dev244
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
@@ -489,9 +489,12 @@ import these from their subpackage, and expect changes in a minor release.
489
489
  | 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
490
  | 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
491
 
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.
492
+ The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Pass
493
+ `market_id=crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` to `open_lane`
494
+ / `discover_deployment` for that deployment. Both require it explicitly:
495
+ `discover_deployment` reads the market's executor grants; `open_lane` takes an executor
496
+ address and uses the market ID to find the vault's matching fuses unless they are
497
+ passed.
495
498
 
496
499
  ### Readers (`ipor_fusion.readers`)
497
500
 
@@ -542,15 +545,18 @@ takes the same object.
542
545
  ## Development
543
546
 
544
547
  ```bash
545
- uv sync --all-extras # Install dependencies
548
+ uv sync --all-extras --locked # Install dependencies (as CI)
546
549
  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`)
550
+ uv run pytest -v -s # All tests; live ones skip without their provider
548
551
  uv run ruff format ./ # Format
549
552
  uv run ruff check ./ # Lint
550
553
  uv run pyright # Type check
551
554
  ```
552
555
 
553
- Integration tests need provider URLs in `.env` (eth_simulateV1 at a pinned fork block — no local node):
556
+ Each integration test runs when its provider URL is in `.env` (eth_simulateV1 at a
557
+ pinned fork block, no local node) and skips otherwise; HyperEVM tests also need
558
+ `HYPEREVM_PROVIDER_URL`, and the source-backed rehearsals a contracts checkout (see
559
+ AGENTS.md):
554
560
 
555
561
  ```bash
556
562
  cp .env.example .env
@@ -441,9 +441,12 @@ import these from their subpackage, and expect changes in a minor release.
441
441
  | 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
442
  | 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
443
 
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.
444
+ The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Pass
445
+ `market_id=crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` to `open_lane`
446
+ / `discover_deployment` for that deployment. Both require it explicitly:
447
+ `discover_deployment` reads the market's executor grants; `open_lane` takes an executor
448
+ address and uses the market ID to find the vault's matching fuses unless they are
449
+ passed.
447
450
 
448
451
  ### Readers (`ipor_fusion.readers`)
449
452
 
@@ -494,15 +497,18 @@ takes the same object.
494
497
  ## Development
495
498
 
496
499
  ```bash
497
- uv sync --all-extras # Install dependencies
500
+ uv sync --all-extras --locked # Install dependencies (as CI)
498
501
  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`)
502
+ uv run pytest -v -s # All tests; live ones skip without their provider
500
503
  uv run ruff format ./ # Format
501
504
  uv run ruff check ./ # Lint
502
505
  uv run pyright # Type check
503
506
  ```
504
507
 
505
- Integration tests need provider URLs in `.env` (eth_simulateV1 at a pinned fork block — no local node):
508
+ Each integration test runs when its provider URL is in `.env` (eth_simulateV1 at a
509
+ pinned fork block, no local node) and skips otherwise; HyperEVM tests also need
510
+ `HYPEREVM_PROVIDER_URL`, and the source-backed rehearsals a contracts checkout (see
511
+ AGENTS.md):
506
512
 
507
513
  ```bash
508
514
  cp .env.example .env
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev234"
3
+ version = "4.0.0.dev244"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -28,7 +28,9 @@ from ipor_fusion.core.access import (
28
28
  AccessManager,
29
29
  RoleAccount,
30
30
  RoleAccountJson,
31
+ RoleAccountsJson,
31
32
  RoleStatus,
33
+ build_role_accounts_json,
32
34
  resolve_access_manager,
33
35
  role_account_sort_key,
34
36
  )
@@ -465,6 +467,8 @@ __all__ = [
465
467
  "RoleStatus",
466
468
  "resolve_access_manager",
467
469
  "role_account_sort_key",
470
+ "RoleAccountsJson",
471
+ "build_role_accounts_json",
468
472
  "RewardsManager",
469
473
  "VestingData",
470
474
  "ERC20",
@@ -20,7 +20,7 @@ def config() -> None:
20
20
  @click.argument("url")
21
21
  @click.option(
22
22
  "--chain-id",
23
- type=int,
23
+ type=click.IntRange(min=1),
24
24
  default=None,
25
25
  help="Chain ID (auto-detected from URL if omitted).",
26
26
  )
@@ -88,3 +88,21 @@ def load_config() -> FusionConfig:
88
88
  def save_config(config: FusionConfig) -> None:
89
89
  CONFIG_DIR.mkdir(parents=True, exist_ok=True)
90
90
  CONFIG_FILE.write_text(config.model_dump_json(indent=2), encoding="utf-8")
91
+
92
+
93
+ def resolve_chain_id(
94
+ cfg: FusionConfig, vault_address: str, chain_id: ChainId | None
95
+ ) -> ChainId | None:
96
+ """The chain a command or tool runs on: the one given, else the saved
97
+ vault's, else the chain of the only configured provider; None when none
98
+ applies. The CLI and the MCP server share it so both resolve alike."""
99
+ if chain_id is not None:
100
+ return chain_id
101
+ if entry := next(
102
+ (v for v in cfg.vaults if v.address.lower() == vault_address.lower()),
103
+ None,
104
+ ):
105
+ return entry.chain_id
106
+ if len(cfg.providers) == 1:
107
+ return ChainId(int(next(iter(cfg.providers))))
108
+ return None
@@ -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
@@ -16,7 +17,7 @@ from eth_typing import ChecksumAddress
16
17
 
17
18
  from ipor_fusion.chains import CHAIN_NAMES
18
19
  from ipor_fusion.cli.config_store import load_config
19
- from ipor_fusion.cli.options import ADDRESS, CHAIN, _resolve_provider
20
+ from ipor_fusion.cli.options import ADDRESS, BLOCK_NUMBER, CHAIN, _resolve_provider
20
21
  from ipor_fusion.cli.vault_rendering import _format_amount, _print_table
21
22
  from ipor_fusion.core.context import Web3Context
22
23
  from ipor_fusion.market_info import (
@@ -81,8 +82,10 @@ def market() -> None:
81
82
  help="Chain ID or name (ethereum, base, arbitrum, ...).",
82
83
  )
83
84
  @click.option(
85
+ "--block-number",
84
86
  "--block",
85
- type=int,
87
+ "block_number",
88
+ type=BLOCK_NUMBER,
86
89
  default=None,
87
90
  help="Block number for on-chain reads (default: latest).",
88
91
  )
@@ -109,7 +112,7 @@ def market() -> None:
109
112
  def morpho_blue(
110
113
  market_id: MorphoBlueMarketId,
111
114
  chain_id: ChainId,
112
- block: int | None,
115
+ block_number: int | None,
113
116
  no_api: bool,
114
117
  vault_filter: str | None,
115
118
  json_output: bool,
@@ -125,8 +128,8 @@ def morpho_blue(
125
128
  cfg = load_config()
126
129
  provider_url = _resolve_provider(cfg, chain_id)
127
130
  ctx = Web3Context.from_url(provider_url)
128
- if block is not None:
129
- ctx = ctx.at_block(block)
131
+ if block_number is not None:
132
+ ctx = ctx.at_block(block_number)
130
133
  reader = MorphoReader(morpho_blue_address(chain_id))
131
134
  mid = market_id
132
135
  params, state = reader.read_existing_market(ctx, mid)
@@ -7,6 +7,7 @@ from web3 import Web3
7
7
  from ipor_fusion.chains import CHAIN_NAME_TO_ID, ensure_supported_chain
8
8
  from ipor_fusion.cli.config_store import (
9
9
  FusionConfig,
10
+ resolve_chain_id,
10
11
  )
11
12
  from ipor_fusion.core.context import Web3Context
12
13
  from ipor_fusion.errors import (
@@ -74,11 +75,11 @@ class ChainType(click.ParamType): # pyright: ignore[reportMissingTypeArgument]
74
75
  self, value: object, param: click.Parameter | None, ctx: click.Context | None
75
76
  ) -> ChainId:
76
77
  if isinstance(value, int):
77
- return ChainId(value)
78
+ return self._positive(value, param, ctx)
78
79
  if not isinstance(value, str):
79
80
  self.fail(f"expected string, got {type(value).__name__}", param, ctx)
80
81
  try:
81
- return ChainId(int(value))
82
+ return self._positive(int(value), param, ctx)
82
83
  except ValueError:
83
84
  pass
84
85
  lower = value.lower().strip()
@@ -91,22 +92,54 @@ class ChainType(click.ParamType): # pyright: ignore[reportMissingTypeArgument]
91
92
  ctx,
92
93
  )
93
94
 
95
+ def _positive(
96
+ self, value: int, param: click.Parameter | None, ctx: click.Context | None
97
+ ) -> ChainId:
98
+ if value <= 0:
99
+ self.fail(
100
+ f"chain ID must be positive, got {value}; omit --chain-id to "
101
+ "auto-detect it",
102
+ param,
103
+ ctx,
104
+ )
105
+ return ChainId(value)
106
+
94
107
 
95
108
  CHAIN = ChainType()
96
109
 
97
110
 
111
+ class BlockNumberType(click.IntRange):
112
+ """A positive block number. 0 is refused rather than read as block 0:
113
+ some callers still pass it meaning "latest"."""
114
+
115
+ name = "block"
116
+
117
+ def __init__(self) -> None:
118
+ super().__init__(min=1)
119
+
120
+ def convert(
121
+ self, value: object, param: click.Parameter | None, ctx: click.Context | None
122
+ ) -> int:
123
+ if value in (0, "0"):
124
+ self.fail(
125
+ "0 is not a block selector: omit --block-number for the latest block",
126
+ param,
127
+ ctx,
128
+ )
129
+ return super().convert(value, param, ctx)
130
+
131
+
132
+ BLOCK_NUMBER = BlockNumberType()
133
+
134
+
98
135
  def _resolve_chain_id(
99
136
  cfg: FusionConfig, vault_address: PlasmaVaultAddress, chain_id: ChainId | None
100
137
  ) -> ChainId:
101
- if chain_id is not None:
102
- return chain_id
103
- if entry := next(
104
- (v for v in cfg.vaults if v.address.lower() == vault_address.lower()),
105
- None,
106
- ):
107
- return entry.chain_id
138
+ if (resolved := resolve_chain_id(cfg, vault_address, chain_id)) is not None:
139
+ return resolved
108
140
  raise click.UsageError(
109
- "Unknown vault — use --chain-id (e.g. --chain-id ethereum, --chain-id 8453)"
141
+ "Cannot tell the chain: the vault is not saved and there is not exactly one "
142
+ "configured provider — use --chain-id (e.g. --chain-id ethereum, --chain-id 8453)"
110
143
  )
111
144
 
112
145
 
@@ -4,7 +4,6 @@ import json
4
4
  import sys
5
5
 
6
6
  import click
7
- from web3 import Web3
8
7
 
9
8
  from ipor_fusion.chains import CHAIN_NAMES
10
9
  from ipor_fusion.cli.config_store import (
@@ -13,7 +12,13 @@ from ipor_fusion.cli.config_store import (
13
12
  load_config,
14
13
  save_config,
15
14
  )
16
- from ipor_fusion.cli.options import CHAIN, VAULT_ADDRESS, _build_ctx, _resolve_provider
15
+ from ipor_fusion.cli.options import (
16
+ BLOCK_NUMBER,
17
+ CHAIN,
18
+ VAULT_ADDRESS,
19
+ _build_ctx,
20
+ _resolve_provider,
21
+ )
17
22
  from ipor_fusion.cli.oracle_mapping_view import _print_oracle_mapping
18
23
  from ipor_fusion.cli.vault_health import (
19
24
  _print_validation,
@@ -27,8 +32,8 @@ from ipor_fusion.cli.vault_rendering import (
27
32
  )
28
33
  from ipor_fusion.config.roles import Roles
29
34
  from ipor_fusion.core.access import (
35
+ build_role_accounts_json,
30
36
  resolve_access_manager,
31
- role_account_sort_key,
32
37
  )
33
38
  from ipor_fusion.core.context import Web3Context
34
39
  from ipor_fusion.core.plasma_vault import PlasmaVault
@@ -181,7 +186,7 @@ vault.add_command(list_vaults, "ls")
181
186
  )
182
187
  @click.option(
183
188
  "--block-number",
184
- type=int,
189
+ type=BLOCK_NUMBER,
185
190
  default=None,
186
191
  help="Block number (default: latest).",
187
192
  )
@@ -226,7 +231,7 @@ def info(
226
231
  )
227
232
  @click.option(
228
233
  "--block-number",
229
- type=int,
234
+ type=BLOCK_NUMBER,
230
235
  default=None,
231
236
  help="Block number (default: latest).",
232
237
  )
@@ -287,7 +292,7 @@ def validate(
287
292
  )
288
293
  @click.option(
289
294
  "--block-number",
290
- type=int,
295
+ type=BLOCK_NUMBER,
291
296
  default=None,
292
297
  help="Block number (default: latest).",
293
298
  )
@@ -326,21 +331,19 @@ def role_accounts(
326
331
  raise click.ClickException(
327
332
  f"RoleGranted log scan failed ({type(exc).__name__}: {exc})."
328
333
  ) from exc
329
- rows = [ra.to_dict() for ra in sorted(accounts, key=role_account_sort_key)]
330
-
334
+ document = build_role_accounts_json(
335
+ accounts,
336
+ vault=vault_address,
337
+ access_manager=manager.address,
338
+ chain_id=chain_id,
339
+ role=role_id,
340
+ )
331
341
  if json_output:
332
- payload = {
333
- "vault": Web3.to_checksum_address(vault_address),
334
- "access_manager": manager.address,
335
- "chain_id": chain_id,
336
- "role_filter": Roles.get_name(role_id) if role_id is not None else None,
337
- "accounts": rows,
338
- }
339
- click.echo(json.dumps(payload, indent=2))
342
+ click.echo(json.dumps(document, indent=2))
340
343
  return
341
344
 
342
345
  click.echo(f"Access Manager: {manager.address}")
343
- _print_role_accounts_table(rows)
346
+ _print_role_accounts_table(document["accounts"])
344
347
 
345
348
 
346
349
  @vault.command("oracle-mapping")
@@ -350,7 +353,7 @@ def role_accounts(
350
353
  )
351
354
  @click.option(
352
355
  "--block-number",
353
- type=click.IntRange(min=0),
356
+ type=BLOCK_NUMBER,
354
357
  default=None,
355
358
  help="Block number (default: latest).",
356
359
  )
@@ -2,7 +2,9 @@ from ipor_fusion.core.access import (
2
2
  AccessManager,
3
3
  RoleAccount,
4
4
  RoleAccountJson,
5
+ RoleAccountsJson,
5
6
  RoleStatus,
7
+ build_role_accounts_json,
6
8
  resolve_access_manager,
7
9
  role_account_sort_key,
8
10
  )
@@ -138,4 +140,6 @@ __all__ = [
138
140
  "price_oracle_middleware",
139
141
  "resolve_access_manager",
140
142
  "role_account_sort_key",
143
+ "RoleAccountsJson",
144
+ "build_role_accounts_json",
141
145
  ]
@@ -1,4 +1,4 @@
1
- from collections.abc import Callable
1
+ from collections.abc import Callable, Sequence
2
2
  from dataclasses import dataclass
3
3
  from typing import TypedDict
4
4
 
@@ -21,7 +21,13 @@ from ipor_fusion.errors import (
21
21
  NotPlasmaVaultError,
22
22
  OwnerNotFoundError,
23
23
  )
24
- from ipor_fusion.types import AccessManagerAddress, Period, PlasmaVaultAddress, RoleId
24
+ from ipor_fusion.types import (
25
+ AccessManagerAddress,
26
+ ChainId,
27
+ Period,
28
+ PlasmaVaultAddress,
29
+ RoleId,
30
+ )
25
31
 
26
32
 
27
33
  class RoleAccountJson(TypedDict):
@@ -71,6 +77,38 @@ def role_account_sort_key(role_account: RoleAccount) -> tuple[str, int]:
71
77
  return (role_account.account.lower(), role_account.role_id)
72
78
 
73
79
 
80
+ class RoleAccountsJson(TypedDict):
81
+ """The ``fusion vault role-accounts --json`` document and the
82
+ ``vault_role_accounts`` MCP tool output (`build_role_accounts_json`)."""
83
+
84
+ vault: PlasmaVaultAddress
85
+ access_manager: AccessManagerAddress
86
+ chain_id: ChainId
87
+ role_filter: str | None
88
+ accounts: list[RoleAccountJson]
89
+
90
+
91
+ def build_role_accounts_json(
92
+ accounts: Sequence[RoleAccount],
93
+ *,
94
+ vault: PlasmaVaultAddress,
95
+ access_manager: AccessManagerAddress,
96
+ chain_id: ChainId,
97
+ role: RoleId | Roles | None = None,
98
+ ) -> RoleAccountsJson:
99
+ """Role holders as the one document both tools publish, in
100
+ `role_account_sort_key` order; ``role`` is the filter applied, if any."""
101
+ return {
102
+ "vault": vault,
103
+ "access_manager": access_manager,
104
+ "chain_id": chain_id,
105
+ "role_filter": Roles.get_name(role) if role is not None else None,
106
+ "accounts": [
107
+ ra.to_dict() for ra in sorted(accounts, key=role_account_sort_key)
108
+ ],
109
+ }
110
+
111
+
74
112
  def _role_status_decoder(values: tuple[bool, int]) -> RoleStatus:
75
113
  is_member, execution_delay = values
76
114
  return RoleStatus(is_member=is_member, execution_delay=Period(execution_delay))