ipor-fusion 4.0.0.dev222__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.dev222 → ipor_fusion-4.0.0.dev234}/AGENTS.md +12 -3
  2. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/MIGRATION_4.md +48 -0
  3. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/PKG-INFO +1 -1
  4. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/pyproject.toml +1 -1
  5. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/__init__.py +14 -0
  6. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_cmd.py +5 -68
  7. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_markets_view.py +11 -0
  8. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/models.py +30 -223
  9. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/server.py +5 -3
  10. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/__init__.py +4 -0
  11. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/lending_health.py +104 -24
  12. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/unpriceable.py +33 -7
  13. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/types.py +1 -0
  14. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/validation.py +41 -2
  15. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_data.py +27 -3
  16. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_info.py +130 -22
  17. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_positions.py +10 -2
  18. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/clean.json +1 -1
  19. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/clean.mcp.json +4 -8
  20. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/deployment_error.json +1 -1
  21. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +4 -8
  22. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/fetched.mcp.json +6 -12
  23. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/full.json +1 -0
  24. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/full.mcp.json +16 -34
  25. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/rich.json +1 -0
  26. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/rich.mcp.json +14 -30
  27. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_helpers.py +27 -3
  28. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_lending_health.py +49 -26
  29. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_vault_validate.py +8 -3
  30. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_models.py +14 -0
  31. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_vault_validate.py +66 -30
  32. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_public_api.py +14 -0
  33. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_unpriceable_tokens.py +11 -2
  34. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_validation.py +145 -2
  35. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_vault_info_fetched_golden.py +1 -0
  36. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.claude-plugin/marketplace.json +0 -0
  37. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.claude-plugin/plugin.json +0 -0
  38. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.env.example +0 -0
  39. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.gemini/settings.json +0 -0
  40. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.git-blame-ignore-revs +0 -0
  41. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.github/workflows/cd.yml +0 -0
  42. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.github/workflows/ci.yml +0 -0
  43. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.github/workflows/commit-lint.yml +0 -0
  44. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.github/workflows/notify-slack.yml +0 -0
  45. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.github/workflows/python-build.yml +0 -0
  46. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.github/workflows/python-publish.yml +0 -0
  47. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.github/workflows/release.yml +0 -0
  48. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.github/workflows/report-slack.yml +0 -0
  49. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.gitignore +0 -0
  50. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.mcp.json +0 -0
  51. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.pre-commit-config.yaml +0 -0
  52. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/.python-version +0 -0
  53. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/CHANGELOG.md +0 -0
  54. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/CLAUDE.md +0 -0
  55. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/CODEOWNERS +0 -0
  56. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/LICENSE +0 -0
  57. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/README.md +0 -0
  58. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/build.sh +0 -0
  59. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/examples/CONTRIBUTING.md +0 -0
  60. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/examples/README.md +0 -0
  61. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  62. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  63. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  64. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  65. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/examples/external_state_margin_leg_base.py +0 -0
  66. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/examples/simple_aave_v3_supply_base.py +0 -0
  67. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/pytest.ini +0 -0
  68. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/scripts/commit-check.sh +0 -0
  69. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/scripts/crosschain_readiness.py +0 -0
  70. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/skills/ipor-deploy-vault/SKILL.md +0 -0
  71. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/about.py +0 -0
  72. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cache.py +0 -0
  73. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/chains.py +0 -0
  74. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/__init__.py +0 -0
  75. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  76. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/config_cmd.py +0 -0
  77. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/config_store.py +0 -0
  78. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/main.py +0 -0
  79. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/market_cmd.py +0 -0
  80. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/options.py +0 -0
  81. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  82. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_health.py +0 -0
  83. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  84. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  85. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/config/__init__.py +0 -0
  86. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/config/roles.py +0 -0
  87. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/__init__.py +0 -0
  88. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/access.py +0 -0
  89. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/address.py +0 -0
  90. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/context.py +0 -0
  91. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/contract.py +0 -0
  92. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/erc20.py +0 -0
  93. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/external_state_executor.py +0 -0
  94. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fee_manager.py +0 -0
  95. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fuse.py +0 -0
  96. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fuse_action.py +0 -0
  97. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fusion_factory.py +0 -0
  98. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/logs.py +0 -0
  99. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/multicall.py +0 -0
  100. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/oracle.py +0 -0
  101. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/plasma_vault.py +0 -0
  102. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/rewards_manager.py +0 -0
  103. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/simulation.py +0 -0
  104. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/simulation_result.py +0 -0
  105. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  106. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/__init__.py +0 -0
  107. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  108. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  109. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  110. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  111. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  112. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  113. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  114. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  115. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  116. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/contracts.py +0 -0
  117. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/discovery.py +0 -0
  118. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/errors.py +0 -0
  119. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  120. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  121. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  122. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  123. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/lane.py +0 -0
  124. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/logs.py +0 -0
  125. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/messages.py +0 -0
  126. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/simulation.py +0 -0
  127. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  128. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  129. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  130. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  131. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  132. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/transport.py +0 -0
  133. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/dependency_graph.py +0 -0
  134. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/entry_cli.py +0 -0
  135. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/entry_mcp.py +0 -0
  136. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/errors.py +0 -0
  137. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/explorer.py +0 -0
  138. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/field_docs.py +0 -0
  139. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/__init__.py +0 -0
  140. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  141. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/async_action.py +0 -0
  142. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/base.py +0 -0
  143. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  144. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/erc4626.py +0 -0
  145. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  146. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/events.py +0 -0
  147. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/external_state.py +0 -0
  148. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
  149. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  150. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  151. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/hypercore.py +0 -0
  152. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
  153. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/merkl.py +0 -0
  154. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/morpho.py +0 -0
  155. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  156. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  157. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/universal.py +0 -0
  158. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/__init__.py +0 -0
  159. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/architecture.md +0 -0
  160. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/glossary.md +0 -0
  161. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/invariants.md +0 -0
  162. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/quickstart.md +0 -0
  163. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/hypercore/__init__.py +0 -0
  164. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/market_ids.py +0 -0
  165. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/market_info.py +0 -0
  166. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/__init__.py +0 -0
  167. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/guide.py +0 -0
  168. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/morpho_api.py +0 -0
  169. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/py.typed +0 -0
  170. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/aave_v3.py +0 -0
  171. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/aave_v4.py +0 -0
  172. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/compound_v3.py +0 -0
  173. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/hypercore.py +0 -0
  174. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  175. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/morpho.py +0 -0
  176. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  177. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  178. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  179. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/position_manager.py +0 -0
  180. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  181. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  182. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/substrates.py +0 -0
  183. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_erc20.py +0 -0
  184. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_managers.py +0 -0
  185. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_reads.py +0 -0
  186. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/UniswapV3UniversalRouter.py +0 -0
  187. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_crosschain.py +0 -0
  188. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_crosschain_lifecycle.py +0 -0
  189. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_crosschain_recovery.py +0 -0
  190. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_euler_v2.py +0 -0
  191. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_event_args.py +0 -0
  192. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_fake_rpc.py +0 -0
  193. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_foundry.py +0 -0
  194. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_golden.py +0 -0
  195. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_guide.py +0 -0
  196. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_hypercore_model.py +0 -0
  197. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_hypercore_shadow.py +0 -0
  198. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_live.py +0 -0
  199. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_multicall.py +0 -0
  200. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_oracle_mapping_fake.py +0 -0
  201. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_simulate.py +0 -0
  202. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/_validation.py +0 -0
  203. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/addresses.py +0 -0
  204. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/conftest.py +0 -0
  205. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/constants.py +0 -0
  206. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  207. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/hypercore_market55.json +0 -0
  208. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
  209. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
  210. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
  211. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
  212. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
  213. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
  214. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
  215. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
  216. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue.json +0 -0
  217. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
  218. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
  219. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
  220. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
  221. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
  222. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
  223. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
  224. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/simulate_v1_request.json +0 -0
  225. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  226. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
  227. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
  228. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/fetched.json +0 -0
  229. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/full.txt +0 -0
  230. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  231. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_about.py +0 -0
  232. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_access_manager.py +0 -0
  233. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cache.py +0 -0
  234. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_chains.py +0 -0
  235. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_changelog.py +0 -0
  236. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_commands.py +0 -0
  237. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_config_store.py +0 -0
  238. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_hypercore.py +0 -0
  239. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_market_golden.py +0 -0
  240. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_market_morpho_blue.py +0 -0
  241. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_oracle_mapping.py +0 -0
  242. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_oracle_mapping_golden.py +0 -0
  243. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_pending_requests_scan.py +0 -0
  244. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_role_accounts.py +0 -0
  245. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_vault_info_golden.py +0 -0
  246. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_cli_vault_predates_getters.py +0 -0
  247. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_contract_call.py +0 -0
  248. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_contract_encoder.py +0 -0
  249. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_ccip_events.py +0 -0
  250. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_ccip_indexer.py +0 -0
  251. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_ccip_quote.py +0 -0
  252. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_chainlink.py +0 -0
  253. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_contracts.py +0 -0
  254. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_encoding.py +0 -0
  255. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_errors.py +0 -0
  256. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_lane.py +0 -0
  257. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_layering.py +0 -0
  258. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_readiness.py +0 -0
  259. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_readiness_report.py +0 -0
  260. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_transport.py +0 -0
  261. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_docs_typing.py +0 -0
  262. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_erc20.py +0 -0
  263. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_error_hierarchy.py +0 -0
  264. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_examples_composed_hypercore.py +0 -0
  265. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_examples_crosschain.py +0 -0
  266. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  267. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_examples_vaults.py +0 -0
  268. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_explorer.py +0 -0
  269. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_external_state_events.py +0 -0
  270. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_external_state_executor.py +0 -0
  271. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_fee_manager.py +0 -0
  272. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_fuse_encoding.py +0 -0
  273. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_fusion_instance.py +0 -0
  274. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_guide.py +0 -0
  275. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_deployment.py +0 -0
  276. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_events.py +0 -0
  277. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_flow.py +0 -0
  278. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_live.py +0 -0
  279. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_outcome.py +0 -0
  280. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_readers.py +0 -0
  281. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_simulation.py +0 -0
  282. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_logs.py +0 -0
  283. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_hypercore.py +0 -0
  284. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_server.py +0 -0
  285. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_multicall.py +0 -0
  286. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_oracle.py +0 -0
  287. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_oracle_mapping.py +0 -0
  288. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_plasma_vault.py +0 -0
  289. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_public_type_reachability.py +0 -0
  290. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_reader_encoding.py +0 -0
  291. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_readiness_selection.py +0 -0
  292. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_revert_decoding.py +0 -0
  293. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_rewards_manager.py +0 -0
  294. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_roles.py +0 -0
  295. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_sdk_layering.py +0 -0
  296. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  297. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  298. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  299. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_canary.py +0 -0
  300. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_hypercore.py +0 -0
  301. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  302. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  303. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  304. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  305. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  306. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  307. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  308. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_examples_arbitrum.py +0 -0
  309. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_examples_base.py +0 -0
  310. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_examples_crosschain.py +0 -0
  311. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_external_state_executor_base.py +0 -0
  312. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  313. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  314. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  315. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_morpho_blue.py +0 -0
  316. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v3.py +0 -0
  317. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  318. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  319. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  320. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  321. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_readers_integration.py +0 -0
  322. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_release_funds.py +0 -0
  323. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_swap_on_base.py +0 -0
  324. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  325. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulation_errors.py +0 -0
  326. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_simulation_observation.py +0 -0
  327. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_solidity_compiler.py +0 -0
  328. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_solidity_mirrors.py +0 -0
  329. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_substrates.py +0 -0
  330. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_types_and_events.py +0 -0
  331. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_vault_data_block.py +0 -0
  332. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_vault_dep_graph.py +0 -0
  333. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_vault_info_onchain_base.py +0 -0
  334. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  335. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_vault_reconciliation_onchain_base.py +0 -0
  336. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_vault_simulator_blocks.py +0 -0
  337. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_web3_context.py +0 -0
  338. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/test_withdraw_manager.py +0 -0
  339. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/typed_client/consumer.py +0 -0
  340. {ipor_fusion-4.0.0.dev222 → ipor_fusion-4.0.0.dev234}/tests/typed_client/rejected.py +0 -0
  341. {ipor_fusion-4.0.0.dev222 → 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
@@ -533,6 +537,11 @@ decoder adds its own fields.
533
537
  - `ZeroBalanceFuse` markets are capabilities (swap, flash-loan,
534
538
  instant-withdrawal, admin) with structurally zero balance, not venues. They
535
539
  go in `zero_balance_fuses` and are not counted as liquidity markets.
540
+ - A validation input that cannot be read is never a silent `ok`: a lending
541
+ position whose reads fail is an `UnreadableLendingMarket` (a `lending_health`
542
+ warning), a failed lending-health or unpriceable-token read is named in
543
+ `VaultData.failed_reads` (an `incomplete_read` warning). Catch the chain and
544
+ transport errors only; degrade-to-`None` is for display-only data.
536
545
  - Reconciliation: `totalAssets = balance_fuses_sum + underlying_on_vault +
537
546
  rewards_manager_balance` (`PlasmaVault._getGrossTotalAssets` adds
538
547
  `IRewardsClaimManager.balanceOf()`, the vested rewards, when a manager is set;
@@ -990,6 +990,54 @@ 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
+
1016
+ - **A failed lending read is a warning, never a silent `ok`.** 3.x caught every
1017
+ exception in the lending-health readers and dropped the position, so a provider
1018
+ outage could leave no lending finding and an `ok` verdict. Now:
1019
+ - A position whose reads fail with a chain or transport error (revert, RPC,
1020
+ timeout, no or malformed data) becomes an `UnreadableLendingMarket` in
1021
+ `VaultLendingHealth.unreadable`.
1022
+ - Each unreadable position is a `lending_health` warning.
1023
+ - An Aave V3 market whose Pool could not be resolved is listed the same way
1024
+ instead of being skipped.
1025
+ - Any other exception propagates.
1026
+ - When the whole lending-health read or the unpriceable-token check fails with
1027
+ a revert, an RPC error or a timeout, `VaultData.failed_reads` names it and
1028
+ validation reports an `incomplete_read` warning. A transport failure (no
1029
+ connection) still aborts the fetch rather than return a snapshot.
1030
+ - A substrate the unpriceable-token check could not cover is named the same
1031
+ way (`unpriceable_priced_tokens: <what>`). Examples: a Morpho position whose
1032
+ read failed, or a reverting `asset()` / `getSilos()`.
1033
+ `fetch_unpriceable_priced_tokens` returns an `UnpriceableCheck` (`tokens`,
1034
+ `unread`) instead of a bare list.
1035
+
1036
+ The `lending_health` JSON block of `vault info` and `vault validate` carries
1037
+ `unreadable`, and a custom `VaultState` adds the `failed_reads` property. A
1038
+ position missing from the breakdown tables is shown as absent, while the
1039
+ checks it feeds report it.
1040
+
993
1041
  - **More markets decode their substrates.** VELODROME_SUPERCHAIN (31, Gauge /
994
1042
  Pool), AGUA_GLOBAL_CARRY (51, VAULT / ASSET), TERM_FINANCE (52, SERVICER address
995
1043
  or COLLATERAL_TOKEN pair key), META_MORPHO_0001, MERKL and
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev222
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.dev222"
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" },
@@ -275,6 +275,8 @@ from ipor_fusion.readers import (
275
275
  RamsesV2Reader,
276
276
  UniswapV3Position,
277
277
  UniswapV3Reader,
278
+ UnpriceableCheck,
279
+ UnreadableLendingMarket,
278
280
  VaultLendingHealth,
279
281
  build_oracle_mapping,
280
282
  fetch_unpriceable_priced_tokens,
@@ -366,7 +368,9 @@ from ipor_fusion.vault_info import (
366
368
  DeploymentErrorJson,
367
369
  DeploymentJson,
368
370
  Erc20BalanceJson,
371
+ Erc20HoldingsJson,
369
372
  FeesJson,
373
+ FindingJson,
370
374
  FuseJson,
371
375
  HealthCheckJson,
372
376
  HighWaterMarkJson,
@@ -385,12 +389,15 @@ from ipor_fusion.vault_info import (
385
389
  ReconciliationJson,
386
390
  SharePriceJson,
387
391
  SubstrateEntryJson,
392
+ UnreadableLendingMarketJson,
388
393
  UsdAmountJson,
389
394
  VaultAssetJson,
390
395
  VaultInfoJson,
391
396
  VaultLinksJson,
397
+ VaultValidationJson,
392
398
  WithdrawManagerJson,
393
399
  build_vault_info_json,
400
+ build_vault_validation_json,
394
401
  )
395
402
 
396
403
  __version__ = package_version()
@@ -631,6 +638,7 @@ __all__ = [
631
638
  "HYPEREVM_CHAIN_ID",
632
639
  "LendingMarketHealth",
633
640
  "VaultLendingHealth",
641
+ "UnreadableLendingMarket",
634
642
  "fetch_vault_lending_health",
635
643
  "OracleAsset",
636
644
  "OracleMapping",
@@ -651,6 +659,7 @@ __all__ = [
651
659
  "build_oracle_mapping",
652
660
  "OracleMappingReader",
653
661
  "MiddlewarePricedToken",
662
+ "UnpriceableCheck",
654
663
  "fetch_unpriceable_priced_tokens",
655
664
  "PositionData",
656
665
  "UniswapV3Reader",
@@ -701,6 +710,7 @@ __all__ = [
701
710
  "HyperCorePerpMarketJson",
702
711
  "HyperCoreSpotLegJson",
703
712
  "LendingHealthJson",
713
+ "UnreadableLendingMarketJson",
704
714
  "LendingMarketJson",
705
715
  "ManagersJson",
706
716
  "MorphoBreakdownJson",
@@ -713,6 +723,10 @@ __all__ = [
713
723
  "UsdAmountJson",
714
724
  "VaultAssetJson",
715
725
  "VaultInfoJson",
726
+ "VaultValidationJson",
727
+ "FindingJson",
728
+ "Erc20HoldingsJson",
729
+ "build_vault_validation_json",
716
730
  "VaultLinksJson",
717
731
  "WithdrawManagerJson",
718
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(
@@ -95,6 +95,17 @@ def _print_lending_health(data: VaultData) -> None: # noqa: C901
95
95
  if m.protocol == "aave_v4":
96
96
  click.echo(f" spoke {m.substrate_id}:")
97
97
  _print_health_lines(m, indent=" ")
98
+ _print_unreadable_health(lh)
99
+
100
+
101
+ def _print_unreadable_health(lh: VaultLendingHealth | None) -> None:
102
+ for u in lh.unreadable if lh is not None else []:
103
+ where = f" {u.substrate_id}" if u.substrate_id else ""
104
+ click.secho(
105
+ f" {format_market_label(u.market_id)}: {u.protocol}{where} "
106
+ f"health unknown — {u.reason}",
107
+ fg="yellow",
108
+ )
98
109
 
99
110
 
100
111
  def _index_lending_health(
@@ -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
@@ -431,8 +417,20 @@ class LendingMarketHealth(_Base):
431
417
  is_critical: bool
432
418
 
433
419
 
420
+ class UnreadableLendingMarket(_Base):
421
+ """A lending position whose health could not be read: unknown, not
422
+ healthy."""
423
+
424
+ protocol: str
425
+ market_id: MarketId
426
+ market_name: str
427
+ substrate_id: str | None
428
+ reason: str
429
+
430
+
434
431
  class LendingHealth(_Base):
435
432
  markets: list[LendingMarketHealth]
433
+ unreadable: list[UnreadableLendingMarket] = Field(default_factory=list)
436
434
  worst_ltv_usage_percent: float | None
437
435
 
438
436
 
@@ -758,242 +756,51 @@ class OracleMappingResponse(_Base):
758
756
  # ---------------------------------------------------------------------------
759
757
 
760
758
 
761
- def _formatted(raw: int, decimals: int) -> str:
762
- return format(Decimal(raw).scaleb(-decimals).normalize(), "f") if raw else "0"
763
-
764
-
765
- def _amount(raw: int, decimals: int, usd: float | None = None) -> Amount:
766
- """An Amount in vault_info's convention, the sign kept in `formatted`."""
767
- return Amount(raw=raw, formatted=_formatted(raw, decimals), usd=usd)
768
-
769
-
770
759
  class FindingModel(_Base):
771
760
  check: Check
772
761
  severity: Severity
773
762
  message: str
774
763
 
775
764
 
776
- class ReconciliationModel(_Base):
777
- """totalAssets against balance fuses, idle underlying and the rewards
778
- claim manager's balance."""
779
-
780
- balance_fuses_total: Amount
781
- underlying_on_vault: Amount
782
- rewards_manager_balance: Amount = Field(
783
- description="RewardsClaimManager.balanceOf(), which totalAssets includes; "
784
- "0 without a manager or when it could not be read."
785
- )
786
- management_fee_netted: Amount = Field(
787
- description="Unrealized management fee subtracted from sum: totalAssets "
788
- "nets it on vaults built before ipor-fusion #475; 0 otherwise."
789
- )
790
- erc20_direct_total: Amount = Field(
791
- description="Informational: already valued by the ERC20_VAULT_BALANCE "
792
- "balance fuse, never added to sum."
793
- )
794
- sum: Amount
795
- on_chain_total_assets: Amount
796
- delta: Amount
797
- delta_percent: float
798
- pending_withdrawals: Amount
799
- implied_market_total: Amount
800
- market_storage_divergence: int = Field(
801
- description="balance_fuses_total minus implied_market_total, raw: "
802
- "per-market caches drifting from the stored global total."
803
- )
804
-
805
- @classmethod
806
- def from_reconciliation(
807
- cls, rec: ValidationReconciliation, decimals: int
808
- ) -> ReconciliationModel:
809
- def amount(raw: int, usd: float | None = None) -> Amount:
810
- return _amount(raw, decimals, usd)
811
-
812
- return cls(
813
- balance_fuses_total=amount(
814
- rec.balance_fuses_total, rec.balance_fuses_total_usd
815
- ),
816
- underlying_on_vault=amount(
817
- rec.underlying_on_vault, rec.underlying_on_vault_usd
818
- ),
819
- rewards_manager_balance=amount(
820
- rec.rewards_manager_balance, rec.rewards_manager_balance_usd
821
- ),
822
- management_fee_netted=amount(
823
- rec.management_fee_netted, rec.management_fee_netted_usd
824
- ),
825
- erc20_direct_total=amount(
826
- rec.erc20_direct_total, rec.erc20_direct_total_usd
827
- ),
828
- sum=amount(rec.sum, rec.sum_usd),
829
- on_chain_total_assets=amount(
830
- rec.on_chain_total_assets, rec.on_chain_total_assets_usd
831
- ),
832
- delta=amount(rec.delta, rec.delta_usd),
833
- delta_percent=rec.delta_percent,
834
- pending_withdrawals=amount(
835
- rec.pending_withdrawals, rec.pending_withdrawals_usd
836
- ),
837
- implied_market_total=amount(rec.implied_market_total),
838
- market_storage_divergence=rec.market_storage_divergence,
839
- )
840
-
841
-
842
- class LendingMarketModel(_Base):
843
- protocol: str
844
- market_id: MarketId
845
- market_name: str
846
- substrate_id: str | None = Field(
847
- description="0x-prefixed Morpho market id or Aave V4 Spoke; null for "
848
- "Aave V3, whose health is account-level."
849
- )
850
- current_ltv: float | None
851
- max_ltv: float
852
- health_factor: float | None
853
- total_collateral_usd: float | None
854
- total_debt_usd: float | None
855
- ltv_usage_percent: float | None
856
-
857
- @classmethod
858
- def from_market(cls, m: ReaderLendingMarketHealth) -> LendingMarketModel:
859
- substrate_id = m.substrate_id
860
- if substrate_id is not None and not substrate_id.startswith("0x"):
861
- substrate_id = f"0x{substrate_id}"
862
- return cls(
863
- protocol=m.protocol,
864
- market_id=m.market_id,
865
- market_name=m.market_name,
866
- substrate_id=substrate_id,
867
- current_ltv=m.current_ltv,
868
- max_ltv=m.max_ltv,
869
- health_factor=m.health_factor,
870
- total_collateral_usd=m.total_collateral_usd,
871
- total_debt_usd=m.total_debt_usd,
872
- ltv_usage_percent=m.ltv_usage_percent,
873
- )
874
-
875
-
876
- class LendingHealthModel(_Base):
877
- markets: list[LendingMarketModel]
878
- worst_ltv_usage_percent: float | None
879
-
880
- @classmethod
881
- def from_health(cls, health: VaultLendingHealth) -> LendingHealthModel:
882
- return cls(
883
- markets=[LendingMarketModel.from_market(m) for m in health.markets],
884
- worst_ltv_usage_percent=health.worst_ltv_usage,
885
- )
886
-
887
-
888
- class Erc20HoldingModel(_Base):
889
- address: str
890
- symbol: str
891
- decimals: int | None
892
- balance: Amount | None
893
- price_usd: float | None
894
- usd_value: float | None
895
- note: str
896
-
897
- @classmethod
898
- def from_holding(cls, token: Erc20Holding) -> Erc20HoldingModel:
899
- balance = (
900
- _amount(token.balance, token.decimals)
901
- if token.balance is not None and token.decimals is not None
902
- else None
903
- )
904
- return cls(
905
- address=token.address,
906
- symbol=token.symbol,
907
- decimals=token.decimals,
908
- balance=balance,
909
- price_usd=token.price_usd,
910
- usd_value=token.usd_value,
911
- note=token.note,
912
- )
913
-
914
-
915
765
  class Erc20HoldingsModel(_Base):
916
- tokens: list[Erc20HoldingModel]
766
+ """The ERC20_VAULT_BALANCE tokens read directly."""
767
+
768
+ tokens: list[ERC20Entry]
917
769
  value_in_asset: Amount
918
770
  market_cached_value: Amount = Field(
919
771
  description="What the ERC20_VAULT_BALANCE market last stored for "
920
772
  "these tokens, in the asset."
921
773
  )
922
- unpriced_tokens: list[str]
923
-
924
- @classmethod
925
- def from_holdings(cls, erc20: Erc20Holdings, decimals: int) -> Erc20HoldingsModel:
926
- return cls(
927
- tokens=[Erc20HoldingModel.from_holding(t) for t in erc20.tokens],
928
- value_in_asset=_amount(erc20.value_in_asset, decimals, erc20.value_usd),
929
- market_cached_value=_amount(erc20.market_cached_value, decimals),
930
- unpriced_tokens=list(erc20.unpriced_tokens),
931
- )
774
+ unpriced_tokens: list[str] = Field(
775
+ description="Held tokens that could not be valued, as 'address (symbol)'."
776
+ )
932
777
 
933
778
 
934
779
  class VaultValidationResponse(_Base):
935
- """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``."""
936
782
 
937
783
  vault: str
938
784
  chain: str
939
785
  chain_id: ChainId
940
786
  block: int
787
+ block_timestamp: int
941
788
  block_timestamp_utc: str
942
789
  asset: AssetInfo
943
790
  verdict: Verdict = Field(
944
791
  description="Worst severity found: critical if any critical finding, "
945
792
  "else warning if any warning, else ok."
946
793
  )
947
- criticals: list[FindingModel]
948
- warnings: list[FindingModel]
949
- passed: list[FindingModel] = Field(
950
- 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."
951
796
  )
952
- reconciliation: ReconciliationModel
953
- lending_health: LendingHealthModel | None = Field(
797
+ reconciliation: Reconciliation
798
+ erc20: Erc20HoldingsModel
799
+ lending_health: LendingHealth | None = Field(
954
800
  description="LTV and health factor per lending market (Morpho, Aave "
955
- "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."
956
803
  )
957
- erc20: Erc20HoldingsModel
958
-
959
- @classmethod
960
- def from_validation(cls, v: VaultValidation, chain: str) -> VaultValidationResponse:
961
- def by(severity: Severity) -> list[FindingModel]:
962
- return [
963
- FindingModel(check=f.check, severity=f.severity, message=f.message)
964
- for f in v.findings
965
- if f.severity == severity
966
- ]
967
-
968
- health = v.lending_health
969
- return cls(
970
- vault=v.vault,
971
- chain=chain,
972
- chain_id=v.chain_id,
973
- block=v.block_number,
974
- block_timestamp_utc=datetime.fromtimestamp(
975
- v.block_timestamp, tz=UTC
976
- ).strftime("%Y-%m-%dT%H:%M:%SZ"),
977
- asset=AssetInfo(
978
- address=v.asset,
979
- symbol=v.asset_symbol,
980
- decimals=v.asset_decimals,
981
- price_usd=v.asset_price_usd,
982
- ),
983
- verdict=v.verdict,
984
- criticals=by("critical"),
985
- warnings=by("warning"),
986
- passed=by("ok"),
987
- reconciliation=ReconciliationModel.from_reconciliation(
988
- v.reconciliation, v.asset_decimals
989
- ),
990
- lending_health=(
991
- LendingHealthModel.from_health(health)
992
- if health is not None and health.has_lending_positions
993
- else None
994
- ),
995
- erc20=Erc20HoldingsModel.from_holdings(v.erc20, v.asset_decimals),
996
- )
997
804
 
998
805
 
999
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
 
@@ -17,6 +17,7 @@ from ipor_fusion.readers.hypercore import (
17
17
  )
18
18
  from ipor_fusion.readers.lending_health import (
19
19
  LendingMarketHealth,
20
+ UnreadableLendingMarket,
20
21
  VaultLendingHealth,
21
22
  fetch_vault_lending_health,
22
23
  )
@@ -56,6 +57,7 @@ from ipor_fusion.readers.ramses_v2 import RamsesV2Position, RamsesV2Reader
56
57
  from ipor_fusion.readers.uniswap_v3 import UniswapV3Position, UniswapV3Reader
57
58
  from ipor_fusion.readers.unpriceable import (
58
59
  MiddlewarePricedToken,
60
+ UnpriceableCheck,
59
61
  fetch_unpriceable_priced_tokens,
60
62
  )
61
63
 
@@ -80,6 +82,7 @@ __all__ = [
80
82
  "HYPEREVM_CHAIN_ID",
81
83
  "LendingMarketHealth",
82
84
  "VaultLendingHealth",
85
+ "UnreadableLendingMarket",
83
86
  "fetch_vault_lending_health",
84
87
  "OracleAsset",
85
88
  "OracleMapping",
@@ -100,6 +103,7 @@ __all__ = [
100
103
  "build_oracle_mapping",
101
104
  "OracleMappingReader",
102
105
  "MiddlewarePricedToken",
106
+ "UnpriceableCheck",
103
107
  "fetch_unpriceable_priced_tokens",
104
108
  "PositionData",
105
109
  "UniswapV3Reader",