ipor-fusion 4.0.0.dev231__tar.gz → 4.0.0.dev234__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 (341) hide show
  1. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/AGENTS.md +7 -3
  2. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/MIGRATION_4.md +23 -0
  3. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/PKG-INFO +1 -1
  4. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/pyproject.toml +1 -1
  5. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/__init__.py +8 -0
  6. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_cmd.py +5 -68
  7. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/models.py +18 -236
  8. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/server.py +5 -3
  9. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/validation.py +1 -1
  10. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_info.py +109 -22
  11. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/clean.json +1 -1
  12. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/clean.mcp.json +4 -8
  13. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/deployment_error.json +1 -1
  14. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +4 -8
  15. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/fetched.mcp.json +6 -12
  16. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/full.mcp.json +15 -34
  17. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/rich.mcp.json +13 -30
  18. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_vault_validate.py +8 -3
  19. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_models.py +5 -0
  20. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_vault_validate.py +66 -30
  21. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_public_api.py +11 -3
  22. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.claude-plugin/marketplace.json +0 -0
  23. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.claude-plugin/plugin.json +0 -0
  24. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.env.example +0 -0
  25. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.gemini/settings.json +0 -0
  26. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.git-blame-ignore-revs +0 -0
  27. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/cd.yml +0 -0
  28. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/ci.yml +0 -0
  29. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/commit-lint.yml +0 -0
  30. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/notify-slack.yml +0 -0
  31. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/python-build.yml +0 -0
  32. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/python-publish.yml +0 -0
  33. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/release.yml +0 -0
  34. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/report-slack.yml +0 -0
  35. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.gitignore +0 -0
  36. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.mcp.json +0 -0
  37. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.pre-commit-config.yaml +0 -0
  38. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.python-version +0 -0
  39. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/CHANGELOG.md +0 -0
  40. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/CLAUDE.md +0 -0
  41. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/CODEOWNERS +0 -0
  42. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/LICENSE +0 -0
  43. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/README.md +0 -0
  44. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/build.sh +0 -0
  45. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/CONTRIBUTING.md +0 -0
  46. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/README.md +0 -0
  47. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  48. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  49. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  50. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  51. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/external_state_margin_leg_base.py +0 -0
  52. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/simple_aave_v3_supply_base.py +0 -0
  53. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/pytest.ini +0 -0
  54. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/scripts/commit-check.sh +0 -0
  55. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/scripts/crosschain_readiness.py +0 -0
  56. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/skills/ipor-deploy-vault/SKILL.md +0 -0
  57. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/about.py +0 -0
  58. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cache.py +0 -0
  59. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/chains.py +0 -0
  60. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/__init__.py +0 -0
  61. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  62. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/config_cmd.py +0 -0
  63. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/config_store.py +0 -0
  64. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/main.py +0 -0
  65. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/market_cmd.py +0 -0
  66. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/options.py +0 -0
  67. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  68. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_health.py +0 -0
  69. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  70. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  71. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  72. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/config/__init__.py +0 -0
  73. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/config/roles.py +0 -0
  74. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/__init__.py +0 -0
  75. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/access.py +0 -0
  76. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/address.py +0 -0
  77. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/context.py +0 -0
  78. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/contract.py +0 -0
  79. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/erc20.py +0 -0
  80. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/external_state_executor.py +0 -0
  81. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fee_manager.py +0 -0
  82. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fuse.py +0 -0
  83. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fuse_action.py +0 -0
  84. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fusion_factory.py +0 -0
  85. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/logs.py +0 -0
  86. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/multicall.py +0 -0
  87. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/oracle.py +0 -0
  88. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/plasma_vault.py +0 -0
  89. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/rewards_manager.py +0 -0
  90. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/simulation.py +0 -0
  91. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/simulation_result.py +0 -0
  92. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  93. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/__init__.py +0 -0
  94. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  95. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  96. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  97. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  98. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  99. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  100. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  101. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  102. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  103. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/contracts.py +0 -0
  104. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/discovery.py +0 -0
  105. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/errors.py +0 -0
  106. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  107. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  108. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  109. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  110. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/lane.py +0 -0
  111. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/logs.py +0 -0
  112. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/messages.py +0 -0
  113. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/simulation.py +0 -0
  114. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  115. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  116. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  117. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  118. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  119. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/transport.py +0 -0
  120. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/dependency_graph.py +0 -0
  121. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/entry_cli.py +0 -0
  122. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/entry_mcp.py +0 -0
  123. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/errors.py +0 -0
  124. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/explorer.py +0 -0
  125. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/field_docs.py +0 -0
  126. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/__init__.py +0 -0
  127. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  128. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/async_action.py +0 -0
  129. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/base.py +0 -0
  130. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  131. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/erc4626.py +0 -0
  132. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  133. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/events.py +0 -0
  134. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/external_state.py +0 -0
  135. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
  136. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  137. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  138. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/hypercore.py +0 -0
  139. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
  140. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/merkl.py +0 -0
  141. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/morpho.py +0 -0
  142. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  143. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  144. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/universal.py +0 -0
  145. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/__init__.py +0 -0
  146. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/architecture.md +0 -0
  147. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/glossary.md +0 -0
  148. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/invariants.md +0 -0
  149. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/quickstart.md +0 -0
  150. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/hypercore/__init__.py +0 -0
  151. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/market_ids.py +0 -0
  152. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/market_info.py +0 -0
  153. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/__init__.py +0 -0
  154. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/guide.py +0 -0
  155. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/morpho_api.py +0 -0
  156. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/py.typed +0 -0
  157. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/__init__.py +0 -0
  158. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/aave_v3.py +0 -0
  159. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/aave_v4.py +0 -0
  160. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/compound_v3.py +0 -0
  161. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/hypercore.py +0 -0
  162. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  163. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/lending_health.py +0 -0
  164. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/morpho.py +0 -0
  165. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  166. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  167. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  168. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/position_manager.py +0 -0
  169. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  170. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  171. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/unpriceable.py +0 -0
  172. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/substrates.py +0 -0
  173. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/types.py +0 -0
  174. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_data.py +0 -0
  175. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_erc20.py +0 -0
  176. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_managers.py +0 -0
  177. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_positions.py +0 -0
  178. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_reads.py +0 -0
  179. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/UniswapV3UniversalRouter.py +0 -0
  180. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_crosschain.py +0 -0
  181. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_crosschain_lifecycle.py +0 -0
  182. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_crosschain_recovery.py +0 -0
  183. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_euler_v2.py +0 -0
  184. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_event_args.py +0 -0
  185. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_fake_rpc.py +0 -0
  186. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_foundry.py +0 -0
  187. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_golden.py +0 -0
  188. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_guide.py +0 -0
  189. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_hypercore_model.py +0 -0
  190. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_hypercore_shadow.py +0 -0
  191. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_live.py +0 -0
  192. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_multicall.py +0 -0
  193. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_oracle_mapping_fake.py +0 -0
  194. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_simulate.py +0 -0
  195. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_validation.py +0 -0
  196. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/addresses.py +0 -0
  197. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/conftest.py +0 -0
  198. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/constants.py +0 -0
  199. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  200. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/hypercore_market55.json +0 -0
  201. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
  202. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
  203. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
  204. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
  205. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
  206. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
  207. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
  208. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
  209. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue.json +0 -0
  210. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
  211. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
  212. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
  213. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
  214. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
  215. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
  216. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
  217. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/simulate_v1_request.json +0 -0
  218. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  219. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
  220. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
  221. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/fetched.json +0 -0
  222. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/full.json +0 -0
  223. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/full.txt +0 -0
  224. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/rich.json +0 -0
  225. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  226. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_about.py +0 -0
  227. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_access_manager.py +0 -0
  228. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cache.py +0 -0
  229. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_chains.py +0 -0
  230. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_changelog.py +0 -0
  231. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_commands.py +0 -0
  232. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_config_store.py +0 -0
  233. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_helpers.py +0 -0
  234. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_hypercore.py +0 -0
  235. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_lending_health.py +0 -0
  236. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_market_golden.py +0 -0
  237. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_market_morpho_blue.py +0 -0
  238. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_oracle_mapping.py +0 -0
  239. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_oracle_mapping_golden.py +0 -0
  240. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_pending_requests_scan.py +0 -0
  241. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_role_accounts.py +0 -0
  242. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_vault_info_golden.py +0 -0
  243. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_vault_predates_getters.py +0 -0
  244. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_contract_call.py +0 -0
  245. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_contract_encoder.py +0 -0
  246. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_ccip_events.py +0 -0
  247. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_ccip_indexer.py +0 -0
  248. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_ccip_quote.py +0 -0
  249. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_chainlink.py +0 -0
  250. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_contracts.py +0 -0
  251. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_encoding.py +0 -0
  252. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_errors.py +0 -0
  253. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_lane.py +0 -0
  254. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_layering.py +0 -0
  255. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_readiness.py +0 -0
  256. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_readiness_report.py +0 -0
  257. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_transport.py +0 -0
  258. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_docs_typing.py +0 -0
  259. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_erc20.py +0 -0
  260. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_error_hierarchy.py +0 -0
  261. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_examples_composed_hypercore.py +0 -0
  262. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_examples_crosschain.py +0 -0
  263. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  264. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_examples_vaults.py +0 -0
  265. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_explorer.py +0 -0
  266. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_external_state_events.py +0 -0
  267. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_external_state_executor.py +0 -0
  268. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_fee_manager.py +0 -0
  269. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_fuse_encoding.py +0 -0
  270. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_fusion_instance.py +0 -0
  271. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_guide.py +0 -0
  272. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_deployment.py +0 -0
  273. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_events.py +0 -0
  274. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_flow.py +0 -0
  275. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_live.py +0 -0
  276. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_outcome.py +0 -0
  277. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_readers.py +0 -0
  278. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_simulation.py +0 -0
  279. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_logs.py +0 -0
  280. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_hypercore.py +0 -0
  281. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_server.py +0 -0
  282. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_multicall.py +0 -0
  283. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_oracle.py +0 -0
  284. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_oracle_mapping.py +0 -0
  285. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_plasma_vault.py +0 -0
  286. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_public_type_reachability.py +0 -0
  287. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_reader_encoding.py +0 -0
  288. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_readiness_selection.py +0 -0
  289. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_revert_decoding.py +0 -0
  290. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_rewards_manager.py +0 -0
  291. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_roles.py +0 -0
  292. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_sdk_layering.py +0 -0
  293. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  294. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  295. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  296. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_canary.py +0 -0
  297. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_hypercore.py +0 -0
  298. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  299. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  300. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  301. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  302. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  303. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  304. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  305. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_examples_arbitrum.py +0 -0
  306. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_examples_base.py +0 -0
  307. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_examples_crosschain.py +0 -0
  308. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_external_state_executor_base.py +0 -0
  309. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  310. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  311. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  312. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_morpho_blue.py +0 -0
  313. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v3.py +0 -0
  314. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  315. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  316. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  317. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  318. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_readers_integration.py +0 -0
  319. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_release_funds.py +0 -0
  320. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_swap_on_base.py +0 -0
  321. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  322. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulation_errors.py +0 -0
  323. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulation_observation.py +0 -0
  324. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_solidity_compiler.py +0 -0
  325. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_solidity_mirrors.py +0 -0
  326. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_substrates.py +0 -0
  327. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_types_and_events.py +0 -0
  328. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_unpriceable_tokens.py +0 -0
  329. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_validation.py +0 -0
  330. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_data_block.py +0 -0
  331. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_dep_graph.py +0 -0
  332. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_info_fetched_golden.py +0 -0
  333. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_info_onchain_base.py +0 -0
  334. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  335. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_reconciliation_onchain_base.py +0 -0
  336. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_simulator_blocks.py +0 -0
  337. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_web3_context.py +0 -0
  338. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_withdraw_manager.py +0 -0
  339. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/typed_client/consumer.py +0 -0
  340. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/typed_client/rejected.py +0 -0
  341. {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/uv.lock +0 -0
@@ -487,9 +487,13 @@ structured home: a one-line summary, behavioral notes, cost warnings. Do not
487
487
  repeat schema content as prose; it drifts.
488
488
 
489
489
  `mcp/models.py` forbids extra keys and stays the source of the published JSON
490
- schema. Adding a field to `build_vault_info_json` (or a `fusion market` document)
491
- means adding it to its TypedDict, to the matching model (with a default for
492
- back-compat) and to the `_full_vault_info_dict` fixture;
490
+ schema. A command whose CLI `--json` and MCP tool return the same data publish one
491
+ document: an SDK builder returns its TypedDict (`build_vault_info_json`,
492
+ `build_vault_validation_json`, the `fusion market` builders) and the tool
493
+ validates it into the mirroring model, never a second hand-built shape. Adding a
494
+ field to a document means adding it to its TypedDict, to the matching model (with
495
+ a default for back-compat) and to the `_full_vault_info_dict` fixture for vault
496
+ info;
493
497
  `TestTypedDocumentsMirrorModels` in `test_mcp_models.py` holds every TypedDict to
494
498
  its model block by block, a union block (`deployment`, `position_breakdown`,
495
499
  `source_detail`) shape by shape. Every block is a model; a block whose optional keys
@@ -990,6 +990,29 @@ 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
+ - **`vault validate` publishes one document.** In 3.x the CLI's `--json` output
994
+ and the MCP `vault_validate` tool returned two different shapes; both now
995
+ return `VaultValidationJson`, built by `build_vault_validation_json(validation,
996
+ chain)` (exported from `ipor_fusion` and `ipor_fusion.vault_info`):
997
+ - `findings` lists every check in order with its `check`, `severity` and
998
+ `message`. It replaces the CLI's `criticals` / `warnings` / `ok` string lists
999
+ and the MCP tool's `criticals` / `warnings` / `passed` lists; filter by
1000
+ `severity`.
1001
+ - `erc20` (`tokens`, `value_in_asset`, `market_cached_value`, `unpriced_tokens`)
1002
+ replaces the CLI's `erc20_balances`; `erc20.tokens` has the same entries.
1003
+ - `reconciliation`, `lending_health` and `asset` share their shapes with the
1004
+ `vault info` document. For the MCP tool this means `delta.percent` instead of
1005
+ `delta_percent`, amounts formatted with thousands separators and at most six
1006
+ decimals (`"14,912.465797"`; `raw` keeps the exact value) and the delta's
1007
+ `formatted` as its absolute value, as in `vault info`.
1008
+ - The MCP tool's output gains `block_timestamp`.
1009
+ - An ERC20 entry whose balance read failed keeps its `decimals` when they were
1010
+ read (in `vault info` too).
1011
+ - The MCP output is the builder's document key for key: an amount without a
1012
+ USD value and an ERC20 entry without a balance no longer gain `null` keys
1013
+ (`usd`, `price_usd`, ...) in either tool, `vault_info` included, and a zero
1014
+ USD delta without a price is `0.0`, not `0`.
1015
+
993
1016
  - **A failed lending read is a warning, never a silent `ok`.** 3.x caught every
994
1017
  exception in the lending-health readers and dropped the position, so a provider
995
1018
  outage could leave no lending finding and an `ok` verdict. Now:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev231
3
+ Version: 4.0.0.dev234
4
4
  Summary: The IPOR Fusion SDK for Python
5
5
  Project-URL: homepage, https://ipor.io
6
6
  Project-URL: repository, https://github.com/IPOR-Labs/ipor-fusion.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev231"
3
+ version = "4.0.0.dev234"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -368,7 +368,9 @@ from ipor_fusion.vault_info import (
368
368
  DeploymentErrorJson,
369
369
  DeploymentJson,
370
370
  Erc20BalanceJson,
371
+ Erc20HoldingsJson,
371
372
  FeesJson,
373
+ FindingJson,
372
374
  FuseJson,
373
375
  HealthCheckJson,
374
376
  HighWaterMarkJson,
@@ -392,8 +394,10 @@ from ipor_fusion.vault_info import (
392
394
  VaultAssetJson,
393
395
  VaultInfoJson,
394
396
  VaultLinksJson,
397
+ VaultValidationJson,
395
398
  WithdrawManagerJson,
396
399
  build_vault_info_json,
400
+ build_vault_validation_json,
397
401
  )
398
402
 
399
403
  __version__ = package_version()
@@ -719,6 +723,10 @@ __all__ = [
719
723
  "UsdAmountJson",
720
724
  "VaultAssetJson",
721
725
  "VaultInfoJson",
726
+ "VaultValidationJson",
727
+ "FindingJson",
728
+ "Erc20HoldingsJson",
729
+ "build_vault_validation_json",
722
730
  "VaultLinksJson",
723
731
  "WithdrawManagerJson",
724
732
  "MorphoApiError",
@@ -2,11 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import sys
5
- from typing import TypedDict
6
5
 
7
6
  import click
8
7
  from web3 import Web3
9
- from web3.types import Timestamp
10
8
 
11
9
  from ipor_fusion.chains import CHAIN_NAMES
12
10
  from ipor_fusion.cli.config_store import (
@@ -42,9 +40,8 @@ from ipor_fusion.errors import (
42
40
  from ipor_fusion.readers.oracle_mapping import (
43
41
  build_oracle_mapping,
44
42
  )
45
- from ipor_fusion.types import ChainId, Check, PlasmaVaultAddress, Severity, Verdict
43
+ from ipor_fusion.types import ChainId, PlasmaVaultAddress
46
44
  from ipor_fusion.validation import (
47
- VaultValidation,
48
45
  validate_vault,
49
46
  )
50
47
  from ipor_fusion.vault_data import (
@@ -52,43 +49,11 @@ from ipor_fusion.vault_data import (
52
49
  fetch_vault_data,
53
50
  )
54
51
  from ipor_fusion.vault_info import (
55
- Erc20BalanceJson,
56
- LendingHealthJson,
57
- ReconciliationJson,
58
- VaultAssetJson,
59
- _erc20_balances_json,
60
- _lending_health_json,
61
- _reconciliation_json,
62
52
  _unix_to_iso,
53
+ build_vault_validation_json,
63
54
  )
64
55
 
65
56
 
66
- class _FindingJson(TypedDict):
67
- check: Check
68
- severity: Severity
69
- message: str
70
-
71
-
72
- class _ValidationJson(TypedDict):
73
- """The ``fusion vault validate --json`` document."""
74
-
75
- vault: PlasmaVaultAddress
76
- chain: str
77
- chain_id: ChainId
78
- block: int
79
- block_timestamp: Timestamp
80
- block_timestamp_utc: str
81
- asset: VaultAssetJson
82
- verdict: Verdict
83
- criticals: list[str]
84
- warnings: list[str]
85
- ok: list[str]
86
- findings: list[_FindingJson]
87
- reconciliation: ReconciliationJson
88
- erc20_balances: list[Erc20BalanceJson]
89
- lending_health: LendingHealthJson | None
90
-
91
-
92
57
  def _auto_save_vault(
93
58
  cfg: FusionConfig,
94
59
  vault_address: PlasmaVaultAddress,
@@ -296,7 +261,9 @@ def validate(
296
261
 
297
262
  chain_label = CHAIN_NAMES.get(chain_id, str(chain_id))
298
263
  if json_output:
299
- click.echo(json.dumps(_validation_json(validation, chain_label), indent=2))
264
+ click.echo(
265
+ json.dumps(build_vault_validation_json(validation, chain_label), indent=2)
266
+ )
300
267
  else:
301
268
  click.echo(f"Vault: {validation.vault}")
302
269
  click.echo(f"Chain: {chain_label} (chain-id={chain_id})")
@@ -307,36 +274,6 @@ def validate(
307
274
  sys.exit(1)
308
275
 
309
276
 
310
- def _validation_json(validation: VaultValidation, chain_label: str) -> _ValidationJson:
311
- return {
312
- "vault": validation.vault,
313
- "chain": chain_label,
314
- "chain_id": validation.chain_id,
315
- "block": validation.block_number,
316
- "block_timestamp": validation.block_timestamp,
317
- "block_timestamp_utc": _unix_to_iso(validation.block_timestamp),
318
- "asset": {
319
- "address": validation.asset,
320
- "symbol": validation.asset_symbol,
321
- "decimals": validation.asset_decimals,
322
- "price_usd": validation.asset_price_usd,
323
- },
324
- "verdict": validation.verdict,
325
- "criticals": list(validation.criticals),
326
- "warnings": list(validation.warnings),
327
- "ok": list(validation.ok),
328
- "findings": [
329
- {"check": f.check, "severity": f.severity, "message": f.message}
330
- for f in validation.findings
331
- ],
332
- "reconciliation": _reconciliation_json(
333
- validation.reconciliation, validation.asset_decimals
334
- ),
335
- "erc20_balances": _erc20_balances_json(validation),
336
- "lending_health": _lending_health_json(validation.lending_health),
337
- }
338
-
339
-
340
277
  @vault.command("role-accounts")
341
278
  @click.argument("vault_address", type=VAULT_ADDRESS)
342
279
  @click.option(
@@ -33,8 +33,6 @@ Design notes:
33
33
 
34
34
  from __future__ import annotations
35
35
 
36
- from datetime import UTC, datetime
37
- from decimal import Decimal
38
36
  from typing import TYPE_CHECKING, Literal
39
37
 
40
38
  from pydantic import (
@@ -62,19 +60,7 @@ from ipor_fusion.types import (
62
60
  if TYPE_CHECKING:
63
61
  from ipor_fusion.about import ChangelogEntry
64
62
  from ipor_fusion.core.access import RoleAccount
65
- from ipor_fusion.readers.lending_health import (
66
- LendingMarketHealth as ReaderLendingMarketHealth,
67
- )
68
- from ipor_fusion.readers.lending_health import VaultLendingHealth
69
63
  from ipor_fusion.readers.oracle_mapping import OracleMapping, OracleNode
70
- from ipor_fusion.validation import (
71
- Erc20Holding,
72
- Erc20Holdings,
73
- VaultValidation,
74
- )
75
- from ipor_fusion.validation import (
76
- Reconciliation as ValidationReconciliation,
77
- )
78
64
 
79
65
 
80
66
  class _Base(BaseModel):
@@ -98,7 +84,7 @@ class _Sparse(_Base):
98
84
  # ---------------------------------------------------------------------------
99
85
 
100
86
 
101
- class Amount(_Base):
87
+ class Amount(_Sparse):
102
88
  """Token amount with both raw and formatted representations."""
103
89
 
104
90
  raw: int | str = Field(description="Raw on-chain integer (or 'unlimited').")
@@ -382,7 +368,7 @@ class BalanceFuseEntry(_Base):
382
368
  )
383
369
 
384
370
 
385
- class ERC20Entry(_Base):
371
+ class ERC20Entry(_Sparse):
386
372
  """ERC-20 balance entry; field set varies by data availability."""
387
373
 
388
374
  address: str
@@ -770,255 +756,51 @@ class OracleMappingResponse(_Base):
770
756
  # ---------------------------------------------------------------------------
771
757
 
772
758
 
773
- def _formatted(raw: int, decimals: int) -> str:
774
- return format(Decimal(raw).scaleb(-decimals).normalize(), "f") if raw else "0"
775
-
776
-
777
- def _amount(raw: int, decimals: int, usd: float | None = None) -> Amount:
778
- """An Amount in vault_info's convention, the sign kept in `formatted`."""
779
- return Amount(raw=raw, formatted=_formatted(raw, decimals), usd=usd)
780
-
781
-
782
759
  class FindingModel(_Base):
783
760
  check: Check
784
761
  severity: Severity
785
762
  message: str
786
763
 
787
764
 
788
- class ReconciliationModel(_Base):
789
- """totalAssets against balance fuses, idle underlying and the rewards
790
- claim manager's balance."""
791
-
792
- balance_fuses_total: Amount
793
- underlying_on_vault: Amount
794
- rewards_manager_balance: Amount = Field(
795
- description="RewardsClaimManager.balanceOf(), which totalAssets includes; "
796
- "0 without a manager or when it could not be read."
797
- )
798
- management_fee_netted: Amount = Field(
799
- description="Unrealized management fee subtracted from sum: totalAssets "
800
- "nets it on vaults built before ipor-fusion #475; 0 otherwise."
801
- )
802
- erc20_direct_total: Amount = Field(
803
- description="Informational: already valued by the ERC20_VAULT_BALANCE "
804
- "balance fuse, never added to sum."
805
- )
806
- sum: Amount
807
- on_chain_total_assets: Amount
808
- delta: Amount
809
- delta_percent: float
810
- pending_withdrawals: Amount
811
- implied_market_total: Amount
812
- market_storage_divergence: int = Field(
813
- description="balance_fuses_total minus implied_market_total, raw: "
814
- "per-market caches drifting from the stored global total."
815
- )
816
-
817
- @classmethod
818
- def from_reconciliation(
819
- cls, rec: ValidationReconciliation, decimals: int
820
- ) -> ReconciliationModel:
821
- def amount(raw: int, usd: float | None = None) -> Amount:
822
- return _amount(raw, decimals, usd)
823
-
824
- return cls(
825
- balance_fuses_total=amount(
826
- rec.balance_fuses_total, rec.balance_fuses_total_usd
827
- ),
828
- underlying_on_vault=amount(
829
- rec.underlying_on_vault, rec.underlying_on_vault_usd
830
- ),
831
- rewards_manager_balance=amount(
832
- rec.rewards_manager_balance, rec.rewards_manager_balance_usd
833
- ),
834
- management_fee_netted=amount(
835
- rec.management_fee_netted, rec.management_fee_netted_usd
836
- ),
837
- erc20_direct_total=amount(
838
- rec.erc20_direct_total, rec.erc20_direct_total_usd
839
- ),
840
- sum=amount(rec.sum, rec.sum_usd),
841
- on_chain_total_assets=amount(
842
- rec.on_chain_total_assets, rec.on_chain_total_assets_usd
843
- ),
844
- delta=amount(rec.delta, rec.delta_usd),
845
- delta_percent=rec.delta_percent,
846
- pending_withdrawals=amount(
847
- rec.pending_withdrawals, rec.pending_withdrawals_usd
848
- ),
849
- implied_market_total=amount(rec.implied_market_total),
850
- market_storage_divergence=rec.market_storage_divergence,
851
- )
852
-
853
-
854
- class LendingMarketModel(_Base):
855
- protocol: str
856
- market_id: MarketId
857
- market_name: str
858
- substrate_id: str | None = Field(
859
- description="0x-prefixed Morpho market id or Aave V4 Spoke; null for "
860
- "Aave V3, whose health is account-level."
861
- )
862
- current_ltv: float | None
863
- max_ltv: float
864
- health_factor: float | None
865
- total_collateral_usd: float | None
866
- total_debt_usd: float | None
867
- ltv_usage_percent: float | None
868
-
869
- @classmethod
870
- def from_market(cls, m: ReaderLendingMarketHealth) -> LendingMarketModel:
871
- substrate_id = m.substrate_id
872
- if substrate_id is not None and not substrate_id.startswith("0x"):
873
- substrate_id = f"0x{substrate_id}"
874
- return cls(
875
- protocol=m.protocol,
876
- market_id=m.market_id,
877
- market_name=m.market_name,
878
- substrate_id=substrate_id,
879
- current_ltv=m.current_ltv,
880
- max_ltv=m.max_ltv,
881
- health_factor=m.health_factor,
882
- total_collateral_usd=m.total_collateral_usd,
883
- total_debt_usd=m.total_debt_usd,
884
- ltv_usage_percent=m.ltv_usage_percent,
885
- )
886
-
887
-
888
- class LendingHealthModel(_Base):
889
- markets: list[LendingMarketModel]
890
- unreadable: list[UnreadableLendingMarket] = Field(
891
- description="Positions whose health could not be read: unknown, not healthy."
892
- )
893
- worst_ltv_usage_percent: float | None
894
-
895
- @classmethod
896
- def from_health(cls, health: VaultLendingHealth) -> LendingHealthModel:
897
- return cls(
898
- markets=[LendingMarketModel.from_market(m) for m in health.markets],
899
- unreadable=[
900
- UnreadableLendingMarket(
901
- protocol=u.protocol,
902
- market_id=u.market_id,
903
- market_name=u.market_name,
904
- substrate_id=u.substrate_id,
905
- reason=u.reason,
906
- )
907
- for u in health.unreadable
908
- ],
909
- worst_ltv_usage_percent=health.worst_ltv_usage,
910
- )
911
-
912
-
913
- class Erc20HoldingModel(_Base):
914
- address: str
915
- symbol: str
916
- decimals: int | None
917
- balance: Amount | None
918
- price_usd: float | None
919
- usd_value: float | None
920
- note: str
921
-
922
- @classmethod
923
- def from_holding(cls, token: Erc20Holding) -> Erc20HoldingModel:
924
- balance = (
925
- _amount(token.balance, token.decimals)
926
- if token.balance is not None and token.decimals is not None
927
- else None
928
- )
929
- return cls(
930
- address=token.address,
931
- symbol=token.symbol,
932
- decimals=token.decimals,
933
- balance=balance,
934
- price_usd=token.price_usd,
935
- usd_value=token.usd_value,
936
- note=token.note,
937
- )
938
-
939
-
940
765
  class Erc20HoldingsModel(_Base):
941
- tokens: list[Erc20HoldingModel]
766
+ """The ERC20_VAULT_BALANCE tokens read directly."""
767
+
768
+ tokens: list[ERC20Entry]
942
769
  value_in_asset: Amount
943
770
  market_cached_value: Amount = Field(
944
771
  description="What the ERC20_VAULT_BALANCE market last stored for "
945
772
  "these tokens, in the asset."
946
773
  )
947
- unpriced_tokens: list[str]
948
-
949
- @classmethod
950
- def from_holdings(cls, erc20: Erc20Holdings, decimals: int) -> Erc20HoldingsModel:
951
- return cls(
952
- tokens=[Erc20HoldingModel.from_holding(t) for t in erc20.tokens],
953
- value_in_asset=_amount(erc20.value_in_asset, decimals, erc20.value_usd),
954
- market_cached_value=_amount(erc20.market_cached_value, decimals),
955
- unpriced_tokens=list(erc20.unpriced_tokens),
956
- )
774
+ unpriced_tokens: list[str] = Field(
775
+ description="Held tokens that could not be valued, as 'address (symbol)'."
776
+ )
957
777
 
958
778
 
959
779
  class VaultValidationResponse(_Base):
960
- """Health verdict of one Plasma Vault at one block."""
780
+ """Health verdict of one Plasma Vault at one block; the same document as
781
+ ``fusion vault validate --json``."""
961
782
 
962
783
  vault: str
963
784
  chain: str
964
785
  chain_id: ChainId
965
786
  block: int
787
+ block_timestamp: int
966
788
  block_timestamp_utc: str
967
789
  asset: AssetInfo
968
790
  verdict: Verdict = Field(
969
791
  description="Worst severity found: critical if any critical finding, "
970
792
  "else warning if any warning, else ok."
971
793
  )
972
- criticals: list[FindingModel]
973
- warnings: list[FindingModel]
974
- passed: list[FindingModel] = Field(
975
- description="Checks that ran and found nothing wrong."
794
+ findings: list[FindingModel] = Field(
795
+ description="Every check in the order it ran, ok ones included."
976
796
  )
977
- reconciliation: ReconciliationModel
978
- lending_health: LendingHealthModel | None = Field(
797
+ reconciliation: Reconciliation
798
+ erc20: Erc20HoldingsModel
799
+ lending_health: LendingHealth | None = Field(
979
800
  description="LTV and health factor per lending market (Morpho, Aave "
980
- "V3/V4); null when the vault has no lending position."
801
+ "V3/V4) and the positions that could not be read; null when the vault "
802
+ "has no lending position."
981
803
  )
982
- erc20: Erc20HoldingsModel
983
-
984
- @classmethod
985
- def from_validation(cls, v: VaultValidation, chain: str) -> VaultValidationResponse:
986
- def by(severity: Severity) -> list[FindingModel]:
987
- return [
988
- FindingModel(check=f.check, severity=f.severity, message=f.message)
989
- for f in v.findings
990
- if f.severity == severity
991
- ]
992
-
993
- health = v.lending_health
994
- return cls(
995
- vault=v.vault,
996
- chain=chain,
997
- chain_id=v.chain_id,
998
- block=v.block_number,
999
- block_timestamp_utc=datetime.fromtimestamp(
1000
- v.block_timestamp, tz=UTC
1001
- ).strftime("%Y-%m-%dT%H:%M:%SZ"),
1002
- asset=AssetInfo(
1003
- address=v.asset,
1004
- symbol=v.asset_symbol,
1005
- decimals=v.asset_decimals,
1006
- price_usd=v.asset_price_usd,
1007
- ),
1008
- verdict=v.verdict,
1009
- criticals=by("critical"),
1010
- warnings=by("warning"),
1011
- passed=by("ok"),
1012
- reconciliation=ReconciliationModel.from_reconciliation(
1013
- v.reconciliation, v.asset_decimals
1014
- ),
1015
- lending_health=(
1016
- LendingHealthModel.from_health(health)
1017
- if health is not None and health.has_lending_positions
1018
- else None
1019
- ),
1020
- erc20=Erc20HoldingsModel.from_holdings(v.erc20, v.asset_decimals),
1021
- )
1022
804
 
1023
805
 
1024
806
  # ---------------------------------------------------------------------------
@@ -65,7 +65,7 @@ from ipor_fusion.vault_data import (
65
65
  fetch_deployment_info,
66
66
  fetch_vault_data,
67
67
  )
68
- from ipor_fusion.vault_info import build_vault_info_json
68
+ from ipor_fusion.vault_info import build_vault_info_json, build_vault_validation_json
69
69
 
70
70
  # chain_id default: detect the chain from the saved vault or the single provider.
71
71
  _AUTO_CHAIN = ChainId(0)
@@ -246,8 +246,10 @@ def vault_validate(
246
246
  resolve_access_manager(ctx, checksum)
247
247
 
248
248
  validation = validate_vault(ctx, checksum, block=effective_block, metadata=True)
249
- return VaultValidationResponse.from_validation(
250
- validation, CHAIN_NAMES.get(chain_id, str(chain_id))
249
+ return VaultValidationResponse.model_validate(
250
+ build_vault_validation_json(
251
+ validation, CHAIN_NAMES.get(chain_id, str(chain_id))
252
+ )
251
253
  )
252
254
 
253
255
 
@@ -345,7 +345,7 @@ def _reconcile(state: VaultState) -> Reconciliation:
345
345
  total = Amount(gross - fee)
346
346
  total_usd = balance_fuses_usd + underlying_usd + rewards_usd - fee_usd
347
347
  delta = Amount(total - on_chain)
348
- delta_usd = abs(total_usd - (on_chain / 10**decimals * price)) if price else 0
348
+ delta_usd = abs(total_usd - (on_chain / 10**decimals * price)) if price else 0.0
349
349
  delta_percent = abs(delta / on_chain * 100) if on_chain else 0.0
350
350
 
351
351
  # totalAssets reads the global market total while balance fuses report