ipor-fusion 4.0.0.dev37__tar.gz → 4.0.0.dev41__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.dev37 → ipor_fusion-4.0.0.dev41}/PKG-INFO +1 -1
  2. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/pyproject.toml +1 -1
  3. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/market_cmd.py +1 -1
  4. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/vault_markets_view.py +1 -1
  5. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/ccip/contracts.py +16 -7
  6. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/ccip/lane.py +2 -2
  7. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/contracts.py +8 -4
  8. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/stargate/contracts.py +10 -4
  9. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/market_info.py +1 -1
  10. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/morpho_api.py +24 -20
  11. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/vault_info.py +1 -1
  12. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_helpers.py +31 -2
  13. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_market_morpho_blue.py +55 -0
  14. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_lane.py +14 -0
  15. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/typed_client/consumer.py +29 -0
  16. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.claude-plugin/marketplace.json +0 -0
  17. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.claude-plugin/plugin.json +0 -0
  18. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.env.example +0 -0
  19. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.gemini/settings.json +0 -0
  20. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.git-blame-ignore-revs +0 -0
  21. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.github/workflows/cd.yml +0 -0
  22. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.github/workflows/ci.yml +0 -0
  23. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.github/workflows/commit-lint.yml +0 -0
  24. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.github/workflows/notify-slack.yml +0 -0
  25. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.github/workflows/python-build.yml +0 -0
  26. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.github/workflows/python-publish.yml +0 -0
  27. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.github/workflows/release.yml +0 -0
  28. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.github/workflows/report-slack.yml +0 -0
  29. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.gitignore +0 -0
  30. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.mcp.json +0 -0
  31. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.pre-commit-config.yaml +0 -0
  32. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/.python-version +0 -0
  33. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/AGENTS.md +0 -0
  34. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/CHANGELOG.md +0 -0
  35. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/CLAUDE.md +0 -0
  36. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/CODEOWNERS +0 -0
  37. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/LICENSE +0 -0
  38. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/README.md +0 -0
  39. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/build.sh +0 -0
  40. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/examples/CONTRIBUTING.md +0 -0
  41. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/examples/README.md +0 -0
  42. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  43. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  44. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  45. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  46. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/examples/external_state_margin_leg_base.py +0 -0
  47. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/examples/simple_aave_v3_supply_base.py +0 -0
  48. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/pytest.ini +0 -0
  49. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/scripts/commit-check.sh +0 -0
  50. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/scripts/crosschain_readiness.py +0 -0
  51. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/skills/ipor-deploy-vault/SKILL.md +0 -0
  52. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/__init__.py +0 -0
  53. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/about.py +0 -0
  54. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cache.py +0 -0
  55. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/chains.py +0 -0
  56. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/__init__.py +0 -0
  57. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  58. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/config_cmd.py +0 -0
  59. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/config_store.py +0 -0
  60. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/main.py +0 -0
  61. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/options.py +0 -0
  62. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  63. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  64. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/vault_health.py +0 -0
  65. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  66. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  67. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/config/__init__.py +0 -0
  68. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/config/roles.py +0 -0
  69. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/__init__.py +0 -0
  70. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/access.py +0 -0
  71. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/address.py +0 -0
  72. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/context.py +0 -0
  73. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/contract.py +0 -0
  74. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/erc20.py +0 -0
  75. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/external_state_executor.py +0 -0
  76. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/fee_manager.py +0 -0
  77. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/fuse_action.py +0 -0
  78. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/fusion_factory.py +0 -0
  79. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/logs.py +0 -0
  80. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/multicall.py +0 -0
  81. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/oracle.py +0 -0
  82. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/plasma_vault.py +0 -0
  83. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/rewards_manager.py +0 -0
  84. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/simulation.py +0 -0
  85. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  86. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/__init__.py +0 -0
  87. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  88. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  89. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  90. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  91. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  92. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  93. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  94. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/discovery.py +0 -0
  95. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/errors.py +0 -0
  96. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/lane.py +0 -0
  97. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/logs.py +0 -0
  98. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/messages.py +0 -0
  99. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/simulation.py +0 -0
  100. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  101. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  102. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  103. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  104. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/crosschain/transport.py +0 -0
  105. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/dependency_graph.py +0 -0
  106. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/entry_cli.py +0 -0
  107. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/entry_mcp.py +0 -0
  108. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/errors.py +0 -0
  109. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/explorer.py +0 -0
  110. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/field_docs.py +0 -0
  111. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/__init__.py +0 -0
  112. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  113. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/async_action.py +0 -0
  114. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/base.py +0 -0
  115. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  116. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
  117. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
  118. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
  119. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
  120. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/erc4626.py +0 -0
  121. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  122. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/events.py +0 -0
  123. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/external_state.py +0 -0
  124. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  125. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  126. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  127. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/hypercore.py +0 -0
  128. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  129. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/merkl.py +0 -0
  130. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/morpho.py +0 -0
  131. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  132. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  133. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/fuses/universal.py +0 -0
  134. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/guide/__init__.py +0 -0
  135. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/guide/architecture.md +0 -0
  136. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/guide/glossary.md +0 -0
  137. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/guide/invariants.md +0 -0
  138. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/guide/quickstart.md +0 -0
  139. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/market_ids.py +0 -0
  140. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/mcp/__init__.py +0 -0
  141. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/mcp/guide.py +0 -0
  142. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/mcp/models.py +0 -0
  143. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/mcp/server.py +0 -0
  144. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/py.typed +0 -0
  145. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/__init__.py +0 -0
  146. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/aave_v3.py +0 -0
  147. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/aave_v4.py +0 -0
  148. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/compound_v3.py +0 -0
  149. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/hypercore.py +0 -0
  150. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  151. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/lending_health.py +0 -0
  152. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/morpho.py +0 -0
  153. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  154. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/position_manager.py +0 -0
  155. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  156. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  157. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/readers/unpriceable.py +0 -0
  158. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/substrates.py +0 -0
  159. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/types.py +0 -0
  160. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/validation.py +0 -0
  161. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/src/ipor_fusion/vault_data.py +0 -0
  162. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/UniswapV3UniversalRouter.py +0 -0
  163. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_crosschain.py +0 -0
  164. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_crosschain_lifecycle.py +0 -0
  165. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_crosschain_recovery.py +0 -0
  166. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_euler_v2.py +0 -0
  167. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_foundry.py +0 -0
  168. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_guide.py +0 -0
  169. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_hypercore_model.py +0 -0
  170. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_hypercore_shadow.py +0 -0
  171. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_multicall.py +0 -0
  172. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_simulate.py +0 -0
  173. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/_validation.py +0 -0
  174. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/addresses.py +0 -0
  175. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/conftest.py +0 -0
  176. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/constants.py +0 -0
  177. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  178. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/fixtures/hypercore_market55.json +0 -0
  179. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/fixtures/vault_info_golden/clean.json +0 -0
  180. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  181. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/fixtures/vault_info_golden/rich.json +0 -0
  182. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  183. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_about.py +0 -0
  184. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_access_manager.py +0 -0
  185. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cache.py +0 -0
  186. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_chains.py +0 -0
  187. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_changelog.py +0 -0
  188. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_commands.py +0 -0
  189. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_config_store.py +0 -0
  190. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_hypercore.py +0 -0
  191. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_lending_health.py +0 -0
  192. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_oracle_mapping.py +0 -0
  193. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_pending_requests_scan.py +0 -0
  194. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_role_accounts.py +0 -0
  195. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_vault_info_golden.py +0 -0
  196. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_vault_predates_getters.py +0 -0
  197. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_cli_vault_validate.py +0 -0
  198. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_contract_call.py +0 -0
  199. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_contract_encoder.py +0 -0
  200. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_ccip_events.py +0 -0
  201. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_ccip_indexer.py +0 -0
  202. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_ccip_quote.py +0 -0
  203. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_chainlink.py +0 -0
  204. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_contracts.py +0 -0
  205. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_encoding.py +0 -0
  206. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_errors.py +0 -0
  207. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_layering.py +0 -0
  208. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_readiness.py +0 -0
  209. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_readiness_report.py +0 -0
  210. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_crosschain_transport.py +0 -0
  211. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_erc20.py +0 -0
  212. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_examples_composed_hypercore.py +0 -0
  213. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_examples_crosschain.py +0 -0
  214. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  215. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_examples_vaults.py +0 -0
  216. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_explorer.py +0 -0
  217. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_external_state_events.py +0 -0
  218. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_external_state_executor.py +0 -0
  219. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_fee_manager.py +0 -0
  220. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_fuse_encoding.py +0 -0
  221. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_guide.py +0 -0
  222. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_hypercore_deployment.py +0 -0
  223. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_hypercore_events.py +0 -0
  224. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_hypercore_flow.py +0 -0
  225. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_hypercore_live.py +0 -0
  226. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_hypercore_outcome.py +0 -0
  227. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_hypercore_readers.py +0 -0
  228. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_hypercore_simulation.py +0 -0
  229. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_logs.py +0 -0
  230. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_mcp_hypercore.py +0 -0
  231. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_mcp_models.py +0 -0
  232. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_mcp_server.py +0 -0
  233. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_mcp_vault_validate.py +0 -0
  234. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_multicall.py +0 -0
  235. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_oracle.py +0 -0
  236. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_oracle_mapping.py +0 -0
  237. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_plasma_vault.py +0 -0
  238. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_reader_encoding.py +0 -0
  239. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_readiness_selection.py +0 -0
  240. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_revert_decoding.py +0 -0
  241. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_rewards_manager.py +0 -0
  242. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_roles.py +0 -0
  243. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_sdk_layering.py +0 -0
  244. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  245. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  246. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  247. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_crosschain_canary.py +0 -0
  248. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_crosschain_hypercore.py +0 -0
  249. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  250. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  251. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  252. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  253. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  254. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  255. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  256. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_examples_arbitrum.py +0 -0
  257. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_examples_base.py +0 -0
  258. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_examples_crosschain.py +0 -0
  259. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_external_state_executor_base.py +0 -0
  260. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  261. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  262. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  263. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_morpho_blue.py +0 -0
  264. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_pilot_v3.py +0 -0
  265. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  266. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  267. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  268. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  269. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_readers_integration.py +0 -0
  270. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_release_funds.py +0 -0
  271. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_swap_on_base.py +0 -0
  272. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  273. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_solidity_compiler.py +0 -0
  274. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_solidity_mirrors.py +0 -0
  275. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_substrates.py +0 -0
  276. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_types_and_events.py +0 -0
  277. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_unpriceable_tokens.py +0 -0
  278. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_validation.py +0 -0
  279. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_vault_dep_graph.py +0 -0
  280. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_vault_info_onchain_base.py +0 -0
  281. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  282. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_vault_simulator_blocks.py +0 -0
  283. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_web3_context.py +0 -0
  284. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/tests/test_withdraw_manager.py +0 -0
  285. {ipor_fusion-4.0.0.dev37 → ipor_fusion-4.0.0.dev41}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev37
3
+ Version: 4.0.0.dev41
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.dev37"
3
+ version = "4.0.0.dev41"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -551,7 +551,7 @@ def _print_vault_v1(
551
551
  return
552
552
  rows: list[tuple[str, ...]] = []
553
553
  for alloc in allocations:
554
- flow = vault.public_allocator_flow_caps.get(alloc.market_id.lower(), (0, 0))
554
+ flow = vault.public_allocator_flow_caps.get(alloc.market_id, (0, 0))
555
555
  flow_str = (
556
556
  f"in {_format_amount(flow[0], alloc.loan_decimals)}" if flow[0] else "—"
557
557
  )
@@ -53,7 +53,7 @@ def _print_lending_health( # noqa: C901
53
53
  for pb in positions:
54
54
  coll_sym = resolve_token_symbol(ctx, pb.collateral_token) or "?"
55
55
  loan_sym = resolve_token_symbol(ctx, pb.loan_token) or "?"
56
- click.echo(f" morpho market 0x{pb.market_id} ({coll_sym}/{loan_sym}):")
56
+ click.echo(f" morpho market {pb.market_id} ({coll_sym}/{loan_sym}):")
57
57
  click.echo(
58
58
  f" Collateral: {_format_token_amount(ctx, int(pb.collateral), pb.collateral_token, prices)}"
59
59
  )
@@ -165,9 +165,12 @@ class CcipCrosschainExecutor(CrosschainExecutor):
165
165
  "lastRemoteStateVersion(uint256)", chain_id, output_types=["uint64"]
166
166
  )
167
167
 
168
- def active_return(self, chain_id: ChainId) -> Call[bytes]:
168
+ def active_return(self, chain_id: ChainId) -> Call[OperationId]:
169
169
  return self._view(
170
- "activeReturn(uint256)", chain_id, output_types=["bytes32"], decoder=bytes
170
+ "activeReturn(uint256)",
171
+ chain_id,
172
+ output_types=["bytes32"],
173
+ decoder=lambda v: OperationId(bytes(v)),
171
174
  )
172
175
 
173
176
  def active_command(self, chain_id: ChainId) -> Call[CommandId]:
@@ -194,11 +197,14 @@ class CcipCrosschainExecutor(CrosschainExecutor):
194
197
  every value-moving operation bumps to invalidate the active proposal."""
195
198
  return self._view("accountingEpoch(uint256)", chain_id, output_types=["uint64"])
196
199
 
197
- def active_proposal_id(self, chain_id: ChainId) -> Call[int]:
200
+ def active_proposal_id(self, chain_id: ChainId) -> Call[ProposalId]:
198
201
  """``activeProposalId(uint256)``: the active balance proposal id for
199
202
  the chain, 0 when there is none."""
200
203
  return self._view(
201
- "activeProposalId(uint256)", chain_id, output_types=["uint256"]
204
+ "activeProposalId(uint256)",
205
+ chain_id,
206
+ output_types=["uint256"],
207
+ decoder=ProposalId,
202
208
  )
203
209
 
204
210
  def attestation_anchor_principal(self, chain_id: ChainId) -> Call[Amount]:
@@ -233,7 +239,7 @@ class CcipCrosschainExecutor(CrosschainExecutor):
233
239
  "processedCcipMessage(bytes32)", message_id, output_types=["bool"]
234
240
  )
235
241
 
236
- def propose_balance(self, observation: BalanceObservation) -> Call[int]:
242
+ def propose_balance(self, observation: BalanceObservation) -> Call[ProposalId]:
237
243
  """``BALANCE_PROPOSER`` only; ``settled_balance`` in local decimals.
238
244
 
239
245
  A write: ``.send()`` it, or ``.call()`` inside a simulation to preview
@@ -243,6 +249,7 @@ class CcipCrosschainExecutor(CrosschainExecutor):
243
249
  "proposeBalance(uint256,uint256,uint64,uint64,uint64,uint64,bytes32)",
244
250
  *observation.as_tuple(),
245
251
  output_types=["uint256"],
252
+ decoder=ProposalId,
246
253
  )
247
254
 
248
255
  def reject_balance_and_block(self, proposal_id: ProposalId) -> Call[None]:
@@ -285,9 +292,11 @@ class CcipCrosschainDispatcher(CrosschainDispatcher):
285
292
  def tracked_vaults_length(self) -> Call[int]:
286
293
  return self._view("trackedVaultsLength()", output_types=["uint256"])
287
294
 
288
- def tracked_position_set_hash(self) -> Call[bytes]:
295
+ def tracked_position_set_hash(self) -> Call[PositionSetHash]:
289
296
  return self._view(
290
- "trackedPositionSetHash()", output_types=["bytes32"], decoder=bytes
297
+ "trackedPositionSetHash()",
298
+ output_types=["bytes32"],
299
+ decoder=lambda v: PositionSetHash(bytes(v)),
291
300
  )
292
301
 
293
302
  def response_queue_head(self) -> Call[int]:
@@ -22,7 +22,7 @@ from ipor_fusion.fuses.crosschain.ccip import (
22
22
  CcipCrosschainSupplyFuse,
23
23
  CcipSendParams,
24
24
  )
25
- from ipor_fusion.types import Amount, ChainId
25
+ from ipor_fusion.types import Amount, ChainId, ProposalId
26
26
 
27
27
 
28
28
  class CcipLane(CrosschainLane):
@@ -134,7 +134,7 @@ class CcipLane(CrosschainLane):
134
134
  lambda observation: _to_lane(observation, rate)
135
135
  )
136
136
 
137
- def propose_balance(self, observation: BalanceObservation) -> Call[int]:
137
+ def propose_balance(self, observation: BalanceObservation) -> Call[ProposalId]:
138
138
  """Returns the proposal id (the function's return value)."""
139
139
  self._require_spoke(observation)
140
140
  return self.executor.propose_balance(observation)
@@ -76,8 +76,10 @@ class CrosschainExecutor(ContractWrapper):
76
76
  def asset(self) -> Call[ChecksumAddress]:
77
77
  return self._view("ASSET()", output_types=["address"], decoder=_address)
78
78
 
79
- def asset_id(self) -> Call[bytes]:
80
- return self._view("ASSET_ID()", output_types=["bytes32"], decoder=bytes)
79
+ def asset_id(self) -> Call[AssetId]:
80
+ return self._view(
81
+ "ASSET_ID()", output_types=["bytes32"], decoder=lambda v: AssetId(bytes(v))
82
+ )
81
83
 
82
84
  def factory(self) -> Call[ChecksumAddress]:
83
85
  return self._view("FACTORY()", output_types=["address"], decoder=_address)
@@ -172,8 +174,10 @@ class CrosschainDispatcher(ContractWrapper):
172
174
  def asset(self) -> Call[ChecksumAddress]:
173
175
  return self._view("ASSET()", output_types=["address"], decoder=_address)
174
176
 
175
- def asset_id(self) -> Call[bytes]:
176
- return self._view("ASSET_ID()", output_types=["bytes32"], decoder=bytes)
177
+ def asset_id(self) -> Call[AssetId]:
178
+ return self._view(
179
+ "ASSET_ID()", output_types=["bytes32"], decoder=lambda v: AssetId(bytes(v))
180
+ )
177
181
 
178
182
  def factory(self) -> Call[ChecksumAddress]:
179
183
  return self._view("FACTORY()", output_types=["address"], decoder=_address)
@@ -170,9 +170,12 @@ class StargateCrosschainExecutor(CrosschainExecutor):
170
170
  "pendingTransferCount(uint256)", chain_id, output_types=["uint256"]
171
171
  )
172
172
 
173
- def pending_request_by_chain(self, chain_id: ChainId) -> Call[int]:
173
+ def pending_request_by_chain(self, chain_id: ChainId) -> Call[DeploymentRequestId]:
174
174
  return self._view(
175
- "pendingRequestByChain(uint256)", chain_id, output_types=["uint256"]
175
+ "pendingRequestByChain(uint256)",
176
+ chain_id,
177
+ output_types=["uint256"],
178
+ decoder=DeploymentRequestId,
176
179
  )
177
180
 
178
181
  def active_command(self, chain_id: ChainId) -> Call[ActiveCommand]:
@@ -191,9 +194,12 @@ class StargateCrosschainExecutor(CrosschainExecutor):
191
194
  decoder=_active_command_decoder,
192
195
  )
193
196
 
194
- def active_proposal_id(self, chain_id: ChainId) -> Call[int]:
197
+ def active_proposal_id(self, chain_id: ChainId) -> Call[ProposalId]:
195
198
  return self._view(
196
- "activeProposalId(uint256)", chain_id, output_types=["uint256"]
199
+ "activeProposalId(uint256)",
200
+ chain_id,
201
+ output_types=["uint256"],
202
+ decoder=ProposalId,
197
203
  )
198
204
 
199
205
  def last_approval_at(self, chain_id: ChainId) -> Call[int]:
@@ -34,7 +34,7 @@ def build_morpho_blue_market_json(
34
34
  market's parameters, state and rates (from `MorphoReader`), plus the
35
35
  MetaMorpho vaults supplying it when ``api_market`` (`fetch_market`) is
36
36
  given, or ``api_error`` when that lookup failed. ``market_id`` is the
37
- 64-hex-char id without ``0x``."""
37
+ canonical id (`parse_morpho_market_id`) and is written as is."""
38
38
  out: dict = {
39
39
  "market_id": market_id,
40
40
  "chain_id": chain_id,
@@ -180,8 +180,9 @@ def fetch_market(
180
180
 
181
181
  def _parse_market(raw: dict) -> MorphoApiMarket:
182
182
  state = raw.get("state") or {}
183
+ market_id = _api_market_id(raw.get("marketId"))
183
184
  return MorphoApiMarket(
184
- market_id=_api_market_id(raw.get("marketId")),
185
+ market_id=market_id,
185
186
  lltv=int(raw["lltv"]),
186
187
  loan_token=_api_address(raw["loanAsset"]["address"]),
187
188
  loan_symbol=raw["loanAsset"]["symbol"],
@@ -199,9 +200,7 @@ def _parse_market(raw: dict) -> MorphoApiMarket:
199
200
  borrow_apy=float(state.get("borrowApy") or 0.0),
200
201
  fee_wad=int(state.get("fee") or 0),
201
202
  timestamp=int(state.get("timestamp") or 0),
202
- vaults=[
203
- _parse_vault(v, raw["marketId"]) for v in raw.get("supplyingVaults") or []
204
- ],
203
+ vaults=[_parse_vault(v, market_id) for v in raw.get("supplyingVaults") or []],
205
204
  )
206
205
 
207
206
 
@@ -373,7 +372,9 @@ class VaultV1Info:
373
372
  allocators: list[ChecksumAddress]
374
373
  allocations: list[VaultV1MarketAllocation]
375
374
  public_allocator: VaultFlowCap | None # vault-level (no flowCap filtering)
376
- public_allocator_flow_caps: dict[str, tuple[int, int]] # marketId -> (maxIn,maxOut)
375
+ public_allocator_flow_caps: dict[
376
+ MorphoBlueMarketId, tuple[int, int]
377
+ ] # (maxIn, maxOut)
377
378
 
378
379
 
379
380
  def fetch_vault(
@@ -618,7 +619,7 @@ def _parse_vault_v1(raw: dict) -> VaultV1Info:
618
619
 
619
620
  pac = raw.get("publicAllocatorConfig")
620
621
  flow_cap_summary: VaultFlowCap | None = None
621
- flow_caps: dict[str, tuple[int, int]] = {}
622
+ flow_caps: dict[MorphoBlueMarketId, tuple[int, int]] = {}
622
623
  if pac:
623
624
  flow_cap_summary = VaultFlowCap(
624
625
  fee_wei=int(pac.get("fee") or 0),
@@ -627,9 +628,11 @@ def _parse_vault_v1(raw: dict) -> VaultV1Info:
627
628
  admin=pac.get("admin"),
628
629
  )
629
630
  for fc in pac.get("flowCaps") or []:
630
- mid = ((fc.get("market") or {}).get("marketId") or "").lower()
631
- if mid:
632
- flow_caps[mid] = (int(fc.get("maxIn") or 0), int(fc.get("maxOut") or 0))
631
+ if raw_mid := (fc.get("market") or {}).get("marketId"):
632
+ flow_caps[_api_market_id(raw_mid)] = (
633
+ int(fc.get("maxIn") or 0),
634
+ int(fc.get("maxOut") or 0),
635
+ )
633
636
 
634
637
  return VaultV1Info(
635
638
  address=_api_address(raw.get("address")),
@@ -655,27 +658,28 @@ def _parse_vault_v1(raw: dict) -> VaultV1Info:
655
658
  )
656
659
 
657
660
 
661
+ def _is_market(entry: dict, market_id: MorphoBlueMarketId) -> bool:
662
+ """Whether an allocation or flow-cap ``entry`` names ``market_id``, in any
663
+ spelling the API returns."""
664
+ raw = (entry.get("market") or {}).get("marketId")
665
+ try:
666
+ return parse_morpho_market_id(str(raw or "")) == market_id
667
+ except ValueError:
668
+ return False
669
+
670
+
658
671
  def _parse_vault(raw: dict, market_id: MorphoBlueMarketId) -> VaultAllocation:
659
672
  asset = raw.get("asset") or {}
660
673
  state = raw.get("state") or {}
661
674
  allocation_for_market: dict = next(
662
- (
663
- a
664
- for a in (state.get("allocation") or [])
665
- if (a.get("market") or {}).get("marketId", "").lower() == market_id
666
- ),
675
+ (a for a in (state.get("allocation") or []) if _is_market(a, market_id)),
667
676
  {},
668
677
  )
669
678
  pac = raw.get("publicAllocatorConfig")
670
679
  flow_cap: VaultFlowCap | None = None
671
680
  if pac:
672
681
  flow_for_market = next(
673
- (
674
- fc
675
- for fc in (pac.get("flowCaps") or [])
676
- if (fc.get("market") or {}).get("marketId", "").lower()
677
- == market_id.lower()
678
- ),
682
+ (fc for fc in (pac.get("flowCaps") or []) if _is_market(fc, market_id)),
679
683
  None,
680
684
  )
681
685
  if flow_for_market:
@@ -505,7 +505,7 @@ def _morpho_breakdown_json(
505
505
  prices = data.token_prices_usd
506
506
  return [
507
507
  {
508
- "morpho_market_id": "0x" + str(pb.market_id),
508
+ "morpho_market_id": pb.market_id,
509
509
  "collateral_symbol": resolve_token_symbol(ctx, pb.collateral_token) or None,
510
510
  "loan_symbol": resolve_token_symbol(ctx, pb.loan_token) or None,
511
511
  "collateral": _build_breakdown_amount_json(
@@ -63,7 +63,11 @@ from ipor_fusion.readers.lending_health import (
63
63
  )
64
64
  from ipor_fusion.readers.morpho import MORPHO_BLUE_ADDRESSES, MorphoPositionBreakdown
65
65
  from ipor_fusion.substrates import decode_substrate, market_name
66
- from ipor_fusion.types import Amount, MarketId, MorphoBlueMarketId
66
+ from ipor_fusion.types import (
67
+ Amount,
68
+ MarketId,
69
+ parse_morpho_market_id,
70
+ )
67
71
  from ipor_fusion.validation import Erc20Holdings
68
72
  from ipor_fusion.vault_data import (
69
73
  FeeData,
@@ -86,6 +90,7 @@ from ipor_fusion.vault_info import (
86
90
  _build_dependency_graph_json,
87
91
  _build_fees_json,
88
92
  _build_share_price_json,
93
+ _morpho_breakdown_json,
89
94
  _partition_balance_fuses,
90
95
  )
91
96
 
@@ -1509,7 +1514,7 @@ _TOKEN_WETH = Web3.to_checksum_address("0x" + "33" * 20)
1509
1514
 
1510
1515
  def _morpho_breakdown(market_id: str = "ab" * 32):
1511
1516
  return MorphoPositionBreakdown(
1512
- market_id=MorphoBlueMarketId(market_id),
1517
+ market_id=parse_morpho_market_id(market_id),
1513
1518
  loan_token=_TOKEN_USDC,
1514
1519
  collateral_token=_TOKEN_WETH,
1515
1520
  collateral=Amount(10 * 10**18),
@@ -1705,6 +1710,30 @@ class TestFetcherCollectors:
1705
1710
  assert 14 in out and 1 not in out
1706
1711
  assert len(out[14]) == 1
1707
1712
 
1713
+ @patch("ipor_fusion.vault_info.resolve_token_decimals", return_value=18)
1714
+ @patch("ipor_fusion.vault_info.resolve_token_symbol", return_value="WETH")
1715
+ @patch("ipor_fusion.vault_data.resolve_token_decimals", return_value=18)
1716
+ @patch("ipor_fusion.vault_data.resolve_token_symbol", return_value="WETH")
1717
+ def test_collected_morpho_id_renders_with_one_prefix(
1718
+ self, _sym, _dec, _view_sym, _view_dec, capsys
1719
+ ):
1720
+ (market_id,) = _collect_morpho_substrates({14: [bytes.fromhex("ab" * 32)]})[14]
1721
+ breakdown = MorphoPositionBreakdown(
1722
+ market_id=market_id,
1723
+ loan_token=_TOKEN_USDC,
1724
+ collateral_token=_TOKEN_WETH,
1725
+ collateral=Amount(0),
1726
+ borrow_assets=Amount(0),
1727
+ supply_assets=Amount(0),
1728
+ )
1729
+ data = _make_data(morpho_positions={14: [breakdown]})
1730
+ (entry,) = _morpho_breakdown_json(MagicMock(), data, [breakdown])
1731
+ assert entry["morpho_market_id"] == "0x" + "ab" * 32
1732
+ _print_lending_health(MagicMock(), data)
1733
+ out = capsys.readouterr().out
1734
+ assert f"morpho market 0x{'ab' * 32} " in out
1735
+ assert "0x0x" not in out
1736
+
1708
1737
  def test_collect_morpho_substrates_skips_wrong_length(self):
1709
1738
  bad = bytes.fromhex("aa" * 16) # wrong length
1710
1739
  out = _collect_morpho_substrates({14: [bad]})
@@ -21,6 +21,7 @@ from ipor_fusion.morpho_api import (
21
21
  fetch_market,
22
22
  fetch_vault,
23
23
  )
24
+ from ipor_fusion.types import parse_morpho_market_id
24
25
 
25
26
  MARKET_ID = "ad656d430bb3d8c1469bf45c8ad4ebae1b04be04757c69fa424eec78d7b3f4dc"
26
27
  LOAN_TOKEN = Web3.to_checksum_address("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
@@ -381,6 +382,60 @@ class TestMorphoApiClient:
381
382
  assert vault.flow_cap.admin == ADMIN_ADDR
382
383
  assert vault.allocators == [PUBLIC_ALLOCATOR_ETH, ADMIN_ADDR]
383
384
 
385
+ def test_fetch_market_matches_allocations_in_any_spelling(self):
386
+ upper = "0x" + MARKET_ID.upper()
387
+ market = {"market": {"marketId": upper}}
388
+ body = {
389
+ "data": {
390
+ "marketById": {
391
+ "marketId": upper,
392
+ "lltv": "915000000000000000",
393
+ "loanAsset": {
394
+ "address": LOAN_TOKEN,
395
+ "symbol": "USDC",
396
+ "decimals": 6,
397
+ },
398
+ "collateralAsset": {
399
+ "address": COLLATERAL_TOKEN,
400
+ "symbol": "WOUSD",
401
+ "decimals": 18,
402
+ },
403
+ "oracle": {"address": ORACLE},
404
+ "irmAddress": IRM,
405
+ "state": {},
406
+ "supplyingVaults": [
407
+ {
408
+ "address": VAULT_ADDR,
409
+ "symbol": "v",
410
+ "name": "v",
411
+ "asset": {"symbol": "USDC", "decimals": 6},
412
+ "state": {
413
+ "allocation": [
414
+ {**market, "supplyAssets": 123, "supplyCap": 456}
415
+ ]
416
+ },
417
+ "publicAllocatorConfig": {
418
+ "fee": 0,
419
+ "flowCaps": [{**market, "maxIn": 7, "maxOut": 8}],
420
+ },
421
+ }
422
+ ],
423
+ }
424
+ }
425
+ }
426
+ with patch("ipor_fusion.morpho_api.urlopen") as mock_open:
427
+ mock_resp = MagicMock()
428
+ mock_resp.read.return_value = json.dumps(body).encode()
429
+ mock_open.return_value.__enter__.return_value = mock_resp
430
+
431
+ result = fetch_market(parse_morpho_market_id(MARKET_ID), 1)
432
+
433
+ assert result.market_id == "0x" + MARKET_ID.lower()
434
+ vault = result.vaults[0]
435
+ assert (vault.supply_assets, vault.supply_cap) == (123, 456)
436
+ assert vault.flow_cap is not None
437
+ assert (vault.flow_cap.max_in, vault.flow_cap.max_out) == (7, 8)
438
+
384
439
  def test_fetch_market_handles_missing_market(self):
385
440
  with patch("ipor_fusion.morpho_api.urlopen") as mock_open:
386
441
  mock_resp = MagicMock()
@@ -803,3 +803,17 @@ class TestDeploymentDiscovery:
803
803
  ccip = next(lane for lane in lanes if isinstance(lane, CcipLane))
804
804
  assert ccip.route == ROUTE and ccip.fuses == CCIP_FUSES
805
805
  assert ccip.decimal_conversion_rate == DECIMAL_CONVERSION_RATE
806
+
807
+
808
+ def test_id_getters_decode_into_the_types_their_writes_take():
809
+ ccip = CcipCrosschainExecutor(MagicMock(), EXECUTOR)
810
+ stargate = StargateCrosschainExecutor(MagicMock(), EXECUTOR)
811
+ word = encode(["bytes32"], [b"\x01" * 32])
812
+ number = encode(["uint256"], [7])
813
+ chain = ChainId(1)
814
+ assert ccip.active_return(chain).decode(word) == b"\x01" * 32
815
+ assert ccip.asset_id().decode(word) == b"\x01" * 32
816
+ assert ccip.active_proposal_id(chain).decode(number) == 7
817
+ assert stargate.active_proposal_id(chain).decode(number) == 7
818
+ assert stargate.pending_request_by_chain(chain).decode(number) == 7
819
+ assert type(ccip.asset_id().decode(word)) is bytes
@@ -26,11 +26,20 @@ from ipor_fusion.chains import ensure_supported_chain
26
26
  from ipor_fusion.core.access import AccessManager
27
27
  from ipor_fusion.core.context import Web3Context
28
28
  from ipor_fusion.core.plasma_vault import PlasmaVault
29
+ from ipor_fusion.crosschain.ccip.contracts import (
30
+ CcipCrosschainDispatcher,
31
+ CcipCrosschainExecutor,
32
+ CcipCrosschainFactory,
33
+ )
29
34
  from ipor_fusion.crosschain.contracts import (
30
35
  CrosschainDispatcher,
31
36
  CrosschainExecutor,
32
37
  CrosschainFactory,
33
38
  )
39
+ from ipor_fusion.crosschain.stargate.contracts import (
40
+ StargateCrosschainExecutor,
41
+ StargateCrosschainFactory,
42
+ )
34
43
  from ipor_fusion.explorer import get_contract_name
35
44
  from ipor_fusion.morpho_api import fetch_market, fetch_vault
36
45
  from ipor_fusion.readers.morpho import morpho_blue_address
@@ -86,3 +95,23 @@ def crosschain(
86
95
  executor.approve_balance(ProposalId(1))
87
96
  dispatcher.command_status(CommandId(bytes(32)))
88
97
  factory.asset_frozen(AssetId(bytes(32)))
98
+
99
+
100
+ def crosschain_reads_feed_writes(
101
+ ccip: CcipCrosschainExecutor,
102
+ ccip_dispatcher: CcipCrosschainDispatcher,
103
+ ccip_factory: CcipCrosschainFactory,
104
+ stargate: StargateCrosschainExecutor,
105
+ stargate_factory: StargateCrosschainFactory,
106
+ ) -> None:
107
+ # Ids read through the SDK go back in without re-wrapping.
108
+ ccip.approve_balance(ccip.active_proposal_id(ETHEREUM).call())
109
+ stargate.approve_balance(stargate.active_proposal_id(ETHEREUM).call())
110
+ ccip_factory.asset_frozen(ccip.asset_id().call())
111
+ ccip_factory.asset_config(ccip_dispatcher.asset_id().call())
112
+ stargate_factory.asset_config(stargate.asset_id().call())
113
+ ccip_dispatcher.processed_operation(ccip.active_return(ETHEREUM).call())
114
+ stargate.deployment_status(stargate.pending_request_by_chain(ETHEREUM).call())
115
+ stargate_factory.cancel_pending_deployment(
116
+ stargate.pending_request_by_chain(ETHEREUM).call()
117
+ )