ipor-fusion 4.0.0.dev66__tar.gz → 4.0.0.dev68__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 (292) hide show
  1. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/MIGRATION_4.md +9 -0
  2. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/PKG-INFO +1 -1
  3. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/pyproject.toml +1 -1
  4. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/fusion_factory.py +64 -1
  5. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/simulation.py +15 -12
  6. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/quote.py +1 -3
  7. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/simulation.py +2 -6
  8. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_external_state_executor.py +1 -1
  9. ipor_fusion-4.0.0.dev68/tests/test_fusion_instance.py +70 -0
  10. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_crosschain_hyperevm.py +1 -3
  11. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_vault_simulator_blocks.py +11 -0
  12. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.claude-plugin/marketplace.json +0 -0
  13. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.claude-plugin/plugin.json +0 -0
  14. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.env.example +0 -0
  15. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.gemini/settings.json +0 -0
  16. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.git-blame-ignore-revs +0 -0
  17. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.github/workflows/cd.yml +0 -0
  18. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.github/workflows/ci.yml +0 -0
  19. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.github/workflows/commit-lint.yml +0 -0
  20. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.github/workflows/notify-slack.yml +0 -0
  21. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.github/workflows/python-build.yml +0 -0
  22. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.github/workflows/python-publish.yml +0 -0
  23. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.github/workflows/release.yml +0 -0
  24. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.github/workflows/report-slack.yml +0 -0
  25. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.gitignore +0 -0
  26. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.mcp.json +0 -0
  27. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.pre-commit-config.yaml +0 -0
  28. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/.python-version +0 -0
  29. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/AGENTS.md +0 -0
  30. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/CHANGELOG.md +0 -0
  31. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/CLAUDE.md +0 -0
  32. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/CODEOWNERS +0 -0
  33. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/LICENSE +0 -0
  34. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/README.md +0 -0
  35. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/build.sh +0 -0
  36. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/examples/CONTRIBUTING.md +0 -0
  37. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/examples/README.md +0 -0
  38. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  39. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  40. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  41. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  42. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/examples/external_state_margin_leg_base.py +0 -0
  43. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/examples/simple_aave_v3_supply_base.py +0 -0
  44. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/pytest.ini +0 -0
  45. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/scripts/commit-check.sh +0 -0
  46. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/scripts/crosschain_readiness.py +0 -0
  47. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/skills/ipor-deploy-vault/SKILL.md +0 -0
  48. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/__init__.py +0 -0
  49. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/about.py +0 -0
  50. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cache.py +0 -0
  51. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/chains.py +0 -0
  52. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/__init__.py +0 -0
  53. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  54. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/config_cmd.py +0 -0
  55. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/config_store.py +0 -0
  56. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/main.py +0 -0
  57. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/market_cmd.py +0 -0
  58. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/options.py +0 -0
  59. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  60. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  61. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_health.py +0 -0
  62. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  63. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  64. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  65. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/config/__init__.py +0 -0
  66. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/config/roles.py +0 -0
  67. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/__init__.py +0 -0
  68. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/access.py +0 -0
  69. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/address.py +0 -0
  70. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/context.py +0 -0
  71. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/contract.py +0 -0
  72. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/erc20.py +0 -0
  73. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/external_state_executor.py +0 -0
  74. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/fee_manager.py +0 -0
  75. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/fuse_action.py +0 -0
  76. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/logs.py +0 -0
  77. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/multicall.py +0 -0
  78. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/oracle.py +0 -0
  79. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/plasma_vault.py +0 -0
  80. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/rewards_manager.py +0 -0
  81. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  82. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/__init__.py +0 -0
  83. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  84. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  85. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  86. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  87. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  88. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  89. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  90. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  91. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/contracts.py +0 -0
  92. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/discovery.py +0 -0
  93. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/errors.py +0 -0
  94. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/lane.py +0 -0
  95. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/logs.py +0 -0
  96. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/messages.py +0 -0
  97. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  98. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  99. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  100. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  101. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  102. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/transport.py +0 -0
  103. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/dependency_graph.py +0 -0
  104. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/entry_cli.py +0 -0
  105. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/entry_mcp.py +0 -0
  106. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/errors.py +0 -0
  107. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/explorer.py +0 -0
  108. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/field_docs.py +0 -0
  109. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/__init__.py +0 -0
  110. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  111. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/async_action.py +0 -0
  112. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/base.py +0 -0
  113. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  114. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
  115. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
  116. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
  117. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
  118. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/erc4626.py +0 -0
  119. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  120. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/events.py +0 -0
  121. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/external_state.py +0 -0
  122. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  123. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  124. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  125. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/hypercore.py +0 -0
  126. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  127. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/merkl.py +0 -0
  128. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/morpho.py +0 -0
  129. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  130. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  131. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/universal.py +0 -0
  132. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/__init__.py +0 -0
  133. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/architecture.md +0 -0
  134. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/glossary.md +0 -0
  135. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/invariants.md +0 -0
  136. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/quickstart.md +0 -0
  137. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/market_ids.py +0 -0
  138. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/market_info.py +0 -0
  139. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/mcp/__init__.py +0 -0
  140. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/mcp/guide.py +0 -0
  141. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/mcp/models.py +0 -0
  142. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/mcp/server.py +0 -0
  143. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/morpho_api.py +0 -0
  144. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/py.typed +0 -0
  145. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/__init__.py +0 -0
  146. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/aave_v3.py +0 -0
  147. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/aave_v4.py +0 -0
  148. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/compound_v3.py +0 -0
  149. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/hypercore.py +0 -0
  150. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  151. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/lending_health.py +0 -0
  152. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/morpho.py +0 -0
  153. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  154. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  155. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  156. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/position_manager.py +0 -0
  157. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  158. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  159. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/unpriceable.py +0 -0
  160. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/substrates.py +0 -0
  161. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/types.py +0 -0
  162. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/validation.py +0 -0
  163. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_data.py +0 -0
  164. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_info.py +0 -0
  165. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_managers.py +0 -0
  166. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_positions.py +0 -0
  167. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_reads.py +0 -0
  168. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/UniswapV3UniversalRouter.py +0 -0
  169. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_crosschain.py +0 -0
  170. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_crosschain_lifecycle.py +0 -0
  171. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_crosschain_recovery.py +0 -0
  172. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_euler_v2.py +0 -0
  173. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_foundry.py +0 -0
  174. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_guide.py +0 -0
  175. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_hypercore_model.py +0 -0
  176. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_hypercore_shadow.py +0 -0
  177. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_multicall.py +0 -0
  178. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_simulate.py +0 -0
  179. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/_validation.py +0 -0
  180. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/addresses.py +0 -0
  181. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/conftest.py +0 -0
  182. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/constants.py +0 -0
  183. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  184. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/fixtures/hypercore_market55.json +0 -0
  185. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/fixtures/vault_info_golden/clean.json +0 -0
  186. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  187. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/fixtures/vault_info_golden/rich.json +0 -0
  188. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  189. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_about.py +0 -0
  190. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_access_manager.py +0 -0
  191. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cache.py +0 -0
  192. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_chains.py +0 -0
  193. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_changelog.py +0 -0
  194. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_commands.py +0 -0
  195. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_config_store.py +0 -0
  196. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_helpers.py +0 -0
  197. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_hypercore.py +0 -0
  198. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_lending_health.py +0 -0
  199. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_market_morpho_blue.py +0 -0
  200. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_oracle_mapping.py +0 -0
  201. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_pending_requests_scan.py +0 -0
  202. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_role_accounts.py +0 -0
  203. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_vault_info_golden.py +0 -0
  204. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_vault_predates_getters.py +0 -0
  205. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_cli_vault_validate.py +0 -0
  206. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_contract_call.py +0 -0
  207. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_contract_encoder.py +0 -0
  208. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_ccip_events.py +0 -0
  209. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_ccip_indexer.py +0 -0
  210. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_ccip_quote.py +0 -0
  211. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_chainlink.py +0 -0
  212. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_contracts.py +0 -0
  213. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_encoding.py +0 -0
  214. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_errors.py +0 -0
  215. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_lane.py +0 -0
  216. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_layering.py +0 -0
  217. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_readiness.py +0 -0
  218. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_readiness_report.py +0 -0
  219. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_transport.py +0 -0
  220. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_erc20.py +0 -0
  221. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_examples_composed_hypercore.py +0 -0
  222. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_examples_crosschain.py +0 -0
  223. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  224. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_examples_vaults.py +0 -0
  225. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_explorer.py +0 -0
  226. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_external_state_events.py +0 -0
  227. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_fee_manager.py +0 -0
  228. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_fuse_encoding.py +0 -0
  229. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_guide.py +0 -0
  230. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_deployment.py +0 -0
  231. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_events.py +0 -0
  232. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_flow.py +0 -0
  233. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_live.py +0 -0
  234. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_outcome.py +0 -0
  235. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_readers.py +0 -0
  236. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_simulation.py +0 -0
  237. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_logs.py +0 -0
  238. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_mcp_hypercore.py +0 -0
  239. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_mcp_models.py +0 -0
  240. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_mcp_server.py +0 -0
  241. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_mcp_vault_validate.py +0 -0
  242. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_multicall.py +0 -0
  243. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_oracle.py +0 -0
  244. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_oracle_mapping.py +0 -0
  245. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_plasma_vault.py +0 -0
  246. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_reader_encoding.py +0 -0
  247. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_readiness_selection.py +0 -0
  248. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_revert_decoding.py +0 -0
  249. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_rewards_manager.py +0 -0
  250. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_roles.py +0 -0
  251. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_sdk_layering.py +0 -0
  252. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  253. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  254. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  255. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_crosschain_canary.py +0 -0
  256. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_crosschain_hypercore.py +0 -0
  257. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  258. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  259. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  260. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  261. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  262. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  263. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_examples_arbitrum.py +0 -0
  264. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_examples_base.py +0 -0
  265. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_examples_crosschain.py +0 -0
  266. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_external_state_executor_base.py +0 -0
  267. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  268. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  269. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  270. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_morpho_blue.py +0 -0
  271. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_pilot_v3.py +0 -0
  272. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  273. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  274. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  275. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  276. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_readers_integration.py +0 -0
  277. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_release_funds.py +0 -0
  278. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_swap_on_base.py +0 -0
  279. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  280. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_solidity_compiler.py +0 -0
  281. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_solidity_mirrors.py +0 -0
  282. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_substrates.py +0 -0
  283. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_types_and_events.py +0 -0
  284. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_unpriceable_tokens.py +0 -0
  285. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_validation.py +0 -0
  286. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_vault_dep_graph.py +0 -0
  287. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_vault_info_onchain_base.py +0 -0
  288. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  289. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_web3_context.py +0 -0
  290. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/test_withdraw_manager.py +0 -0
  291. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/tests/typed_client/consumer.py +0 -0
  292. {ipor_fusion-4.0.0.dev66 → ipor_fusion-4.0.0.dev68}/uv.lock +0 -0
@@ -110,6 +110,15 @@ Every fuse constructor, `FuseAction.fuse`, `BalanceFuse.fuse`,
110
110
  `FusionInstance.access_manager` return one. `resolve_access_manager` still takes any
111
111
  `ChecksumAddress`: it is what checks that the address is a vault.
112
112
 
113
+ **Simulation and factory records.** `VaultSimulator(web3, *, vault=None, alpha=None,
114
+ block=...)` takes every argument after `web3` by keyword, and `vault` and `alpha` are
115
+ optional: a simulator that only observes, deploys or sends `execute_call(call,
116
+ from_=...)` needs neither, so drop a zero-address placeholder. `execute()` without
117
+ `vault` and `alpha` raises `ValueError`, as does `execute_call` without `from_` or
118
+ `alpha`. `FusionInstance.from_dict(data)` rebuilds a stored instance (the
119
+ `dataclasses.asdict` form) with every field parsed; use it instead of
120
+ `FusionInstance(**data)`, which passes raw strings into the role-typed fields.
121
+
113
122
  **Markets and substrates.** Every `IporFusionMarkets` member is a `MarketId`.
114
123
  `decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
115
124
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev66
3
+ Version: 4.0.0.dev68
4
4
  Summary: The IPOR Fusion SDK for Python
5
5
  Project-URL: homepage, https://ipor.io
6
6
  Project-URL: repository, https://github.com/IPOR-Labs/ipor-fusion.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev66"
3
+ version = "4.0.0.dev68"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -22,7 +22,9 @@ For external-signer flows, grab the bytes directly:
22
22
 
23
23
  from __future__ import annotations
24
24
 
25
+ from collections.abc import Callable, Mapping
25
26
  from dataclasses import dataclass
27
+ from typing import Any
26
28
 
27
29
  from eth_abi import decode as abi_decode
28
30
  from eth_typing import ChecksumAddress
@@ -31,7 +33,13 @@ from web3 import Web3
31
33
  from web3.types import TxReceipt
32
34
 
33
35
  from ipor_fusion.core.contract import Call, ContractWrapper
34
- from ipor_fusion.types import AccessManagerAddress, Period, PlasmaVaultAddress
36
+ from ipor_fusion.types import (
37
+ AccessManagerAddress,
38
+ Period,
39
+ PlasmaVaultAddress,
40
+ parse_access_manager_address,
41
+ parse_plasma_vault_address,
42
+ )
35
43
 
36
44
 
37
45
  @dataclass(slots=True, frozen=True)
@@ -70,6 +78,61 @@ class FusionInstance:
70
78
  context_manager: ChecksumAddress
71
79
  price_manager: ChecksumAddress
72
80
 
81
+ @classmethod
82
+ def from_dict(
83
+ cls, data: Mapping[str, Any]
84
+ ) -> FusionInstance: # proctor: ignore[primitives-untyped-dict]
85
+ """Rebuild an instance from its JSON form (``dataclasses.asdict``),
86
+ parsing every field: role addresses through their parsers, the rest
87
+ checksummed, integers and strings type-checked. Raises ``ValueError``
88
+ on a missing, extra or malformed field."""
89
+ missing = _INSTANCE_FIELDS.keys() - data.keys()
90
+ extra = data.keys() - _INSTANCE_FIELDS.keys()
91
+ if missing or extra:
92
+ raise ValueError(
93
+ f"FusionInstance fields: missing {sorted(missing)}, extra {sorted(extra)}"
94
+ )
95
+ return cls(
96
+ **{name: parse(data[name]) for name, parse in _INSTANCE_FIELDS.items()}
97
+ )
98
+
99
+
100
+ def _integer(value: Any) -> int:
101
+ if type(value) is not int:
102
+ raise ValueError(f"expected an integer: {value!r}")
103
+ return value
104
+
105
+
106
+ def _text(value: Any) -> str:
107
+ if not isinstance(value, str):
108
+ raise ValueError(f"expected a string: {value!r}")
109
+ return value
110
+
111
+
112
+ def _address(value: Any) -> ChecksumAddress:
113
+ return Web3.to_checksum_address(_text(value))
114
+
115
+
116
+ _INSTANCE_FIELDS: dict[str, Callable[[Any], Any]] = {
117
+ "index": _integer,
118
+ "version": _integer,
119
+ "asset_name": _text,
120
+ "asset_symbol": _text,
121
+ "asset_decimals": _integer,
122
+ "underlying_token": _address,
123
+ "underlying_token_symbol": _text,
124
+ "underlying_token_decimals": _integer,
125
+ "initial_owner": _address,
126
+ "plasma_vault": lambda value: parse_plasma_vault_address(_text(value)),
127
+ "plasma_vault_base": _address,
128
+ "access_manager": lambda value: parse_access_manager_address(_text(value)),
129
+ "fee_manager": _address,
130
+ "rewards_manager": _address,
131
+ "withdraw_manager": _address,
132
+ "context_manager": _address,
133
+ "price_manager": _address,
134
+ }
135
+
73
136
 
74
137
  _FUSION_INSTANCE_OUTPUT_TYPES: list[str] = [
75
138
  "uint256", # index
@@ -16,7 +16,7 @@ from web3.utils.address import get_create_address
16
16
  from ipor_fusion.core.contract import Call, _encode_calldata
17
17
  from ipor_fusion.core.fuse_action import FuseAction
18
18
  from ipor_fusion.errors import SimulationError, decode_custom_error
19
- from ipor_fusion.types import ZERO_ADDRESS, PlasmaVaultAddress
19
+ from ipor_fusion.types import PlasmaVaultAddress
20
20
 
21
21
  #: ``n`` (the sent block's index in the run) -> account overrides for it.
22
22
  StateOverrideProvider = Callable[[int], Mapping[str, Mapping[str, Any]]]
@@ -190,15 +190,16 @@ class VaultSimulator:
190
190
  def __init__(
191
191
  self,
192
192
  web3: Web3,
193
- vault: PlasmaVaultAddress,
194
- alpha: ChecksumAddress,
193
+ *,
194
+ vault: PlasmaVaultAddress | None = None,
195
+ alpha: ChecksumAddress | None = None,
195
196
  block: BlockIdentifier = "latest",
196
197
  validation: bool = False,
197
198
  trace_transfers: bool = False,
198
199
  ):
199
200
  self._web3 = web3
200
- self._vault = Web3.to_checksum_address(vault)
201
- self._alpha = Web3.to_checksum_address(alpha)
201
+ self._vault = vault
202
+ self._alpha = alpha
202
203
  self._block: BlockIdentifier = block
203
204
  self._validation = validation
204
205
  self._trace_transfers = trace_transfers
@@ -341,6 +342,8 @@ class VaultSimulator:
341
342
 
342
343
  def execute(self, actions: list[FuseAction]) -> VaultSimulator:
343
344
  """Queue a default `execute((address,bytes)[])` batch on the vault, from alpha."""
345
+ if self._vault is None or self._alpha is None:
346
+ raise ValueError("execute needs a simulator built with vault= and alpha=")
344
347
  data = FuseAction.encode_execute_payload(actions, "execute((address,bytes)[])")
345
348
  return self._queue_execute(self._vault, data, self._alpha)
346
349
 
@@ -351,11 +354,12 @@ class VaultSimulator:
351
354
  e.g. `RewardsManager.claim_rewards(...)` where the wrapper differs from
352
355
  `PlasmaVault.execute`. `from_` defaults to alpha.
353
356
  """
354
- return self._queue_execute(
355
- call.to,
356
- call.data,
357
- Web3.to_checksum_address(from_) if from_ else self._alpha,
358
- )
357
+ sender = from_ or self._alpha
358
+ if sender is None:
359
+ raise ValueError(
360
+ "execute_call needs from_ or a simulator built with alpha="
361
+ )
362
+ return self._queue_execute(call.to, call.data, sender)
359
363
 
360
364
  def _queue_execute(
361
365
  self, to: ChecksumAddress, data: bytes, from_: ChecksumAddress
@@ -751,8 +755,7 @@ def erc20_balance_slot(
751
755
  if max_slot < 0:
752
756
  raise ValueError(no_slot)
753
757
 
754
- zero: Any = ZERO_ADDRESS
755
- sim = VaultSimulator(web3, vault=zero, alpha=zero, block=block)
758
+ sim = VaultSimulator(web3, block=block)
756
759
  for slot in range(max_slot + 1):
757
760
  holder = Web3.to_checksum_address(
758
761
  "0x" + keccak(encode(["uint256"], [slot]))[-20:].hex()
@@ -18,13 +18,11 @@ from web3.types import BlockIdentifier
18
18
 
19
19
  from ipor_fusion.core.contract import Call
20
20
  from ipor_fusion.core.simulation import VaultSimulator
21
- from ipor_fusion.types import parse_plasma_vault_address
22
21
 
23
22
  #: Selector of ``CcipInsufficientNativeFeeBalance(uint256 balance, uint256 required)``.
24
23
  INSUFFICIENT_NATIVE_FEE_SELECTOR: bytes = keccak(
25
24
  text="CcipInsufficientNativeFeeBalance(uint256,uint256)"
26
25
  )[:4]
27
- _NO_VAULT = parse_plasma_vault_address(bytes(20))
28
26
 
29
27
 
30
28
  def quote_ccip_native_fee(
@@ -45,7 +43,7 @@ def quote_ccip_native_fee(
45
43
  valid at ``block`` up to the fee check; a revert before it, or a call that
46
44
  sends no message, raises ``ValueError``.
47
45
  """
48
- simulator = VaultSimulator(web3, vault=_NO_VAULT, alpha=from_, block=block)
46
+ simulator = VaultSimulator(web3, alpha=from_, block=block)
49
47
  simulator.with_state_override(payer, balance=hex(0))
50
48
  simulator.add_call(call, from_=from_, label="quote", gas=gas)
51
49
  (result,) = simulator.run().calls
@@ -25,7 +25,6 @@ Example -- bridge from a source vault and let the dispatcher settle::
25
25
  from __future__ import annotations
26
26
 
27
27
  from collections.abc import Mapping
28
- from typing import Any
29
28
 
30
29
  from eth_typing import ChecksumAddress
31
30
  from web3 import Web3
@@ -41,7 +40,7 @@ from ipor_fusion.crosschain.transport import (
41
40
  CrosschainTransport,
42
41
  OutboundMessage,
43
42
  )
44
- from ipor_fusion.types import ZERO_ADDRESS, ChainId, PlasmaVaultAddress
43
+ from ipor_fusion.types import ChainId, PlasmaVaultAddress
45
44
 
46
45
  #: What the synthetic token source holds: enough for any test amount.
47
46
  SYNTHETIC_BALANCE = 2**96
@@ -89,10 +88,7 @@ class CrosschainSimulator:
89
88
  raise ValueError(f"chain {chain_id} is not configured on the transport")
90
89
  if chain_id in self._chains:
91
90
  raise ValueError(f"chain {chain_id} already added")
92
- zero: Any = ZERO_ADDRESS
93
- sim = VaultSimulator(
94
- web3, vault=vault or zero, alpha=alpha or zero, block=block
95
- )
91
+ sim = VaultSimulator(web3, vault=vault, alpha=alpha, block=block)
96
92
  # A chain without a real token holder to impersonate gets a synthetic
97
93
  # one, funded once by a storage override; deliveries transfer from it.
98
94
  for token in self._transport.synthetic_credit_tokens(chain_id):
@@ -64,7 +64,7 @@ def _observed(call, return_data: bytes):
64
64
  Feeds a synthetic `eth_simulateV1` response to `_parse_response`, so the
65
65
  result is whatever `observe` would actually report -- including its handling
66
66
  of a decoder that raises."""
67
- sim = VaultSimulator(MagicMock(), EXECUTOR_ADDR, EXECUTOR_ADDR)
67
+ sim = VaultSimulator(MagicMock())
68
68
  sim.observe("slot", call)
69
69
  result = sim._parse_response(
70
70
  simulate_response(
@@ -0,0 +1,70 @@
1
+ """`FusionInstance.from_dict` parses a stored instance back into its typed
2
+ fields instead of passing raw JSON strings into role-typed addresses."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from dataclasses import asdict
7
+
8
+ import pytest
9
+ from web3 import Web3
10
+
11
+ from ipor_fusion.core.fusion_factory import FusionInstance
12
+
13
+
14
+ def _stored() -> dict:
15
+ address = {
16
+ name: "0x" + f"{i:02x}" * 20
17
+ for i, name in enumerate(
18
+ [
19
+ "underlying_token",
20
+ "initial_owner",
21
+ "plasma_vault",
22
+ "plasma_vault_base",
23
+ "access_manager",
24
+ "fee_manager",
25
+ "rewards_manager",
26
+ "withdraw_manager",
27
+ "context_manager",
28
+ "price_manager",
29
+ ],
30
+ start=0xA1,
31
+ )
32
+ }
33
+ return {
34
+ "index": 3,
35
+ "version": 2,
36
+ "asset_name": "IPOR USDC",
37
+ "asset_symbol": "ipUSDC",
38
+ "asset_decimals": 8,
39
+ "underlying_token_symbol": "USDC",
40
+ "underlying_token_decimals": 6,
41
+ **address,
42
+ }
43
+
44
+
45
+ def test_from_dict_checksums_every_address_and_round_trips():
46
+ stored = _stored()
47
+ instance = FusionInstance.from_dict(stored)
48
+ assert instance.plasma_vault == Web3.to_checksum_address(stored["plasma_vault"])
49
+ assert instance.access_manager == Web3.to_checksum_address(stored["access_manager"])
50
+ assert instance.price_manager == Web3.to_checksum_address(stored["price_manager"])
51
+ assert FusionInstance.from_dict(asdict(instance)) == instance
52
+
53
+
54
+ @pytest.mark.parametrize(
55
+ ("change", "match"),
56
+ [
57
+ (lambda d: d.pop("plasma_vault"), "missing"),
58
+ (lambda d: d.update(extra=1), "extra"),
59
+ (lambda d: d.update(plasma_vault="0x1234"), "address"),
60
+ (lambda d: d.update(plasma_vault=" " + d["plasma_vault"]), "address"),
61
+ (lambda d: d.update(asset_decimals="8"), "integer"),
62
+ (lambda d: d.update(index=True), "integer"),
63
+ (lambda d: d.update(asset_symbol=None), "string"),
64
+ ],
65
+ )
66
+ def test_from_dict_rejects_a_malformed_field(change, match):
67
+ stored = _stored()
68
+ change(stored)
69
+ with pytest.raises(ValueError, match=match):
70
+ FusionInstance.from_dict(stored)
@@ -343,7 +343,6 @@ def test_simulate_contract_creation(web3_arb):
343
343
  init_code = bytes.fromhex("600a600c600039600a6000f3") + runtime
344
344
  simulator = VaultSimulator(
345
345
  web3_arb,
346
- vault=ZERO_ADDRESS,
347
346
  alpha=SIMULATED_DEPLOYER,
348
347
  block=ARBITRUM_BLOCK,
349
348
  )
@@ -374,7 +373,6 @@ def test_compile_and_deploy_crosschain_fuses(web3_arb):
374
373
  )
375
374
  simulator = VaultSimulator(
376
375
  web3_arb,
377
- vault=ZERO_ADDRESS,
378
376
  alpha=SIMULATED_DEPLOYER,
379
377
  block=ARBITRUM_BLOCK,
380
378
  )
@@ -804,7 +802,7 @@ def test_simulate_clone_htest_vault(
804
802
  OWNER,
805
803
  )
806
804
  preview = clone.call()
807
- simulator = VaultSimulator(web3, vault=ZERO_ADDRESS, alpha=OWNER, block=block)
805
+ simulator = VaultSimulator(web3, alpha=OWNER, block=block)
808
806
  simulator.with_block_override(gasLimit=30_000_000)
809
807
  simulator.add_call(clone, from_=OWNER, label="clone_vault")
810
808
  vault = PlasmaVault(ctx, preview.plasma_vault)
@@ -573,3 +573,14 @@ def test_add_transaction_rejects_what_it_cannot_send_faithfully(transaction, mes
573
573
  sim, _ = _simulator()
574
574
  with pytest.raises(ValueError, match=message):
575
575
  sim.add_transaction(transaction)
576
+
577
+
578
+ def test_a_simulator_without_vault_or_alpha_observes_but_cannot_execute():
579
+ sim = VaultSimulator(MagicMock(), block=100)
580
+ call = Call(to=PAYER, data=b"\x01", ctx=MagicMock())
581
+ with pytest.raises(ValueError, match="vault= and alpha="):
582
+ sim.execute([])
583
+ with pytest.raises(ValueError, match="from_ or"):
584
+ sim.execute_call(call)
585
+ sim.execute_call(call, from_=PAYER)
586
+ assert sim.has_calls