ipor-fusion 4.0.0.dev39__tar.gz → 4.0.0.dev43__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.dev39 → ipor_fusion-4.0.0.dev43}/PKG-INFO +1 -1
  2. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/pyproject.toml +1 -1
  3. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/vault_cmd.py +1 -1
  4. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/ccip/contracts.py +16 -7
  5. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/ccip/lane.py +2 -2
  6. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/contracts.py +8 -4
  7. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/stargate/contracts.py +10 -4
  8. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/mcp/server.py +6 -10
  9. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/morpho_api.py +14 -12
  10. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_market_morpho_blue.py +30 -0
  11. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_lane.py +14 -0
  12. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_mcp_server.py +19 -6
  13. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/typed_client/consumer.py +29 -0
  14. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.claude-plugin/marketplace.json +0 -0
  15. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.claude-plugin/plugin.json +0 -0
  16. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.env.example +0 -0
  17. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.gemini/settings.json +0 -0
  18. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.git-blame-ignore-revs +0 -0
  19. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.github/workflows/cd.yml +0 -0
  20. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.github/workflows/ci.yml +0 -0
  21. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.github/workflows/commit-lint.yml +0 -0
  22. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.github/workflows/notify-slack.yml +0 -0
  23. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.github/workflows/python-build.yml +0 -0
  24. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.github/workflows/python-publish.yml +0 -0
  25. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.github/workflows/release.yml +0 -0
  26. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.github/workflows/report-slack.yml +0 -0
  27. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.gitignore +0 -0
  28. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.mcp.json +0 -0
  29. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.pre-commit-config.yaml +0 -0
  30. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/.python-version +0 -0
  31. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/AGENTS.md +0 -0
  32. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/CHANGELOG.md +0 -0
  33. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/CLAUDE.md +0 -0
  34. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/CODEOWNERS +0 -0
  35. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/LICENSE +0 -0
  36. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/README.md +0 -0
  37. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/build.sh +0 -0
  38. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/examples/CONTRIBUTING.md +0 -0
  39. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/examples/README.md +0 -0
  40. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  41. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  42. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  43. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  44. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/examples/external_state_margin_leg_base.py +0 -0
  45. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/examples/simple_aave_v3_supply_base.py +0 -0
  46. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/pytest.ini +0 -0
  47. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/scripts/commit-check.sh +0 -0
  48. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/scripts/crosschain_readiness.py +0 -0
  49. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/skills/ipor-deploy-vault/SKILL.md +0 -0
  50. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/__init__.py +0 -0
  51. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/about.py +0 -0
  52. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cache.py +0 -0
  53. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/chains.py +0 -0
  54. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/__init__.py +0 -0
  55. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  56. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/config_cmd.py +0 -0
  57. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/config_store.py +0 -0
  58. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/main.py +0 -0
  59. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/market_cmd.py +0 -0
  60. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/options.py +0 -0
  61. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  62. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/vault_health.py +0 -0
  63. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  64. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  65. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  66. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/config/__init__.py +0 -0
  67. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/config/roles.py +0 -0
  68. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/__init__.py +0 -0
  69. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/access.py +0 -0
  70. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/address.py +0 -0
  71. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/context.py +0 -0
  72. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/contract.py +0 -0
  73. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/erc20.py +0 -0
  74. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/external_state_executor.py +0 -0
  75. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/fee_manager.py +0 -0
  76. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/fuse_action.py +0 -0
  77. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/fusion_factory.py +0 -0
  78. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/logs.py +0 -0
  79. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/multicall.py +0 -0
  80. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/oracle.py +0 -0
  81. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/plasma_vault.py +0 -0
  82. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/rewards_manager.py +0 -0
  83. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/simulation.py +0 -0
  84. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  85. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/__init__.py +0 -0
  86. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  87. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  88. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  89. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  90. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  91. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  92. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  93. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/discovery.py +0 -0
  94. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/errors.py +0 -0
  95. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/lane.py +0 -0
  96. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/logs.py +0 -0
  97. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/messages.py +0 -0
  98. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/simulation.py +0 -0
  99. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  100. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  101. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  102. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  103. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/crosschain/transport.py +0 -0
  104. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/dependency_graph.py +0 -0
  105. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/entry_cli.py +0 -0
  106. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/entry_mcp.py +0 -0
  107. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/errors.py +0 -0
  108. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/explorer.py +0 -0
  109. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/field_docs.py +0 -0
  110. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/__init__.py +0 -0
  111. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  112. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/async_action.py +0 -0
  113. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/base.py +0 -0
  114. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  115. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
  116. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
  117. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
  118. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
  119. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/erc4626.py +0 -0
  120. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  121. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/events.py +0 -0
  122. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/external_state.py +0 -0
  123. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  124. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  125. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  126. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/hypercore.py +0 -0
  127. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  128. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/merkl.py +0 -0
  129. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/morpho.py +0 -0
  130. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  131. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  132. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/fuses/universal.py +0 -0
  133. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/guide/__init__.py +0 -0
  134. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/guide/architecture.md +0 -0
  135. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/guide/glossary.md +0 -0
  136. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/guide/invariants.md +0 -0
  137. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/guide/quickstart.md +0 -0
  138. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/market_ids.py +0 -0
  139. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/market_info.py +0 -0
  140. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/mcp/__init__.py +0 -0
  141. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/mcp/guide.py +0 -0
  142. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/mcp/models.py +0 -0
  143. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/py.typed +0 -0
  144. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/__init__.py +0 -0
  145. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/aave_v3.py +0 -0
  146. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/aave_v4.py +0 -0
  147. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/compound_v3.py +0 -0
  148. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/hypercore.py +0 -0
  149. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  150. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/lending_health.py +0 -0
  151. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/morpho.py +0 -0
  152. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  153. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/position_manager.py +0 -0
  154. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  155. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  156. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/readers/unpriceable.py +0 -0
  157. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/substrates.py +0 -0
  158. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/types.py +0 -0
  159. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/validation.py +0 -0
  160. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/vault_data.py +0 -0
  161. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/src/ipor_fusion/vault_info.py +0 -0
  162. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/UniswapV3UniversalRouter.py +0 -0
  163. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_crosschain.py +0 -0
  164. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_crosschain_lifecycle.py +0 -0
  165. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_crosschain_recovery.py +0 -0
  166. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_euler_v2.py +0 -0
  167. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_foundry.py +0 -0
  168. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_guide.py +0 -0
  169. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_hypercore_model.py +0 -0
  170. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_hypercore_shadow.py +0 -0
  171. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_multicall.py +0 -0
  172. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_simulate.py +0 -0
  173. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/_validation.py +0 -0
  174. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/addresses.py +0 -0
  175. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/conftest.py +0 -0
  176. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/constants.py +0 -0
  177. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  178. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/fixtures/hypercore_market55.json +0 -0
  179. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/fixtures/vault_info_golden/clean.json +0 -0
  180. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  181. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/fixtures/vault_info_golden/rich.json +0 -0
  182. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  183. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_about.py +0 -0
  184. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_access_manager.py +0 -0
  185. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cache.py +0 -0
  186. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_chains.py +0 -0
  187. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_changelog.py +0 -0
  188. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_commands.py +0 -0
  189. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_config_store.py +0 -0
  190. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_helpers.py +0 -0
  191. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_hypercore.py +0 -0
  192. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_lending_health.py +0 -0
  193. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_oracle_mapping.py +0 -0
  194. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_pending_requests_scan.py +0 -0
  195. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_role_accounts.py +0 -0
  196. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_vault_info_golden.py +0 -0
  197. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_vault_predates_getters.py +0 -0
  198. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_cli_vault_validate.py +0 -0
  199. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_contract_call.py +0 -0
  200. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_contract_encoder.py +0 -0
  201. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_ccip_events.py +0 -0
  202. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_ccip_indexer.py +0 -0
  203. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_ccip_quote.py +0 -0
  204. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_chainlink.py +0 -0
  205. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_contracts.py +0 -0
  206. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_encoding.py +0 -0
  207. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_errors.py +0 -0
  208. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_layering.py +0 -0
  209. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_readiness.py +0 -0
  210. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_readiness_report.py +0 -0
  211. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_crosschain_transport.py +0 -0
  212. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_erc20.py +0 -0
  213. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_examples_composed_hypercore.py +0 -0
  214. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_examples_crosschain.py +0 -0
  215. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  216. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_examples_vaults.py +0 -0
  217. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_explorer.py +0 -0
  218. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_external_state_events.py +0 -0
  219. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_external_state_executor.py +0 -0
  220. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_fee_manager.py +0 -0
  221. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_fuse_encoding.py +0 -0
  222. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_guide.py +0 -0
  223. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_hypercore_deployment.py +0 -0
  224. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_hypercore_events.py +0 -0
  225. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_hypercore_flow.py +0 -0
  226. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_hypercore_live.py +0 -0
  227. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_hypercore_outcome.py +0 -0
  228. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_hypercore_readers.py +0 -0
  229. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_hypercore_simulation.py +0 -0
  230. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_logs.py +0 -0
  231. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_mcp_hypercore.py +0 -0
  232. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_mcp_models.py +0 -0
  233. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_mcp_vault_validate.py +0 -0
  234. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_multicall.py +0 -0
  235. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_oracle.py +0 -0
  236. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_oracle_mapping.py +0 -0
  237. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_plasma_vault.py +0 -0
  238. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_reader_encoding.py +0 -0
  239. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_readiness_selection.py +0 -0
  240. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_revert_decoding.py +0 -0
  241. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_rewards_manager.py +0 -0
  242. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_roles.py +0 -0
  243. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_sdk_layering.py +0 -0
  244. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  245. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  246. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  247. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_crosschain_canary.py +0 -0
  248. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_crosschain_hypercore.py +0 -0
  249. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  250. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  251. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  252. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  253. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  254. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  255. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  256. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_examples_arbitrum.py +0 -0
  257. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_examples_base.py +0 -0
  258. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_examples_crosschain.py +0 -0
  259. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_external_state_executor_base.py +0 -0
  260. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  261. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  262. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  263. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_morpho_blue.py +0 -0
  264. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_pilot_v3.py +0 -0
  265. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  266. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  267. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  268. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  269. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_readers_integration.py +0 -0
  270. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_release_funds.py +0 -0
  271. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_swap_on_base.py +0 -0
  272. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  273. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_solidity_compiler.py +0 -0
  274. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_solidity_mirrors.py +0 -0
  275. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_substrates.py +0 -0
  276. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_types_and_events.py +0 -0
  277. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_unpriceable_tokens.py +0 -0
  278. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_validation.py +0 -0
  279. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_vault_dep_graph.py +0 -0
  280. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_vault_info_onchain_base.py +0 -0
  281. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  282. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_vault_simulator_blocks.py +0 -0
  283. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_web3_context.py +0 -0
  284. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/tests/test_withdraw_manager.py +0 -0
  285. {ipor_fusion-4.0.0.dev39 → ipor_fusion-4.0.0.dev43}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev39
3
+ Version: 4.0.0.dev43
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.dev39"
3
+ version = "4.0.0.dev43"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -131,7 +131,7 @@ def add(address: ChecksumAddress, label: str | None, chain_id: ChainId | None) -
131
131
 
132
132
  @vault.command("remove")
133
133
  @click.argument("address", type=ADDRESS)
134
- def remove(address: str) -> None:
134
+ def remove(address: ChecksumAddress) -> None:
135
135
  """Remove a saved vault."""
136
136
  cfg = load_config()
137
137
  before = len(cfg.vaults)
@@ -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]:
@@ -437,6 +437,7 @@ def vault_add(
437
437
  label: Human-readable label (fetched from on-chain name() if empty).
438
438
  chain_id: Chain ID (auto-detected when only one provider configured).
439
439
  """
440
+ checksum = Web3.to_checksum_address(address)
440
441
  cfg = load_config()
441
442
 
442
443
  if not chain_id:
@@ -451,26 +452,21 @@ def vault_add(
451
452
  if not label:
452
453
  provider_url = _resolve_provider(cfg, chain_id)
453
454
  ctx = Web3Context.from_url(provider_url)
454
- checksum = Web3.to_checksum_address(address)
455
455
  try:
456
456
  label = PlasmaVault(ctx, checksum).name().call()
457
457
  except Exception:
458
458
  label = checksum
459
459
 
460
460
  for vault_entry in cfg.vaults:
461
- if vault_entry.address.lower() == address.lower():
461
+ if vault_entry.address.lower() == checksum.lower():
462
462
  vault_entry.label = label
463
463
  vault_entry.chain_id = chain_id
464
464
  save_config(cfg)
465
- return ActionResult(message=f"Vault {label} ({address}) updated.")
465
+ return ActionResult(message=f"Vault {label} ({checksum}) updated.")
466
466
 
467
- cfg.vaults.append(
468
- VaultEntry(
469
- address=Web3.to_checksum_address(address), label=label, chain_id=chain_id
470
- )
471
- )
467
+ cfg.vaults.append(VaultEntry(address=checksum, label=label, chain_id=chain_id))
472
468
  save_config(cfg)
473
- return ActionResult(message=f"Vault {label} ({address}) added.")
469
+ return ActionResult(message=f"Vault {label} ({checksum}) added.")
474
470
 
475
471
 
476
472
  @mcp.tool()
@@ -635,7 +631,7 @@ def market_meta_morpho(
635
631
  chain_id: EVM chain ID.
636
632
  """
637
633
  try:
638
- info = fetch_vault(vault_address, chain_id)
634
+ info = fetch_vault(Web3.to_checksum_address(vault_address), chain_id)
639
635
  except MorphoApiError as exc:
640
636
  raise ValueError(str(exc)) from exc
641
637
  payload = build_meta_morpho_json(info, chain_id)
@@ -91,7 +91,7 @@ class VaultFlowCap:
91
91
  fee_wei: int
92
92
  max_in: int
93
93
  max_out: int
94
- admin: str | None # the address that can configure these flow caps
94
+ admin: ChecksumAddress | None # the address that can configure these flow caps
95
95
 
96
96
 
97
97
  @dataclass(slots=True)
@@ -106,7 +106,7 @@ class VaultAllocation:
106
106
  total_assets: int
107
107
  supply_assets: int # currently allocated to *this* market
108
108
  supply_cap: int # cap configured by curator for *this* market
109
- allocators: list[str] # addresses with ALLOCATOR_ROLE on the vault
109
+ allocators: list[ChecksumAddress] # addresses with ALLOCATOR_ROLE on the vault
110
110
  flow_cap: VaultFlowCap | None # None if PublicAllocator not configured
111
111
 
112
112
 
@@ -122,8 +122,8 @@ class MorphoApiMarket:
122
122
  collateral_token: ChecksumAddress
123
123
  collateral_symbol: str
124
124
  collateral_decimals: int
125
- oracle: str
126
- irm: str
125
+ oracle: ChecksumAddress
126
+ irm: ChecksumAddress
127
127
  supply_assets: int
128
128
  borrow_assets: int
129
129
  liquidity_assets: int
@@ -190,8 +190,8 @@ def _parse_market(raw: dict) -> MorphoApiMarket:
190
190
  collateral_token=_api_address(raw["collateralAsset"]["address"]),
191
191
  collateral_symbol=raw["collateralAsset"]["symbol"],
192
192
  collateral_decimals=int(raw["collateralAsset"]["decimals"]),
193
- oracle=raw["oracle"]["address"],
194
- irm=raw["irmAddress"],
193
+ oracle=_api_address(raw["oracle"]["address"]),
194
+ irm=_api_address(raw["irmAddress"]),
195
195
  supply_assets=int(state.get("supplyAssets") or 0),
196
196
  borrow_assets=int(state.get("borrowAssets") or 0),
197
197
  liquidity_assets=int(state.get("liquidityAssets") or 0),
@@ -283,8 +283,8 @@ class VaultV2Cap:
283
283
  market_id: MorphoBlueMarketId | None
284
284
  loan_token: ChecksumAddress | None
285
285
  collateral_token: ChecksumAddress | None
286
- oracle: str | None
287
- irm: str | None
286
+ oracle: ChecksumAddress | None
287
+ irm: ChecksumAddress | None
288
288
  lltv: int | None
289
289
 
290
290
  @property
@@ -378,7 +378,7 @@ class VaultV1Info:
378
378
 
379
379
 
380
380
  def fetch_vault(
381
- address: str, chain_id: ChainId, *, timeout: float = 10.0
381
+ address: ChecksumAddress, chain_id: ChainId, *, timeout: float = 10.0
382
382
  ) -> VaultV2Info | VaultV1Info:
383
383
  """Fetch a Morpho vault by address. Tries V2 first, falls back to V1.
384
384
 
@@ -625,7 +625,7 @@ def _parse_vault_v1(raw: dict) -> VaultV1Info:
625
625
  fee_wei=int(pac.get("fee") or 0),
626
626
  max_in=0,
627
627
  max_out=0,
628
- admin=pac.get("admin"),
628
+ admin=_api_optional_address(pac.get("admin")),
629
629
  )
630
630
  for fc in pac.get("flowCaps") or []:
631
631
  if raw_mid := (fc.get("market") or {}).get("marketId"):
@@ -687,10 +687,12 @@ def _parse_vault(raw: dict, market_id: MorphoBlueMarketId) -> VaultAllocation:
687
687
  fee_wei=int(pac.get("fee") or 0),
688
688
  max_in=int(flow_for_market.get("maxIn") or 0),
689
689
  max_out=int(flow_for_market.get("maxOut") or 0),
690
- admin=pac.get("admin"),
690
+ admin=_api_optional_address(pac.get("admin")),
691
691
  )
692
692
  allocators = [
693
- a["address"] for a in (raw.get("allocators") or []) if a.get("address")
693
+ _api_address(a["address"])
694
+ for a in (raw.get("allocators") or [])
695
+ if a.get("address")
694
696
  ]
695
697
  return VaultAllocation(
696
698
  vault_address=_api_address(raw["address"]),
@@ -4,6 +4,7 @@
4
4
  import json
5
5
  from unittest.mock import MagicMock, patch
6
6
 
7
+ import pytest
7
8
  from click.testing import CliRunner
8
9
  from eth_abi import encode
9
10
  from web3 import Web3
@@ -18,6 +19,7 @@ from ipor_fusion.morpho_api import (
18
19
  MorphoApiMarket,
19
20
  VaultAllocation,
20
21
  VaultFlowCap,
22
+ _parse_market,
21
23
  fetch_market,
22
24
  fetch_vault,
23
25
  )
@@ -786,3 +788,31 @@ class TestMetaMorphoCommand:
786
788
  assert len(market_caps) == 1
787
789
  assert market_caps[0]["loan_token"] == LOAN_TOKEN
788
790
  assert market_caps[0]["lltv"] == str(915 * 10**15)
791
+
792
+
793
+ class TestMorphoApiAddresses:
794
+ def _raw(self, oracle: str) -> dict:
795
+ return {
796
+ "marketId": "0x" + MARKET_ID,
797
+ "lltv": "915000000000000000",
798
+ "loanAsset": {"address": LOAN_TOKEN.lower(), "symbol": "U", "decimals": 6},
799
+ "collateralAsset": {
800
+ "address": COLLATERAL_TOKEN,
801
+ "symbol": "C",
802
+ "decimals": 18,
803
+ },
804
+ "oracle": {"address": oracle},
805
+ "irmAddress": IRM.lower(),
806
+ "state": {},
807
+ "supplyingVaults": [],
808
+ }
809
+
810
+ def test_addresses_are_checksummed(self):
811
+ market = _parse_market(self._raw(ORACLE.lower()))
812
+ assert market.loan_token == LOAN_TOKEN
813
+ assert market.oracle == ORACLE
814
+ assert market.irm == IRM
815
+
816
+ def test_a_malformed_address_is_an_api_error(self):
817
+ with pytest.raises(MorphoApiError):
818
+ _parse_market(self._raw("invalid oracle"))
@@ -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
@@ -89,12 +89,15 @@ def _config_with_keyed_provider():
89
89
  return FusionConfig(providers={"1": "https://rpc.example.com/v2/SECRETKEY"})
90
90
 
91
91
 
92
- def _config_with_vault():
92
+ _SAVED_VAULT = "0x43ee0243ea8cf02f7087d8b16c8d2007cc9c7ca2"
93
+
94
+
95
+ def _config_with_vault(address: str = _SAVED_VAULT):
93
96
  from ipor_fusion.cli.config_store import FusionConfig, VaultEntry
94
97
 
95
98
  return FusionConfig(
96
99
  providers={"1": "https://rpc.example.com"},
97
- vaults=[VaultEntry(address="0xABC", label="Test", chain_id=1)],
100
+ vaults=[VaultEntry(address=address, label="Test", chain_id=1)],
98
101
  )
99
102
 
100
103
 
@@ -264,7 +267,7 @@ class TestConfigShow:
264
267
  def test_config_with_vault(self, _):
265
268
  result = config_show()
266
269
  assert len(result.vaults) == 1
267
- assert result.vaults[0].address == "0xABC"
270
+ assert result.vaults[0].address == Web3.to_checksum_address(_SAVED_VAULT)
268
271
 
269
272
  @patch(
270
273
  "ipor_fusion.mcp.server.load_config",
@@ -319,7 +322,7 @@ class TestVaultList:
319
322
  def test_with_vault(self, _):
320
323
  result = vault_list()
321
324
  assert len(result) == 1
322
- assert result[0].address == "0xABC"
325
+ assert result[0].address == Web3.to_checksum_address(_SAVED_VAULT)
323
326
  assert result[0].label == "Test"
324
327
  assert result[0].chain_id == 1
325
328
 
@@ -348,13 +351,23 @@ class TestVaultAdd:
348
351
  with pytest.raises(ValueError):
349
352
  vault_add(address="0xDEF", label="New Vault", chain_id=1)
350
353
 
354
+ @patch("ipor_fusion.mcp.server.save_config")
355
+ @patch(
356
+ "ipor_fusion.mcp.server.load_config",
357
+ return_value=_config_with_vault("0xABC"),
358
+ )
359
+ def test_a_malformed_saved_entry_is_not_updated(self, mock_load, mock_save):
360
+ with pytest.raises(ValueError):
361
+ vault_add(address="0xABC", label="Updated", chain_id=1)
362
+ mock_save.assert_not_called()
363
+
351
364
  @patch("ipor_fusion.mcp.server.save_config")
352
365
  @patch(
353
366
  "ipor_fusion.mcp.server.load_config",
354
367
  return_value=_config_with_vault(),
355
368
  )
356
369
  def test_update_existing(self, mock_load, mock_save):
357
- result = vault_add(address="0xABC", label="Updated", chain_id=1)
370
+ result = vault_add(address=_SAVED_VAULT, label="Updated", chain_id=1)
358
371
  assert "updated" in result.message.lower()
359
372
  saved_cfg = mock_save.call_args[0][0]
360
373
  assert len(saved_cfg.vaults) == 1
@@ -368,7 +381,7 @@ class TestVaultRemove:
368
381
  return_value=_config_with_vault(),
369
382
  )
370
383
  def test_remove_existing(self, mock_load, mock_save):
371
- result = vault_remove(address="0xABC")
384
+ result = vault_remove(address=_SAVED_VAULT)
372
385
  assert "removed" in result.message.lower()
373
386
  saved_cfg = mock_save.call_args[0][0]
374
387
  assert len(saved_cfg.vaults) == 0
@@ -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
+ )