ipor-fusion 4.0.0.dev126__tar.gz → 4.0.0.dev137__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 (298) hide show
  1. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/AGENTS.md +9 -3
  2. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/MIGRATION_4.md +65 -20
  3. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/PKG-INFO +1 -1
  4. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/examples/CONTRIBUTING.md +1 -5
  5. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/examples/advanced_euler_v2_credit_market_base.py +3 -2
  6. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/examples/advanced_euler_v2_looping_arbitrum.py +3 -2
  7. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +8 -9
  8. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +4 -5
  9. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/examples/external_state_margin_leg_base.py +4 -10
  10. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/examples/simple_aave_v3_supply_base.py +3 -2
  11. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/pyproject.toml +1 -1
  12. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/scripts/crosschain_readiness.py +5 -7
  13. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/__init__.py +10 -6
  14. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/market_cmd.py +9 -9
  15. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/options.py +1 -1
  16. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_cmd.py +1 -1
  17. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/access.py +9 -1
  18. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/context.py +2 -4
  19. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/contract.py +1 -1
  20. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/external_state_executor.py +37 -172
  21. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/multicall.py +1 -0
  22. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/oracle.py +5 -3
  23. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/plasma_vault.py +6 -5
  24. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/__init__.py +8 -2
  25. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/__init__.py +1 -1
  26. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/chainlink.py +9 -8
  27. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/codec.py +8 -8
  28. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/contracts.py +5 -4
  29. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/indexer.py +1 -8
  30. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/transport.py +3 -3
  31. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/fuses/ccip.py +2 -2
  32. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/lane.py +67 -5
  33. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/dependency_graph.py +2 -2
  34. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/errors.py +21 -3
  35. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/explorer.py +2 -2
  36. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/__init__.py +2 -2
  37. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/external_state_events.py +1 -7
  38. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/hypercore.py +2 -2
  39. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/market_info.py +6 -3
  40. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/mcp/server.py +10 -3
  41. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/hypercore.py +11 -9
  42. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/hypercore_outcome.py +2 -5
  43. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/unpriceable.py +3 -2
  44. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/substrates.py +2 -2
  45. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/types.py +2 -0
  46. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_data.py +6 -6
  47. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_crosschain.py +1 -3
  48. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_crosschain_lifecycle.py +6 -4
  49. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_access_manager.py +14 -0
  50. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_commands.py +1 -1
  51. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_oracle_mapping.py +7 -4
  52. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_lane.py +110 -1
  53. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_error_hierarchy.py +12 -0
  54. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_explorer.py +11 -3
  55. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_external_state_executor.py +10 -366
  56. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_fuse_encoding.py +3 -3
  57. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_deployment.py +1 -2
  58. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_live.py +8 -6
  59. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_outcome.py +1 -2
  60. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_readers.py +6 -6
  61. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_simulation.py +0 -3
  62. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_mcp_server.py +7 -4
  63. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_public_api.py +10 -3
  64. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_borrow_aave_v3_mainnet.py +6 -4
  65. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_claim_merkl_wrapper_base.py +3 -2
  66. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_claim_morpho_rewards.py +3 -2
  67. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_crosschain_hypercore.py +1 -2
  68. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_crosschain_hyperevm.py +1 -3
  69. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_batch_base.py +3 -2
  70. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_credit_market_base.py +3 -2
  71. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_swap_deploy_base.py +3 -2
  72. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +3 -2
  73. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_swap_registry_base.py +3 -2
  74. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_external_state_executor_base.py +3 -2
  75. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_looping_aave_v3_base.py +3 -2
  76. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_looping_morpho_blue_base.py +3 -2
  77. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_morpho_blue.py +0 -3
  78. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_pilot_v3.py +12 -8
  79. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_pilot_v4_uniswap.py +15 -10
  80. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_pilot_v5_ramses.py +12 -8
  81. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_pilot_v5_scheduled.py +18 -12
  82. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_readers_integration.py +1 -3
  83. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_release_funds.py +6 -4
  84. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_swap_on_base.py +6 -4
  85. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_vault_from_scratch_base.py +3 -2
  86. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_unpriceable_tokens.py +5 -5
  87. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +1 -2
  88. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_web3_context.py +2 -4
  89. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/typed_client/consumer.py +62 -2
  90. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/typed_client/rejected.py +28 -0
  91. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.claude-plugin/marketplace.json +0 -0
  92. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.claude-plugin/plugin.json +0 -0
  93. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.env.example +0 -0
  94. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.gemini/settings.json +0 -0
  95. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.git-blame-ignore-revs +0 -0
  96. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.github/workflows/cd.yml +0 -0
  97. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.github/workflows/ci.yml +0 -0
  98. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.github/workflows/commit-lint.yml +0 -0
  99. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.github/workflows/notify-slack.yml +0 -0
  100. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.github/workflows/python-build.yml +0 -0
  101. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.github/workflows/python-publish.yml +0 -0
  102. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.github/workflows/release.yml +0 -0
  103. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.github/workflows/report-slack.yml +0 -0
  104. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.gitignore +0 -0
  105. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.mcp.json +0 -0
  106. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.pre-commit-config.yaml +0 -0
  107. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/.python-version +0 -0
  108. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/CHANGELOG.md +0 -0
  109. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/CLAUDE.md +0 -0
  110. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/CODEOWNERS +0 -0
  111. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/LICENSE +0 -0
  112. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/README.md +0 -0
  113. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/build.sh +0 -0
  114. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/examples/README.md +0 -0
  115. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/pytest.ini +0 -0
  116. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/scripts/commit-check.sh +0 -0
  117. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/skills/ipor-deploy-vault/SKILL.md +0 -0
  118. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/about.py +0 -0
  119. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cache.py +0 -0
  120. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/chains.py +0 -0
  121. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/__init__.py +0 -0
  122. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  123. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/config_cmd.py +0 -0
  124. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/config_store.py +0 -0
  125. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/main.py +0 -0
  126. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  127. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_health.py +0 -0
  128. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  129. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  130. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  131. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/config/__init__.py +0 -0
  132. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/config/roles.py +0 -0
  133. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/__init__.py +0 -0
  134. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/address.py +0 -0
  135. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/erc20.py +0 -0
  136. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/fee_manager.py +0 -0
  137. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/fuse.py +0 -0
  138. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/fuse_action.py +0 -0
  139. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/fusion_factory.py +0 -0
  140. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/logs.py +0 -0
  141. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/rewards_manager.py +0 -0
  142. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/simulation.py +0 -0
  143. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  144. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  145. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  146. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  147. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/contracts.py +0 -0
  148. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/discovery.py +0 -0
  149. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/errors.py +0 -0
  150. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  151. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  152. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  153. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/logs.py +0 -0
  154. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/messages.py +0 -0
  155. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/simulation.py +0 -0
  156. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  157. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  158. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  159. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  160. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  161. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/transport.py +0 -0
  162. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/entry_cli.py +0 -0
  163. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/entry_mcp.py +0 -0
  164. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/field_docs.py +0 -0
  165. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  166. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/async_action.py +0 -0
  167. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/base.py +0 -0
  168. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  169. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/erc4626.py +0 -0
  170. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  171. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/events.py +0 -0
  172. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/external_state.py +0 -0
  173. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  174. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  175. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  176. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/merkl.py +0 -0
  177. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/morpho.py +0 -0
  178. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  179. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  180. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/universal.py +0 -0
  181. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/__init__.py +0 -0
  182. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/architecture.md +0 -0
  183. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/glossary.md +0 -0
  184. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/invariants.md +0 -0
  185. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/quickstart.md +0 -0
  186. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/market_ids.py +0 -0
  187. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/mcp/__init__.py +0 -0
  188. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/mcp/guide.py +0 -0
  189. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/mcp/models.py +0 -0
  190. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/morpho_api.py +0 -0
  191. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/py.typed +0 -0
  192. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/__init__.py +0 -0
  193. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/aave_v3.py +0 -0
  194. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/aave_v4.py +0 -0
  195. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/compound_v3.py +0 -0
  196. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/lending_health.py +0 -0
  197. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/morpho.py +0 -0
  198. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  199. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  200. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  201. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/position_manager.py +0 -0
  202. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  203. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  204. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/validation.py +0 -0
  205. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_erc20.py +0 -0
  206. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_info.py +0 -0
  207. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_managers.py +0 -0
  208. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_positions.py +0 -0
  209. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_reads.py +0 -0
  210. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/UniswapV3UniversalRouter.py +0 -0
  211. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_crosschain_recovery.py +0 -0
  212. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_euler_v2.py +0 -0
  213. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_foundry.py +0 -0
  214. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_guide.py +0 -0
  215. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_hypercore_model.py +0 -0
  216. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_hypercore_shadow.py +0 -0
  217. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_multicall.py +0 -0
  218. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_simulate.py +0 -0
  219. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/_validation.py +0 -0
  220. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/addresses.py +0 -0
  221. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/conftest.py +0 -0
  222. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/constants.py +0 -0
  223. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  224. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/fixtures/hypercore_market55.json +0 -0
  225. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/fixtures/vault_info_golden/clean.json +0 -0
  226. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  227. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/fixtures/vault_info_golden/rich.json +0 -0
  228. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  229. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_about.py +0 -0
  230. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cache.py +0 -0
  231. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_chains.py +0 -0
  232. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_changelog.py +0 -0
  233. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_config_store.py +0 -0
  234. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_helpers.py +0 -0
  235. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_hypercore.py +0 -0
  236. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_lending_health.py +0 -0
  237. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_market_morpho_blue.py +0 -0
  238. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_pending_requests_scan.py +0 -0
  239. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_role_accounts.py +0 -0
  240. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_vault_info_golden.py +0 -0
  241. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_vault_predates_getters.py +0 -0
  242. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_cli_vault_validate.py +0 -0
  243. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_contract_call.py +0 -0
  244. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_contract_encoder.py +0 -0
  245. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_ccip_events.py +0 -0
  246. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_ccip_indexer.py +0 -0
  247. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_ccip_quote.py +0 -0
  248. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_chainlink.py +0 -0
  249. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_contracts.py +0 -0
  250. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_encoding.py +0 -0
  251. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_errors.py +0 -0
  252. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_layering.py +0 -0
  253. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_readiness.py +0 -0
  254. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_readiness_report.py +0 -0
  255. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_transport.py +0 -0
  256. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_erc20.py +0 -0
  257. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_examples_composed_hypercore.py +0 -0
  258. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_examples_crosschain.py +0 -0
  259. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  260. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_examples_vaults.py +0 -0
  261. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_external_state_events.py +0 -0
  262. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_fee_manager.py +0 -0
  263. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_fusion_instance.py +0 -0
  264. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_guide.py +0 -0
  265. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_events.py +0 -0
  266. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_flow.py +0 -0
  267. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_logs.py +0 -0
  268. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_mcp_hypercore.py +0 -0
  269. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_mcp_models.py +0 -0
  270. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_mcp_vault_validate.py +0 -0
  271. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_multicall.py +0 -0
  272. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_oracle.py +0 -0
  273. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_oracle_mapping.py +0 -0
  274. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_plasma_vault.py +0 -0
  275. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_public_type_reachability.py +0 -0
  276. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_reader_encoding.py +0 -0
  277. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_readiness_selection.py +0 -0
  278. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_revert_decoding.py +0 -0
  279. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_rewards_manager.py +0 -0
  280. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_roles.py +0 -0
  281. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_sdk_layering.py +0 -0
  282. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_crosschain_canary.py +0 -0
  283. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  284. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_examples_arbitrum.py +0 -0
  285. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_examples_base.py +0 -0
  286. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_examples_crosschain.py +0 -0
  287. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  288. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  289. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_solidity_compiler.py +0 -0
  290. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_solidity_mirrors.py +0 -0
  291. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_substrates.py +0 -0
  292. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_types_and_events.py +0 -0
  293. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_validation.py +0 -0
  294. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_vault_dep_graph.py +0 -0
  295. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_vault_info_onchain_base.py +0 -0
  296. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_vault_simulator_blocks.py +0 -0
  297. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/tests/test_withdraw_manager.py +0 -0
  298. {ipor_fusion-4.0.0.dev126 → ipor_fusion-4.0.0.dev137}/uv.lock +0 -0
@@ -186,7 +186,8 @@ the lifecycle matrix follows that.
186
186
 
187
187
  ## Layout (`src/ipor_fusion/`)
188
188
 
189
- - `chains.py` chain registry; `market_ids.py` `IporFusionMarkets`; `types.py`, `errors.py`
189
+ - `chains.py` chain registry; `market_ids.py` `IporFusionMarkets`; `types.py`; `errors.py`
190
+ defines every SDK exception class (`tests/test_error_hierarchy.py` pins it)
190
191
  - `about.py` — package version, repository URL, CHANGELOG parsing (`fusion changelog`,
191
192
  `server_info`); `field_docs.py` — `DOCS`, the JSON field descriptions shared by CLI
192
193
  output and MCP models
@@ -311,13 +312,18 @@ or examples.
311
312
  that depends on another read) is named `read_*` and takes `ctx` first:
312
313
  `PlasmaVault.read_balance_fuses(ctx)`, `AccessManager.read_all_role_accounts(ctx)`,
313
314
  `MorphoReader.read_rates(ctx, id)`. Decide by the I/O a method performs, not
314
- by the class name. Resolvers that probe the chain to pick a wrapper
315
+ by the class name; the prefix is reserved for that, so a method that builds
316
+ a `Call` offline (`PlasmaVault.universal_read`) or a pure helper
317
+ (`precompile_index_of`) never takes it. Resolvers that probe the chain to pick a wrapper
315
318
  (`price_oracle_middleware(ctx, a)`, `resolve_access_manager`,
316
319
  `ExternalStateExecutor.for_vault`) return context-free wrappers. Executors
317
320
  and orchestrators own their contexts: `Multicall3(ctx)`, `VaultSimulator`,
318
321
  `CrosschainSimulator`, `OracleMappingReader(ctx, oracle)` and
319
322
  `CrosschainLane` (`hub_ctx` for the executor, `spoke_ctx` for the
320
- dispatcher at the same address: run a lane's Calls on the matching one).
323
+ dispatcher at the same address; its eager `read_hub_state()` and
324
+ `read_observation()` run on the right one, and its Calls on whichever the
325
+ caller picks). A context is pinned by copy, `ctx.at_block(n)`;
326
+ `default_block` is read-only.
321
327
  A chain-dependent decode takes the chain explicitly
322
328
  (`ExternalStateExecutor.pending_proposals(account, chain_id=)`); a decoder
323
329
  never consults the executing context.
@@ -33,7 +33,7 @@ values; they do not validate byte widths or address spelling.
33
33
  | `MorphoBlueMarketId` | `parse_morpho_market_id(value)`: 32 bytes or hex in any spelling, always `0x` + 64 lowercase digits |
34
34
  | `TxHash` | `parse_tx_hash(value)` (32 bytes or `0x`/`0X`-prefixed hex in any case) |
35
35
  | `PlasmaVaultAddress`, `FuseAddress`, `AccessManagerAddress` | from the SDK (`PlasmaVault.address`, `get_fuses()`, `get_access_manager_address()`, `FusionInstance`, discovery), or `parse_plasma_vault_address` / `parse_fuse_address` / `parse_access_manager_address(value)` for a raw address; they label the role and check nothing on chain |
36
- | crosschain ids | `CommandId(b)`, `MessageId(b)`, `AssetId(b)`, `OperationId(b)`, `TransferId(b)`, `ReturnRequestId(b)`, `PositionSetHash(b)` (bytes32); `ProposalId(n)`, `DeploymentRequestId(n)`, `StargateAssetId(n)` (int) |
36
+ | crosschain ids | `CommandId(b)`, `MessageId(b)`, `AssetId(b)`, `OperationId(b)`, `TransferId(b)`, `ReturnRequestId(b)`, `PositionSetHash(b)` (bytes32); `ProposalId(n)`, `DeploymentRequestId(n)`, `StargateAssetId(n)`, `LayerZeroEid(n)`, `CcipChainSelector(n)` (int) |
37
37
  | HyperCore ids | `HyperCoreTokenIndex(n)` (or `USDC_TOKEN_INDEX`), `HyperCoreAsset(n)` (or `hip3_action_asset(dex, index)`), `HyperCoreOrderId(n)`, `HyperCoreClientOrderId(n)`, `MarginTableId(n)`, `HyperCoreActionId(n)`, `PayloadHash(b)` |
38
38
  | external state | `ProposalHash(b)` |
39
39
  | `BlockIdentifier` | a block number, `"latest"` / `"pending"` / ..., or a block hash |
@@ -92,6 +92,9 @@ chain is always the context's:
92
92
  `Call.call(ctx, *, block=None)` (see [Context-free wrappers](#context-free-wrappers)). `Web3Context.get_logs(to_block=None)` ends at
93
93
  the context's default block and `Web3Context.get_block(block=None)` returns it,
94
94
  both instead of ``"latest"``.
95
+ - `Web3Context.default_block` is read-only: `ctx.default_block = n` raises
96
+ `AttributeError`. Pin a copy instead, `ctx = ctx.at_block(n)`; the old setter
97
+ changed the block under every thread sharing the context.
95
98
 
96
99
  Snapshot fields carry their units: `VaultData.total_assets`,
97
100
  `underlying_balance_on_vault` and `market_balances` values are `Amount`;
@@ -122,15 +125,21 @@ context built with it shares the same `n` slots. A context built from your own
122
125
  `limit.install(web3)` (innermost, as `from_url` does) or
123
126
  `web3.middleware_onion.inject(limit.middleware, layer=0)`.
124
127
 
125
- `ensure_supported_chain` takes `ChainId`. `fetch_unpriceable_priced_tokens` takes the oracle and
126
- underlying as `ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
128
+ `ensure_supported_chain` takes `ChainId`. `fetch_unpriceable_priced_tokens(ctx, *,
129
+ price_oracle, underlying, balance_fuses, market_substrates, morpho_positions)` takes
130
+ everything after `ctx` by keyword, the oracle and underlying as `ChecksumAddress`.
131
+ `resolve_token_symbol`, `resolve_token_decimals`,
127
132
  `get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
128
- fits), and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
133
+ fits) and `api_key` by keyword only (`get_contract_name(chain_id, address, *,
134
+ api_key=None, cache=None)`, `get_deployment_tx(chain_id, address, *, api_key=None)`),
135
+ and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
129
136
  typed for `str` still fits). Market-keyed data is keyed by `MarketId`:
130
137
  `VaultData.market_substrates`, `dependency_graph`, `morpho_positions`,
131
138
  `aave_positions`, `market_balances`; the `VaultState` protocol, `fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens`
132
139
  and the `dependency_graph` helpers (`compute_update_reach`, `compute_update_groups`,
133
140
  `find_orphan_fuse_markets`, `find_markets_missing_erc20_dependency`).
141
+ `find_orphan_fuse_markets` returns `dict[MarketId, list[FuseAddress]]`, so its
142
+ fuses go into `PlasmaVault.remove_fuses` as they are.
134
143
 
135
144
  `VaultData.price_oracle_addr` is renamed `price_oracle`, like its sibling
136
145
  address fields (a custom `VaultState` renames it too).
@@ -148,7 +157,8 @@ The Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient`
148
157
  `LendingMarketHealth.market_id` and `MiddlewarePricedToken.market_id` are
149
158
  `MarketId`; `Erc20Holding.address` is `HexAddress`, and `SubstrateInfo.address` is
150
159
  `HexAddress | None` (`None`, no longer `""`, when a substrate carries no address).
151
- `LegacyPriceOracleMiddleware` takes `base_currency_decimals: Decimals`.
160
+ `LegacyPriceOracleMiddleware` takes `base_currency_decimals: Decimals`, and
161
+ `PriceOracleMiddleware.base_currency_decimals()` returns `Call[Decimals]`.
152
162
  `get_logs_adaptive(chain_id=)` and `LogSource.get_logs` take `ChainId`;
153
163
  `quote_ccip_native_fee(block=)` and `OracleMappingReader.asset_source_events` take a
154
164
  `BlockIdentifier`.
@@ -167,7 +177,9 @@ the async command helpers `Command.request_shares` / `redeem_from_request`,
167
177
  Every fuse constructor, `FuseAction.fuse`, `BalanceFuse.fuse`,
168
178
  `PlasmaVault.add_fuses` / `remove_fuses` / `add_balance_fuse` / `remove_balance_fuse`
169
179
  / `balance_fuse_value`, `RewardsManager.add_reward_fuses` / `is_reward_fuse_supported`,
170
- `LaneFuses` and the HyperCore balance-fuse argument take a `FuseAddress`, and
180
+ `LaneFuses`, the HyperCore balance-fuse argument and the
181
+ `PlasmaVault.configure_instant_withdrawal_fuses` configs
182
+ (`Sequence[tuple[FuseAddress, Sequence[bytes]]]`) take a `FuseAddress`, and
171
183
  `get_fuses()`, `get_instant_withdrawal_fuses()`, `get_rewards_fuses()` and
172
184
  `VaultData.fuses` / `instant_fuses` / `fuse_markets` keys return it.
173
185
  `AccessManager(address)` takes an `AccessManagerAddress`;
@@ -196,7 +208,13 @@ from_=...)` needs neither, so drop a zero-address placeholder. `execute()` witho
196
208
  the fee fields are `Wei`). LayerZero endpoint ids are `LayerZeroEid`:
197
209
  `StargateChain.eid`, `Packet.src_eid` / `dst_eid`, `EnforcedOptionParam.eid`,
198
210
  `executor_eid()` and `eid_of()` return it and `chain_id_of`, `peers`, `lz_receive_calldata(src_eid=)`
199
- and the Stargate fuse's `eid=` take it (`LayerZeroEid(30101)`).
211
+ and the Stargate fuse's `eid=` take it (`LayerZeroEid(30101)`). CCIP chain
212
+ selectors are `CcipChainSelector`: `CcipChain.chain_selector`,
213
+ `CcipRouteConfig.chain_selector`, `CcipTokenLane.chain_selector`, the `MessageV1`,
214
+ `CcipMessageSent` and `Any2EVMMessage` selectors and `CcipRouter.get_off_ramps()`
215
+ carry it, and `chain_id_of_selector`, `CcipRouter.is_chain_supported` /
216
+ `get_on_ramp`, `CcipTokenPool.is_supported_chain` and `ccip_token_lane` take it
217
+ (`CcipChainSelector(4949039107694359620)`).
200
218
  `encode_oft_compose_msg(*, nonce=, src_eid=, amount_ld=, compose_msg=)` takes
201
219
  keyword arguments only. `ZERO_ADDRESS` is a `ChecksumAddress` (same value).
202
220
 
@@ -229,7 +247,10 @@ gone: use `morpho_blue_address(chain_id)`.
229
247
  `MorphoPositionBreakdown.market_id` (as read from substrates) and the keys of
230
248
  `VaultV1Info.public_allocator_flow_caps` are canonical. `fetch_market`, `fetch_vault`,
231
249
  `morpho_blue_address`, `build_*_json` take `ChainId`; `fetch_vault` takes a
232
- `ChecksumAddress`. Every address on the `morpho_api` records is `ChecksumAddress`;
250
+ `ChecksumAddress`. `build_morpho_blue_market_json(*, market_id, chain_id, params,
251
+ state, rates, api_market=None, api_error=None, public_allocator=None)` takes every
252
+ argument by keyword, `public_allocator` as `ChecksumAddress | None`.
253
+ Every address on the `morpho_api` records is `ChecksumAddress`;
233
254
  optional ones (`curator`, `guardian`, `fee_recipient`, `performance_fee_recipient`,
234
255
  `management_fee_recipient`, `inner_vault`, `VaultFlowCap.admin`, the `VaultV2Cap`
235
256
  market params) are `ChecksumAddress | None`.
@@ -249,14 +270,24 @@ on CCIP, `active_return` → `OperationId`, `asset_id`, `tracked_position_set_ha
249
270
  before passing it on.
250
271
 
251
272
  **HyperCore (preview).** The fuses, `HyperCoreSubstrates`, `HyperCoreReader`,
252
- `read_hypercore_transfer_snapshot` and the helpers `system_address`, `read_index_of`,
273
+ `read_hypercore_transfer_snapshot` and the helpers `system_address`, `precompile_index_of`
274
+ (3.x `read_index_of`, renamed: it reads nothing),
253
275
  `perp_dex_of`, `is_native_perp_asset`, `is_hip3_asset` take the HyperCore ids;
254
276
  `HyperCorePerpAssetInfo`, `HyperCorePerpMarket`, `HyperCoreSpotLeg`,
255
277
  `HyperCorePendingState` and `HyperCoreTransferSnapshot` carry them.
278
+ `HyperCorePendingReader(vault, reader)` takes the vault's `PlasmaVaultAddress`, not a
279
+ `PlasmaVault` wrapper.
256
280
 
257
281
  **External state.** `ExternalStateExecutor.proposal_hash` returns and `confirm_balance`
258
- takes a `ProposalHash`; `BalanceProposal.proposal_hash` and `NavMark.proposal_hash`
259
- are `ProposalHash`; `ExternalStateEvent.transaction_hash` is a `TxHash`.
282
+ takes a `ProposalHash`; `BalanceProposal.proposal_hash` is a `ProposalHash`;
283
+ `ExternalStateEvent.transaction_hash` is a `TxHash`.
284
+ `ExternalStateVaultState.last_checked_custodian_timestamp`,
285
+ `BalanceFuseSnapshot.last_custodian_update_timestamp`, `BalanceProposal.proposed_at`,
286
+ `last_updated()` and `get_oldest_update_timestamp()` are web3's `Timestamp`, and
287
+ `ExternalStateExecutor.proposal_hash(proposed_at=)` takes one.
288
+ `ExternalStateExecutor.mark_nav` and `NavMark` are gone: send `propose_balance` and
289
+ `confirm_balance` from separate custodians, then
290
+ `PlasmaVault.update_markets_balances([IporFusionMarkets.EXTERNAL_STATE])`.
260
291
 
261
292
  **Errors and explorer.** `TransactionError(tx_hash=)` takes a `TxHash`,
262
293
  `LogScanError(address=)` a `ChecksumAddress`; `get_revert_reason` takes a `TxHash`;
@@ -324,7 +355,8 @@ placeholder wrapper.
324
355
  `.decode(raw)` and `.map(f)` need no context. `Call(...)` no longer takes `ctx=`.
325
356
  - A wrapper method that executes to answer (storage, log scans, dependent reads) is
326
357
  named `read_*` and takes the context first (table below).
327
- - Executors and orchestrators keep their contexts: `Multicall3(ctx)` (every batch,
358
+ - Executors and orchestrators keep their contexts: `Multicall3(ctx, *, address=,
359
+ batch_size=)` (options by keyword; every batch,
328
360
  every re-run of a failed sub-call and the per-call fallback run on its `ctx`, so a
329
361
  pinned context keeps them all at its block; it is no longer a `ContractWrapper`,
330
362
  `aggregate3(calls)` still returns the raw `Call`), `VaultSimulator`,
@@ -339,7 +371,14 @@ placeholder wrapper.
339
371
  `command_in_flight` and `transfer_in_flight` read on `hub_ctx`. The Calls a lane
340
372
  returns are unexecuted, and the executor and dispatcher share one address, so pick
341
373
  the context yourself: `lane.get_balance().call(lane.hub_ctx)`,
342
- `lane.observation().call(lane.spoke_ctx)`. `CrosschainTransport.executor(address)`
374
+ `lane.observation().call(lane.spoke_ctx)`, or let the lane pick:
375
+ `lane.read_hub_state()` reads every executor bucket and counter
376
+ (`LaneHubState`) on `hub_ctx` through `Multicall3`, at one block (a `latest`, `safe` or
377
+ `finalized` default block resolved to its number once, so a per-call fallback
378
+ stays on it; a number or hash as is);
379
+ `lane.read_observation()` runs the observation on `spoke_ctx`. Keep
380
+ `transfer_in_flight()` / `command_in_flight()` for those decisions: the counters
381
+ do not see a CCIP recall or tell a parked command from a pending one. `CrosschainTransport.executor(address)`
343
382
  and `.dispatcher(address)` no longer take a context.
344
383
  - Chain-bound decoding takes the chain explicitly:
345
384
  `ExternalStateExecutor.pending_proposals(account, *, chain_id)`,
@@ -349,7 +388,7 @@ placeholder wrapper.
349
388
  and `VaultSimulator.observe` stamp the same hash for the same bytes, whatever
350
389
  context executes them. Pass `ctx.chain_id` for the chain you read from.
351
390
  `pending_proposals` now refuses an executor at the zero address when the Call is
352
- built, not when it is decoded. `mark_nav` takes the chain from its contexts.
391
+ built, not when it is decoded.
353
392
 
354
393
  | 3.x | 4.0 |
355
394
  |---|---|
@@ -377,6 +416,10 @@ placeholder wrapper.
377
416
  | `MorphoReader.rates_from(market, params)` | `MorphoReader.read_rates_from(ctx, market, params)` (it reads the IRM) |
378
417
  | `AaveV3Reader.position_breakdown(asset, user)` | `AaveV3Reader.read_position_breakdown(ctx, asset, user)` |
379
418
  | `AaveV3FuseReader.pool()` | `AaveV3FuseReader.read_pool(ctx)` |
419
+ | `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) |
420
+ | `read_index_of(asset)` | `precompile_index_of(asset)` |
421
+ | `ctx.default_block = n` | `ctx = ctx.at_block(n)` |
422
+ | `Multicall3(ctx, address)` | `Multicall3(ctx, address=address)` |
380
423
  | `executor.pending_proposals(account)` | `executor.pending_proposals(account, chain_id=ctx.chain_id)` |
381
424
  | `executor.parse_balance_proposed(log)` / `find_balance_proposed(logs)` | `...(log, chain_id=...)` / `...(logs, chain_id=...)` |
382
425
  | `CcipLane(executor=, dispatcher=, spoke_chain_id=, ...)` | `CcipLane(hub_ctx=, spoke_ctx=, executor=, dispatcher=, ...)` (same for `StargateLane`) |
@@ -509,8 +552,12 @@ is the wrong one.
509
552
  - `MorphoApiError` (still a `RuntimeError`), `ExternalStateEventDecodeError` (still a
510
553
  `ValueError`) and `VerifierResultUnavailable` (still a `LookupError`) now derive
511
554
  from `IporFusionError`, so an `except IporFusionError` placed before their own
512
- handlers catches them first. `MorphoApiError` lives in `ipor_fusion.errors` and is
513
- exported from `ipor_fusion`; `ipor_fusion.morpho_api.MorphoApiError` still works.
555
+ handlers catches them first. All three live in `ipor_fusion.errors`, like every
556
+ other SDK exception, and are exported from `ipor_fusion`; the modules that raise
557
+ them (`ipor_fusion.morpho_api`, `ipor_fusion.fuses.external_state_events`,
558
+ `ipor_fusion.crosschain.ccip.indexer`) still import the same classes.
559
+ - `AccessManager.read_owner(ctx)` raises the new `OwnerNotFoundError` (a
560
+ `LookupError`) when no account holds `OWNER_ROLE`, instead of an `IndexError`.
514
561
  - `EmptyCallResultError` is no longer an eth_abi `InsufficientDataBytes` (nor a
515
562
  `DecodingError`). Code that caught either around `.call()` to tolerate a missing
516
563
  contract or getter must catch `EmptyCallResultError`; return data that is present
@@ -560,10 +607,8 @@ is the wrong one.
560
607
  `parse_balance_proposed` / `find_balance_proposed` for one of the executor's own
561
608
  `BalanceProposed` logs whose data is missing or malformed (including data that is
562
609
  not hex at all, which raised `TypeError`) or whose hash is not the hash of its own
563
- fields; and `mark_nav` when the propose receipt it fetched has no such log or
564
- records another value or proposer. A log that is not the event or not from the
565
- executor, and `find_balance_proposed` over logs that hold no match, are still
566
- `ValueError`.
610
+ fields. A log that is not the event or not from the executor, and
611
+ `find_balance_proposed` over logs that hold no match, are still `ValueError`.
567
612
  - `ccip_token_lane` raises the new `UnsupportedContractVersionError` (still a
568
613
  `ValueError`) for an OnRamp version whose layout is not verified.
569
614
  - `quote_ccip_native_fee` raises `SimulationError` (label `"quote"`, with
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev126
3
+ Version: 4.0.0.dev137
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
@@ -63,11 +63,7 @@ role-separation note is about. Label the collapse itself, not the calls: sending
63
63
  as two separate calls *is* the production shape, and only "both keys in one process" is
64
64
  simulation-only.
65
65
 
66
- Cross-reference the SDK method a production caller would use instead, where one exists -- but only
67
- where the SDK method is the one to reach for. `ExternalStateExecutor.mark_nav` is the counter-example:
68
- it composes both custodian calls, its own docstring warns that this "does not preserve the
69
- dual-custodian separation", and it is expected to be deprecated in favour of the two primitives. Do
70
- not point readers at it.
66
+ Cross-reference the SDK method a production caller would use instead, where one exists.
71
67
 
72
68
  ## 3. One self-contained file
73
69
 
@@ -367,8 +367,9 @@ def withdraw_collateral_action(max_amount: Amount) -> FuseAction:
367
367
 
368
368
  def run_simulation(web3: Web3) -> SimulationResult:
369
369
  """Build, fund and run the full Euler V2 credit lifecycle in one batch."""
370
- ctx = Web3Context(web3=web3, chain_id=BASE_CHAIN_ID, signer=OWNER)
371
- ctx.default_block = PINNED_BLOCK
370
+ ctx = Web3Context(web3=web3, chain_id=BASE_CHAIN_ID, signer=OWNER).at_block(
371
+ PINNED_BLOCK
372
+ )
372
373
  factory = FusionFactory(BASE_FUSION_FACTORY)
373
374
 
374
375
  # 1. Preview the deterministic addresses (eth_call, no gas, no state change).
@@ -315,8 +315,9 @@ def build_loop_action() -> FuseAction:
315
315
 
316
316
  def run_simulation(web3: Web3) -> SimulationResult:
317
317
  """Build, configure and run the WBTC/USDC leveraged loop without broadcasting."""
318
- ctx = Web3Context(web3=web3, chain_id=ARBITRUM_CHAIN_ID, signer=OWNER)
319
- ctx.default_block = PINNED_BLOCK
318
+ ctx = Web3Context(web3=web3, chain_id=ARBITRUM_CHAIN_ID, signer=OWNER).at_block(
319
+ PINNED_BLOCK
320
+ )
320
321
  factory = FusionFactory(ARBITRUM_FUSION_FACTORY)
321
322
  preview = factory.clone(**clone_args()).call(ctx)
322
323
 
@@ -76,6 +76,7 @@ from ipor_fusion import (
76
76
  USDC_SYSTEM_ADDRESS,
77
77
  USDC_TOKEN_INDEX,
78
78
  CcipChain,
79
+ CcipChainSelector,
79
80
  CcipCrosschainExecutor,
80
81
  CcipTransport,
81
82
  Command,
@@ -94,10 +95,10 @@ from ipor_fusion import (
94
95
  open_lane,
95
96
  parse_fuse_address,
96
97
  parse_plasma_vault_address,
98
+ precompile_index_of,
97
99
  quote_ccip_native_fee,
98
100
  read_hypercore_evm_exit_ceiling,
99
101
  read_hypercore_vault_state,
100
- read_index_of,
101
102
  register_custom_errors,
102
103
  )
103
104
  from ipor_fusion.fuses import (
@@ -133,8 +134,8 @@ register_custom_errors(HYPERCORE_SEND_FUSE_ERRORS)
133
134
  ARBITRUM_CHAIN_ID = ChainId(42161)
134
135
  HYPEREVM_CHAIN_ID = ChainId(999)
135
136
  # Chainlink CCIP routers and chain selectors (docs.chain.link -> CCIP -> Directory).
136
- ARBITRUM_CCIP_SELECTOR = 4949039107694359620
137
- HYPEREVM_CCIP_SELECTOR = 2442541497099098535
137
+ ARBITRUM_CCIP_SELECTOR = CcipChainSelector(4949039107694359620)
138
+ HYPEREVM_CCIP_SELECTOR = CcipChainSelector(2442541497099098535)
138
139
  ARBITRUM_CCIP_ROUTER = Web3.to_checksum_address(
139
140
  "0x141fa059441E0ca23ce184B6A78bafD2A517DdE8"
140
141
  )
@@ -428,7 +429,7 @@ def core_leg_previews(web3_hype: Web3) -> list[Preview]:
428
429
  xyz_equity_usd6 = int(
429
430
  reader.account_margin_summary(XYZ_DEX, HYPERCORE_VAULT).call(ctx).account_value
430
431
  )
431
- read_index = read_index_of(XYZ_NVDA_ASSET)
432
+ read_index = precompile_index_of(XYZ_NVDA_ASSET)
432
433
  # The precompile quotes a perp price with ``6 - szDecimals`` decimals; the
433
434
  # order fuse takes 8 implied decimals. Read szDecimals, then rescale.
434
435
  sz_decimals = int(reader.perp_asset_info(read_index).call(ctx).sz_decimals)
@@ -662,9 +663,7 @@ def _transport() -> CcipTransport:
662
663
 
663
664
 
664
665
  def _pinned(web3: Web3, chain_id: ChainId, block: int) -> Web3Context:
665
- ctx = Web3Context(web3, chain_id=chain_id)
666
- ctx.default_block = block
667
- return ctx
666
+ return Web3Context(web3, chain_id=chain_id).at_block(block)
668
667
 
669
668
 
670
669
  class _Tail(NamedTuple):
@@ -889,8 +888,8 @@ def recall_attest_claim(web3_arb: Web3, web3_hype: Web3) -> dict[str, int]:
889
888
 
890
889
  # Where the live cycle stood: the settled bucket carries attestation 8, the
891
890
  # dispatcher holds the redeemed idle and the residual shares.
892
- settled_before = int(lane.settled_remote_balance().call(hub_ctx))
893
- observation_before = lane.observation().call(spoke_ctx)
891
+ settled_before = int(lane.read_hub_state().settled_remote_balance)
892
+ observation_before = lane.read_observation()
894
893
  remote_idle = Amount(int(observation_before.tracked_idle))
895
894
  residual_shares = int(
896
895
  PlasmaVault(HYPERCORE_VAULT).balance_of(EXECUTOR).call(spoke_ctx)
@@ -70,6 +70,7 @@ from web3 import Web3
70
70
  from ipor_fusion import (
71
71
  ERC20,
72
72
  CcipChain,
73
+ CcipChainSelector,
73
74
  CcipTransport,
74
75
  Command,
75
76
  CrosschainLane,
@@ -93,8 +94,8 @@ log = logging.getLogger("crosschain_ccip_usdc_arbitrum_hyperevm")
93
94
  # (docs.chain.link -> CCIP -> Directory -> Arbitrum One / Hyperliquid).
94
95
  ARBITRUM_CHAIN_ID = ChainId(42161)
95
96
  HYPEREVM_CHAIN_ID = ChainId(999)
96
- ARBITRUM_CCIP_SELECTOR = 4949039107694359620
97
- HYPEREVM_CCIP_SELECTOR = 2442541497099098535
97
+ ARBITRUM_CCIP_SELECTOR = CcipChainSelector(4949039107694359620)
98
+ HYPEREVM_CCIP_SELECTOR = CcipChainSelector(2442541497099098535)
98
99
  ARBITRUM_CCIP_ROUTER = Web3.to_checksum_address(
99
100
  "0x141fa059441E0ca23ce184B6A78bafD2A517DdE8"
100
101
  )
@@ -205,9 +206,7 @@ def _connected_web3(env_var: str, chain_id: ChainId) -> Web3:
205
206
 
206
207
 
207
208
  def _pinned_context(web3: Web3, chain_id: ChainId, block: int) -> Web3Context:
208
- ctx = Web3Context(web3, chain_id=chain_id)
209
- ctx.default_block = block
210
- return ctx
209
+ return Web3Context(web3, chain_id=chain_id).at_block(block)
211
210
 
212
211
 
213
212
  # ── Pure builders (no chain access, unit-testable offline) ───────────────────
@@ -807,8 +807,9 @@ def run_simulation(web3: Web3) -> SimulationResult:
807
807
  function does (exactly one failure, with the expected revert selector), not
808
808
  with ``result.all_success``.
809
809
  """
810
- ctx = Web3Context(web3=web3, chain_id=BASE_CHAIN_ID, signer=OWNER)
811
- ctx.default_block = PINNED_BLOCK
810
+ ctx = Web3Context(web3=web3, chain_id=BASE_CHAIN_ID, signer=OWNER).at_block(
811
+ PINNED_BLOCK
812
+ )
812
813
 
813
814
  factory = FusionFactory(BASE_FUSION_FACTORY)
814
815
 
@@ -894,13 +895,6 @@ def run_simulation(web3: Web3) -> SimulationResult:
894
895
  # driven from this one process. In a real deployment they live in separate
895
896
  # systems -- if one process can send both, a single compromise moves the
896
897
  # vault's NAV and the two-party design has bought nothing.
897
- #
898
- # ExternalStateExecutor.mark_nav runs the propose, the confirm and -- when
899
- # handed a vault -- the refresh in one call. It cannot be used here for a
900
- # mechanical reason: it .send(ctx)s each leg, and nothing is broadcast under
901
- # eth_simulateV1. It is also not what a production deployment wants, for the
902
- # reason above: one process holding both custodian keys is one key wearing
903
- # two hats.
904
898
  sim.next_block().with_block_time_shift(PROPOSE_OFFSET)
905
899
  sim.add_call(
906
900
  call=executor.propose_balance(VENUE_BALANCE_ACCOUNT, MARGIN_AMOUNT),
@@ -938,7 +932,7 @@ def run_simulation(web3: Web3) -> SimulationResult:
938
932
  # to one custodian by either route, however separate the two keys are -- and
939
933
  # that is why a production deployment cannot drive both halves from one
940
934
  # process, whatever this example does under simulation.
941
- proposed_at = baseline_timestamp + PROPOSE_OFFSET
935
+ proposed_at = Timestamp(baseline_timestamp + PROPOSE_OFFSET)
942
936
  proposal_hash = ExternalStateExecutor.proposal_hash(
943
937
  executor=executor_address,
944
938
  chain_id=BASE_CHAIN_ID,
@@ -225,8 +225,9 @@ def build_supply_action(amount: Amount) -> FuseAction:
225
225
 
226
226
  def run_simulation(web3: Web3) -> SimulationResult:
227
227
  """Build, fund, deposit and supply -- all in one eth_simulateV1 batch."""
228
- ctx = Web3Context(web3=web3, chain_id=BASE_CHAIN_ID, signer=OWNER)
229
- ctx.default_block = PINNED_BLOCK
228
+ ctx = Web3Context(web3=web3, chain_id=BASE_CHAIN_ID, signer=OWNER).at_block(
229
+ PINNED_BLOCK
230
+ )
230
231
  factory = FusionFactory(BASE_FUSION_FACTORY)
231
232
 
232
233
  # 1. Preview the deterministic addresses (eth_call, no gas, no state change).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev126"
3
+ version = "4.0.0.dev137"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -43,7 +43,7 @@ from web3.types import RPCEndpoint
43
43
 
44
44
  from ipor_fusion import ERC20, Call, Web3Context, ccip_token_lane
45
45
  from ipor_fusion.crosschain import CcipCrosschainFactory, CcipOffRamp, CcipRouter
46
- from ipor_fusion.types import ChainId
46
+ from ipor_fusion.types import CcipChainSelector, ChainId
47
47
 
48
48
  SCHEMA = "ipor-fusion.crosschain-readiness/4"
49
49
  EXIT_OK = 0
@@ -126,7 +126,7 @@ class ChainSpec:
126
126
 
127
127
  name: str
128
128
  chain_id: ChainId
129
- chain_selector: int
129
+ chain_selector: CcipChainSelector
130
130
  env_var: str
131
131
  router: ChecksumAddress
132
132
  usdc: ChecksumAddress
@@ -137,7 +137,7 @@ class ChainSpec:
137
137
  ARBITRUM = ChainSpec(
138
138
  name="arbitrum",
139
139
  chain_id=ChainId(42161),
140
- chain_selector=4949039107694359620,
140
+ chain_selector=CcipChainSelector(4949039107694359620),
141
141
  env_var="ARBITRUM_PROVIDER_URL",
142
142
  router=Web3.to_checksum_address("0x141fa059441E0ca23ce184B6A78bafD2A517DdE8"),
143
143
  usdc=Web3.to_checksum_address("0xaf88d065e77c8cC2239327C5EDb3A432268e5831"),
@@ -150,7 +150,7 @@ ARBITRUM = ChainSpec(
150
150
  HYPEREVM = ChainSpec(
151
151
  name="hyperevm",
152
152
  chain_id=ChainId(999),
153
- chain_selector=2442541497099098535,
153
+ chain_selector=CcipChainSelector(2442541497099098535),
154
154
  env_var="HYPEREVM_PROVIDER_URL",
155
155
  router=Web3.to_checksum_address("0x13b3332b66389B1467CA6eBd6fa79775CCeF65ec"),
156
156
  usdc=Web3.to_checksum_address("0xb88339CB7199b77E23DB6E890353E22632Ba630f"),
@@ -756,9 +756,7 @@ def connect(spec: ChainSpec) -> Web3Context:
756
756
  raise ReadinessConfigError(
757
757
  f"{spec.env_var}: chain id {chain_id}, expected {spec.chain_id}"
758
758
  )
759
- ctx = Web3Context(web3=web3, chain_id=spec.chain_id)
760
- ctx.default_block = head
761
- return ctx
759
+ return Web3Context(web3=web3, chain_id=spec.chain_id).at_block(head)
762
760
 
763
761
 
764
762
  def config_error_report(message: str) -> dict[str, Any]:
@@ -39,7 +39,6 @@ from ipor_fusion.core.external_state_executor import (
39
39
  BalanceProposal,
40
40
  ExternalStateExecutor,
41
41
  ExternalStateVaultState,
42
- NavMark,
43
42
  RawLog,
44
43
  read_external_state,
45
44
  )
@@ -145,6 +144,7 @@ from ipor_fusion.crosschain import (
145
144
  EVMTokenAmount,
146
145
  ExecutorInitParams,
147
146
  LaneFuses,
147
+ LaneHubState,
148
148
  LaneObservation,
149
149
  MessageExecutionState,
150
150
  MessageV1,
@@ -169,7 +169,6 @@ from ipor_fusion.crosschain import (
169
169
  TaxiMessage,
170
170
  TokenTransferV1,
171
171
  VerifierResult,
172
- VerifierResultUnavailable,
173
172
  ccip_events,
174
173
  ccip_receive_calldata,
175
174
  ccip_token_lane,
@@ -206,18 +205,21 @@ from ipor_fusion.errors import (
206
205
  CrosschainDiscoveryError,
207
206
  EmptyCallResultError,
208
207
  ExternalStateError,
208
+ ExternalStateEventDecodeError,
209
209
  HyperCoreValuationError,
210
210
  IporFusionError,
211
211
  LogScanError,
212
212
  LogSourceUnavailable,
213
213
  MorphoMarketNotFoundError,
214
214
  NotPlasmaVaultError,
215
+ OwnerNotFoundError,
215
216
  SimulationError,
216
217
  SimulationRunError,
217
218
  TransactionError,
218
219
  UnsupportedChainError,
219
220
  UnsupportedContractVersionError,
220
221
  UnsupportedVaultVersionError,
222
+ VerifierResultUnavailable,
221
223
  decode_custom_error,
222
224
  get_revert_reason,
223
225
  register_custom_errors,
@@ -301,7 +303,7 @@ from ipor_fusion.fuses import (
301
303
  is_hip3_asset,
302
304
  is_native_perp_asset,
303
305
  perp_dex_of,
304
- read_index_of,
306
+ precompile_index_of,
305
307
  system_address,
306
308
  )
307
309
  from ipor_fusion.fuses.external_state_events import (
@@ -309,7 +311,6 @@ from ipor_fusion.fuses.external_state_events import (
309
311
  EXTERNAL_STATE_EVENTS,
310
312
  EXTERNAL_STATE_TOPICS,
311
313
  ExternalStateEvent,
312
- ExternalStateEventDecodeError,
313
314
  ExternalStateEventSpec,
314
315
  decode_external_state_event,
315
316
  external_state_event_name,
@@ -412,6 +413,7 @@ from ipor_fusion.types import (
412
413
  Amount,
413
414
  AssetId,
414
415
  AssetSource,
416
+ CcipChainSelector,
415
417
  ChainId,
416
418
  Check,
417
419
  CommandId,
@@ -553,7 +555,6 @@ __all__ = [
553
555
  "BalanceFuseSnapshot",
554
556
  "ExternalStateVaultState",
555
557
  "read_external_state",
556
- "NavMark",
557
558
  "CrosschainExecutor",
558
559
  "StargateCrosschainExecutor",
559
560
  "StargateCrosschainDispatcher",
@@ -582,6 +583,7 @@ __all__ = [
582
583
  "StargateLane",
583
584
  "CcipLane",
584
585
  "LaneFuses",
586
+ "LaneHubState",
585
587
  "LaneObservation",
586
588
  "open_lane",
587
589
  "open_lanes",
@@ -719,7 +721,7 @@ __all__ = [
719
721
  "is_hip3_asset",
720
722
  "is_native_perp_asset",
721
723
  "perp_dex_of",
722
- "read_index_of",
724
+ "precompile_index_of",
723
725
  "UniversalTokenSwapperAbi",
724
726
  "UniversalTokenSwapperFuse",
725
727
  "UniversalTokenSwapperSubstrates",
@@ -756,6 +758,7 @@ __all__ = [
756
758
  "LogSourceUnavailable",
757
759
  "MorphoMarketNotFoundError",
758
760
  "NotPlasmaVaultError",
761
+ "OwnerNotFoundError",
759
762
  "TransactionError",
760
763
  "SimulationError",
761
764
  "SimulationRunError",
@@ -819,6 +822,7 @@ __all__ = [
819
822
  "ReturnRequestId",
820
823
  "StargateAssetId",
821
824
  "LayerZeroEid",
825
+ "CcipChainSelector",
822
826
  "TransferId",
823
827
  "Price",
824
828
  "Period",
@@ -123,7 +123,7 @@ def morpho_blue(
123
123
  provider_url = _resolve_provider(cfg, chain_id)
124
124
  ctx = Web3Context.from_url(provider_url)
125
125
  if block is not None:
126
- ctx.default_block = block
126
+ ctx = ctx.at_block(block)
127
127
  reader = MorphoReader(morpho_blue_address(chain_id))
128
128
  mid = market_id
129
129
  params, state = reader.read_existing_market(ctx, mid)
@@ -143,14 +143,14 @@ def morpho_blue(
143
143
  click.echo(
144
144
  json_lib.dumps(
145
145
  build_morpho_blue_market_json(
146
- market_id,
147
- chain_id,
148
- params,
149
- state,
150
- rates,
151
- api_market,
152
- api_error,
153
- public_allocator,
146
+ market_id=market_id,
147
+ chain_id=chain_id,
148
+ params=params,
149
+ state=state,
150
+ rates=rates,
151
+ api_market=api_market,
152
+ api_error=api_error,
153
+ public_allocator=public_allocator,
154
154
  ),
155
155
  indent=2,
156
156
  )
@@ -123,7 +123,7 @@ def _build_ctx(
123
123
  provider_url = _resolve_provider(cfg, chain_id)
124
124
  ctx = Web3Context.from_url(provider_url)
125
125
  if block_number is not None:
126
- ctx.default_block = block_number
126
+ ctx = ctx.at_block(block_number)
127
127
  return chain_id, ctx
128
128
 
129
129
 
@@ -441,7 +441,7 @@ def oracle_mapping(
441
441
  effective_block = (
442
442
  block_number if block_number is not None else ctx.web3.eth.block_number
443
443
  )
444
- ctx.default_block = effective_block
444
+ ctx = ctx.at_block(effective_block)
445
445
 
446
446
  try:
447
447
  resolve_access_manager(ctx, vault_address)