ipor-fusion 4.0.0.dev83__tar.gz → 4.0.0.dev88__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 (296) hide show
  1. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/AGENTS.md +8 -4
  2. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/MIGRATION_4.md +87 -10
  3. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/PKG-INFO +1 -1
  4. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/examples/advanced_euler_v2_credit_market_base.py +2 -2
  5. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/examples/advanced_euler_v2_looping_arbitrum.py +2 -2
  6. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/examples/external_state_margin_leg_base.py +4 -4
  7. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/examples/simple_aave_v3_supply_base.py +2 -2
  8. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/pyproject.toml +1 -1
  9. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/__init__.py +8 -0
  10. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/access.py +3 -8
  11. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/contract.py +14 -30
  12. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/external_state_executor.py +76 -48
  13. ipor_fusion-4.0.0.dev83/src/ipor_fusion/fuses/base.py → ipor_fusion-4.0.0.dev88/src/ipor_fusion/core/fuse.py +17 -8
  14. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/multicall.py +6 -4
  15. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/oracle.py +2 -2
  16. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/plasma_vault.py +5 -9
  17. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/simulation.py +9 -6
  18. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/withdraw_manager.py +4 -4
  19. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/ccip/chainlink.py +5 -4
  20. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/ccip/indexer.py +51 -16
  21. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/ccip/quote.py +7 -4
  22. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/errors.py +51 -11
  23. ipor_fusion-4.0.0.dev88/src/ipor_fusion/fuses/base.py +17 -0
  24. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/crosschain/base.py +3 -6
  25. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/crosschain/ccip.py +3 -2
  26. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/crosschain/stargate.py +2 -1
  27. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/universal.py +0 -10
  28. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/mcp/server.py +1 -4
  29. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/aave_v3.py +5 -3
  30. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/hypercore.py +19 -8
  31. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/hypercore_outcome.py +4 -1
  32. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/morpho.py +3 -3
  33. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_crosschain_recovery.py +1 -1
  34. ipor_fusion-4.0.0.dev88/tests/test_contract_encoder.py +118 -0
  35. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_ccip_indexer.py +52 -0
  36. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_ccip_quote.py +6 -3
  37. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_chainlink.py +2 -1
  38. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_layering.py +13 -0
  39. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_error_hierarchy.py +8 -0
  40. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_external_state_executor.py +59 -55
  41. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_fee_manager.py +2 -2
  42. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_fuse_encoding.py +17 -0
  43. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_hypercore_flow.py +3 -3
  44. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_hypercore_outcome.py +12 -1
  45. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_hypercore_readers.py +17 -3
  46. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_mcp_server.py +1 -5
  47. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_plasma_vault.py +1 -1
  48. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_public_api.py +4 -0
  49. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_fusion_factory_clone_base.py +7 -23
  50. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_plasma_vault_setters_base.py +1 -4
  51. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_solidity_mirrors.py +1 -1
  52. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_vault_simulator_blocks.py +3 -3
  53. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/typed_client/consumer.py +8 -0
  54. ipor_fusion-4.0.0.dev83/tests/test_contract_encoder.py +0 -118
  55. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.claude-plugin/marketplace.json +0 -0
  56. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.claude-plugin/plugin.json +0 -0
  57. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.env.example +0 -0
  58. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.gemini/settings.json +0 -0
  59. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.git-blame-ignore-revs +0 -0
  60. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.github/workflows/cd.yml +0 -0
  61. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.github/workflows/ci.yml +0 -0
  62. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.github/workflows/commit-lint.yml +0 -0
  63. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.github/workflows/notify-slack.yml +0 -0
  64. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.github/workflows/python-build.yml +0 -0
  65. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.github/workflows/python-publish.yml +0 -0
  66. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.github/workflows/release.yml +0 -0
  67. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.github/workflows/report-slack.yml +0 -0
  68. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.gitignore +0 -0
  69. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.mcp.json +0 -0
  70. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.pre-commit-config.yaml +0 -0
  71. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/.python-version +0 -0
  72. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/CHANGELOG.md +0 -0
  73. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/CLAUDE.md +0 -0
  74. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/CODEOWNERS +0 -0
  75. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/LICENSE +0 -0
  76. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/README.md +0 -0
  77. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/build.sh +0 -0
  78. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/examples/CONTRIBUTING.md +0 -0
  79. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/examples/README.md +0 -0
  80. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  81. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  82. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/pytest.ini +0 -0
  83. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/scripts/commit-check.sh +0 -0
  84. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/scripts/crosschain_readiness.py +0 -0
  85. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/skills/ipor-deploy-vault/SKILL.md +0 -0
  86. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/about.py +0 -0
  87. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cache.py +0 -0
  88. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/chains.py +0 -0
  89. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/__init__.py +0 -0
  90. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  91. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/config_cmd.py +0 -0
  92. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/config_store.py +0 -0
  93. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/main.py +0 -0
  94. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/market_cmd.py +0 -0
  95. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/options.py +0 -0
  96. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  97. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  98. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/vault_health.py +0 -0
  99. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  100. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  101. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  102. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/config/__init__.py +0 -0
  103. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/config/roles.py +0 -0
  104. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/__init__.py +0 -0
  105. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/address.py +0 -0
  106. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/context.py +0 -0
  107. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/erc20.py +0 -0
  108. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/fee_manager.py +0 -0
  109. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/fuse_action.py +0 -0
  110. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/fusion_factory.py +0 -0
  111. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/logs.py +0 -0
  112. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/core/rewards_manager.py +0 -0
  113. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/__init__.py +0 -0
  114. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  115. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  116. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  117. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  118. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  119. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  120. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/contracts.py +0 -0
  121. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/discovery.py +0 -0
  122. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/errors.py +0 -0
  123. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/lane.py +0 -0
  124. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/logs.py +0 -0
  125. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/messages.py +0 -0
  126. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/simulation.py +0 -0
  127. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  128. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  129. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  130. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  131. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  132. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/crosschain/transport.py +0 -0
  133. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/dependency_graph.py +0 -0
  134. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/entry_cli.py +0 -0
  135. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/entry_mcp.py +0 -0
  136. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/explorer.py +0 -0
  137. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/field_docs.py +0 -0
  138. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/__init__.py +0 -0
  139. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  140. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/async_action.py +0 -0
  141. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  142. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
  143. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/erc4626.py +0 -0
  144. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  145. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/events.py +0 -0
  146. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/external_state.py +0 -0
  147. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  148. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  149. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  150. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/hypercore.py +0 -0
  151. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  152. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/merkl.py +0 -0
  153. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/morpho.py +0 -0
  154. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  155. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  156. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/guide/__init__.py +0 -0
  157. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/guide/architecture.md +0 -0
  158. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/guide/glossary.md +0 -0
  159. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/guide/invariants.md +0 -0
  160. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/guide/quickstart.md +0 -0
  161. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/market_ids.py +0 -0
  162. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/market_info.py +0 -0
  163. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/mcp/__init__.py +0 -0
  164. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/mcp/guide.py +0 -0
  165. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/mcp/models.py +0 -0
  166. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/morpho_api.py +0 -0
  167. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/py.typed +0 -0
  168. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/__init__.py +0 -0
  169. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/aave_v4.py +0 -0
  170. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/compound_v3.py +0 -0
  171. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/lending_health.py +0 -0
  172. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  173. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  174. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  175. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/position_manager.py +0 -0
  176. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  177. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  178. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/readers/unpriceable.py +0 -0
  179. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/substrates.py +0 -0
  180. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/types.py +0 -0
  181. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/validation.py +0 -0
  182. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/vault_data.py +0 -0
  183. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/vault_info.py +0 -0
  184. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/vault_managers.py +0 -0
  185. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/vault_positions.py +0 -0
  186. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/src/ipor_fusion/vault_reads.py +0 -0
  187. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/UniswapV3UniversalRouter.py +0 -0
  188. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_crosschain.py +0 -0
  189. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_crosschain_lifecycle.py +0 -0
  190. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_euler_v2.py +0 -0
  191. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_foundry.py +0 -0
  192. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_guide.py +0 -0
  193. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_hypercore_model.py +0 -0
  194. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_hypercore_shadow.py +0 -0
  195. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_multicall.py +0 -0
  196. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_simulate.py +0 -0
  197. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/_validation.py +0 -0
  198. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/addresses.py +0 -0
  199. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/conftest.py +0 -0
  200. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/constants.py +0 -0
  201. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  202. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/fixtures/hypercore_market55.json +0 -0
  203. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/fixtures/vault_info_golden/clean.json +0 -0
  204. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  205. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/fixtures/vault_info_golden/rich.json +0 -0
  206. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  207. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_about.py +0 -0
  208. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_access_manager.py +0 -0
  209. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cache.py +0 -0
  210. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_chains.py +0 -0
  211. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_changelog.py +0 -0
  212. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_commands.py +0 -0
  213. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_config_store.py +0 -0
  214. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_helpers.py +0 -0
  215. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_hypercore.py +0 -0
  216. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_lending_health.py +0 -0
  217. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_market_morpho_blue.py +0 -0
  218. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_oracle_mapping.py +0 -0
  219. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_pending_requests_scan.py +0 -0
  220. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_role_accounts.py +0 -0
  221. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_vault_info_golden.py +0 -0
  222. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_vault_predates_getters.py +0 -0
  223. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_cli_vault_validate.py +0 -0
  224. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_contract_call.py +0 -0
  225. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_ccip_events.py +0 -0
  226. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_contracts.py +0 -0
  227. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_encoding.py +0 -0
  228. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_errors.py +0 -0
  229. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_lane.py +0 -0
  230. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_readiness.py +0 -0
  231. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_readiness_report.py +0 -0
  232. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_crosschain_transport.py +0 -0
  233. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_erc20.py +0 -0
  234. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_examples_composed_hypercore.py +0 -0
  235. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_examples_crosschain.py +0 -0
  236. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  237. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_examples_vaults.py +0 -0
  238. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_explorer.py +0 -0
  239. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_external_state_events.py +0 -0
  240. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_fusion_instance.py +0 -0
  241. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_guide.py +0 -0
  242. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_hypercore_deployment.py +0 -0
  243. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_hypercore_events.py +0 -0
  244. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_hypercore_live.py +0 -0
  245. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_hypercore_simulation.py +0 -0
  246. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_logs.py +0 -0
  247. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_mcp_hypercore.py +0 -0
  248. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_mcp_models.py +0 -0
  249. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_mcp_vault_validate.py +0 -0
  250. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_multicall.py +0 -0
  251. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_oracle.py +0 -0
  252. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_oracle_mapping.py +0 -0
  253. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_reader_encoding.py +0 -0
  254. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_readiness_selection.py +0 -0
  255. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_revert_decoding.py +0 -0
  256. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_rewards_manager.py +0 -0
  257. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_roles.py +0 -0
  258. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_sdk_layering.py +0 -0
  259. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  260. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  261. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  262. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_crosschain_canary.py +0 -0
  263. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_crosschain_hypercore.py +0 -0
  264. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  265. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  266. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  267. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  268. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  269. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  270. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  271. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_examples_arbitrum.py +0 -0
  272. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_examples_base.py +0 -0
  273. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_examples_crosschain.py +0 -0
  274. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_external_state_executor_base.py +0 -0
  275. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  276. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  277. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_morpho_blue.py +0 -0
  278. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_pilot_v3.py +0 -0
  279. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  280. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  281. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  282. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_readers_integration.py +0 -0
  283. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_release_funds.py +0 -0
  284. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_swap_on_base.py +0 -0
  285. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  286. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_solidity_compiler.py +0 -0
  287. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_substrates.py +0 -0
  288. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_types_and_events.py +0 -0
  289. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_unpriceable_tokens.py +0 -0
  290. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_validation.py +0 -0
  291. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_vault_dep_graph.py +0 -0
  292. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_vault_info_onchain_base.py +0 -0
  293. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  294. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_web3_context.py +0 -0
  295. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/tests/test_withdraw_manager.py +0 -0
  296. {ipor_fusion-4.0.0.dev83 → ipor_fusion-4.0.0.dev88}/uv.lock +0 -0
@@ -218,12 +218,14 @@ the lifecycle matrix follows that.
218
218
  `simulation` (`VaultSimulator`, eth_simulateV1, `erc20_balance_slot` and ERC-20 balance
219
219
  overrides), `oracle`, `fusion_factory`,
220
220
  `external_state_executor` (NAV marks for market 50), `erc20`, `fuse_action`
221
- (`FuseAction`), `address` (`_validate_not_zero_address`). `core` imports neither
221
+ (`FuseAction`), `fuse` (the `Fuse` base, `StakeFuse`, the shared validators and
222
+ substrate packers), `address` (`_validate_not_zero_address`). `core` imports neither
222
223
  `fuses` nor `crosschain` (`tests/test_sdk_layering.py`)
223
224
  - `fuses/` — per-protocol fuse encoders (aave_v3, async_action, compound_v3, crosschain/,
224
225
  erc4626, euler_v2, external_state, fluid_instadapp, gearbox_v3, merkl, morpho, ramses_v2,
225
- uniswap_v3, universal, `events.py`); `base.py` holds `Fuse` and the
226
- shared validators and re-exports `FuseAction` and `ZERO_ADDRESS` (from `types`)
226
+ uniswap_v3, universal, `events.py`); `base.py` re-exports the `core/fuse.py` base and validators,
227
+ `FuseAction` and `ZERO_ADDRESS` (from `types`). A fuse is a value: equality and
228
+ hash cover its class and every attribute it stores
227
229
  - `crosschain/` — crosschain Plasma Vaults (executor on the hub chain, dispatcher at
228
230
  the same address on each spoke chain), split like the contracts: shared `messages`
229
231
  (`Command`, `MsgType`, envelopes), `contracts` (wrapper bases), `transport`
@@ -276,7 +278,9 @@ or examples.
276
278
  - `Call[T]` (`core/contract.py`): every `ContractWrapper` method (one per
277
279
  Solidity function) returns a `Call` instead of executing. `.call()` runs
278
280
  `eth_call` and decodes to `T`; `.send()` signs and submits, returning a
279
- `TxReceipt`; `.calldata` hands the bytes to an external signer;
281
+ `TxReceipt`; `.calldata` hands the bytes to an external signer (a wrapper
282
+ built with `ctx=None`, e.g. `PlasmaVault(None, address)`, builds calldata
283
+ offline; there is no separate encoder factory);
280
284
  `.build_transaction()` / `Web3Context.estimate_gas` preview without signing.
281
285
  A `Call` without `output_types` (a write) raises on `.call()`. The same
282
286
  `Call` feeds `VaultSimulator.observe`, so reads, sends and simulations share
@@ -93,7 +93,7 @@ The Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient`
93
93
  `quote_ccip_native_fee(block=)` and `OracleMappingReader.asset_source_events` take a
94
94
  `BlockIdentifier`.
95
95
 
96
- **Address roles.** `PlasmaVault(ctx, address)`, `PlasmaVault.encoder(address)`,
96
+ **Address roles.** `PlasmaVault(ctx, address)` (`ctx` may be `None`, see below),
97
97
  `validate_vault`, `fetch_deployment_info`, the HyperCore, lending-health and
98
98
  oracle-mapping readers, `read_external_state`, `VaultSimulator(vault=)`,
99
99
  `CrosschainSimulator`, crosschain discovery, the hub-manager inputs
@@ -194,6 +194,35 @@ are `ProposalHash`; `ExternalStateEvent.transaction_hash` is a `TxHash`.
194
194
  except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
195
195
  `MarketId`. Calling an MCP tool function directly from Python takes `ChainId`.
196
196
 
197
+ **Offline wrappers.** `ContractWrapper.encoder()` (and the `PlasmaVault` /
198
+ `AccessManager` overrides) is gone. Every wrapper constructor takes
199
+ `ctx: Web3Context | None`, so build an offline wrapper with the normal
200
+ constructor and an address: `PlasmaVault(None, vault)`, `ERC20(None, token)`,
201
+ `FusionFactory(None, factory)`, `LegacyPriceOracleMiddleware(None, oracle,
202
+ Decimals(8))`. The address is required (there is no zero-address placeholder) and
203
+ subclass parameters stay required, so the wrapper is fully initialized:
204
+ `.encoder()` skipped the constructor, and a legacy oracle built that way failed to
205
+ decode `get_asset_price`. `.calldata` still needs no context; `.call()`, `.send()`,
206
+ `.build_transaction()` and `.estimate_gas()` raise `ValueError` until one is passed
207
+ to them, and a method that reads the chain itself (log scans such as
208
+ `get_balance_fuses` or `get_pending_requests`, `withdraw_manager_address`,
209
+ `Multicall3.aggregate` / `try_aggregate`, the Aave V3 and Morpho position reads)
210
+ raises `ValueError("Web3Context required: <wrapper> at <address> ...")` instead of
211
+ an `AttributeError` on `None`. `ExternalStateExecutor` still refuses the zero
212
+ address for its hash and event readers.
213
+
214
+ | 3.x | 4.0 |
215
+ |---|---|
216
+ | `PlasmaVault.encoder(vault)` | `PlasmaVault(None, vault)` |
217
+ | `ERC20.encoder()` (no address) | `ERC20(None, token)`: pass the real address |
218
+ | `Cls.__new__(Cls)` + `_ctx` / `_address` | `Cls(None, address)` |
219
+
220
+ **Fuse base.** `Fuse`, `StakeFuse` and the shared validators and substrate packers
221
+ live in `ipor_fusion.core.fuse`; `ipor_fusion.fuses.base`, `ipor_fusion.fuses` and
222
+ `ipor_fusion` re-export them, so existing imports keep working. Fuse equality and
223
+ hash now cover every attribute a fuse stores, not only its class and address (see
224
+ [Behavior changes](#behavior-changes)).
225
+
197
226
  **Moved, still importable.** `FuseAction` lives in `ipor_fusion.core.fuse_action` and
198
227
  `ZERO_ADDRESS` in `ipor_fusion.types`; both are re-exported from
199
228
  `ipor_fusion.fuses.base` and `FuseAction` from `ipor_fusion`. The CLI's internal
@@ -202,6 +231,12 @@ except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
202
231
 
203
232
  ## Behavior changes
204
233
 
234
+ - Two fuses are equal (and hash alike) only when they have the same class and the
235
+ same stored state. `StakeFuse` subclasses (`GearboxStakeFuse`,
236
+ `FluidInstadappStakingFuse`) with the same fuse address but different
237
+ `staking_address` were equal and collapsed in a set or dict key; they are now
238
+ distinct. A custom `Fuse` subclass is compared on its instance attributes, which
239
+ must be hashable; drop an `__eq__` / `__hash__` override that only added them.
205
240
  - The MCP `vault_info` document reports the vault address checksummed (the CLI
206
241
  already did).
207
242
  - `SubstrateInfo.address` is `None` instead of `""` for a substrate without an
@@ -238,15 +273,20 @@ except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
238
273
  ## Errors
239
274
 
240
275
  Every exception class the SDK defines now derives from `IporFusionError` and is
241
- importable from `ipor_fusion`; the builtin bases they had are kept. The rule the
242
- changes below apply is: a caller's mistake (a bad argument, or an operation the
243
- object was not built for) raises a plain `ValueError`; a chain, provider or remote
244
- API answering something the SDK cannot use raises an `IporFusionError` subclass.
245
- 4.0 applies it to the paths listed here, not yet everywhere: the HyperCore NAV
246
- reader (`read_hypercore_nav`) still raises `ValueError` for a failed precompile read,
247
- a missing oracle price or an unsupported dex configuration,
248
- `fetch_verifier_result` can still raise `KeyError` or `ValueError` on a malformed
249
- indexer answer, and `erc20_balance_slot` raises `ValueError` when no slot answers.
276
+ importable from `ipor_fusion`; the builtin bases they had are kept. One rule holds
277
+ across the whole SDK: a caller's mistake (a bad argument, or an operation the object
278
+ was not built for) raises a plain `ValueError` (`TypeError` for an argument of the
279
+ wrong type, `KeyError` for an unknown name in a mapping-style lookup such as
280
+ `ipor_fusion.guide.guide_text`); a chain, provider or remote API answering something the
281
+ SDK cannot use raises an `IporFusionError` subclass. Except for
282
+ `CrosschainDiscoveryError` (discovery) and `SimulationError` (`quote_ccip_native_fee`),
283
+ every subclass that replaces a `ValueError` raise also derives from `ValueError`, so
284
+ `except ValueError` keeps catching it; catch the subclass to tell a chain fact from a
285
+ bad argument. Receipts,
286
+ logs and transports passed in that do not contain what the call needs (a clone
287
+ receipt without its events, logs without the lane's `BalanceProposed`, a CCIP
288
+ message to a chain the transport does not configure) stay `ValueError`: the input
289
+ is the wrong one.
250
290
 
251
291
  - `MorphoApiError` (still a `RuntimeError`), `ExternalStateEventDecodeError` (still a
252
292
  `ValueError`) and `VerifierResultUnavailable` (still a `LookupError`) now derive
@@ -274,3 +314,40 @@ indexer answer, and `erc20_balance_slot` raises `ValueError` when no slot answer
274
314
  - The Morpho API client raises `MorphoApiError` for every response it cannot read and
275
315
  for any `OSError` or `http.client.HTTPException` while fetching (it caught only
276
316
  `URLError` and `TimeoutError` before).
317
+ - The MCP `market_meta_morpho` tool lets `MorphoApiError` propagate instead of
318
+ re-raising it as `ValueError`. MCP clients see the same tool error message; only a
319
+ direct Python call of the tool function sees the new type.
320
+ - `read_hypercore_nav` (and `read_hypercore_vault_state`, which calls it) raises the new
321
+ `HyperCoreValuationError` (still a `ValueError`) for a failed `spotBalance`,
322
+ `tokenInfo` or `accountMarginSummary` precompile read, a granted spot token the
323
+ oracle does not price, a price overflow and an unsupported HIP-3 dex bitmap;
324
+ `HyperCoreNav.balance_wad` raises it for a negative NAV.
325
+ - `read_hypercore_transfer_snapshot` raises `UnsupportedChainError` (still a
326
+ `ValueError`) on a chain with no known `HyperCorePendingReader` when none is passed.
327
+ - `fetch_verifier_result` no longer leaks `KeyError`, `TypeError` or `ValueError` from
328
+ an indexer answer it cannot read (not JSON, a missing or mistyped field, invalid hex
329
+ or address, an empty or non-array CCV list, or a result for a different message id,
330
+ both now checked): that
331
+ indexer is skipped like an unreachable one, and when none serves a readable result
332
+ it raises `VerifierResultUnavailable` naming each indexer's reason. A `message_id`
333
+ that is not 32 bytes is still a `ValueError`.
334
+ - `erc20_balance_slot` raises the new `BalanceSlotNotFoundError` (still a
335
+ `ValueError`) when no slot up to `max_slot` answers as the balances mapping; a
336
+ negative `max_slot` is a `ValueError` with its own message.
337
+ - The external-state market raises the new `ExternalStateError` (still a
338
+ `ValueError`): `read_external_state` for a storage word longer than 32 bytes, an
339
+ executor word with bits above the address or a pause word other than 0 or 1;
340
+ `ExternalStateExecutor.for_vault` when no executor is deployed and, instead of
341
+ eth_abi's `NonEmptyPaddingBytes`, for a slot that is not an address;
342
+ `parse_balance_proposed` / `find_balance_proposed` for one of the executor's own
343
+ `BalanceProposed` logs whose data is missing or malformed (including data that is
344
+ not hex at all, which raised `TypeError`) or whose hash is not the hash of its own
345
+ fields; and `mark_nav` when the propose receipt it fetched has no such log or
346
+ records another value or proposer. A log that is not the event or not from the
347
+ executor, and `find_balance_proposed` over logs that hold no match, are still
348
+ `ValueError`.
349
+ - `ccip_token_lane` raises the new `UnsupportedContractVersionError` (still a
350
+ `ValueError`) for an OnRamp version whose layout is not verified.
351
+ - `quote_ccip_native_fee` raises `SimulationError` (label `"quote"`, with
352
+ `revert_reason`) instead of `ValueError` when the call reverts before the CCIP fee
353
+ check. A call that succeeds without sending a message is still a `ValueError`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev83
3
+ Version: 4.0.0.dev88
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
@@ -261,12 +261,12 @@ def clone_args() -> dict:
261
261
  def unsigned_clone_calldata() -> bytes:
262
262
  """Unsigned creation calldata (selector + ABI-encoded args) for the deploy.
263
263
 
264
- Built with a ctx-less encoder, so it needs no provider. Hand these bytes to
264
+ Built on a wrapper with ctx=None, so it needs no provider. Hand these bytes to
265
265
  an external signer / multisig instead of calling ``.send()``. NOTE: the owner
266
266
  encoded here is the simulation ``OWNER`` (a throwaway EOA) -- set your real
267
267
  owner in ``clone_args()`` before deploying, or the vault is owned by it.
268
268
  """
269
- return FusionFactory.encoder(BASE_FUSION_FACTORY).clone(**clone_args()).calldata
269
+ return FusionFactory(None, BASE_FUSION_FACTORY).clone(**clone_args()).calldata
270
270
 
271
271
 
272
272
  def market_substrates() -> list[bytes]:
@@ -208,7 +208,7 @@ def clone_args() -> dict:
208
208
 
209
209
 
210
210
  def unsigned_clone_calldata() -> bytes:
211
- return FusionFactory.encoder(ARBITRUM_FUSION_FACTORY).clone(**clone_args()).calldata
211
+ return FusionFactory(None, ARBITRUM_FUSION_FACTORY).clone(**clone_args()).calldata
212
212
 
213
213
 
214
214
  def euler_market_substrates() -> list[bytes]:
@@ -260,7 +260,7 @@ def enable_euler_actions() -> list[FuseAction]:
260
260
  def build_swap_action() -> FuseAction:
261
261
  """Swap the borrowed USDC through Uniswap V3 and require flash repayment."""
262
262
  transfer_data = (
263
- ERC20.encoder(ARBITRUM_USDC)
263
+ ERC20(None, ARBITRUM_USDC)
264
264
  .transfer(UNISWAP_UNIVERSAL_ROUTER, BORROW_USDC)
265
265
  .calldata
266
266
  )
@@ -438,12 +438,12 @@ def clone_args() -> dict:
438
438
  def unsigned_clone_calldata() -> bytes:
439
439
  """Unsigned creation calldata (selector + ABI-encoded args) for the deploy.
440
440
 
441
- Built with a ctx-less encoder, so it needs no provider. Hand these bytes to
441
+ Built on a wrapper with ctx=None, so it needs no provider. Hand these bytes to
442
442
  an external signer / multisig instead of calling ``.send()``. NOTE: the owner
443
443
  encoded here is the simulation ``OWNER`` (a throwaway EOA) -- set your real
444
444
  owner in ``clone_args()`` before deploying, or the vault is owned by it.
445
445
  """
446
- return FusionFactory.encoder(BASE_FUSION_FACTORY).clone(**clone_args()).calldata
446
+ return FusionFactory(None, BASE_FUSION_FACTORY).clone(**clone_args()).calldata
447
447
 
448
448
 
449
449
  def market_substrates() -> list[bytes]:
@@ -534,9 +534,9 @@ def market_substrates() -> list[bytes]:
534
534
  def venue_transfer_calldata(amount: Amount) -> bytes:
535
535
  """``USDC.transfer(VENUE_DEPOSIT_ADDRESS, amount)`` calldata, run from the executor.
536
536
 
537
- Encoded with a ctx-less ERC20 wrapper -- pure bytes, no chain access.
537
+ Encoded with an ERC20 wrapper built with ctx=None -- pure bytes, no chain access.
538
538
  """
539
- return ERC20.encoder(BASE_USDC).transfer(VENUE_DEPOSIT_ADDRESS, amount).calldata
539
+ return ERC20(None, BASE_USDC).transfer(VENUE_DEPOSIT_ADDRESS, amount).calldata
540
540
 
541
541
 
542
542
  def build_enter_action(amount: Amount) -> FuseAction:
@@ -201,12 +201,12 @@ def clone_args() -> dict:
201
201
  def unsigned_clone_calldata() -> bytes:
202
202
  """Unsigned creation calldata (selector + ABI-encoded args) for the deploy.
203
203
 
204
- Built with a ctx-less encoder, so it needs no provider. Hand these bytes to
204
+ Built on a wrapper with ctx=None, so it needs no provider. Hand these bytes to
205
205
  an external signer / multisig instead of calling ``.send()``. NOTE: the owner
206
206
  encoded here is the simulation ``OWNER`` (a throwaway EOA) -- set your real
207
207
  owner in ``clone_args()`` before deploying, or the vault is owned by it.
208
208
  """
209
- return FusionFactory.encoder(BASE_FUSION_FACTORY).clone(**clone_args()).calldata
209
+ return FusionFactory(None, BASE_FUSION_FACTORY).clone(**clone_args()).calldata
210
210
 
211
211
 
212
212
  def build_supply_action(amount: Amount) -> FuseAction:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev83"
3
+ version = "4.0.0.dev88"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -173,9 +173,12 @@ from ipor_fusion.dependency_graph import (
173
173
  )
174
174
  from ipor_fusion.errors import (
175
175
  CUSTOM_ERRORS,
176
+ BalanceSlotNotFoundError,
176
177
  ContractNotFoundError,
177
178
  CrosschainDiscoveryError,
178
179
  EmptyCallResultError,
180
+ ExternalStateError,
181
+ HyperCoreValuationError,
179
182
  IporFusionError,
180
183
  LogScanError,
181
184
  LogSourceUnavailable,
@@ -185,6 +188,7 @@ from ipor_fusion.errors import (
185
188
  SimulationRunError,
186
189
  TransactionError,
187
190
  UnsupportedChainError,
191
+ UnsupportedContractVersionError,
188
192
  UnsupportedVaultVersionError,
189
193
  decode_custom_error,
190
194
  get_revert_reason,
@@ -717,9 +721,12 @@ __all__ = [
717
721
  "format_market_label",
718
722
  "market_name",
719
723
  "DOCS",
724
+ "BalanceSlotNotFoundError",
720
725
  "ContractNotFoundError",
721
726
  "CrosschainDiscoveryError",
722
727
  "EmptyCallResultError",
728
+ "ExternalStateError",
729
+ "HyperCoreValuationError",
723
730
  "IporFusionError",
724
731
  "LogScanError",
725
732
  "LogSourceUnavailable",
@@ -729,6 +736,7 @@ __all__ = [
729
736
  "SimulationError",
730
737
  "SimulationRunError",
731
738
  "UnsupportedChainError",
739
+ "UnsupportedContractVersionError",
732
740
  "UnsupportedVaultVersionError",
733
741
  "CUSTOM_ERRORS",
734
742
  "decode_custom_error",
@@ -1,6 +1,5 @@
1
1
  from collections.abc import Callable
2
2
  from dataclasses import dataclass
3
- from typing import Self
4
3
 
5
4
  from eth_abi import decode
6
5
  from eth_abi.exceptions import InsufficientDataBytes
@@ -68,17 +67,13 @@ def _role_status_decoder(values: tuple) -> RoleStatus:
68
67
  class AccessManager(ContractWrapper):
69
68
  """Manages role-based access control for PlasmaVault operations."""
70
69
 
71
- def __init__(self, ctx: Web3Context, address: AccessManagerAddress):
70
+ def __init__(self, ctx: Web3Context | None, address: AccessManagerAddress):
72
71
  super().__init__(ctx, address)
73
72
 
74
73
  @property
75
74
  def address(self) -> AccessManagerAddress:
76
75
  return AccessManagerAddress(self._address)
77
76
 
78
- @classmethod
79
- def encoder(cls, address: AccessManagerAddress | None = None) -> Self: # type: ignore[override]
80
- return super().encoder(address)
81
-
82
77
  def grant_role(
83
78
  self, role_id: RoleId | Roles, account: ChecksumAddress, execution_delay: Period
84
79
  ) -> Call[None]:
@@ -161,7 +156,7 @@ class AccessManager(ContractWrapper):
161
156
  if predicate(role_id, account):
162
157
  candidates[(role_id, account)] = None
163
158
 
164
- statuses = Multicall3(self._ctx).aggregate(
159
+ statuses = Multicall3(self._require_ctx()).aggregate(
165
160
  [self.has_role(role_id, account) for role_id, account in candidates]
166
161
  )
167
162
  return [
@@ -180,7 +175,7 @@ class AccessManager(ContractWrapper):
180
175
  Web3.keccak(text="RoleGranted(uint64,address,uint32,uint48,bool)")
181
176
  ).to_0x_hex()
182
177
  return list(
183
- self._ctx.get_logs(
178
+ self._require_ctx().get_logs(
184
179
  contract_address=self._address, topics=[event_signature_hash]
185
180
  )
186
181
  )
@@ -129,18 +129,19 @@ class Call(Generic[T]):
129
129
  return actual
130
130
 
131
131
 
132
- _ENCODER_PLACEHOLDER_ADDRESS: ChecksumAddress = cast(
133
- ChecksumAddress, "0x0000000000000000000000000000000000000000"
134
- )
135
-
136
-
137
132
  class ContractWrapper:
138
133
  """Base wrapper. Subclasses expose one method per Solidity function that
139
134
  returns a `Call[T]` — never executing eagerly. Callers chain `.call()` for
140
135
  reads, `.send()` for writes, or hand the `Call` to `VaultSimulator`.
136
+
137
+ `ctx` may be ``None`` for offline use, e.g. external-signer flows (HTTP
138
+ signing service, multisig) that only need `Call.calldata`: every builder
139
+ works, and `.call()` / `.send()` on its Calls raise until a context is
140
+ passed to them. Methods that read the chain themselves (log scans, storage
141
+ reads, batched reads) raise `ValueError` on such a wrapper.
141
142
  """
142
143
 
143
- def __init__(self, ctx: Web3Context, address: ChecksumAddress):
144
+ def __init__(self, ctx: Web3Context | None, address: ChecksumAddress):
144
145
  self._ctx = ctx
145
146
  self._address = Web3.to_checksum_address(address)
146
147
 
@@ -148,30 +149,13 @@ class ContractWrapper:
148
149
  def address(self) -> ChecksumAddress:
149
150
  return self._address
150
151
 
151
- @classmethod
152
- def encoder(cls, address: ChecksumAddress | None = None):
153
- """Build a ctx-less wrapper: every builder method, no Web3Context.
154
-
155
- Named for its first use, external-signer flows (HTTP signing service,
156
- multisig), which only need `Call.calldata` — the actual `to` and `ctx`
157
- are filled in by the signer. It serves any builder that needs no chain
158
- access, decoders included. Calling `.call()` or `.send()` on a Call
159
- produced this way raises (no ctx).
160
-
161
- Pass `address` unless `.calldata` really is the only consumer. The
162
- encoding ignores it, but a builder that reads `self._address`
163
- semantically — matching it against a log's emitter, say, or folding it
164
- into a hash — runs against the placeholder zero address without one,
165
- and so rejects input that is genuinely the caller's.
166
- """
167
- instance = cls.__new__(cls)
168
- instance._ctx = None
169
- instance._address = (
170
- Web3.to_checksum_address(address)
171
- if address is not None
172
- else _ENCODER_PLACEHOLDER_ADDRESS
173
- )
174
- return instance
152
+ def _require_ctx(self) -> Web3Context:
153
+ if self._ctx is None:
154
+ raise ValueError(
155
+ f"Web3Context required: {type(self).__name__} at {self._address} "
156
+ "was built without one (ctx=None), which only builds calldata."
157
+ )
158
+ return self._ctx
175
159
 
176
160
  def _view(
177
161
  self,