ipor-fusion 4.0.0.dev54__tar.gz → 4.0.0.dev60__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 (289) hide show
  1. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/AGENTS.md +12 -1
  2. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/MIGRATION_4.md +49 -23
  3. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/PKG-INFO +1 -1
  4. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/examples/advanced_euler_v2_credit_market_base.py +6 -7
  5. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/examples/advanced_euler_v2_looping_arbitrum.py +10 -19
  6. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +10 -6
  7. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +3 -2
  8. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/examples/external_state_margin_leg_base.py +3 -2
  9. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/examples/simple_aave_v3_supply_base.py +3 -2
  10. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/pyproject.toml +1 -1
  11. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/__init__.py +12 -0
  12. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/options.py +14 -4
  13. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/vault_cmd.py +20 -21
  14. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/vault_info_view.py +9 -12
  15. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/vault_markets_view.py +5 -5
  16. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/access.py +18 -2
  17. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/external_state_executor.py +10 -3
  18. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/fuse_action.py +3 -2
  19. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/fusion_factory.py +7 -6
  20. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/plasma_vault.py +49 -19
  21. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/rewards_manager.py +8 -7
  22. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/simulation.py +2 -2
  23. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/withdraw_manager.py +2 -2
  24. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/ccip/contracts.py +2 -1
  25. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/ccip/quote.py +2 -1
  26. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/contracts.py +10 -4
  27. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/discovery.py +11 -5
  28. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/lane.py +4 -4
  29. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/messages.py +10 -6
  30. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/simulation.py +2 -2
  31. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/stargate/contracts.py +2 -1
  32. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/dependency_graph.py +2 -4
  33. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/base.py +4 -4
  34. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/fluid_instadapp.py +2 -2
  35. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/gearbox_v3.py +2 -2
  36. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/universal.py +2 -2
  37. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/mcp/server.py +10 -7
  38. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/hypercore.py +6 -4
  39. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/hypercore_outcome.py +4 -4
  40. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/lending_health.py +5 -4
  41. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/oracle_mapping.py +9 -3
  42. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/types.py +28 -0
  43. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/validation.py +5 -5
  44. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/vault_data.py +36 -462
  45. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/vault_info.py +13 -8
  46. ipor_fusion-4.0.0.dev60/src/ipor_fusion/vault_managers.py +185 -0
  47. ipor_fusion-4.0.0.dev60/src/ipor_fusion/vault_positions.py +238 -0
  48. ipor_fusion-4.0.0.dev60/src/ipor_fusion/vault_reads.py +81 -0
  49. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/conftest.py +4 -1
  50. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_commands.py +4 -4
  51. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_helpers.py +21 -22
  52. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_pending_requests_scan.py +4 -7
  53. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_vault_info_golden.py +0 -1
  54. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_types_and_events.py +24 -0
  55. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/typed_client/consumer.py +30 -3
  56. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.claude-plugin/marketplace.json +0 -0
  57. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.claude-plugin/plugin.json +0 -0
  58. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.env.example +0 -0
  59. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.gemini/settings.json +0 -0
  60. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.git-blame-ignore-revs +0 -0
  61. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.github/workflows/cd.yml +0 -0
  62. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.github/workflows/ci.yml +0 -0
  63. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.github/workflows/commit-lint.yml +0 -0
  64. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.github/workflows/notify-slack.yml +0 -0
  65. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.github/workflows/python-build.yml +0 -0
  66. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.github/workflows/python-publish.yml +0 -0
  67. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.github/workflows/release.yml +0 -0
  68. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.github/workflows/report-slack.yml +0 -0
  69. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.gitignore +0 -0
  70. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.mcp.json +0 -0
  71. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.pre-commit-config.yaml +0 -0
  72. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/.python-version +0 -0
  73. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/CHANGELOG.md +0 -0
  74. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/CLAUDE.md +0 -0
  75. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/CODEOWNERS +0 -0
  76. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/LICENSE +0 -0
  77. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/README.md +0 -0
  78. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/build.sh +0 -0
  79. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/examples/CONTRIBUTING.md +0 -0
  80. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/examples/README.md +0 -0
  81. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/pytest.ini +0 -0
  82. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/scripts/commit-check.sh +0 -0
  83. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/scripts/crosschain_readiness.py +0 -0
  84. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/skills/ipor-deploy-vault/SKILL.md +0 -0
  85. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/about.py +0 -0
  86. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cache.py +0 -0
  87. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/chains.py +0 -0
  88. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/__init__.py +0 -0
  89. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  90. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/config_cmd.py +0 -0
  91. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/config_store.py +0 -0
  92. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/main.py +0 -0
  93. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/market_cmd.py +0 -0
  94. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  95. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/vault_health.py +0 -0
  96. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  97. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/config/__init__.py +0 -0
  98. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/config/roles.py +0 -0
  99. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/__init__.py +0 -0
  100. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/address.py +0 -0
  101. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/context.py +0 -0
  102. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/contract.py +0 -0
  103. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/erc20.py +0 -0
  104. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/fee_manager.py +0 -0
  105. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/logs.py +0 -0
  106. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/multicall.py +0 -0
  107. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/core/oracle.py +0 -0
  108. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/__init__.py +0 -0
  109. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  110. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  111. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  112. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  113. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  114. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  115. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  116. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/errors.py +0 -0
  117. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/logs.py +0 -0
  118. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  119. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  120. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  121. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  122. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/crosschain/transport.py +0 -0
  123. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/entry_cli.py +0 -0
  124. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/entry_mcp.py +0 -0
  125. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/errors.py +0 -0
  126. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/explorer.py +0 -0
  127. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/field_docs.py +0 -0
  128. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/__init__.py +0 -0
  129. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  130. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/async_action.py +0 -0
  131. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  132. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
  133. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
  134. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
  135. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
  136. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/erc4626.py +0 -0
  137. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  138. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/events.py +0 -0
  139. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/external_state.py +0 -0
  140. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  141. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/hypercore.py +0 -0
  142. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  143. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/merkl.py +0 -0
  144. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/morpho.py +0 -0
  145. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  146. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  147. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/guide/__init__.py +0 -0
  148. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/guide/architecture.md +0 -0
  149. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/guide/glossary.md +0 -0
  150. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/guide/invariants.md +0 -0
  151. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/guide/quickstart.md +0 -0
  152. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/market_ids.py +0 -0
  153. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/market_info.py +0 -0
  154. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/mcp/__init__.py +0 -0
  155. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/mcp/guide.py +0 -0
  156. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/mcp/models.py +0 -0
  157. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/morpho_api.py +0 -0
  158. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/py.typed +0 -0
  159. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/__init__.py +0 -0
  160. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/aave_v3.py +0 -0
  161. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/aave_v4.py +0 -0
  162. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/compound_v3.py +0 -0
  163. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/morpho.py +0 -0
  164. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/position_manager.py +0 -0
  165. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  166. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  167. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/readers/unpriceable.py +0 -0
  168. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/src/ipor_fusion/substrates.py +0 -0
  169. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/UniswapV3UniversalRouter.py +0 -0
  170. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_crosschain.py +0 -0
  171. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_crosschain_lifecycle.py +0 -0
  172. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_crosschain_recovery.py +0 -0
  173. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_euler_v2.py +0 -0
  174. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_foundry.py +0 -0
  175. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_guide.py +0 -0
  176. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_hypercore_model.py +0 -0
  177. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_hypercore_shadow.py +0 -0
  178. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_multicall.py +0 -0
  179. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_simulate.py +0 -0
  180. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/_validation.py +0 -0
  181. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/addresses.py +0 -0
  182. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/constants.py +0 -0
  183. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  184. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/fixtures/hypercore_market55.json +0 -0
  185. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/fixtures/vault_info_golden/clean.json +0 -0
  186. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  187. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/fixtures/vault_info_golden/rich.json +0 -0
  188. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  189. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_about.py +0 -0
  190. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_access_manager.py +0 -0
  191. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cache.py +0 -0
  192. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_chains.py +0 -0
  193. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_changelog.py +0 -0
  194. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_config_store.py +0 -0
  195. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_hypercore.py +0 -0
  196. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_lending_health.py +0 -0
  197. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_market_morpho_blue.py +0 -0
  198. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_oracle_mapping.py +0 -0
  199. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_role_accounts.py +0 -0
  200. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_vault_predates_getters.py +0 -0
  201. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_cli_vault_validate.py +0 -0
  202. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_contract_call.py +0 -0
  203. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_contract_encoder.py +0 -0
  204. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_ccip_events.py +0 -0
  205. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_ccip_indexer.py +0 -0
  206. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_ccip_quote.py +0 -0
  207. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_chainlink.py +0 -0
  208. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_contracts.py +0 -0
  209. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_encoding.py +0 -0
  210. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_errors.py +0 -0
  211. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_lane.py +0 -0
  212. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_layering.py +0 -0
  213. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_readiness.py +0 -0
  214. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_readiness_report.py +0 -0
  215. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_crosschain_transport.py +0 -0
  216. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_erc20.py +0 -0
  217. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_examples_composed_hypercore.py +0 -0
  218. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_examples_crosschain.py +0 -0
  219. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  220. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_examples_vaults.py +0 -0
  221. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_explorer.py +0 -0
  222. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_external_state_events.py +0 -0
  223. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_external_state_executor.py +0 -0
  224. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_fee_manager.py +0 -0
  225. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_fuse_encoding.py +0 -0
  226. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_guide.py +0 -0
  227. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_hypercore_deployment.py +0 -0
  228. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_hypercore_events.py +0 -0
  229. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_hypercore_flow.py +0 -0
  230. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_hypercore_live.py +0 -0
  231. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_hypercore_outcome.py +0 -0
  232. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_hypercore_readers.py +0 -0
  233. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_hypercore_simulation.py +0 -0
  234. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_logs.py +0 -0
  235. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_mcp_hypercore.py +0 -0
  236. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_mcp_models.py +0 -0
  237. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_mcp_server.py +0 -0
  238. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_mcp_vault_validate.py +0 -0
  239. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_multicall.py +0 -0
  240. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_oracle.py +0 -0
  241. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_oracle_mapping.py +0 -0
  242. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_plasma_vault.py +0 -0
  243. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_reader_encoding.py +0 -0
  244. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_readiness_selection.py +0 -0
  245. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_revert_decoding.py +0 -0
  246. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_rewards_manager.py +0 -0
  247. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_roles.py +0 -0
  248. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_sdk_layering.py +0 -0
  249. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  250. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  251. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  252. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_crosschain_canary.py +0 -0
  253. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_crosschain_hypercore.py +0 -0
  254. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  255. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  256. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  257. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  258. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  259. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  260. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  261. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_examples_arbitrum.py +0 -0
  262. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_examples_base.py +0 -0
  263. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_examples_crosschain.py +0 -0
  264. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_external_state_executor_base.py +0 -0
  265. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  266. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  267. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  268. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_morpho_blue.py +0 -0
  269. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_pilot_v3.py +0 -0
  270. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  271. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  272. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  273. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  274. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_readers_integration.py +0 -0
  275. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_release_funds.py +0 -0
  276. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_swap_on_base.py +0 -0
  277. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  278. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_solidity_compiler.py +0 -0
  279. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_solidity_mirrors.py +0 -0
  280. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_substrates.py +0 -0
  281. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_unpriceable_tokens.py +0 -0
  282. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_validation.py +0 -0
  283. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_vault_dep_graph.py +0 -0
  284. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_vault_info_onchain_base.py +0 -0
  285. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  286. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_vault_simulator_blocks.py +0 -0
  287. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_web3_context.py +0 -0
  288. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/tests/test_withdraw_manager.py +0 -0
  289. {ipor_fusion-4.0.0.dev54 → ipor_fusion-4.0.0.dev60}/uv.lock +0 -0
@@ -184,7 +184,10 @@ the lifecycle matrix follows that.
184
184
  balance-graph checks
185
185
  - `vault_data.py` — public vault read: `fetch_vault_data` → `VaultData` (with `FeeData`,
186
186
  `WithdrawManagerData`), `fetch_deployment_info`, cached `resolve_token_symbol` /
187
- `resolve_token_decimals`; `vault_info.py` — `build_vault_info_json`, the
187
+ `resolve_token_decimals`; it composes `vault_reads.py` (the shared Multicall3 batch
188
+ and degrade-to-``None`` helpers), `vault_managers.py` (fee and WithdrawManager
189
+ snapshots) and `vault_positions.py` (Morpho/Aave position breakdowns and their token
190
+ prices); `vault_info.py` — `build_vault_info_json`, the
188
191
  `fusion vault info --json` document (contract names via an injected `resolve_name`),
189
192
  and `health_check_lists`; `explorer.py` Etherscan V2 (single endpoint, needs API key);
190
193
  `cache.py` the on-disk contract and deployment caches (`~/.cache/ipor-fusion/`).
@@ -283,6 +286,14 @@ or examples.
283
286
  local node.
284
287
  - Amounts are raw on-chain integers; `types.py` `NewType`s (`Amount`, `Shares`,
285
288
  `MarketId`, ...) name the unit, and nothing scales by decimals.
289
+ - Three address roles are nominal: `PlasmaVaultAddress`, `FuseAddress` and
290
+ `AccessManagerAddress` (NewTypes of `ChecksumAddress`). Their wrappers, getters and
291
+ records return them, so a vault, a fuse and an access manager cannot be passed for
292
+ each other; `parse_*_address` brands raw input. Generic plumbing (`ContractWrapper`,
293
+ `Call`, `Multicall3`, logs, storage, simulation overrides, ERC20 and account
294
+ inputs, event emitters) and recognition boundaries (`resolve_access_manager`'s
295
+ input) stay `ChecksumAddress`; external vaults (ERC4626, Euler, MetaMorpho) are not
296
+ `PlasmaVaultAddress`.
286
297
  - Ids and addresses are nominal in every signature: `ChainId`, `MarketId` (each
287
298
  `IporFusionMarkets` member is one), `RoleId | Roles`, `ChecksumAddress`;
288
299
  `HexAddress` only for the lowercase addresses decoded from substrates;
@@ -32,6 +32,7 @@ widths or address spelling.
32
32
  | `HexAddress` | any `ChecksumAddress`, or `parse_hex_address(value)` (20 bytes or hex in any spelling → lowercase `0x` form) |
33
33
  | `MorphoBlueMarketId` | `parse_morpho_market_id(value)`: 32 bytes or hex in any spelling, always `0x` + 64 lowercase digits |
34
34
  | `TxHash` | `parse_tx_hash(value)` (32 bytes or hex in any spelling) |
35
+ | `PlasmaVaultAddress`, `FuseAddress`, `AccessManagerAddress` | from the SDK (`PlasmaVault.address`, `get_fuses()`, `get_access_manager_address()`, `FusionInstance`, discovery), or `parse_plasma_vault_address` / `parse_fuse_address` / `parse_access_manager_address(value)` for a raw address; they label the role and check nothing on chain |
35
36
  | crosschain ids | `CommandId(b)`, `MessageId(b)`, `AssetId(b)`, `OperationId(b)`, `TransferId(b)`, `ReturnRequestId(b)`, `PositionSetHash(b)` (bytes32); `ProposalId(n)`, `DeploymentRequestId(n)`, `StargateAssetId(n)` (int) |
36
37
  | HyperCore ids | `HyperCoreTokenIndex(n)` (or `USDC_TOKEN_INDEX`), `HyperCoreAsset(n)` (or `hip3_action_asset(dex, index)`), `HyperCoreOrderId(n)`, `HyperCoreClientOrderId(n)`, `MarginTableId(n)`, `HyperCoreActionId(n)`, `PayloadHash(b)` |
37
38
  | external state | `ProposalHash(b)` |
@@ -45,37 +46,62 @@ a `bytes32` return (recall) request is `ReturnRequestId`.
45
46
  ## What changed, by area
46
47
 
47
48
  **Vault reads and validation.** `fetch_vault_data(chain_id=)`, `fetch_deployment_info`,
48
- `build_vault_info_json`, `ensure_supported_chain`, `fetch_vault_lending_health` take
49
- `ChainId`; vault and oracle addresses
50
- (`validate_vault`, `fetch_deployment_info`, `build_vault_info_json`,
51
- `fetch_unpriceable_priced_tokens`) take `ChecksumAddress`; `resolve_token_symbol`,
52
- `resolve_token_decimals`, `get_contract_name`, `get_deployment_tx` take `HexAddress`
53
- (a checksum address fits). The `resolve_name` / `resolve_symbol` callbacks receive a
54
- `HexAddress` (a callback typed for `str` still fits). Market-keyed data is keyed by
55
- `MarketId`: `VaultData.market_substrates`, `dependency_graph`, `fuse_markets`,
56
- `morpho_positions`, `aave_positions`; the `VaultState` protocol, `validate_vault_state`
49
+ `build_vault_info_json`, `ensure_supported_chain` and `fetch_vault_lending_health`
50
+ take `ChainId`. The vault is a `PlasmaVaultAddress` (see *Address roles*) in
51
+ `validate_vault` and `fetch_deployment_info`; `build_vault_info_json(ctx, plasma_vault,
52
+ data, chain_id, chain_label)` takes no address and labels the document with
53
+ `plasma_vault.address`. `fetch_unpriceable_priced_tokens` takes the oracle and
54
+ underlying as `ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
55
+ `get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
56
+ fits), and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
57
+ typed for `str` still fits). Market-keyed data is keyed by `MarketId`:
58
+ `VaultData.market_substrates`, `dependency_graph`, `morpho_positions`,
59
+ `aave_positions`; the `VaultState` protocol, `validate_vault_state`
57
60
  (`market_balances`), `fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens`
58
61
  and the `dependency_graph` helpers (`compute_update_reach`, `compute_update_groups`,
59
62
  `find_orphan_fuse_markets`, `find_markets_missing_erc20_dependency`).
60
- A custom `VaultState` implementation also annotates `asset` as `ChecksumAddress` and
61
- its `unpriceable_priced_tokens` items' `market_id` as `MarketId`
62
- (`UnpriceableToken`), and `price_oracle_addr` / `fuse_markets` keys as
63
- `ChecksumAddress`. `VaultData.asset`, `access_manager`, `price_oracle_addr`,
64
- `rewards_manager`, `withdraw_manager`, `fuses` and `instant_fuses` (and the
65
- `fuse_markets` keys), `FeeData.fee_manager` / `ipor_dao_fee_recipient`, and
66
- `VaultValidation.vault` / `asset` are `ChecksumAddress`; `VaultData.balance_fuses`
67
- is `list[BalanceFuse]` and `token_prices_usd` is keyed by `HexAddress` (lowercase);
68
- the Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient` are
69
- `ChecksumAddress`;
70
- `VaultValidation.chain_id` is `ChainId`; `LendingMarketHealth.market_id` and
71
- `MiddlewarePricedToken.market_id` are `MarketId`; `Erc20Holding.address` is
72
- `HexAddress`, and `SubstrateInfo.address` is `HexAddress | None` (`None`, no longer
73
- `""`, when a substrate carries no address).
63
+
64
+ `VaultData.asset`, `price_oracle_addr`, `rewards_manager`, `withdraw_manager`,
65
+ `FeeData.fee_manager` / `ipor_dao_fee_recipient` and `VaultValidation.vault` /
66
+ `asset` are `ChecksumAddress` (`VaultValidation.vault` a `PlasmaVaultAddress`);
67
+ `VaultData.access_manager` is an `AccessManagerAddress`, `fuses` / `instant_fuses`
68
+ and the `fuse_markets` keys are `FuseAddress`, `balance_fuses` is
69
+ `list[BalanceFuse]`, and `token_prices_usd` is keyed by lowercase `HexAddress`. A
70
+ custom `VaultState` implementation annotates `asset` and `price_oracle_addr` as
71
+ `ChecksumAddress`, `fuse_markets` keys as `FuseAddress`, and its
72
+ `unpriceable_priced_tokens` items' `market_id` as `MarketId` (`UnpriceableToken`).
73
+ The Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient` are
74
+ `ChecksumAddress`. `VaultValidation.chain_id` is `ChainId`;
75
+ `LendingMarketHealth.market_id` and `MiddlewarePricedToken.market_id` are
76
+ `MarketId`; `Erc20Holding.address` is `HexAddress`, and `SubstrateInfo.address` is
77
+ `HexAddress | None` (`None`, no longer `""`, when a substrate carries no address).
74
78
  `LegacyPriceOracleMiddleware` takes `base_currency_decimals: Decimals`.
75
79
  `get_logs_adaptive(chain_id=)` and `LogSource.get_logs` take `ChainId`;
76
80
  `quote_ccip_native_fee(block=)` and `OracleMappingReader.asset_source_events` take a
77
81
  `BlockIdentifier`.
78
82
 
83
+ **Address roles.** `PlasmaVault(ctx, address)`, `PlasmaVault.encoder(address)`,
84
+ `validate_vault`, `fetch_deployment_info`, the HyperCore, lending-health and
85
+ oracle-mapping readers, `read_external_state`, `VaultSimulator(vault=)`,
86
+ `CrosschainSimulator`, crosschain discovery, the hub-manager inputs
87
+ (`ExecutorInitParams.manager`, `create_executor(manager=)`, `executors_of(manager=)`),
88
+ the async command helpers `Command.request_shares` / `redeem_from_request`,
89
+ `pending_request_shares` and `WithdrawManager.update_plasma_vault_address` take a
90
+ `PlasmaVaultAddress`; `.address`, `FusionInstance.plasma_vault` and the executor's
91
+ `manager()` return one. Crosschain deposit and redeem destinations stay
92
+ `ChecksumAddress`: `Command.deposit` / `redeem`, `is_allowed_vault`,
93
+ `is_tracked_vault` and the remote-vault substrate accept any allowlisted ERC4626.
94
+ Every fuse constructor, `FuseAction.fuse`, `BalanceFuse.fuse`,
95
+ `PlasmaVault.add_fuses` / `remove_fuses` / `add_balance_fuse` / `remove_balance_fuse`
96
+ / `balance_fuse_value`, `RewardsManager.add_reward_fuses` / `is_reward_fuse_supported`,
97
+ `LaneFuses` and the HyperCore balance-fuse argument take a `FuseAddress`, and
98
+ `get_fuses()`, `get_instant_withdrawal_fuses()`, `get_rewards_fuses()` and
99
+ `VaultData.fuses` / `instant_fuses` / `fuse_markets` keys return it.
100
+ `AccessManager(ctx, address)` takes an `AccessManagerAddress`;
101
+ `get_access_manager_address()`, `VaultData.access_manager` and
102
+ `FusionInstance.access_manager` return one. `resolve_access_manager` still takes any
103
+ `ChecksumAddress`: it is what checks that the address is a vault.
104
+
79
105
  **Markets and substrates.** Every `IporFusionMarkets` member is a `MarketId`.
80
106
  `decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
81
107
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev54
3
+ Version: 4.0.0.dev60
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
@@ -59,6 +59,7 @@ from ipor_fusion import (
59
59
  VaultSimulator,
60
60
  Web3Context,
61
61
  is_simulate_v1_supported,
62
+ parse_fuse_address,
62
63
  )
63
64
  from ipor_fusion.core import FusionFactory
64
65
  from ipor_fusion.core.contract import Call
@@ -92,24 +93,22 @@ BASE_FUSION_FACTORY = Web3.to_checksum_address(
92
93
  # The four Euler V2 *functional* fuses (registry names quoted). Each is a
93
94
  # stateless encoder for one EVC operation; the balance fuse below is what values
94
95
  # the resulting position for NAV.
95
- EULER_SUPPLY_FUSE = Web3.to_checksum_address( # "SupplyFuseEulerV2"
96
+ EULER_SUPPLY_FUSE = parse_fuse_address( # "SupplyFuseEulerV2"
96
97
  "0x598326fcEDE2C1B8E9023a20C18FFf6Dea5306A4"
97
98
  )
98
- EULER_COLLATERAL_FUSE = Web3.to_checksum_address( # "CollateralFuseEulerV2"
99
+ EULER_COLLATERAL_FUSE = parse_fuse_address( # "CollateralFuseEulerV2"
99
100
  "0x12c479f8aB53D4884fc76F803dD24eb8B6D17a94"
100
101
  )
101
- EULER_CONTROLLER_FUSE = Web3.to_checksum_address( # "ControllerFuseEulerV2"
102
+ EULER_CONTROLLER_FUSE = parse_fuse_address( # "ControllerFuseEulerV2"
102
103
  "0x108c8cFB9e00681FfA1fa3b654937E8b3BCd2E64"
103
104
  )
104
- EULER_BORROW_FUSE = Web3.to_checksum_address( # "BorrowFuseEulerV2"
105
+ EULER_BORROW_FUSE = parse_fuse_address( # "BorrowFuseEulerV2"
105
106
  "0x906496F0D4C733275F892b1a6fC92eD56639B379"
106
107
  )
107
108
  # The Euler V2 *balance* fuse (registry name "BalanceFuseEulerV2") -- distinct
108
109
  # from the functional fuses; this is what lets total_assets_in_market(EULER_MARKET)
109
110
  # value the collateral net of debt.
110
- EULER_BALANCE_FUSE = Web3.to_checksum_address(
111
- "0xF8A6AA09bB55f2319113b0DA88883F392e66A5fa"
112
- )
111
+ EULER_BALANCE_FUSE = parse_fuse_address("0xF8A6AA09bB55f2319113b0DA88883F392e66A5fa")
113
112
 
114
113
  # Euler V2 eVaults on Base: the collateral eVault (cbETH) and the debt eVault
115
114
  # (WETH). These are Euler protocol (EVK) deployments, NOT IPOR Fusion addresses,
@@ -44,6 +44,7 @@ from ipor_fusion import (
44
44
  VaultSimulator,
45
45
  Web3Context,
46
46
  is_simulate_v1_supported,
47
+ parse_fuse_address,
47
48
  )
48
49
  from ipor_fusion.core import FusionFactory
49
50
  from ipor_fusion.core.contract import Call
@@ -77,31 +78,21 @@ MORPHO_FLASH_MARKET = MarketId(IporFusionMarkets.MORPHO_FLASH_LOAN)
77
78
  ARBITRUM_FUSION_FACTORY = Web3.to_checksum_address(
78
79
  "0x134fCAce7a2C7Ef3dF2479B62f03ddabAEa922d5"
79
80
  )
80
- EULER_SUPPLY_FUSE = Web3.to_checksum_address(
81
- "0x920f6c81666877490A8D6dcFEFd85d151Ef04B7d"
82
- )
83
- EULER_COLLATERAL_FUSE = Web3.to_checksum_address(
84
- "0x6D35934cB431B36338fF88641BEBF540e826bd7D"
85
- )
86
- EULER_CONTROLLER_FUSE = Web3.to_checksum_address(
87
- "0x2A02Ae392816441E804aa9eCcf5c0333911Dc989"
88
- )
89
- EULER_BORROW_FUSE = Web3.to_checksum_address(
90
- "0x10eb9B247e7Ce00F3290F66d8B5E83BfD311375F"
91
- )
92
- EULER_BALANCE_FUSE = Web3.to_checksum_address(
93
- "0xF7dF7b8eB5B0c6d02b1b8FEC769dFE65D9a27E3b"
94
- )
95
- MORPHO_FLASH_LOAN_FUSE = Web3.to_checksum_address(
81
+ EULER_SUPPLY_FUSE = parse_fuse_address("0x920f6c81666877490A8D6dcFEFd85d151Ef04B7d")
82
+ EULER_COLLATERAL_FUSE = parse_fuse_address("0x6D35934cB431B36338fF88641BEBF540e826bd7D")
83
+ EULER_CONTROLLER_FUSE = parse_fuse_address("0x2A02Ae392816441E804aa9eCcf5c0333911Dc989")
84
+ EULER_BORROW_FUSE = parse_fuse_address("0x10eb9B247e7Ce00F3290F66d8B5E83BfD311375F")
85
+ EULER_BALANCE_FUSE = parse_fuse_address("0xF7dF7b8eB5B0c6d02b1b8FEC769dFE65D9a27E3b")
86
+ MORPHO_FLASH_LOAN_FUSE = parse_fuse_address(
96
87
  "0xeA36D6478CAAE9a5CDbEA0814bDe98533320d742"
97
88
  )
98
- MORPHO_FLASH_BALANCE_FUSE = Web3.to_checksum_address(
89
+ MORPHO_FLASH_BALANCE_FUSE = parse_fuse_address(
99
90
  "0xAD5EdaEc3cd9a25C8Cc42284dDeF57dCFF40341d"
100
91
  )
101
- UNIVERSAL_SWAPPER_FUSE_V2 = Web3.to_checksum_address(
92
+ UNIVERSAL_SWAPPER_FUSE_V2 = parse_fuse_address(
102
93
  "0x799b979A2377268ac2D5E43247cbd6F797229Ef3"
103
94
  )
104
- UNIVERSAL_SWAPPER_BALANCE_FUSE_V2 = Web3.to_checksum_address(
95
+ UNIVERSAL_SWAPPER_BALANCE_FUSE_V2 = parse_fuse_address(
105
96
  "0x69199E8360b3Baa536Cda2bd71dFFb30F79fBeF7"
106
97
  )
107
98
  MORPHO_CALLBACK_HANDLER = Web3.to_checksum_address(
@@ -87,6 +87,8 @@ from ipor_fusion import (
87
87
  decode_substrate,
88
88
  erc20_balance_slot,
89
89
  open_lane,
90
+ parse_fuse_address,
91
+ parse_plasma_vault_address,
90
92
  quote_ccip_native_fee,
91
93
  read_hypercore_evm_exit_ceiling,
92
94
  read_hypercore_vault_state,
@@ -148,13 +150,15 @@ HYPEREVM_USDC = Web3.to_checksum_address("0xb88339CB7199b77E23DB6E890353E22632Ba
148
150
  # The composed test deployment the live cycles drove (created by the IPOR SDK
149
151
  # team against the v3 CCIP crosschain factory pair). Not in the ipor-abi
150
152
  # registry; verify with ``discover_deployment`` on the hub vault.
151
- HUB_VAULT = Web3.to_checksum_address("0x8BeED16DB1f354B0BFa31eF7e747102e26eb8FbF")
153
+ HUB_VAULT = parse_plasma_vault_address("0x8BeED16DB1f354B0BFa31eF7e747102e26eb8FbF")
152
154
  # The executor on Arbitrum and the dispatcher on HyperEVM: one CREATE3 address.
153
155
  EXECUTOR = Web3.to_checksum_address("0xcb83A78BD8e8c284d5704B62945EF2C454266dB0")
154
156
  # The remote vault: a HyperCore vault (market 55) on HyperEVM, and the Core
155
157
  # account the trade runs in.
156
- HYPERCORE_VAULT = Web3.to_checksum_address("0xB8De01Ca5ca57A14671E1c696337134a02B4A478")
157
- HYPERCORE_BALANCE_FUSE = Web3.to_checksum_address(
158
+ HYPERCORE_VAULT = parse_plasma_vault_address(
159
+ "0xB8De01Ca5ca57A14671E1c696337134a02B4A478"
160
+ )
161
+ HYPERCORE_BALANCE_FUSE = parse_fuse_address(
158
162
  "0xaC7404Ec2Bc3F2d3c0853633e0c3edfA25DC50F9"
159
163
  )
160
164
  HYPERCORE_MARKET = IporFusionMarkets.HYPERCORE
@@ -398,13 +402,13 @@ def core_leg_previews(web3_hype: Web3) -> list[Preview]:
398
402
  # 2. The fuses the vault registered for market 55, read from the vault.
399
403
  fuses = {Web3.to_checksum_address(f) for f in vault.get_fuses().call()}
400
404
  deposit_fuse = HyperCoreDepositFuse(
401
- Web3.to_checksum_address("0xDbE31Da504ac89B618664bB02e1521c335a41E73")
405
+ parse_fuse_address("0xDbE31Da504ac89B618664bB02e1521c335a41E73")
402
406
  )
403
407
  send_fuse = HyperCoreSendFuse(
404
- Web3.to_checksum_address("0x984D84183008C52d9A645C3ba4863F0A29b4785A")
408
+ parse_fuse_address("0x984D84183008C52d9A645C3ba4863F0A29b4785A")
405
409
  )
406
410
  order_fuse = HyperCoreOrderFuse(
407
- Web3.to_checksum_address("0x8Dc849FC55D9195cc987CA4ddFF5eA022651c173")
411
+ parse_fuse_address("0x8Dc849FC55D9195cc987CA4ddFF5eA022651c173")
408
412
  )
409
413
  for fuse in (deposit_fuse, send_fuse, order_fuse):
410
414
  _check(
@@ -81,6 +81,7 @@ from ipor_fusion import (
81
81
  erc20_balance_slot,
82
82
  is_simulate_v1_supported,
83
83
  open_lane,
84
+ parse_plasma_vault_address,
84
85
  )
85
86
  from ipor_fusion.market_ids import IporFusionMarkets
86
87
  from ipor_fusion.types import Amount, ChainId, MarketId, Period, Shares
@@ -114,10 +115,10 @@ HYPEREVM_USDC = Web3.to_checksum_address("0xb88339CB7199b77E23DB6E890353E22632Ba
114
115
  # live as a regression fixture (the SDK's own tests pin the same addresses). Verify
115
116
  # them yourself with ``discover_deployment(hub_ctx, HUB_VAULT, CROSSCHAIN_MARKET)``,
116
117
  # which reads the executor, its fuses and its spoke from the vault's grants.
117
- HUB_VAULT = Web3.to_checksum_address("0x8BeED16DB1f354B0BFa31eF7e747102e26eb8FbF")
118
+ HUB_VAULT = parse_plasma_vault_address("0x8BeED16DB1f354B0BFa31eF7e747102e26eb8FbF")
118
119
  # The executor on Arbitrum and the dispatcher on HyperEVM: one CREATE3 address.
119
120
  EXECUTOR = Web3.to_checksum_address("0xcb83A78BD8e8c284d5704B62945EF2C454266dB0")
120
- SPOKE_VAULT = Web3.to_checksum_address("0xA342eBA00bD725799828A34EA55D25a55958f643")
121
+ SPOKE_VAULT = parse_plasma_vault_address("0xA342eBA00bD725799828A34EA55D25a55958f643")
121
122
  # ``IporFusionMarkets.CROSSCHAIN``: the market the four crosschain fuses are
122
123
  # registered under on the hub vault.
123
124
  CROSSCHAIN_MARKET = MarketId(int(IporFusionMarkets.CROSSCHAIN))
@@ -90,6 +90,7 @@ from ipor_fusion import (
90
90
  Web3Context,
91
91
  WithdrawManager,
92
92
  is_simulate_v1_supported,
93
+ parse_fuse_address,
93
94
  )
94
95
  from ipor_fusion.core import FusionFactory
95
96
  from ipor_fusion.core.fusion_factory import FusionInstance
@@ -124,12 +125,12 @@ EXTERNAL_STATE_MARKET = MarketId(IporFusionMarkets.EXTERNAL_STATE)
124
125
 
125
126
  # External-state *operation* fuse (enter/exit) -- the functional fuse. It also
126
127
  # lazily deploys the per-vault ExternalStateExecutor on the first enter.
127
- BASE_EXTERNAL_STATE_OPERATION_FUSE = Web3.to_checksum_address(
128
+ BASE_EXTERNAL_STATE_OPERATION_FUSE = parse_fuse_address(
128
129
  "0x01f05736B79C7abcFbeeCe76B1C27e54f7A07D03"
129
130
  )
130
131
  # External-state *balance* fuse -- distinct from the operation fuse; this is what
131
132
  # lets total_assets_in_market(EXTERNAL_STATE_MARKET) value the attested figure.
132
- BASE_EXTERNAL_STATE_BALANCE_FUSE = Web3.to_checksum_address(
133
+ BASE_EXTERNAL_STATE_BALANCE_FUSE = parse_fuse_address(
133
134
  "0xCDEb32ACF766587b5Eac9dbfB875145ec05E3517"
134
135
  )
135
136
 
@@ -48,6 +48,7 @@ from ipor_fusion import (
48
48
  VaultSimulator,
49
49
  Web3Context,
50
50
  is_simulate_v1_supported,
51
+ parse_fuse_address,
51
52
  )
52
53
  from ipor_fusion.core import FusionFactory
53
54
  from ipor_fusion.fuses import AaveV3SupplyFuse
@@ -78,13 +79,13 @@ AAVE_MARKET = MarketId(IporFusionMarkets.AAVE_V3)
78
79
 
79
80
  # Aave V3 *supply* fuse (registry name SupplyFuseAaveV3) -- the functional fuse
80
81
  # that encodes supply/withdraw.
81
- BASE_AAVE_V3_SUPPLY_FUSE = Web3.to_checksum_address(
82
+ BASE_AAVE_V3_SUPPLY_FUSE = parse_fuse_address(
82
83
  "0x26fD6EF391E98C78CfCA27e00c3d15be4D941625"
83
84
  )
84
85
  # Aave V3 *balance* fuse (registry name AaveV3WithPriceOracleMiddlewareBalanceFuse)
85
86
  # -- distinct from the supply fuse; this is what lets
86
87
  # total_assets_in_market(AAVE_MARKET) value the position.
87
- BASE_AAVE_V3_BALANCE_FUSE = Web3.to_checksum_address(
88
+ BASE_AAVE_V3_BALANCE_FUSE = parse_fuse_address(
88
89
  "0x952573Ec1B6895a88a95CA523097083d4da4D8e5"
89
90
  )
90
91
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev54"
3
+ version = "4.0.0.dev60"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -310,6 +310,7 @@ from ipor_fusion.substrates import (
310
310
  )
311
311
  from ipor_fusion.types import (
312
312
  MAX_UINT256,
313
+ AccessManagerAddress,
313
314
  Amount,
314
315
  AssetId,
315
316
  AssetSource,
@@ -318,6 +319,7 @@ from ipor_fusion.types import (
318
319
  Decimals,
319
320
  DeploymentRequestId,
320
321
  Fee,
322
+ FuseAddress,
321
323
  HyperCoreActionId,
322
324
  HyperCoreAsset,
323
325
  HyperCoreClientOrderId,
@@ -333,6 +335,7 @@ from ipor_fusion.types import (
333
335
  OperationId,
334
336
  PayloadHash,
335
337
  Period,
338
+ PlasmaVaultAddress,
336
339
  PositionSetHash,
337
340
  Price,
338
341
  ProposalHash,
@@ -345,8 +348,11 @@ from ipor_fusion.types import (
345
348
  TokenId,
346
349
  TransferId,
347
350
  TxHash,
351
+ parse_access_manager_address,
352
+ parse_fuse_address,
348
353
  parse_hex_address,
349
354
  parse_morpho_market_id,
355
+ parse_plasma_vault_address,
350
356
  parse_tx_hash,
351
357
  )
352
358
  from ipor_fusion.validation import (
@@ -626,7 +632,13 @@ __all__ = [
626
632
  "PayloadHash",
627
633
  "ProposalHash",
628
634
  "TxHash",
635
+ "PlasmaVaultAddress",
636
+ "FuseAddress",
637
+ "AccessManagerAddress",
638
+ "parse_access_manager_address",
639
+ "parse_fuse_address",
629
640
  "parse_hex_address",
641
+ "parse_plasma_vault_address",
630
642
  "parse_morpho_market_id",
631
643
  "parse_tx_hash",
632
644
  "AssetId",
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import click
4
- from eth_typing import ChecksumAddress
5
4
  from web3 import Web3
6
5
 
7
6
  from ipor_fusion.chains import CHAIN_NAME_TO_ID, ensure_supported_chain
@@ -12,7 +11,7 @@ from ipor_fusion.core.context import Web3Context
12
11
  from ipor_fusion.errors import (
13
12
  UnsupportedChainError,
14
13
  )
15
- from ipor_fusion.types import ChainId
14
+ from ipor_fusion.types import ChainId, PlasmaVaultAddress
16
15
 
17
16
 
18
17
  class AddressType(click.ParamType):
@@ -48,6 +47,17 @@ class AddressType(click.ParamType):
48
47
  ADDRESS = AddressType()
49
48
 
50
49
 
50
+ class VaultAddressType(AddressType):
51
+ """An address the command treats as a Plasma Vault; the command's probe
52
+ (`resolve_access_manager`) is what checks it on chain."""
53
+
54
+ def convert(self, value, param, ctx) -> PlasmaVaultAddress: # type: ignore[override]
55
+ return PlasmaVaultAddress(super().convert(value, param, ctx))
56
+
57
+
58
+ VAULT_ADDRESS = VaultAddressType()
59
+
60
+
51
61
  class ChainType(click.ParamType):
52
62
  """Accepts chain ID as int or chain name (ethereum, base, arbitrum, ...)."""
53
63
 
@@ -75,7 +85,7 @@ CHAIN = ChainType()
75
85
 
76
86
 
77
87
  def _resolve_chain_id(
78
- cfg: FusionConfig, vault_address: ChecksumAddress, chain_id: ChainId | None
88
+ cfg: FusionConfig, vault_address: PlasmaVaultAddress, chain_id: ChainId | None
79
89
  ) -> ChainId:
80
90
  if chain_id is not None:
81
91
  return chain_id
@@ -100,7 +110,7 @@ def _resolve_provider(cfg: FusionConfig, chain_id: ChainId) -> str:
100
110
 
101
111
  def _build_ctx(
102
112
  cfg: FusionConfig,
103
- vault_address: ChecksumAddress,
113
+ vault_address: PlasmaVaultAddress,
104
114
  chain_id: ChainId | None,
105
115
  block_number: int | None,
106
116
  ) -> tuple[ChainId, Web3Context]:
@@ -6,7 +6,6 @@ from functools import partial
6
6
  from typing import Any
7
7
 
8
8
  import click
9
- from eth_typing import ChecksumAddress
10
9
  from web3 import Web3
11
10
 
12
11
  from ipor_fusion.chains import CHAIN_NAMES
@@ -16,7 +15,7 @@ from ipor_fusion.cli.config_store import (
16
15
  load_config,
17
16
  save_config,
18
17
  )
19
- from ipor_fusion.cli.options import ADDRESS, CHAIN, _build_ctx, _resolve_provider
18
+ from ipor_fusion.cli.options import CHAIN, VAULT_ADDRESS, _build_ctx, _resolve_provider
20
19
  from ipor_fusion.cli.oracle_mapping_view import _print_oracle_mapping
21
20
  from ipor_fusion.cli.vault_health import (
22
21
  _print_validation,
@@ -43,7 +42,7 @@ from ipor_fusion.errors import (
43
42
  from ipor_fusion.readers.oracle_mapping import (
44
43
  build_oracle_mapping,
45
44
  )
46
- from ipor_fusion.types import ChainId
45
+ from ipor_fusion.types import ChainId, PlasmaVaultAddress
47
46
  from ipor_fusion.validation import (
48
47
  VaultValidation,
49
48
  validate_vault,
@@ -63,7 +62,7 @@ from ipor_fusion.vault_info import (
63
62
 
64
63
  def _auto_save_vault(
65
64
  cfg: FusionConfig,
66
- vault_address: ChecksumAddress,
65
+ vault_address: PlasmaVaultAddress,
67
66
  chain_id: ChainId,
68
67
  plasma_vault: PlasmaVault,
69
68
  ) -> None:
@@ -85,7 +84,7 @@ def vault() -> None:
85
84
 
86
85
 
87
86
  @vault.command("add")
88
- @click.argument("address", type=ADDRESS)
87
+ @click.argument("address", type=VAULT_ADDRESS)
89
88
  @click.option(
90
89
  "--label", default=None, help="Label (default: fetched from on-chain name())."
91
90
  )
@@ -95,7 +94,9 @@ def vault() -> None:
95
94
  default=None,
96
95
  help="Chain ID or name (auto-detected when only one provider is configured).",
97
96
  )
98
- def add(address: ChecksumAddress, label: str | None, chain_id: ChainId | None) -> None:
97
+ def add(
98
+ address: PlasmaVaultAddress, label: str | None, chain_id: ChainId | None
99
+ ) -> None:
99
100
  """Save a vault to the config."""
100
101
  cfg = load_config()
101
102
 
@@ -111,7 +112,7 @@ def add(address: ChecksumAddress, label: str | None, chain_id: ChainId | None) -
111
112
  if label is None:
112
113
  provider_url = _resolve_provider(cfg, chain_id)
113
114
  ctx = Web3Context.from_url(provider_url)
114
- checksum = Web3.to_checksum_address(address)
115
+ checksum = address
115
116
  try:
116
117
  label = PlasmaVault(ctx, checksum).name().call()
117
118
  except Exception:
@@ -130,8 +131,8 @@ def add(address: ChecksumAddress, label: str | None, chain_id: ChainId | None) -
130
131
 
131
132
 
132
133
  @vault.command("remove")
133
- @click.argument("address", type=ADDRESS)
134
- def remove(address: ChecksumAddress) -> None:
134
+ @click.argument("address", type=VAULT_ADDRESS)
135
+ def remove(address: PlasmaVaultAddress) -> None:
135
136
  """Remove a saved vault."""
136
137
  cfg = load_config()
137
138
  before = len(cfg.vaults)
@@ -180,7 +181,7 @@ vault.add_command(list_vaults, "ls")
180
181
 
181
182
 
182
183
  @vault.command("info")
183
- @click.argument("vault_address", type=ADDRESS)
184
+ @click.argument("vault_address", type=VAULT_ADDRESS)
184
185
  @click.option(
185
186
  "--chain-id", type=CHAIN, default=None, help="Chain ID or name (e.g. 1, ethereum)."
186
187
  )
@@ -194,7 +195,7 @@ vault.add_command(list_vaults, "ls")
194
195
  "--json", "json_output", is_flag=True, default=False, help="Output as JSON."
195
196
  )
196
197
  def info(
197
- vault_address: ChecksumAddress,
198
+ vault_address: PlasmaVaultAddress,
198
199
  chain_id: ChainId | None,
199
200
  block_number: int | None,
200
201
  json_output: bool,
@@ -206,7 +207,7 @@ def info(
206
207
  """
207
208
  cfg = load_config()
208
209
  chain_id, ctx = _build_ctx(cfg, vault_address, chain_id, block_number)
209
- checksum_address = Web3.to_checksum_address(vault_address)
210
+ checksum_address = vault_address
210
211
 
211
212
  # Cheap single-call probe: friendly errors for "nothing deployed here" and
212
213
  # "not a Plasma Vault" (revert and empty-return flavors alike) before the
@@ -230,7 +231,7 @@ def info(
230
231
 
231
232
 
232
233
  @vault.command("validate")
233
- @click.argument("vault_address", type=ADDRESS)
234
+ @click.argument("vault_address", type=VAULT_ADDRESS)
234
235
  @click.option(
235
236
  "--chain-id", type=CHAIN, default=None, help="Chain ID or name (e.g. 1, ethereum)."
236
237
  )
@@ -244,7 +245,7 @@ def info(
244
245
  "--json", "json_output", is_flag=True, default=False, help="Output as JSON."
245
246
  )
246
247
  def validate(
247
- vault_address: ChecksumAddress,
248
+ vault_address: PlasmaVaultAddress,
248
249
  chain_id: ChainId | None,
249
250
  block_number: int | None,
250
251
  json_output: bool,
@@ -316,7 +317,7 @@ def _validation_json(validation: VaultValidation, chain_label: str) -> dict[str,
316
317
 
317
318
 
318
319
  @vault.command("role-accounts")
319
- @click.argument("vault_address", type=ADDRESS)
320
+ @click.argument("vault_address", type=VAULT_ADDRESS)
320
321
  @click.option(
321
322
  "--role",
322
323
  default="",
@@ -336,7 +337,7 @@ def _validation_json(validation: VaultValidation, chain_label: str) -> dict[str,
336
337
  "--json", "json_output", is_flag=True, default=False, help="Output as JSON."
337
338
  )
338
339
  def role_accounts(
339
- vault_address: ChecksumAddress,
340
+ vault_address: PlasmaVaultAddress,
340
341
  role: str,
341
342
  chain_id: ChainId | None,
342
343
  block_number: int | None,
@@ -385,7 +386,7 @@ def role_accounts(
385
386
 
386
387
 
387
388
  @vault.command("oracle-mapping")
388
- @click.argument("vault_address", type=ADDRESS)
389
+ @click.argument("vault_address", type=VAULT_ADDRESS)
389
390
  @click.option(
390
391
  "--chain-id", type=CHAIN, default=None, help="Chain ID or name (e.g. 1, ethereum)."
391
392
  )
@@ -406,7 +407,7 @@ def role_accounts(
406
407
  "--json", "json_output", is_flag=True, default=False, help="Output as JSON."
407
408
  )
408
409
  def oracle_mapping(
409
- vault_address: ChecksumAddress,
410
+ vault_address: PlasmaVaultAddress,
410
411
  chain_id: ChainId | None,
411
412
  block_number: int | None,
412
413
  max_depth: int,
@@ -458,9 +459,7 @@ def oracle_mapping(
458
459
  raise click.UsageError(str(exc)) from exc
459
460
 
460
461
  try:
461
- mapping = build_oracle_mapping(
462
- ctx, Web3.to_checksum_address(vault_address), effective_block, max_depth
463
- )
462
+ mapping = build_oracle_mapping(ctx, vault_address, effective_block, max_depth)
464
463
  except UnsupportedVaultVersionError as exc:
465
464
  raise click.ClickException(str(exc)) from exc
466
465