ipor-fusion 4.0.0.dev28__tar.gz → 4.0.0.dev32__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 (285) hide show
  1. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/AGENTS.md +3 -1
  2. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/PKG-INFO +1 -1
  3. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/pyproject.toml +1 -1
  4. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/__init__.py +22 -0
  5. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/market_cmd.py +18 -26
  6. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/ccip/chainlink.py +2 -2
  7. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/ccip/codec.py +4 -3
  8. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/ccip/contracts.py +25 -14
  9. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/ccip/indexer.py +4 -3
  10. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/contracts.py +12 -5
  11. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/lane.py +6 -6
  12. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/messages.py +7 -3
  13. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/stargate/contracts.py +26 -13
  14. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/stargate/layerzero.py +7 -7
  15. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/stargate/transport.py +2 -2
  16. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/transport.py +2 -2
  17. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/crosschain/stargate.py +4 -3
  18. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/market_info.py +3 -3
  19. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/mcp/server.py +4 -9
  20. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/morpho_api.py +20 -13
  21. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/lending_health.py +8 -4
  22. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/types.py +31 -1
  23. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/vault_data.py +7 -4
  24. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_market_morpho_blue.py +3 -1
  25. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/typed_client/consumer.py +27 -2
  26. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.claude-plugin/marketplace.json +0 -0
  27. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.claude-plugin/plugin.json +0 -0
  28. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.env.example +0 -0
  29. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.gemini/settings.json +0 -0
  30. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.git-blame-ignore-revs +0 -0
  31. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.github/workflows/cd.yml +0 -0
  32. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.github/workflows/ci.yml +0 -0
  33. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.github/workflows/commit-lint.yml +0 -0
  34. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.github/workflows/notify-slack.yml +0 -0
  35. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.github/workflows/python-build.yml +0 -0
  36. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.github/workflows/python-publish.yml +0 -0
  37. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.github/workflows/release.yml +0 -0
  38. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.github/workflows/report-slack.yml +0 -0
  39. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.gitignore +0 -0
  40. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.mcp.json +0 -0
  41. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.pre-commit-config.yaml +0 -0
  42. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/.python-version +0 -0
  43. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/CHANGELOG.md +0 -0
  44. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/CLAUDE.md +0 -0
  45. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/CODEOWNERS +0 -0
  46. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/LICENSE +0 -0
  47. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/README.md +0 -0
  48. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/build.sh +0 -0
  49. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/examples/CONTRIBUTING.md +0 -0
  50. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/examples/README.md +0 -0
  51. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  52. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  53. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  54. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  55. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/examples/external_state_margin_leg_base.py +0 -0
  56. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/examples/simple_aave_v3_supply_base.py +0 -0
  57. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/pytest.ini +0 -0
  58. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/scripts/commit-check.sh +0 -0
  59. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/scripts/crosschain_readiness.py +0 -0
  60. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/skills/ipor-deploy-vault/SKILL.md +0 -0
  61. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/about.py +0 -0
  62. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cache.py +0 -0
  63. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/chains.py +0 -0
  64. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/__init__.py +0 -0
  65. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  66. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/config_cmd.py +0 -0
  67. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/config_store.py +0 -0
  68. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/main.py +0 -0
  69. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/options.py +0 -0
  70. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  71. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  72. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/vault_health.py +0 -0
  73. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  74. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  75. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  76. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/config/__init__.py +0 -0
  77. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/config/roles.py +0 -0
  78. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/__init__.py +0 -0
  79. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/access.py +0 -0
  80. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/address.py +0 -0
  81. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/context.py +0 -0
  82. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/contract.py +0 -0
  83. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/erc20.py +0 -0
  84. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/external_state_executor.py +0 -0
  85. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/fee_manager.py +0 -0
  86. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/fuse_action.py +0 -0
  87. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/fusion_factory.py +0 -0
  88. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/logs.py +0 -0
  89. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/multicall.py +0 -0
  90. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/oracle.py +0 -0
  91. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/plasma_vault.py +0 -0
  92. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/rewards_manager.py +0 -0
  93. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/simulation.py +0 -0
  94. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  95. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/__init__.py +0 -0
  96. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  97. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  98. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  99. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  100. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  101. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/discovery.py +0 -0
  102. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/errors.py +0 -0
  103. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/logs.py +0 -0
  104. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/simulation.py +0 -0
  105. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  106. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  107. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/dependency_graph.py +0 -0
  108. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/entry_cli.py +0 -0
  109. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/entry_mcp.py +0 -0
  110. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/errors.py +0 -0
  111. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/explorer.py +0 -0
  112. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/field_docs.py +0 -0
  113. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/__init__.py +0 -0
  114. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  115. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/async_action.py +0 -0
  116. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/base.py +0 -0
  117. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  118. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
  119. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
  120. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
  121. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/erc4626.py +0 -0
  122. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  123. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/events.py +0 -0
  124. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/external_state.py +0 -0
  125. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  126. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  127. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  128. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/hypercore.py +0 -0
  129. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  130. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/merkl.py +0 -0
  131. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/morpho.py +0 -0
  132. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  133. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  134. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/fuses/universal.py +0 -0
  135. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/guide/__init__.py +0 -0
  136. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/guide/architecture.md +0 -0
  137. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/guide/glossary.md +0 -0
  138. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/guide/invariants.md +0 -0
  139. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/guide/quickstart.md +0 -0
  140. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/market_ids.py +0 -0
  141. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/mcp/__init__.py +0 -0
  142. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/mcp/guide.py +0 -0
  143. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/mcp/models.py +0 -0
  144. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/py.typed +0 -0
  145. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/__init__.py +0 -0
  146. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/aave_v3.py +0 -0
  147. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/aave_v4.py +0 -0
  148. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/compound_v3.py +0 -0
  149. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/hypercore.py +0 -0
  150. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  151. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/morpho.py +0 -0
  152. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  153. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/position_manager.py +0 -0
  154. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  155. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  156. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/readers/unpriceable.py +0 -0
  157. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/substrates.py +0 -0
  158. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/validation.py +0 -0
  159. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/src/ipor_fusion/vault_info.py +0 -0
  160. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/UniswapV3UniversalRouter.py +0 -0
  161. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_crosschain.py +0 -0
  162. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_crosschain_lifecycle.py +0 -0
  163. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_crosschain_recovery.py +0 -0
  164. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_euler_v2.py +0 -0
  165. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_foundry.py +0 -0
  166. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_guide.py +0 -0
  167. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_hypercore_model.py +0 -0
  168. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_hypercore_shadow.py +0 -0
  169. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_multicall.py +0 -0
  170. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_simulate.py +0 -0
  171. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/_validation.py +0 -0
  172. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/addresses.py +0 -0
  173. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/conftest.py +0 -0
  174. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/constants.py +0 -0
  175. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  176. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/fixtures/hypercore_market55.json +0 -0
  177. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/fixtures/vault_info_golden/clean.json +0 -0
  178. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  179. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/fixtures/vault_info_golden/rich.json +0 -0
  180. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  181. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_about.py +0 -0
  182. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_access_manager.py +0 -0
  183. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cache.py +0 -0
  184. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_chains.py +0 -0
  185. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_changelog.py +0 -0
  186. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_commands.py +0 -0
  187. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_config_store.py +0 -0
  188. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_helpers.py +0 -0
  189. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_hypercore.py +0 -0
  190. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_lending_health.py +0 -0
  191. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_oracle_mapping.py +0 -0
  192. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_pending_requests_scan.py +0 -0
  193. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_role_accounts.py +0 -0
  194. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_vault_info_golden.py +0 -0
  195. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_vault_predates_getters.py +0 -0
  196. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_cli_vault_validate.py +0 -0
  197. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_contract_call.py +0 -0
  198. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_contract_encoder.py +0 -0
  199. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_ccip_events.py +0 -0
  200. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_ccip_indexer.py +0 -0
  201. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_ccip_quote.py +0 -0
  202. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_chainlink.py +0 -0
  203. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_contracts.py +0 -0
  204. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_encoding.py +0 -0
  205. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_errors.py +0 -0
  206. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_lane.py +0 -0
  207. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_layering.py +0 -0
  208. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_readiness.py +0 -0
  209. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_readiness_report.py +0 -0
  210. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_crosschain_transport.py +0 -0
  211. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_erc20.py +0 -0
  212. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_examples_composed_hypercore.py +0 -0
  213. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_examples_crosschain.py +0 -0
  214. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  215. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_examples_vaults.py +0 -0
  216. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_explorer.py +0 -0
  217. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_external_state_events.py +0 -0
  218. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_external_state_executor.py +0 -0
  219. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_fee_manager.py +0 -0
  220. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_fuse_encoding.py +0 -0
  221. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_guide.py +0 -0
  222. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_hypercore_deployment.py +0 -0
  223. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_hypercore_events.py +0 -0
  224. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_hypercore_flow.py +0 -0
  225. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_hypercore_live.py +0 -0
  226. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_hypercore_outcome.py +0 -0
  227. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_hypercore_readers.py +0 -0
  228. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_hypercore_simulation.py +0 -0
  229. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_logs.py +0 -0
  230. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_mcp_hypercore.py +0 -0
  231. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_mcp_models.py +0 -0
  232. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_mcp_server.py +0 -0
  233. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_mcp_vault_validate.py +0 -0
  234. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_multicall.py +0 -0
  235. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_oracle.py +0 -0
  236. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_oracle_mapping.py +0 -0
  237. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_plasma_vault.py +0 -0
  238. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_reader_encoding.py +0 -0
  239. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_readiness_selection.py +0 -0
  240. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_revert_decoding.py +0 -0
  241. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_rewards_manager.py +0 -0
  242. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_roles.py +0 -0
  243. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_sdk_layering.py +0 -0
  244. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  245. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  246. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  247. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_crosschain_canary.py +0 -0
  248. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_crosschain_hypercore.py +0 -0
  249. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  250. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  251. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  252. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  253. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  254. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  255. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  256. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_examples_arbitrum.py +0 -0
  257. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_examples_base.py +0 -0
  258. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_examples_crosschain.py +0 -0
  259. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_external_state_executor_base.py +0 -0
  260. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  261. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  262. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  263. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_morpho_blue.py +0 -0
  264. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_pilot_v3.py +0 -0
  265. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  266. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  267. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  268. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  269. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_readers_integration.py +0 -0
  270. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_release_funds.py +0 -0
  271. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_swap_on_base.py +0 -0
  272. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  273. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_solidity_compiler.py +0 -0
  274. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_solidity_mirrors.py +0 -0
  275. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_substrates.py +0 -0
  276. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_types_and_events.py +0 -0
  277. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_unpriceable_tokens.py +0 -0
  278. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_validation.py +0 -0
  279. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_vault_dep_graph.py +0 -0
  280. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_vault_info_onchain_base.py +0 -0
  281. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  282. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_vault_simulator_blocks.py +0 -0
  283. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_web3_context.py +0 -0
  284. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/tests/test_withdraw_manager.py +0 -0
  285. {ipor_fusion-4.0.0.dev28 → ipor_fusion-4.0.0.dev32}/uv.lock +0 -0
@@ -285,7 +285,9 @@ or examples.
285
285
  `MarketId`, ...) name the unit, and nothing scales by decimals.
286
286
  - Ids and addresses are nominal in every signature: `ChainId`, `MarketId` (each
287
287
  `IporFusionMarkets` member is one), `RoleId | Roles`, `ChecksumAddress`;
288
- `HexAddress` only for the lowercase addresses decoded from substrates. Raw input
288
+ `HexAddress` only for the lowercase addresses decoded from substrates;
289
+ `MorphoBlueMarketId` is always `0x` + 64 lowercase hex, built by
290
+ `parse_morpho_market_id`. Raw input
289
291
  is parsed once at the boundary (click param types, MCP tools, the user config).
290
292
  `src/`, `examples/` and `tests/typed_client/` are checked with
291
293
  `reportArgumentType` on, so a bare `int`/`str` passed for one fails pyright; the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev28
3
+ Version: 4.0.0.dev32
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.dev28"
3
+ version = "4.0.0.dev32"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -311,21 +311,32 @@ from ipor_fusion.substrates import (
311
311
  from ipor_fusion.types import (
312
312
  MAX_UINT256,
313
313
  Amount,
314
+ AssetId,
314
315
  AssetSource,
315
316
  ChainId,
317
+ CommandId,
316
318
  Decimals,
319
+ DeploymentRequestId,
317
320
  Fee,
318
321
  Liquidity,
319
322
  MappingStatus,
320
323
  MarketId,
324
+ MessageId,
321
325
  MorphoBlueMarketId,
322
326
  NodeStatus,
327
+ OperationId,
323
328
  Period,
329
+ PositionSetHash,
324
330
  Price,
331
+ ProposalId,
332
+ ReturnRequestId,
325
333
  RoleId,
326
334
  Shares,
335
+ StargateAssetId,
327
336
  Tick,
328
337
  TokenId,
338
+ TransferId,
339
+ parse_morpho_market_id,
329
340
  )
330
341
  from ipor_fusion.validation import (
331
342
  Erc20Holding,
@@ -595,6 +606,17 @@ __all__ = [
595
606
  "RoleId",
596
607
  "ChainId",
597
608
  "MorphoBlueMarketId",
609
+ "parse_morpho_market_id",
610
+ "AssetId",
611
+ "CommandId",
612
+ "DeploymentRequestId",
613
+ "MessageId",
614
+ "OperationId",
615
+ "PositionSetHash",
616
+ "ProposalId",
617
+ "ReturnRequestId",
618
+ "StargateAssetId",
619
+ "TransferId",
598
620
  "Price",
599
621
  "Period",
600
622
  "MAX_UINT256",
@@ -42,7 +42,7 @@ from ipor_fusion.readers.morpho import (
42
42
  MorphoReader,
43
43
  morpho_blue_address,
44
44
  )
45
- from ipor_fusion.types import ChainId, MorphoBlueMarketId
45
+ from ipor_fusion.types import ChainId, MorphoBlueMarketId, parse_morpho_market_id
46
46
  from ipor_fusion.vault_info import BLOCK_EXPLORER_URLS
47
47
 
48
48
 
@@ -54,12 +54,10 @@ class MarketIdType(click.ParamType):
54
54
  def convert(self, value, param, ctx): # type: ignore[override]
55
55
  if not isinstance(value, str):
56
56
  self.fail(f"expected string, got {type(value).__name__}", param, ctx)
57
- raw = value.strip().removeprefix("0x").removeprefix("0X")
58
- if len(raw) != 64 or not all(c in "0123456789abcdefABCDEF" for c in raw):
59
- self.fail(
60
- f"invalid Morpho market ID (expected 32-byte hex): {value}", param, ctx
61
- )
62
- return raw.lower()
57
+ try:
58
+ return parse_morpho_market_id(value)
59
+ except ValueError as exc:
60
+ self.fail(str(exc), param, ctx)
63
61
 
64
62
 
65
63
  MARKET_ID = MarketIdType()
@@ -106,7 +104,7 @@ def market() -> None:
106
104
  help="Emit machine-readable JSON instead of a table.",
107
105
  )
108
106
  def morpho_blue(
109
- market_id: str,
107
+ market_id: MorphoBlueMarketId,
110
108
  chain_id: ChainId,
111
109
  block: int | None,
112
110
  no_api: bool,
@@ -127,7 +125,7 @@ def morpho_blue(
127
125
  if block is not None:
128
126
  ctx.default_block = block
129
127
  reader = MorphoReader(ctx, morpho_blue_address(chain_id))
130
- mid = MorphoBlueMarketId(market_id)
128
+ mid = market_id
131
129
  params, state = reader.require_market(mid)
132
130
  rates = reader.rates_from(state, params)
133
131
 
@@ -171,7 +169,7 @@ def morpho_blue(
171
169
 
172
170
 
173
171
  def _print_market(
174
- market_id: str,
172
+ market_id: MorphoBlueMarketId,
175
173
  chain_id: ChainId,
176
174
  params: MorphoMarketParams,
177
175
  state: MorphoMarket,
@@ -181,11 +179,11 @@ def _print_market(
181
179
  chain_name = CHAIN_NAMES.get(chain_id, str(chain_id))
182
180
  explorer = BLOCK_EXPLORER_URLS.get(chain_id, "")
183
181
  liquidity = state.total_supply_assets - state.total_borrow_assets
184
- click.secho(f"Morpho Blue market 0x{market_id}", bold=True)
182
+ click.secho(f"Morpho Blue market {market_id}", bold=True)
185
183
  click.echo(f" Chain: {chain_name} (id {chain_id})")
186
184
  if explorer:
187
185
  click.echo(
188
- f" App: https://app.morpho.org/{chain_name}/market/0x{market_id}"
186
+ f" App: https://app.morpho.org/{chain_name}/market/{market_id}"
189
187
  )
190
188
  click.echo(f" Loan token: {params.loan_token}")
191
189
  click.echo(f" Collateral: {params.collateral_token}")
@@ -332,7 +330,7 @@ def _print_reallocate_hint(params: MorphoMarketParams) -> None:
332
330
  @click.option(
333
331
  "--market",
334
332
  "market_filter",
335
- type=str,
333
+ type=MARKET_ID,
336
334
  default=None,
337
335
  help="Filter caps/allocations to a single Morpho Blue market ID.",
338
336
  )
@@ -346,7 +344,7 @@ def _print_reallocate_hint(params: MorphoMarketParams) -> None:
346
344
  def meta_morpho(
347
345
  vault_address: ChecksumAddress,
348
346
  chain_id: ChainId,
349
- market_filter: str | None,
347
+ market_filter: MorphoBlueMarketId | None,
350
348
  json_output: bool,
351
349
  ) -> None:
352
350
  """Inspect a MetaMorpho V1 or Morpho Vault V2 by address.
@@ -361,22 +359,18 @@ def meta_morpho(
361
359
  except MorphoApiError as exc:
362
360
  raise click.UsageError(str(exc)) from exc
363
361
 
364
- market_filter_norm = (
365
- ("0x" + market_filter.removeprefix("0x").lower()) if market_filter else None
366
- )
367
-
368
362
  if json_output:
369
363
  click.echo(json_lib.dumps(build_meta_morpho_json(info, chain_id), indent=2))
370
364
  return
371
365
 
372
366
  if isinstance(info, VaultV2Info):
373
- _print_vault_v2(info, chain_id, market_filter_norm)
367
+ _print_vault_v2(info, chain_id, market_filter)
374
368
  else:
375
- _print_vault_v1(info, chain_id, market_filter_norm)
369
+ _print_vault_v1(info, chain_id, market_filter)
376
370
 
377
371
 
378
372
  def _print_vault_v2(
379
- vault: VaultV2Info, chain_id: ChainId, market_filter: str | None
373
+ vault: VaultV2Info, chain_id: ChainId, market_filter: MorphoBlueMarketId | None
380
374
  ) -> None:
381
375
  chain_name = CHAIN_NAMES.get(chain_id, str(chain_id))
382
376
  click.secho(f"Morpho Vault V2 — {vault.name} ({vault.symbol})", bold=True)
@@ -433,9 +427,7 @@ def _print_vault_v2(
433
427
  market_caps = [c for c in vault.caps if c.cap_type == "MarketV1"]
434
428
  other_caps = [c for c in vault.caps if c.cap_type != "MarketV1"]
435
429
  if market_filter:
436
- market_caps = [
437
- c for c in market_caps if (c.market_id or "").lower() == market_filter
438
- ]
430
+ market_caps = [c for c in market_caps if c.market_id == market_filter]
439
431
 
440
432
  click.secho(f"Morpho Blue market caps ({len(market_caps)}):", bold=True)
441
433
  _print_market_caps(market_caps, vault.asset_symbol, vault.asset_decimals)
@@ -508,7 +500,7 @@ def _print_other_caps(
508
500
 
509
501
 
510
502
  def _print_vault_v1(
511
- vault: VaultV1Info, chain_id: ChainId, market_filter: str | None
503
+ vault: VaultV1Info, chain_id: ChainId, market_filter: MorphoBlueMarketId | None
512
504
  ) -> None:
513
505
  chain_name = CHAIN_NAMES.get(chain_id, str(chain_id))
514
506
  public_allocator = PUBLIC_ALLOCATOR_ADDRESSES.get(chain_id)
@@ -552,7 +544,7 @@ def _print_vault_v1(
552
544
 
553
545
  allocations = vault.allocations
554
546
  if market_filter:
555
- allocations = [a for a in allocations if a.market_id.lower() == market_filter]
547
+ allocations = [a for a in allocations if a.market_id == market_filter]
556
548
  click.secho(f"Allocations ({len(allocations)}):", bold=True)
557
549
  if not allocations:
558
550
  click.echo(" (none)")
@@ -16,7 +16,7 @@ from eth_typing import ChecksumAddress
16
16
  from ipor_fusion.core.context import Web3Context
17
17
  from ipor_fusion.core.contract import Call, ContractWrapper
18
18
  from ipor_fusion.crosschain.contracts import _address
19
- from ipor_fusion.types import ZERO_ADDRESS
19
+ from ipor_fusion.types import ZERO_ADDRESS, MessageId
20
20
 
21
21
 
22
22
  class CcipRouter(ContractWrapper):
@@ -191,7 +191,7 @@ class CcipOffRamp(ContractWrapper):
191
191
  def type_and_version(self) -> Call[str]:
192
192
  return self._view("typeAndVersion()", output_types=["string"])
193
193
 
194
- def execution_state(self, message_id: bytes) -> Call[MessageExecutionState]:
194
+ def execution_state(self, message_id: MessageId) -> Call[MessageExecutionState]:
195
195
  return self._view(
196
196
  "getExecutionState(bytes32)",
197
197
  message_id,
@@ -21,6 +21,7 @@ from web3 import Web3
21
21
 
22
22
  from ipor_fusion.core.contract import _encode_calldata
23
23
  from ipor_fusion.crosschain.logs import log_bytes
24
+ from ipor_fusion.types import MessageId
24
25
 
25
26
  #: topic0 of ``OnRamp 2.0.0``'s ``CCIPMessageSent``; ``destChainSelector``,
26
27
  #: ``sender`` and ``messageId`` are indexed.
@@ -176,7 +177,7 @@ def evm_address(raw: bytes) -> ChecksumAddress:
176
177
  class CcipMessageSent:
177
178
  """One ``CCIPMessageSent`` log: the Router-assigned id plus the decoded message."""
178
179
 
179
- message_id: bytes
180
+ message_id: MessageId
180
181
  dest_chain_selector: int
181
182
  sender: ChecksumAddress
182
183
  message: MessageV1
@@ -193,7 +194,7 @@ class CcipMessageSent:
193
194
  _EVENT_DATA_TYPES, log_bytes(log["data"])
194
195
  )
195
196
  return cls(
196
- message_id=topics[3],
197
+ message_id=MessageId(bytes(topics[3])),
197
198
  dest_chain_selector=int.from_bytes(topics[1], "big"),
198
199
  sender=Web3.to_checksum_address(topics[2][12:]),
199
200
  message=MessageV1.decode(bytes(encoded)),
@@ -215,7 +216,7 @@ class Any2EVMMessage:
215
216
  ``sender`` is the source address; it is ABI-encoded into the wire field,
216
217
  which is how the receivers decode it."""
217
218
 
218
- message_id: bytes
219
+ message_id: MessageId
219
220
  source_chain_selector: int
220
221
  sender: ChecksumAddress
221
222
  data: bytes
@@ -18,7 +18,16 @@ from ipor_fusion.crosschain.contracts import (
18
18
  _address,
19
19
  )
20
20
  from ipor_fusion.crosschain.messages import CommandStatus, CrosschainTransportKind
21
- from ipor_fusion.types import Amount, ChainId
21
+ from ipor_fusion.types import (
22
+ Amount,
23
+ AssetId,
24
+ ChainId,
25
+ CommandId,
26
+ MessageId,
27
+ OperationId,
28
+ PositionSetHash,
29
+ ProposalId,
30
+ )
22
31
 
23
32
 
24
33
  @dataclass(frozen=True, slots=True)
@@ -30,7 +39,7 @@ class CcipObservation:
30
39
  queued_token_amount: Amount
31
40
  state_version: int
32
41
  command_config_epoch: int
33
- tracked_position_set_hash: bytes
42
+ tracked_position_set_hash: PositionSetHash
34
43
 
35
44
 
36
45
  @dataclass(frozen=True, slots=True)
@@ -161,13 +170,13 @@ class CcipCrosschainExecutor(CrosschainExecutor):
161
170
  "activeReturn(uint256)", chain_id, output_types=["bytes32"], decoder=bytes
162
171
  )
163
172
 
164
- def active_command(self, chain_id: ChainId) -> Call[bytes]:
173
+ def active_command(self, chain_id: ChainId) -> Call[CommandId]:
165
174
  """The active command id for the chain, or 32 zero bytes."""
166
175
  return self._view(
167
176
  "activeCommand(uint256)",
168
177
  chain_id,
169
178
  output_types=["bytes32"],
170
- decoder=bytes,
179
+ decoder=lambda value: CommandId(bytes(value)),
171
180
  )
172
181
 
173
182
  def next_command_sequence(self, chain_id: ChainId) -> Call[int]:
@@ -203,7 +212,7 @@ class CcipCrosschainExecutor(CrosschainExecutor):
203
212
  decoder=Amount,
204
213
  )
205
214
 
206
- def command_status(self, command_id: bytes) -> Call[CommandStatus]:
215
+ def command_status(self, command_id: CommandId) -> Call[CommandStatus]:
207
216
  return self._view(
208
217
  "commandStatus(bytes32)",
209
218
  command_id,
@@ -219,7 +228,7 @@ class CcipCrosschainExecutor(CrosschainExecutor):
219
228
  "pendingTransferCount(uint256)", chain_id, output_types=["uint8"]
220
229
  )
221
230
 
222
- def processed_ccip_message(self, message_id: bytes) -> Call[bool]:
231
+ def processed_ccip_message(self, message_id: MessageId) -> Call[bool]:
223
232
  return self._view(
224
233
  "processedCcipMessage(bytes32)", message_id, output_types=["bool"]
225
234
  )
@@ -236,7 +245,7 @@ class CcipCrosschainExecutor(CrosschainExecutor):
236
245
  output_types=["uint256"],
237
246
  )
238
247
 
239
- def reject_balance_and_block(self, proposal_id: int) -> Call[None]:
248
+ def reject_balance_and_block(self, proposal_id: ProposalId) -> Call[None]:
240
249
  """``BALANCE_APPROVER`` only: reject and fail the chain closed."""
241
250
  return self._write("rejectBalanceAndBlock(uint256)", proposal_id)
242
251
 
@@ -292,12 +301,12 @@ class CcipCrosschainDispatcher(CrosschainDispatcher):
292
301
  "queuedTokenAmount()", output_types=["uint256"], decoder=Amount
293
302
  )
294
303
 
295
- def processed_operation(self, operation_id: bytes) -> Call[bool]:
304
+ def processed_operation(self, operation_id: OperationId) -> Call[bool]:
296
305
  return self._view(
297
306
  "processedOperation(bytes32)", operation_id, output_types=["bool"]
298
307
  )
299
308
 
300
- def processed_ccip_message(self, message_id: bytes) -> Call[bool]:
309
+ def processed_ccip_message(self, message_id: MessageId) -> Call[bool]:
301
310
  return self._view(
302
311
  "processedCcipMessage(bytes32)", message_id, output_types=["bool"]
303
312
  )
@@ -316,7 +325,7 @@ def _ccip_observation_decoder(values: tuple) -> CcipObservation:
316
325
  queued_token_amount=Amount(queued),
317
326
  state_version=int(version),
318
327
  command_config_epoch=int(epoch),
319
- tracked_position_set_hash=bytes(set_hash),
328
+ tracked_position_set_hash=PositionSetHash(bytes(set_hash)),
320
329
  )
321
330
 
322
331
 
@@ -327,7 +336,7 @@ class CcipCrosschainFactory(CrosschainFactory):
327
336
  def create_executor(
328
337
  self,
329
338
  user_salt: bytes,
330
- asset_id: bytes,
339
+ asset_id: AssetId,
331
340
  manager: ChecksumAddress,
332
341
  safety: SafetyConfig,
333
342
  ) -> Call[ChecksumAddress]:
@@ -365,7 +374,9 @@ class CcipCrosschainFactory(CrosschainFactory):
365
374
  decoder=ChainId,
366
375
  )
367
376
 
368
- def asset_config(self, asset_id: bytes) -> Call[tuple[ChecksumAddress, int, bool]]:
377
+ def asset_config(
378
+ self, asset_id: AssetId
379
+ ) -> Call[tuple[ChecksumAddress, int, bool]]:
369
380
  """``(token, sharedDecimals, enabled)`` for an asset id."""
370
381
  return self._view(
371
382
  "assetConfig(bytes32)",
@@ -444,7 +455,7 @@ class CcipCrosschainFactory(CrosschainFactory):
444
455
  )
445
456
 
446
457
  def schedule_asset(
447
- self, asset_id: bytes, token: ChecksumAddress, shared_decimals: int
458
+ self, asset_id: AssetId, token: ChecksumAddress, shared_decimals: int
448
459
  ) -> Call[None]:
449
460
  """Owner-only; executable after ``config_delay`` with the same arguments."""
450
461
  return self._write(
@@ -452,7 +463,7 @@ class CcipCrosschainFactory(CrosschainFactory):
452
463
  )
453
464
 
454
465
  def execute_asset(
455
- self, asset_id: bytes, token: ChecksumAddress, shared_decimals: int
466
+ self, asset_id: AssetId, token: ChecksumAddress, shared_decimals: int
456
467
  ) -> Call[None]:
457
468
  return self._write(
458
469
  "executeAsset(bytes32,address,uint8)", asset_id, token, shared_decimals
@@ -21,6 +21,7 @@ from web3 import Web3
21
21
 
22
22
  from ipor_fusion.core.contract import Call
23
23
  from ipor_fusion.crosschain.ccip.chainlink import CcipOffRamp
24
+ from ipor_fusion.types import MessageId
24
25
 
25
26
  #: Chainlink Labs' public mainnet indexers, tried in order.
26
27
  INDEXER_URLS: tuple[str, ...] = (
@@ -40,7 +41,7 @@ class VerifierResultUnavailable(LookupError):
40
41
  class VerifierResult:
41
42
  """One message's committee result as the indexer serves it."""
42
43
 
43
- message_id: bytes
44
+ message_id: MessageId
44
45
  #: The CCVs the message names, in the order ``execute`` expects.
45
46
  ccv_addresses: tuple[ChecksumAddress, ...]
46
47
  #: The verifier result for the committee CCV: version tag, signature
@@ -54,7 +55,7 @@ class VerifierResult:
54
55
 
55
56
 
56
57
  def fetch_verifier_result(
57
- message_id: bytes,
58
+ message_id: MessageId,
58
59
  *,
59
60
  indexers: Sequence[str] = INDEXER_URLS,
60
61
  timeout: float = 15.0,
@@ -82,7 +83,7 @@ def fetch_verifier_result(
82
83
  continue
83
84
  result = results[0]["verifierResult"]
84
85
  return VerifierResult(
85
- message_id=bytes.fromhex(result["message_id"][2:]),
86
+ message_id=MessageId(bytes.fromhex(result["message_id"][2:])),
86
87
  ccv_addresses=tuple(
87
88
  Web3.to_checksum_address(a) for a in result["message_ccv_addresses"]
88
89
  ),
@@ -19,7 +19,14 @@ from web3 import Web3
19
19
 
20
20
  from ipor_fusion.core.contract import Call, ContractWrapper
21
21
  from ipor_fusion.crosschain.messages import CommandStatus
22
- from ipor_fusion.types import Amount, ChainId
22
+ from ipor_fusion.types import (
23
+ Amount,
24
+ AssetId,
25
+ ChainId,
26
+ CommandId,
27
+ PositionSetHash,
28
+ ProposalId,
29
+ )
23
30
 
24
31
 
25
32
  @dataclass(frozen=True, slots=True)
@@ -36,7 +43,7 @@ class BalanceObservation:
36
43
  remote_block: int
37
44
  remote_timestamp: int
38
45
  expiry: int
39
- tracked_position_set_hash: bytes
46
+ tracked_position_set_hash: PositionSetHash
40
47
 
41
48
  def as_tuple(self) -> tuple:
42
49
  return (
@@ -150,7 +157,7 @@ class CrosschainExecutor(ContractWrapper):
150
157
  "lastApprovedObservedAt(uint256)", chain_id, output_types=["uint64"]
151
158
  )
152
159
 
153
- def approve_balance(self, proposal_id: int) -> Call[None]:
160
+ def approve_balance(self, proposal_id: ProposalId) -> Call[None]:
154
161
  """``BALANCE_APPROVER`` only."""
155
162
  return self._write("approveBalance(uint256)", proposal_id)
156
163
 
@@ -205,7 +212,7 @@ class CrosschainDispatcher(ContractWrapper):
205
212
  "pendingRequestShares(address)", vault, output_types=["uint256"]
206
213
  )
207
214
 
208
- def command_status(self, command_id: bytes) -> Call[CommandStatus]:
215
+ def command_status(self, command_id: CommandId) -> Call[CommandStatus]:
209
216
  return self._view(
210
217
  "commandStatus(bytes32)",
211
218
  command_id,
@@ -253,7 +260,7 @@ class CrosschainFactory(ContractWrapper):
253
260
  def is_allowed_creator(self, creator: ChecksumAddress) -> Call[bool]:
254
261
  return self._view("isAllowedCreator(address)", creator, output_types=["bool"])
255
262
 
256
- def asset_frozen(self, asset_id: bytes) -> Call[bool]:
263
+ def asset_frozen(self, asset_id: AssetId) -> Call[bool]:
257
264
  return self._view("assetFrozen(bytes32)", asset_id, output_types=["bool"])
258
265
 
259
266
  def creation_codes_configured(self) -> Call[bool]:
@@ -40,7 +40,7 @@ from ipor_fusion.fuses.crosschain.base import (
40
40
  CrosschainSupplyFuse,
41
41
  SendParams,
42
42
  )
43
- from ipor_fusion.types import Amount, ChainId
43
+ from ipor_fusion.types import Amount, ChainId, PositionSetHash, ProposalId
44
44
 
45
45
 
46
46
  @dataclass(frozen=True, slots=True)
@@ -54,7 +54,7 @@ class LaneObservation:
54
54
  accounted_balance: Amount
55
55
  state_version: int
56
56
  command_config_epoch: int
57
- tracked_position_set_hash: bytes
57
+ tracked_position_set_hash: PositionSetHash
58
58
 
59
59
 
60
60
  @dataclass(frozen=True, slots=True)
@@ -241,11 +241,11 @@ class CrosschainLane(ABC):
241
241
  last = self.last_approved_observed_at().call()
242
242
  return last == 0 or now - last > self.staleness_max().call() - margin
243
243
 
244
- def approve_balance(self, proposal_id: int) -> Call[None]:
244
+ def approve_balance(self, proposal_id: ProposalId) -> Call[None]:
245
245
  """``BALANCE_APPROVER`` only."""
246
246
  return self.executor.approve_balance(proposal_id)
247
247
 
248
- def proposal_id_from_logs(self, logs: Iterable[Mapping]) -> int:
248
+ def proposal_id_from_logs(self, logs: Iterable[Mapping]) -> ProposalId:
249
249
  """The id of the proposal this lane's executor emitted in ``logs``
250
250
  (a receipt's or a simulated call's), from its ``BalanceProposed``."""
251
251
  for log in logs:
@@ -254,10 +254,10 @@ class CrosschainLane(ABC):
254
254
  and log_address(log) == self.executor_address
255
255
  ):
256
256
  (proposal_id,) = decode(["uint256"], log_bytes(log["data"])[:32])
257
- return int(proposal_id)
257
+ return ProposalId(int(proposal_id))
258
258
  raise ValueError("no BalanceProposed log from the executor")
259
259
 
260
- def proposal_id_from_receipt(self, receipt: Mapping) -> int:
260
+ def proposal_id_from_receipt(self, receipt: Mapping) -> ProposalId:
261
261
  return self.proposal_id_from_logs(receipt["logs"])
262
262
 
263
263
  def _require_spoke(self, observation: BalanceObservation) -> None:
@@ -18,7 +18,7 @@ from eth_typing import ChecksumAddress
18
18
  from web3 import Web3
19
19
 
20
20
  from ipor_fusion.core.address import _validate_not_zero_address
21
- from ipor_fusion.types import Amount, Shares
21
+ from ipor_fusion.types import Amount, CommandId, Shares
22
22
 
23
23
  #: ``CrosschainConstants.CODEC_VERSION`` -- the Stargate/LayerZero envelope version.
24
24
  CODEC_VERSION = 3
@@ -128,6 +128,10 @@ _ZERO_SHARES = Shares(0)
128
128
  COMMAND_TUPLE_TYPE = "(bytes32,uint64,uint64,uint8,bytes)"
129
129
 
130
130
 
131
+ # The zero command id: no command, or one not yet assigned by the executor.
132
+ NO_COMMAND_ID = CommandId(bytes(32))
133
+
134
+
131
135
  @dataclass(frozen=True, slots=True)
132
136
  class Command:
133
137
  """``CrosschainMessages.Command``: one business command for a dispatcher.
@@ -143,7 +147,7 @@ class Command:
143
147
 
144
148
  action: BusinessAction
145
149
  action_data: bytes
146
- command_id: bytes = bytes(32)
150
+ command_id: CommandId = NO_COMMAND_ID
147
151
  sequence: int = 0
148
152
  command_config_epoch: int = 0
149
153
 
@@ -167,7 +171,7 @@ class Command:
167
171
  return cls(
168
172
  action=BusinessAction(action),
169
173
  action_data=bytes(action_data),
170
- command_id=bytes(command_id),
174
+ command_id=CommandId(bytes(command_id)),
171
175
  sequence=int(sequence),
172
176
  command_config_epoch=int(config_epoch),
173
177
  )