ipor-fusion 4.0.0.dev171__tar.gz → 4.0.0.dev185__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 (341) hide show
  1. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.github/workflows/python-publish.yml +5 -0
  2. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/AGENTS.md +19 -7
  3. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/MIGRATION_4.md +115 -54
  4. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/PKG-INFO +14 -13
  5. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/README.md +13 -12
  6. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/examples/advanced_euler_v2_credit_market_base.py +14 -2
  7. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/examples/advanced_euler_v2_looping_arbitrum.py +14 -2
  8. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +16 -8
  9. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/examples/external_state_margin_leg_base.py +30 -11
  10. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/examples/simple_aave_v3_supply_base.py +15 -2
  11. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/pyproject.toml +12 -7
  12. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/scripts/crosschain_readiness.py +378 -115
  13. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/__init__.py +14 -4
  14. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/market_cmd.py +5 -2
  15. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/options.py +16 -5
  16. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/vault_markets_view.py +1 -1
  17. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/__init__.py +66 -0
  18. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/access.py +2 -2
  19. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/contract.py +18 -6
  20. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/external_state_executor.py +2 -10
  21. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/fee_manager.py +6 -2
  22. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/fuse.py +3 -2
  23. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/fusion_factory.py +24 -17
  24. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/logs.py +10 -1
  25. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/oracle.py +3 -3
  26. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/plasma_vault.py +7 -5
  27. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/rewards_manager.py +6 -6
  28. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/simulation.py +52 -219
  29. ipor_fusion-4.0.0.dev185/src/ipor_fusion/core/simulation_result.py +311 -0
  30. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/withdraw_manager.py +6 -4
  31. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/ccip/chainlink.py +3 -1
  32. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/ccip/codec.py +4 -4
  33. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/ccip/contracts.py +24 -4
  34. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/ccip/events.py +6 -5
  35. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/ccip/quote.py +1 -1
  36. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/ccip/transport.py +3 -2
  37. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/contracts.py +5 -2
  38. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/lane.py +5 -4
  39. ipor_fusion-4.0.0.dev185/src/ipor_fusion/crosschain/logs.py +38 -0
  40. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/messages.py +11 -5
  41. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/stargate/contracts.py +22 -3
  42. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/stargate/layerzero.py +3 -2
  43. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/stargate/transport.py +3 -2
  44. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/transport.py +3 -2
  45. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/errors.py +6 -0
  46. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/__init__.py +54 -0
  47. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/euler_v2.py +35 -17
  48. ipor_fusion-4.0.0.dev185/src/ipor_fusion/fuses/events.py +178 -0
  49. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/external_state_event_decoding.py +14 -116
  50. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/hypercore_event_decoding.py +23 -40
  51. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/aave_v3.py +20 -3
  52. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/aave_v4.py +3 -1
  53. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/hypercore.py +33 -12
  54. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/morpho.py +20 -5
  55. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/position_manager.py +25 -8
  56. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/unpriceable.py +7 -3
  57. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/vault_erc20.py +10 -3
  58. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/vault_positions.py +2 -2
  59. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_euler_v2.py +1 -1
  60. ipor_fusion-4.0.0.dev185/tests/_fake_rpc.py +106 -0
  61. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_simulate.py +14 -2
  62. ipor_fusion-4.0.0.dev185/tests/fixtures/vault_info_golden/fetched.cli.json +199 -0
  63. ipor_fusion-4.0.0.dev185/tests/fixtures/vault_info_golden/fetched.json +197 -0
  64. ipor_fusion-4.0.0.dev185/tests/fixtures/vault_info_golden/fetched.mcp.json +207 -0
  65. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_contract_call.py +40 -1
  66. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_contracts.py +1 -1
  67. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_errors.py +1 -1
  68. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_lane.py +1 -1
  69. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_transport.py +2 -1
  70. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_error_hierarchy.py +2 -0
  71. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_examples_vaults.py +1 -1
  72. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_external_state_executor.py +10 -9
  73. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_hypercore_events.py +62 -0
  74. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_oracle.py +4 -3
  75. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_public_api.py +76 -0
  76. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_revert_decoding.py +7 -6
  77. ipor_fusion-4.0.0.dev185/tests/test_simulation_observation.py +267 -0
  78. ipor_fusion-4.0.0.dev185/tests/test_vault_info_fetched_golden.py +209 -0
  79. ipor_fusion-4.0.0.dev171/src/ipor_fusion/crosschain/logs.py +0 -27
  80. ipor_fusion-4.0.0.dev171/src/ipor_fusion/fuses/events.py +0 -45
  81. ipor_fusion-4.0.0.dev171/tests/test_simulation_observation.py +0 -126
  82. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.claude-plugin/marketplace.json +0 -0
  83. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.claude-plugin/plugin.json +0 -0
  84. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.env.example +0 -0
  85. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.gemini/settings.json +0 -0
  86. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.git-blame-ignore-revs +0 -0
  87. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.github/workflows/cd.yml +0 -0
  88. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.github/workflows/ci.yml +0 -0
  89. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.github/workflows/commit-lint.yml +0 -0
  90. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.github/workflows/notify-slack.yml +0 -0
  91. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.github/workflows/python-build.yml +0 -0
  92. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.github/workflows/release.yml +0 -0
  93. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.github/workflows/report-slack.yml +0 -0
  94. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.gitignore +0 -0
  95. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.mcp.json +0 -0
  96. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.pre-commit-config.yaml +0 -0
  97. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/.python-version +0 -0
  98. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/CHANGELOG.md +0 -0
  99. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/CLAUDE.md +0 -0
  100. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/CODEOWNERS +0 -0
  101. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/LICENSE +0 -0
  102. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/build.sh +0 -0
  103. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/examples/CONTRIBUTING.md +0 -0
  104. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/examples/README.md +0 -0
  105. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  106. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/pytest.ini +0 -0
  107. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/scripts/commit-check.sh +0 -0
  108. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/skills/ipor-deploy-vault/SKILL.md +0 -0
  109. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/about.py +0 -0
  110. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cache.py +0 -0
  111. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/chains.py +0 -0
  112. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/__init__.py +0 -0
  113. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  114. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/config_cmd.py +0 -0
  115. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/config_store.py +0 -0
  116. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/main.py +0 -0
  117. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  118. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  119. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/vault_health.py +0 -0
  120. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  121. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  122. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/config/__init__.py +0 -0
  123. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/config/roles.py +0 -0
  124. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/address.py +0 -0
  125. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/context.py +0 -0
  126. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/erc20.py +0 -0
  127. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/fuse_action.py +0 -0
  128. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/core/multicall.py +0 -0
  129. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/__init__.py +0 -0
  130. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  131. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  132. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  133. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/discovery.py +0 -0
  134. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/errors.py +0 -0
  135. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  136. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  137. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  138. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  139. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/simulation.py +0 -0
  140. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  141. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  142. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/dependency_graph.py +0 -0
  143. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/entry_cli.py +0 -0
  144. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/entry_mcp.py +0 -0
  145. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/explorer.py +0 -0
  146. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/field_docs.py +0 -0
  147. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  148. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/async_action.py +0 -0
  149. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/base.py +0 -0
  150. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  151. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/erc4626.py +0 -0
  152. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/external_state.py +0 -0
  153. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  154. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  155. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/hypercore.py +0 -0
  156. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/merkl.py +0 -0
  157. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/morpho.py +0 -0
  158. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  159. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  160. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/fuses/universal.py +0 -0
  161. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/guide/__init__.py +0 -0
  162. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/guide/architecture.md +0 -0
  163. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/guide/glossary.md +0 -0
  164. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/guide/invariants.md +0 -0
  165. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/guide/quickstart.md +0 -0
  166. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/market_ids.py +0 -0
  167. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/market_info.py +0 -0
  168. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/mcp/__init__.py +0 -0
  169. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/mcp/guide.py +0 -0
  170. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/mcp/models.py +0 -0
  171. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/mcp/server.py +0 -0
  172. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/morpho_api.py +0 -0
  173. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/py.typed +0 -0
  174. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/__init__.py +0 -0
  175. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/compound_v3.py +0 -0
  176. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  177. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/lending_health.py +0 -0
  178. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  179. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  180. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  181. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  182. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  183. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/substrates.py +0 -0
  184. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/types.py +0 -0
  185. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/validation.py +0 -0
  186. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/vault_data.py +0 -0
  187. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/vault_info.py +0 -0
  188. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/vault_managers.py +0 -0
  189. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/src/ipor_fusion/vault_reads.py +0 -0
  190. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/UniswapV3UniversalRouter.py +0 -0
  191. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_crosschain.py +0 -0
  192. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_crosschain_lifecycle.py +0 -0
  193. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_crosschain_recovery.py +0 -0
  194. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_event_args.py +0 -0
  195. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_foundry.py +0 -0
  196. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_golden.py +0 -0
  197. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_guide.py +0 -0
  198. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_hypercore_model.py +0 -0
  199. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_hypercore_shadow.py +0 -0
  200. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_multicall.py +0 -0
  201. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_oracle_mapping_fake.py +0 -0
  202. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/_validation.py +0 -0
  203. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/addresses.py +0 -0
  204. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/conftest.py +0 -0
  205. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/constants.py +0 -0
  206. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  207. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/hypercore_market55.json +0 -0
  208. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
  209. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
  210. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
  211. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
  212. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
  213. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
  214. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
  215. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
  216. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_blue.json +0 -0
  217. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
  218. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
  219. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
  220. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
  221. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
  222. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
  223. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
  224. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/simulate_v1_request.json +0 -0
  225. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/clean.json +0 -0
  226. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
  227. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  228. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
  229. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
  230. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
  231. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/full.json +0 -0
  232. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
  233. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/full.txt +0 -0
  234. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/rich.json +0 -0
  235. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
  236. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  237. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_about.py +0 -0
  238. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_access_manager.py +0 -0
  239. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cache.py +0 -0
  240. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_chains.py +0 -0
  241. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_changelog.py +0 -0
  242. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_commands.py +0 -0
  243. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_config_store.py +0 -0
  244. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_helpers.py +0 -0
  245. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_hypercore.py +0 -0
  246. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_lending_health.py +0 -0
  247. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_market_golden.py +0 -0
  248. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_market_morpho_blue.py +0 -0
  249. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_oracle_mapping.py +0 -0
  250. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_oracle_mapping_golden.py +0 -0
  251. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_pending_requests_scan.py +0 -0
  252. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_role_accounts.py +0 -0
  253. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_vault_info_golden.py +0 -0
  254. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_vault_predates_getters.py +0 -0
  255. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_cli_vault_validate.py +0 -0
  256. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_contract_encoder.py +0 -0
  257. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_ccip_events.py +0 -0
  258. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_ccip_indexer.py +0 -0
  259. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_ccip_quote.py +0 -0
  260. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_chainlink.py +0 -0
  261. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_encoding.py +0 -0
  262. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_layering.py +0 -0
  263. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_readiness.py +0 -0
  264. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_crosschain_readiness_report.py +0 -0
  265. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_docs_typing.py +0 -0
  266. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_erc20.py +0 -0
  267. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_examples_composed_hypercore.py +0 -0
  268. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_examples_crosschain.py +0 -0
  269. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  270. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_explorer.py +0 -0
  271. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_external_state_events.py +0 -0
  272. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_fee_manager.py +0 -0
  273. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_fuse_encoding.py +0 -0
  274. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_fusion_instance.py +0 -0
  275. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_guide.py +0 -0
  276. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_hypercore_deployment.py +0 -0
  277. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_hypercore_flow.py +0 -0
  278. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_hypercore_live.py +0 -0
  279. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_hypercore_outcome.py +0 -0
  280. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_hypercore_readers.py +0 -0
  281. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_hypercore_simulation.py +0 -0
  282. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_logs.py +0 -0
  283. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_mcp_hypercore.py +0 -0
  284. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_mcp_models.py +0 -0
  285. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_mcp_server.py +0 -0
  286. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_mcp_vault_validate.py +0 -0
  287. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_multicall.py +0 -0
  288. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_oracle_mapping.py +0 -0
  289. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_plasma_vault.py +0 -0
  290. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_public_type_reachability.py +0 -0
  291. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_reader_encoding.py +0 -0
  292. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_readiness_selection.py +0 -0
  293. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_rewards_manager.py +0 -0
  294. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_roles.py +0 -0
  295. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_sdk_layering.py +0 -0
  296. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  297. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  298. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  299. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_crosschain_canary.py +0 -0
  300. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_crosschain_hypercore.py +0 -0
  301. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  302. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  303. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  304. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  305. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  306. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  307. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  308. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_examples_arbitrum.py +0 -0
  309. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_examples_base.py +0 -0
  310. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_examples_crosschain.py +0 -0
  311. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_external_state_executor_base.py +0 -0
  312. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  313. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  314. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  315. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_morpho_blue.py +0 -0
  316. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_pilot_v3.py +0 -0
  317. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  318. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  319. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  320. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  321. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_readers_integration.py +0 -0
  322. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_release_funds.py +0 -0
  323. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_swap_on_base.py +0 -0
  324. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  325. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_simulation_errors.py +0 -0
  326. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_solidity_compiler.py +0 -0
  327. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_solidity_mirrors.py +0 -0
  328. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_substrates.py +0 -0
  329. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_types_and_events.py +0 -0
  330. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_unpriceable_tokens.py +0 -0
  331. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_validation.py +0 -0
  332. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_vault_data_block.py +0 -0
  333. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_vault_dep_graph.py +0 -0
  334. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_vault_info_onchain_base.py +0 -0
  335. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  336. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_vault_simulator_blocks.py +0 -0
  337. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_web3_context.py +0 -0
  338. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/test_withdraw_manager.py +0 -0
  339. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/typed_client/consumer.py +0 -0
  340. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/tests/typed_client/rejected.py +0 -0
  341. {ipor_fusion-4.0.0.dev171 → ipor_fusion-4.0.0.dev185}/uv.lock +0 -0
@@ -133,6 +133,11 @@ jobs:
133
133
  run: uv run ruff check ./
134
134
  id: static-code-analyze
135
135
 
136
+ - name: Type check
137
+ if: inputs.static-code-analysis-enabled
138
+ run: uv run pyright
139
+ id: type-check
140
+
136
141
  - name: Version release (semantic)
137
142
  if: inputs.publish-enabled && !inputs.skip-version-bump && !inputs.dev-build
138
143
  id: release
@@ -26,7 +26,7 @@ repository-local work that does not require protocol context.
26
26
  uv sync --all-extras --locked # install, same as CI
27
27
  uv run ruff format ./ # format (CI runs --check)
28
28
  uv run ruff check ./ # lint: bandit S, C901 max-complexity 10, PLR limits
29
- uv run pyright # types, basic mode, src + tests
29
+ uv run pyright # types, basic mode, src + tests + examples + scripts
30
30
  uv run pytest # all tests + coverage gate (fail_under = 95)
31
31
  uv run pytest -m "cli or mcp" --no-cov # fast offline subset
32
32
  uv run pytest --run-readiness -m readiness --no-cov -rA # opt-in live Ethereum-hub POC checks
@@ -40,8 +40,12 @@ uv lock --check # uv.lock in sync
40
40
  `fusion market morpho-blue|meta-morpho --json`, `fusion vault oracle-mapping
41
41
  --json` and the matching MCP tools (`vault_info`, `market_morpho_blue`,
42
42
  `market_meta_morpho`, `vault_oracle_mapping`, called through the server) is pinned
43
- in `tests/fixtures/*_golden/` (`tests/_golden.py`, the `*_golden.py` tests), from
44
- recorded inputs and no network. A change to any of them fails those tests;
43
+ in `tests/fixtures/*_golden/` (`tests/_golden.py`, the `*_golden.py` tests), with
44
+ no network. Most scenarios render synthetic snapshots (a hand-built `VaultData` or
45
+ fixed reader answers over a fake chain) to cover every rendering branch;
46
+ `test_vault_info_fetched_golden.py` runs `fetch_vault_data` unpatched over a fake
47
+ JSON-RPC provider (`tests/_fake_rpc.py`) to pin the whole fetch-to-output path. A
48
+ change to any of them fails those tests;
45
49
  regenerate with `UPDATE_GOLDEN=1` only when the change is intended and review the
46
50
  fixture diff. Without it the tests only compare.
47
51
 
@@ -252,14 +256,19 @@ the lifecycle matrix follows that.
252
256
  prefer a storage or view read over a scan, as `read_balance_fuses` and
253
257
  `read_withdraw_manager_address` do), `plasma_vault`, `access`, `withdraw_manager`, `rewards_manager`, `fee_manager`,
254
258
  `simulation` (`VaultSimulator`, eth_simulateV1, `erc20_balance_slot` and ERC-20 balance
255
- overrides), `oracle`, `fusion_factory`,
259
+ overrides), `simulation_result` (the run's records, `SimulatedLog`, and the parsing
260
+ of the node's response), `oracle`, `fusion_factory`,
256
261
  `external_state_executor` (NAV marks for market 50), `erc20`, `fuse_action`
257
262
  (`FuseAction`), `fuse` (the `Fuse` base, `StakeFuse`, the shared validators and
258
263
  substrate packers), `address` (`_validate_not_zero_address`). `core` imports neither
259
264
  `fuses` nor `crosschain` (`tests/test_sdk_layering.py`)
260
265
  - `fuses/` — per-protocol fuse encoders (aave_v3, async_action, compound_v3,
261
266
  erc4626, euler_v2, external_state, fluid_instadapp, gearbox_v3, merkl, morpho, ramses_v2,
262
- uniswap_v3, universal, `events.py`); `base.py` re-exports the `core/fuse.py` base and validators,
267
+ uniswap_v3, universal); `events.py` (`extract_events`, `abi_value` and the raw-log
268
+ event decoder both event families share), `external_state_event_decoding.py` and
269
+ `hypercore_event_decoding.py` (the ExternalState and HyperCore event specs, classes
270
+ and decoders; internal modules, the names are exported from `ipor_fusion.fuses`);
271
+ `base.py` re-exports the `core/fuse.py` base and validators,
263
272
  `FuseAction` and `ZERO_ADDRESS` (from `types`). A fuse is a value: equality and
264
273
  hash cover its class and every attribute it stores
265
274
  - `crosschain/` — crosschain Plasma Vaults (executor on the hub chain, dispatcher at
@@ -389,9 +398,12 @@ or examples.
389
398
  `MorphoBlueMarketId` is always `0x` + 64 lowercase hex, built by
390
399
  `parse_morpho_market_id`. Raw input
391
400
  is parsed once at the boundary (click param types, MCP tools, the user config).
392
- `src/`, `examples/` and `tests/typed_client/` are checked with
401
+ `src/`, `examples/`, `scripts/` and `tests/typed_client/` are checked with
393
402
  `reportArgumentType` on, so a bare `int`/`str` passed for one fails pyright; the
394
- other tests keep it off and pass literals.
403
+ other tests keep it off and pass literals. The same roots turn on
404
+ `reportMissingTypeArgument` and `reportUnknownParameterType`: spell every type
405
+ argument (`tuple[int, str]` for a decoded ABI tuple, `Call[object]` where the
406
+ result is ignored, `RawLog` for a raw log), never a bare generic.
395
407
  - Addresses: fuse, factory and manager addresses per chain come from
396
408
  `ipor-abi` `addresses.json`; a fuse must be registered on the vault
397
409
  (`PlasmaVault.get_fuses()`) and its market granted substrates before an
@@ -23,41 +23,58 @@ the details, and each change is described once, in its section.
23
23
  Work through these in order; each is what breaks first at run time after the one
24
24
  before it is fixed.
25
25
 
26
- 1. **Wrappers are built from an address; execution takes the context.**
26
+ 1. **Removed and renamed imports fail first, at import.** The crosschain encoders
27
+ left `ipor_fusion.fuses.crosschain` for `ipor_fusion.crosschain.fuses` (no shim);
28
+ the event decoder modules `ipor_fusion.fuses.external_state_events` and
29
+ `ipor_fusion.fuses.hypercore_events` are renamed `external_state_event_decoding`
30
+ and `hypercore_event_decoding` (import their names from `ipor_fusion` or
31
+ `ipor_fusion.fuses`); `IporFusionMarkets.RWA`, `MORPHO_BLUE_ADDRESS`, the 3.x cache
32
+ functions, `read_erc20_holdings`, `ExternalStateExecutor.mark_nav` and `NavMark`
33
+ are gone. See [Moved, removed and reshaped](#moved-removed-and-reshaped).
34
+ 2. **Wrappers are built from an address; execution takes the context.**
27
35
  `PlasmaVault(vault)`, `ERC20(token)`, `AccessManager(manager)`; then
28
36
  `.call(ctx)`, `.send(ctx)`, `.build_transaction(ctx)`, `.estimate_gas(ctx)`. See
29
37
  [Context-free wrappers](#context-free-wrappers).
30
- 2. **Methods that read the chain to answer are `read_*(ctx, ...)`**:
38
+ 3. **Methods that read the chain to answer are `read_*(ctx, ...)`**:
31
39
  `vault.get_balance_fuses()` is `vault.read_balance_fuses(ctx)`,
32
40
  `access_manager.owner()` is `read_owner(ctx)`. See
33
41
  [Renamed methods](#renamed-methods).
34
- 3. **Pin a block with `ctx = ctx.at_block(n)`.** Assigning `ctx.default_block`
42
+ 4. **Pin a block with `ctx = ctx.at_block(n)`.** Assigning `ctx.default_block`
35
43
  raises `AttributeError`. See [Vault reads and validation](#vault-reads-and-validation).
36
- 4. **The vault read is one snapshot.** `fetch_vault_data(ctx, vault, *, block=None,
44
+ 5. **The vault read is one snapshot.** `fetch_vault_data(ctx, vault, *, block=None,
37
45
  lending=True, metadata=True)` takes the vault address and the chain from `ctx`;
38
46
  `validate_vault_state(state)` and `build_vault_info_json(data, *,
39
47
  resolve_name=None)` are pure. See [Vault reads and validation](#vault-reads-and-validation).
40
- 5. **Moved and removed names.** The crosschain encoders left
41
- `ipor_fusion.fuses.crosschain` (no shim); `IporFusionMarkets.RWA`,
42
- `MORPHO_BLUE_ADDRESS`, the 3.x cache functions, `read_erc20_holdings`,
43
- `ExternalStateExecutor.mark_nav` and `NavMark` are gone; several functions take
48
+ 6. **Simulations return typed observations.** `sim.observe("label", call)` raises
49
+ `TypeError`: `read = sim.observe(call, label="label")` returns a
50
+ `SimulationObservation`, read as `read.value` after `run()`;
51
+ `SimulationResult.get`, `SimulationResult.observations` and
52
+ `SimulatedCallResult.decoded` are removed (`AttributeError`). `label` is
53
+ keyword-only on `observe`, `add_call`, `add_transaction` and `deploy_contract`.
54
+ A failed `run()` leaves every observation unresolved instead of holding the
55
+ previous run's value. The result records are frozen, logs are `SimulatedLog`
56
+ records, and a failed call always carries an `error`. See
57
+ [Typed batches and simulation observations](#typed-batches-and-simulation-observations)
58
+ and [Behavior changes](#behavior-changes).
59
+ 7. **Options by keyword and other reshaped signatures.** Several functions take
44
60
  their options by keyword or renamed a parameter. See
45
61
  [Moved, removed and reshaped](#moved-removed-and-reshaped).
46
- 6. **Errors.** Every SDK exception derives from `IporFusionError`; some raises changed
47
- class (empty call results, crosschain discovery, simulation, Morpho API). See
48
- [Errors](#errors).
49
- 7. **Cache and request limit.** `Web3Context.from_url` bounds requests in flight at
62
+ 8. **Errors.** Every SDK exception derives from `IporFusionError`; some raises changed
63
+ class (empty call results, crosschain discovery, simulation, Morpho API, malformed
64
+ HyperCore logs). See [Errors](#errors).
65
+ 9. **Cache and request limit.** `Web3Context.from_url` bounds requests in flight at
50
66
  8 (`max_concurrent_requests`; `InFlightLimit` to share a bound, `None` to lift
51
67
  it), and the contract cache is injected per context (`NullCache()` keeps
52
68
  nothing). See [Contract cache and request limit](#contract-cache-and-request-limit).
53
- 8. **Typed callers** (pyright, mypy, Pylance): ids, addresses and amounts are
54
- nominal types. Wrap literals and raw values, `ChainId(8453)`, `Period(0)`,
55
- `Amount(n)`, `parse_fuse_address(...)`; values the SDK returns already carry
56
- their type. See [Nominal types](#nominal-types).
57
- 9. **Typed JSON documents.** `build_vault_info_json` and the `fusion market`
58
- builders return TypedDicts (`VaultInfoJson`, ...); code annotating them
59
- `dict[str, Any]` switches to the TypedDict or `Mapping[str, object]`. See
60
- [Typed JSON documents](#typed-json-documents).
69
+ 10. **Typed callers** (pyright, mypy, Pylance): ids, addresses and amounts are
70
+ nominal types. Wrap literals and raw values, `ChainId(8453)`, `Period(0)`,
71
+ `Amount(n)`, `parse_fuse_address(...)`; values the SDK returns already carry
72
+ their type. See [Nominal types](#nominal-types).
73
+ 11. **Typed JSON documents and events.** `build_vault_info_json` and the `fusion
74
+ market` builders return TypedDicts (`VaultInfoJson`, ...); code annotating them
75
+ `dict[str, Any]` switches to the TypedDict or `Mapping[str, object]`. Decoded
76
+ events are discriminated unions narrowed by `event.name`. See
77
+ [Typed JSON documents](#typed-json-documents) and [Decoded events](#decoded-events).
61
78
 
62
79
  Then read [Behavior changes](#behavior-changes): they apply to every caller,
63
80
  type-checked or not.
@@ -174,7 +191,13 @@ assets.value, fuses.value, name.value
174
191
  instead of the simulator, so it no longer chains, and its label is optional (it
175
192
  names the read in `result.calls` and in failures). After `run()`,
176
193
  `observation.value` is the read decoded as its `Call` decodes it, from the latest
177
- run; `observation.result` is its `SimulatedCallResult`.
194
+ run; `observation.result` is its `SimulatedCallResult`. Every `run()` unresolves the
195
+ observations when it starts and resolves them only once the whole response has
196
+ parsed: after a failed run (a transport error, an RPC error, a malformed response,
197
+ raised as `SimulationRunError` for the last two) reading one raises `RuntimeError`
198
+ until a run succeeds. `label` is keyword-only on `add_call` and `add_transaction`
199
+ too, as on `observe` and `deploy_contract`: `sim.add_call(call, from_, "label")`
200
+ raises `TypeError`, pass `label="label"`.
178
201
  `CrosschainSimulator.observe(chain_id, call, *, label=None)` returns the same,
179
202
  resolved by every relay round. `SimulationResult.get`, `SimulationResult.observations`
180
203
  and `SimulatedCallResult.decoded` are removed: a list of results of many types has no
@@ -189,9 +212,16 @@ sim.run()
189
212
  after.value - before.value
190
213
  ```
191
214
 
192
- `Call` is a frozen dataclass and covariant in its result type: assigning to a
193
- field raises, and a `Call[Amount]` is a `Call[int]` and a `Call[object]`, so a
194
- `Sequence[Call[object]]` takes any calls without a cast.
215
+ `Call` is a frozen, hashable dataclass and covariant in its result type:
216
+ assigning to a field raises, `output_types` is stored as a tuple (whatever sequence
217
+ it was built with, so compare it with a tuple), and a `Call[Amount]` is a
218
+ `Call[int]` and a `Call[object]`, so a `Sequence[Call[object]]` takes any calls
219
+ without a cast. `add_call` and `execute_call` take a `Call[object]`.
220
+
221
+ The logs of a simulated call (`SimulatedCallResult.logs`,
222
+ `SimulationResult.execute_logs`) are `SimulatedLog` TypedDicts: `address`, `topics`
223
+ and `data` as the node's `0x` text, plus its block and transaction identity fields
224
+ when it sends them. Other keys a node adds are not kept.
195
225
 
196
226
  ### Renamed methods
197
227
 
@@ -214,6 +244,7 @@ field raises, and a `Call[Amount]` is a `Call[int]` and a `Call[object]`, so a
214
244
  | `AaveV3FuseReader.pool()` | `AaveV3FuseReader.read_pool(ctx)` |
215
245
  | `PlasmaVault.read(target, data)` / `read_as(target, data, ...)` | `PlasmaVault.universal_read(target, data)` / `universal_read_decoded(target, data, ...)` (they build a Call; `read_*` names only methods that execute) |
216
246
  | `read_index_of(asset)` | `precompile_index_of(asset)` (it reads nothing) |
247
+ | `EulerSwapStaticParams.to_tuple()` (and `EulerSwapDynamicParams`, `EulerSwapInitialState`, `EulerV2BatchItem`) | `.as_tuple()`, a typed tuple instead of a list |
217
248
  | `ctx.default_block = n` | `ctx = ctx.at_block(n)` |
218
249
 
219
250
  A service that caches wrappers per chain can now share one wrapper across chains and
@@ -406,9 +437,15 @@ is the wrong one.
406
437
  - `Call.call(ctx)` and `Call.decode()` on a write-only `Call` raise `ValueError`, not
407
438
  `RuntimeError`. `CrosschainSimulator.relay` raises `ValueError`, not
408
439
  `RuntimeError`, for a message to a chain that was not added.
409
- - `VaultSimulator.run` (provider error from `eth_simulateV1`, or a result count that
410
- does not match the queued calls) and `CrosschainSimulator.relay` (not settled within
411
- `max_rounds`) raise the new `SimulationRunError`, still a `RuntimeError`.
440
+ - `VaultSimulator.run` (provider error from `eth_simulateV1`, a result count that
441
+ does not match the queued calls, or a response not in the documented shape, which
442
+ raised `KeyError`, `TypeError` or `ValueError` before: a call result without a
443
+ `0x0`/`0x1` `status`, which 3.x read as a success, or `logs` that is not a list) and
444
+ `CrosschainSimulator.relay` (not settled within `max_rounds`) raise the new
445
+ `SimulationRunError`, still a `RuntimeError`.
446
+ - `decode_hypercore_event` raises the new `HyperCoreEventDecodeError` (a
447
+ `ValueError`) for a log whose topic it recognizes but whose other parts are
448
+ malformed; see [Decoded events](#decoded-events).
412
449
  - The Morpho API client raises `MorphoApiError` for every answer it cannot use and
413
450
  every transport failure, never `KeyError`, `TypeError` or `ValueError`:
414
451
  - a required market id or address (market and V1 allocation ids, V1 flow-cap ids,
@@ -855,34 +892,58 @@ that only some entries carry is `NotRequired` (a balance fuse's `pct_of_total` a
855
892
  `ipor_fusion` and `ipor_fusion.readers`). Narrow by a key before reading a
856
893
  kind-specific one: `if detail is not None and "aggregator" in detail:
857
894
  detail["description"]`.
858
- - Decoded events are discriminated unions. `ExternalStateEvent` and `HyperCoreEvent`
859
- are type aliases over one frozen dataclass per event (`ExternalStateUnpausedEvent`,
860
- `HyperCoreDepositFuseEnterEvent`, ...), each with `name: Literal["<event>"]` and
861
- `args` typed as that event's TypedDict (`ExternalStateUnpausedArgs`, ...). Comparing
862
- `name` narrows the event and its `args`, with no key test:
863
-
864
- ```python
865
- for event in external_state_events(receipt):
866
- if event.name == "ExternalStateUnpaused":
867
- event.args["signer"] # ChecksumAddress
868
- match hc_event:
869
- case HyperCoreDepositFuseEnterEvent(args=args):
870
- args["evmAsset"]
871
- ```
872
-
873
- Without that comparison `event.args["signer"]` does not type-check.
874
- `isinstance(x, ExternalStateEvent)` still works (the alias is a union); the events
875
- are no longer built positionally (`ExternalStateEvent(...)` is not a class: build
876
- the event's own class with keywords, its `name` is preset).
877
- `EXTERNAL_STATE_EVENT_CLASSES` / `HYPERCORE_EVENT_CLASSES` map an event name to its
878
- class; they replace `EXTERNAL_STATE_EVENT_ARGS` / `HYPERCORE_EVENT_ARGS`, which are
879
- removed. An event's args TypedDict is its class's `args` annotation:
880
- `typing.get_type_hints(EXTERNAL_STATE_EVENT_CLASSES[name])["args"]`. Values carry nominal types (`Amount`, `MarketId`, `FuseAddress`,
881
- `HyperCoreTokenIndex`, `PayloadHash`, web3's `Timestamp`, ...), and
882
- `HyperCoreEvent.fuse` is a `FuseAddress | None` on every event.
883
895
  - `RawLog` (the log shape `ExternalStateExecutor.parse_balance_proposed` /
884
- `find_balance_proposed` accept) is `Mapping[str, object]`, not `Mapping[str, Any]`;
885
- a web3 `LogReceipt` still fits it.
896
+ `find_balance_proposed`, the event decoders and the crosschain log helpers
897
+ accept) is `Mapping[str, object]`, not `Mapping[str, Any]`; a web3 `LogReceipt`
898
+ still fits it.
899
+ - `Command.decode_action_data()` returns `tuple[object, ...]`: narrow each value
900
+ (an `int`, a lowercase hex `str` address or `bytes`, by the action) before use.
901
+ - `CrosschainLane.proposal_id_from_receipt(receipt)` is typed to take web3's
902
+ `TxReceipt`; for a raw JSON-RPC or minimal `{"logs": ...}` receipt call
903
+ `lane.proposal_id_from_logs(receipt["logs"])`, which takes any `RawLog`s.
904
+
905
+ ## Decoded events
906
+
907
+ Decoded events are discriminated unions. `ExternalStateEvent` and `HyperCoreEvent`
908
+ are type aliases over one frozen dataclass per event (`ExternalStateUnpausedEvent`,
909
+ `HyperCoreDepositFuseEnterEvent`, ...), each with `name: Literal["<event>"]` and
910
+ `args` typed as that event's TypedDict (`ExternalStateUnpausedArgs`, ...). Comparing
911
+ `name` narrows the event and its `args`, with no key test:
912
+
913
+ ```python
914
+ for event in external_state_events(receipt):
915
+ if event.name == "ExternalStateUnpaused":
916
+ event.args["signer"] # ChecksumAddress
917
+ match hc_event:
918
+ case HyperCoreDepositFuseEnterEvent(args=args):
919
+ args["evmAsset"]
920
+ ```
921
+
922
+ Without that comparison `event.args["signer"]` does not type-check.
923
+ `isinstance(x, ExternalStateEvent)` still works (the alias is a union); the events
924
+ are no longer built positionally (`ExternalStateEvent(...)` is not a class: build the
925
+ event's own class with keywords, its `name` is preset).
926
+ `EXTERNAL_STATE_EVENT_CLASSES` / `HYPERCORE_EVENT_CLASSES` map an event name to its
927
+ class. An event's args TypedDict is its class's `args` annotation:
928
+ `typing.get_type_hints(EXTERNAL_STATE_EVENT_CLASSES[name])["args"]`. Values carry
929
+ nominal types (`Amount`, `MarketId`, `FuseAddress`, `HyperCoreTokenIndex`,
930
+ `PayloadHash`, web3's `Timestamp`, ...), and `HyperCoreEvent.fuse` is a
931
+ `FuseAddress | None` on every event. Every name is importable from `ipor_fusion` and
932
+ from `ipor_fusion.fuses`.
933
+
934
+ The two families share one input contract. `decode_external_state_event(log)` and
935
+ `decode_hypercore_event(log)` take a web3 receipt log or a raw JSON-RPC / indexer
936
+ mapping (hex text topics, data, hashes and quantities) and return `None` when topic0
937
+ is not one of theirs, or cannot be a topic; once the topic is recognized, a malformed
938
+ part raises `ExternalStateEventDecodeError` / `HyperCoreEventDecodeError` (both an
939
+ `IporFusionError` and a `ValueError`). `external_state_event_name(topic)` and
940
+ `hypercore_event_name(topic)` return `None` for any other topic or a value that
941
+ cannot be one. For HyperCore this changes 3.x: `decode_hypercore_event` took only a
942
+ receipt log with bytes topics and let eth_abi's errors through,
943
+ `hypercore_event_name` raised on bad hex and also matched hex text without `0x`
944
+ (which now returns `None`: pass `"0x" + topic.hex()` or `HexBytes(topic).to_0x_hex()`),
945
+ and `log_index` was passed through as given (a hex string from a raw log); the events now also carry `block_number` and
946
+ `transaction_hash`, like the ExternalState ones.
886
947
 
887
948
  ## Behavior changes
888
949
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev171
3
+ Version: 4.0.0.dev185
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
@@ -123,7 +123,7 @@ alt="Release">
123
123
  > vault read is one pinned snapshot, and several modules and names moved. A 3.x call
124
124
  > such as `PlasmaVault(ctx, address)` fails only when it runs, so a broad
125
125
  > `except Exception` can hide it. Follow the ordered checklist in
126
- > [MIGRATION_4.md, *Start here*](MIGRATION_4.md#start-here).
126
+ > [MIGRATION_4.md, *Start here*](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/MIGRATION_4.md#start-here).
127
127
 
128
128
  ## Quickstart
129
129
 
@@ -298,7 +298,7 @@ resources `fusion://glossary`, `fusion://architecture`, `fusion://invariants` an
298
298
 
299
299
  ## Agent skills
300
300
 
301
- [`skills/ipor-deploy-vault/SKILL.md`](skills/ipor-deploy-vault/SKILL.md) teaches a
301
+ [`skills/ipor-deploy-vault/SKILL.md`](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/skills/ipor-deploy-vault/SKILL.md) teaches a
302
302
  coding agent the full clone → roles → market → access posture → deposit → execute
303
303
  walk, with the invariants and the revert selectors, before it writes vault code.
304
304
  Its body is `fusion://invariants` and `fusion://quickstart` from `ipor_fusion.guide`
@@ -318,7 +318,7 @@ npx skills add IPOR-Labs/ipor-fusion.py -g -a codex # or gemini-cli, cursor
318
318
 
319
319
  ## Common errors
320
320
 
321
- Reverts on the deploy-and-configure path, keyed by selector so a failed transaction is greppable. This table and the full invariants ship in the wheel as [`ipor_fusion.guide`](src/ipor_fusion/guide/invariants.md), which `fusion-mcp` serves as `fusion://invariants`.
321
+ Reverts on the deploy-and-configure path, keyed by selector so a failed transaction is greppable. This table and the full invariants ship in the wheel as [`ipor_fusion.guide`](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/src/ipor_fusion/guide/invariants.md), which `fusion-mcp` serves as `fusion://invariants`.
322
322
 
323
323
  | Selector | Revert | What happened | Fix |
324
324
  |---|---|---|---|
@@ -334,7 +334,7 @@ Configuration order on a fresh vault: `add_fuses` → `grant_market_substrates`
334
334
 
335
335
  A flash-loan fuse also needs `update_callback_handler(handler, sender, selector)` before its first `execute`: Morpho Blue calls back `onMorphoFlashLoan(uint256,bytes)`, the Euler EVC `onEulerFlashLoan(bytes)`, and an unregistered callback reverts `HandlerNotFound()`.
336
336
 
337
- The full clone → configure → deposit → execute sequence is exercised in [`tests/test_simulate_vault_from_scratch_base.py`](tests/test_simulate_vault_from_scratch_base.py).
337
+ The full clone → configure → deposit → execute sequence is exercised in [`tests/test_simulate_vault_from_scratch_base.py`](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/tests/test_simulate_vault_from_scratch_base.py).
338
338
 
339
339
  Custom errors of the crosschain contracts decode by name: importing
340
340
  `ipor_fusion.crosschain` registers their signatures (`CROSSCHAIN_ERROR_SIGNATURES`),
@@ -346,22 +346,22 @@ raises `SimulationError` naming the first failed call.
346
346
  ## Vault construction examples
347
347
 
348
348
  Runnable, canonical examples for building and configuring a vault from scratch live in
349
- [`examples`](examples). They preview and simulate through `eth_simulateV1` — nothing is
349
+ [`examples`](https://github.com/IPOR-Labs/ipor-fusion.py/tree/main/examples). They preview and simulate through `eth_simulateV1` — nothing is
350
350
  ever signed or broadcast.
351
351
 
352
- - [Simple Aave V3 supply vault](examples/simple_aave_v3_supply_base.py) — start here for vault
352
+ - [Simple Aave V3 supply vault](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/simple_aave_v3_supply_base.py) — start here for vault
353
353
  creation, role bootstrap, and configuring a single supported market.
354
- - [Advanced Euler V2 credit-market vault](examples/advanced_euler_v2_credit_market_base.py) —
354
+ - [Advanced Euler V2 credit-market vault](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_euler_v2_credit_market_base.py) —
355
355
  continue here for composing multiple functional fuses, typed Euler substrates and sub-accounts,
356
356
  and an ordered collateral → borrow → repay → unwind lifecycle.
357
- - [External-state margin leg](examples/external_state_margin_leg_base.py) — a vault whose capital
357
+ - [External-state margin leg](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/external_state_margin_leg_base.py) — a vault whose capital
358
358
  sits off-chain: typed market-50 substrates, a dual-custodian propose/confirm marking the NAV,
359
359
  the scheduled-withdrawal prep an alpha performs, and a mismatched confirmation being rejected.
360
- - [Crosschain CCIP lifecycle](examples/crosschain_ccip_usdc_arbitrum_hyperevm.py) (preview feature) — a vault
360
+ - [Crosschain CCIP lifecycle](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py) (preview feature) — a vault
361
361
  whose capital works on another chain: supply USDC from an Arbitrum hub to a HyperEVM spoke
362
362
  over CCIP, attest the remote balance with two keys, deposit and redeem on the spoke, recall
363
363
  and claim, driven through `CrosschainLane` and relayed by `CrosschainSimulator`.
364
- - [Composed HyperCore flow](examples/composed_hypercore_flow_arbitrum_hyperevm.py) (preview feature) — the
364
+ - [Composed HyperCore flow](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/composed_hypercore_flow_arbitrum_hyperevm.py) (preview feature) — the
365
365
  same hub funding a HyperCore vault (market 55) that trades on Hyperliquid Core: the Core leg
366
366
  (bridge, dex transfer, order, close, exit) built from the vault's substrate grants and estimated
367
367
  with `eth_estimateGas` at head (EVM acceptance only, Core executes later), the REDEEM sized by the vault's EVM exit ceiling behind the executor's
@@ -448,6 +448,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
448
448
  | `ExternalStateExecutor` | NAV propose/confirm for off-vault capital (market 50); the executor's thresholds (`staleness_max`, `big_change_bps`, `dust_threshold`, `min_update_interval`), the live aggregate (`get_balance_fuse_snapshot`) and the pre-hook's staleness clock (`get_oldest_update_timestamp`) |
449
449
  | `read_external_state`, `ExternalStateVaultState` | The market's vault-scoped state from the vault's ERC-7201 storage at one block: executor, the balance fuse's cached total, the custodian timestamp it last checked and the pause flag the pre-hook enforces (no getter exposes them) |
450
450
  | `decode_external_state_event`, `EXTERNAL_STATE_TOPICS` | The ExternalState events the vault emits (big change, cache updates, unpause, rescue, executor creation and deployment, operation enter/exit), decoded by topic with their raw log identity; a malformed listed log raises |
451
+ | `decode_hypercore_event`, `hypercore_events` | **Preview.** The HyperCore fuse, pending-library, settlement and reporter events, decoded by topic with their raw log identity under the same input contract; a malformed listed log raises `HyperCoreEventDecodeError` |
451
452
  | `StargateCrosschainExecutor`, `CcipCrosschainExecutor` | **Preview.** Crosschain executor reads and attestation (`propose_balance`, `approve_balance`) |
452
453
  | `StargateCrosschainDispatcher`, `CcipCrosschainDispatcher` | Remote dispatcher state (`observation`, tracked idle, command lane) |
453
454
  | `StargateCrosschainFactory`, `CcipCrosschainFactory` | Executor creation and route/asset configuration reads |
@@ -475,11 +476,11 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
475
476
  | Universal | `UniversalTokenSwapperFuse` |
476
477
  | Off-vault capital | `AsyncActionFuse` (market 40), `ExternalStateOperationFuse` (market 50) |
477
478
  | Crosschain (preview) | `StargateCrosschainSupplyFuse`, `StargateCrosschainCommandFuse`, `CcipCrosschainSupplyFuse`, `CcipCrosschainCommandFuse`, `CrosschainClaimFuse` (market 54); transport-agnostic bases `CrosschainSupplyFuse` / `CrosschainCommandFuse` with `StargateSendParams` / `CcipSendParams`, plus `Command`, `CrosschainSubstrateLib`, `OptionsBuilder` |
479
+ | HyperCore (Hyperliquid, market 55, preview) | `HyperCoreDepositFuse`, `HyperCoreMarginFuse`, `HyperCoreSendFuse`, `HyperCoreOrderFuse`, `HyperCoreCancelFuse`, `HyperCoreBuilderFeeFuse`, `HyperCoreSubstrates` |
478
480
 
479
481
  The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Use
480
482
  `crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` for that deployment;
481
483
  `open_lane` discovers the vault's actual market grants rather than assuming 54.
482
- | HyperCore (Hyperliquid, market 55, preview) | `HyperCoreDepositFuse`, `HyperCoreMarginFuse`, `HyperCoreSendFuse`, `HyperCoreOrderFuse`, `HyperCoreCancelFuse`, `HyperCoreBuilderFeeFuse`, `HyperCoreSubstrates` |
483
484
 
484
485
  ### Readers (`ipor_fusion.readers`)
485
486
 
@@ -543,7 +544,7 @@ cp .env.example .env
543
544
  # Edit .env with ARBITRUM_PROVIDER_URL, ETHEREUM_PROVIDER_URL, BASE_PROVIDER_URL
544
545
  ```
545
546
 
546
- Contributor and coding-agent instructions (commands, conventions, invariants, domain rules) live in [AGENTS.md](AGENTS.md).
547
+ Contributor and coding-agent instructions (commands, conventions, invariants, domain rules) live in [AGENTS.md](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/AGENTS.md).
547
548
 
548
549
  ## Examples
549
550
 
@@ -75,7 +75,7 @@ alt="Release">
75
75
  > vault read is one pinned snapshot, and several modules and names moved. A 3.x call
76
76
  > such as `PlasmaVault(ctx, address)` fails only when it runs, so a broad
77
77
  > `except Exception` can hide it. Follow the ordered checklist in
78
- > [MIGRATION_4.md, *Start here*](MIGRATION_4.md#start-here).
78
+ > [MIGRATION_4.md, *Start here*](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/MIGRATION_4.md#start-here).
79
79
 
80
80
  ## Quickstart
81
81
 
@@ -250,7 +250,7 @@ resources `fusion://glossary`, `fusion://architecture`, `fusion://invariants` an
250
250
 
251
251
  ## Agent skills
252
252
 
253
- [`skills/ipor-deploy-vault/SKILL.md`](skills/ipor-deploy-vault/SKILL.md) teaches a
253
+ [`skills/ipor-deploy-vault/SKILL.md`](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/skills/ipor-deploy-vault/SKILL.md) teaches a
254
254
  coding agent the full clone → roles → market → access posture → deposit → execute
255
255
  walk, with the invariants and the revert selectors, before it writes vault code.
256
256
  Its body is `fusion://invariants` and `fusion://quickstart` from `ipor_fusion.guide`
@@ -270,7 +270,7 @@ npx skills add IPOR-Labs/ipor-fusion.py -g -a codex # or gemini-cli, cursor
270
270
 
271
271
  ## Common errors
272
272
 
273
- Reverts on the deploy-and-configure path, keyed by selector so a failed transaction is greppable. This table and the full invariants ship in the wheel as [`ipor_fusion.guide`](src/ipor_fusion/guide/invariants.md), which `fusion-mcp` serves as `fusion://invariants`.
273
+ Reverts on the deploy-and-configure path, keyed by selector so a failed transaction is greppable. This table and the full invariants ship in the wheel as [`ipor_fusion.guide`](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/src/ipor_fusion/guide/invariants.md), which `fusion-mcp` serves as `fusion://invariants`.
274
274
 
275
275
  | Selector | Revert | What happened | Fix |
276
276
  |---|---|---|---|
@@ -286,7 +286,7 @@ Configuration order on a fresh vault: `add_fuses` → `grant_market_substrates`
286
286
 
287
287
  A flash-loan fuse also needs `update_callback_handler(handler, sender, selector)` before its first `execute`: Morpho Blue calls back `onMorphoFlashLoan(uint256,bytes)`, the Euler EVC `onEulerFlashLoan(bytes)`, and an unregistered callback reverts `HandlerNotFound()`.
288
288
 
289
- The full clone → configure → deposit → execute sequence is exercised in [`tests/test_simulate_vault_from_scratch_base.py`](tests/test_simulate_vault_from_scratch_base.py).
289
+ The full clone → configure → deposit → execute sequence is exercised in [`tests/test_simulate_vault_from_scratch_base.py`](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/tests/test_simulate_vault_from_scratch_base.py).
290
290
 
291
291
  Custom errors of the crosschain contracts decode by name: importing
292
292
  `ipor_fusion.crosschain` registers their signatures (`CROSSCHAIN_ERROR_SIGNATURES`),
@@ -298,22 +298,22 @@ raises `SimulationError` naming the first failed call.
298
298
  ## Vault construction examples
299
299
 
300
300
  Runnable, canonical examples for building and configuring a vault from scratch live in
301
- [`examples`](examples). They preview and simulate through `eth_simulateV1` — nothing is
301
+ [`examples`](https://github.com/IPOR-Labs/ipor-fusion.py/tree/main/examples). They preview and simulate through `eth_simulateV1` — nothing is
302
302
  ever signed or broadcast.
303
303
 
304
- - [Simple Aave V3 supply vault](examples/simple_aave_v3_supply_base.py) — start here for vault
304
+ - [Simple Aave V3 supply vault](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/simple_aave_v3_supply_base.py) — start here for vault
305
305
  creation, role bootstrap, and configuring a single supported market.
306
- - [Advanced Euler V2 credit-market vault](examples/advanced_euler_v2_credit_market_base.py) —
306
+ - [Advanced Euler V2 credit-market vault](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_euler_v2_credit_market_base.py) —
307
307
  continue here for composing multiple functional fuses, typed Euler substrates and sub-accounts,
308
308
  and an ordered collateral → borrow → repay → unwind lifecycle.
309
- - [External-state margin leg](examples/external_state_margin_leg_base.py) — a vault whose capital
309
+ - [External-state margin leg](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/external_state_margin_leg_base.py) — a vault whose capital
310
310
  sits off-chain: typed market-50 substrates, a dual-custodian propose/confirm marking the NAV,
311
311
  the scheduled-withdrawal prep an alpha performs, and a mismatched confirmation being rejected.
312
- - [Crosschain CCIP lifecycle](examples/crosschain_ccip_usdc_arbitrum_hyperevm.py) (preview feature) — a vault
312
+ - [Crosschain CCIP lifecycle](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py) (preview feature) — a vault
313
313
  whose capital works on another chain: supply USDC from an Arbitrum hub to a HyperEVM spoke
314
314
  over CCIP, attest the remote balance with two keys, deposit and redeem on the spoke, recall
315
315
  and claim, driven through `CrosschainLane` and relayed by `CrosschainSimulator`.
316
- - [Composed HyperCore flow](examples/composed_hypercore_flow_arbitrum_hyperevm.py) (preview feature) — the
316
+ - [Composed HyperCore flow](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/composed_hypercore_flow_arbitrum_hyperevm.py) (preview feature) — the
317
317
  same hub funding a HyperCore vault (market 55) that trades on Hyperliquid Core: the Core leg
318
318
  (bridge, dex transfer, order, close, exit) built from the vault's substrate grants and estimated
319
319
  with `eth_estimateGas` at head (EVM acceptance only, Core executes later), the REDEEM sized by the vault's EVM exit ceiling behind the executor's
@@ -400,6 +400,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
400
400
  | `ExternalStateExecutor` | NAV propose/confirm for off-vault capital (market 50); the executor's thresholds (`staleness_max`, `big_change_bps`, `dust_threshold`, `min_update_interval`), the live aggregate (`get_balance_fuse_snapshot`) and the pre-hook's staleness clock (`get_oldest_update_timestamp`) |
401
401
  | `read_external_state`, `ExternalStateVaultState` | The market's vault-scoped state from the vault's ERC-7201 storage at one block: executor, the balance fuse's cached total, the custodian timestamp it last checked and the pause flag the pre-hook enforces (no getter exposes them) |
402
402
  | `decode_external_state_event`, `EXTERNAL_STATE_TOPICS` | The ExternalState events the vault emits (big change, cache updates, unpause, rescue, executor creation and deployment, operation enter/exit), decoded by topic with their raw log identity; a malformed listed log raises |
403
+ | `decode_hypercore_event`, `hypercore_events` | **Preview.** The HyperCore fuse, pending-library, settlement and reporter events, decoded by topic with their raw log identity under the same input contract; a malformed listed log raises `HyperCoreEventDecodeError` |
403
404
  | `StargateCrosschainExecutor`, `CcipCrosschainExecutor` | **Preview.** Crosschain executor reads and attestation (`propose_balance`, `approve_balance`) |
404
405
  | `StargateCrosschainDispatcher`, `CcipCrosschainDispatcher` | Remote dispatcher state (`observation`, tracked idle, command lane) |
405
406
  | `StargateCrosschainFactory`, `CcipCrosschainFactory` | Executor creation and route/asset configuration reads |
@@ -427,11 +428,11 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
427
428
  | Universal | `UniversalTokenSwapperFuse` |
428
429
  | Off-vault capital | `AsyncActionFuse` (market 40), `ExternalStateOperationFuse` (market 50) |
429
430
  | Crosschain (preview) | `StargateCrosschainSupplyFuse`, `StargateCrosschainCommandFuse`, `CcipCrosschainSupplyFuse`, `CcipCrosschainCommandFuse`, `CrosschainClaimFuse` (market 54); transport-agnostic bases `CrosschainSupplyFuse` / `CrosschainCommandFuse` with `StargateSendParams` / `CcipSendParams`, plus `Command`, `CrosschainSubstrateLib`, `OptionsBuilder` |
431
+ | HyperCore (Hyperliquid, market 55, preview) | `HyperCoreDepositFuse`, `HyperCoreMarginFuse`, `HyperCoreSendFuse`, `HyperCoreOrderFuse`, `HyperCoreCancelFuse`, `HyperCoreBuilderFeeFuse`, `HyperCoreSubstrates` |
430
432
 
431
433
  The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Use
432
434
  `crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` for that deployment;
433
435
  `open_lane` discovers the vault's actual market grants rather than assuming 54.
434
- | HyperCore (Hyperliquid, market 55, preview) | `HyperCoreDepositFuse`, `HyperCoreMarginFuse`, `HyperCoreSendFuse`, `HyperCoreOrderFuse`, `HyperCoreCancelFuse`, `HyperCoreBuilderFeeFuse`, `HyperCoreSubstrates` |
435
436
 
436
437
  ### Readers (`ipor_fusion.readers`)
437
438
 
@@ -495,7 +496,7 @@ cp .env.example .env
495
496
  # Edit .env with ARBITRUM_PROVIDER_URL, ETHEREUM_PROVIDER_URL, BASE_PROVIDER_URL
496
497
  ```
497
498
 
498
- Contributor and coding-agent instructions (commands, conventions, invariants, domain rules) live in [AGENTS.md](AGENTS.md).
499
+ Contributor and coding-agent instructions (commands, conventions, invariants, domain rules) live in [AGENTS.md](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/AGENTS.md).
499
500
 
500
501
  ## Examples
501
502
 
@@ -43,6 +43,7 @@ from __future__ import annotations
43
43
  import logging
44
44
  import os
45
45
  import sys
46
+ from typing import TypedDict
46
47
 
47
48
  from eth_abi import encode
48
49
  from eth_typing import ChecksumAddress
@@ -238,7 +239,18 @@ def _evault_debt_of(
238
239
  # ── Pure builders (no chain access -- safe to import and unit-test offline) ───
239
240
 
240
241
 
241
- def clone_args() -> dict:
242
+ class CloneKwargs(TypedDict):
243
+ """The keyword arguments of ``FusionFactory.clone``."""
244
+
245
+ asset_name: str
246
+ asset_symbol: str
247
+ underlying_token: ChecksumAddress
248
+ redemption_delay_seconds: Period
249
+ owner: ChecksumAddress
250
+ dao_fee_package_index: int
251
+
252
+
253
+ def clone_args() -> CloneKwargs:
242
254
  """Arguments for FusionFactory.clone(...).
243
255
 
244
256
  The preview and the in-batch create MUST use identical args: same args plus
@@ -250,7 +262,7 @@ def clone_args() -> dict:
250
262
  "asset_name": "IPOR WETH Euler Credit Vault (example)",
251
263
  "asset_symbol": "ipWETHec",
252
264
  "underlying_token": BASE_WETH,
253
- "redemption_delay_seconds": 0,
265
+ "redemption_delay_seconds": Period(0),
254
266
  "owner": OWNER,
255
267
  "dao_fee_package_index": 0,
256
268
  }
@@ -27,6 +27,7 @@ from __future__ import annotations
27
27
  import logging
28
28
  import os
29
29
  import sys
30
+ from typing import TypedDict
30
31
 
31
32
  from eth_abi import encode
32
33
  from eth_abi.packed import encode_packed
@@ -193,12 +194,23 @@ def _debt_of(
193
194
  # ── Pure builders (no chain access) ──────────────────────────────────────────
194
195
 
195
196
 
196
- def clone_args() -> dict:
197
+ class CloneKwargs(TypedDict):
198
+ """The keyword arguments of ``FusionFactory.clone``."""
199
+
200
+ asset_name: str
201
+ asset_symbol: str
202
+ underlying_token: ChecksumAddress
203
+ redemption_delay_seconds: Period
204
+ owner: ChecksumAddress
205
+ dao_fee_package_index: int
206
+
207
+
208
+ def clone_args() -> CloneKwargs:
197
209
  return {
198
210
  "asset_name": "IPOR WBTC Euler Loop Vault (example)",
199
211
  "asset_symbol": "ipWBTCel",
200
212
  "underlying_token": ARBITRUM_WBTC,
201
- "redemption_delay_seconds": 0,
213
+ "redemption_delay_seconds": Period(0),
202
214
  "owner": OWNER,
203
215
  "dao_fee_package_index": 0,
204
216
  }