ipor-fusion 4.0.0.dev129__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.dev129 → ipor_fusion-4.0.0.dev137}/AGENTS.md +9 -3
  2. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/MIGRATION_4.md +57 -13
  3. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/PKG-INFO +1 -1
  4. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/examples/advanced_euler_v2_credit_market_base.py +3 -2
  5. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/examples/advanced_euler_v2_looping_arbitrum.py +3 -2
  6. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +8 -9
  7. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +4 -5
  8. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/examples/external_state_margin_leg_base.py +4 -3
  9. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/examples/simple_aave_v3_supply_base.py +3 -2
  10. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/pyproject.toml +1 -1
  11. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/scripts/crosschain_readiness.py +5 -7
  12. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/__init__.py +10 -4
  13. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/market_cmd.py +9 -9
  14. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/options.py +1 -1
  15. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_cmd.py +1 -1
  16. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/access.py +9 -1
  17. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/context.py +2 -4
  18. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/contract.py +1 -1
  19. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/external_state_executor.py +18 -13
  20. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/multicall.py +1 -0
  21. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/oracle.py +5 -3
  22. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/plasma_vault.py +6 -5
  23. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/__init__.py +8 -2
  24. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/__init__.py +1 -1
  25. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/chainlink.py +9 -8
  26. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/codec.py +8 -8
  27. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/contracts.py +5 -4
  28. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/indexer.py +1 -8
  29. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/transport.py +3 -3
  30. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/fuses/ccip.py +2 -2
  31. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/lane.py +67 -5
  32. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/dependency_graph.py +2 -2
  33. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/errors.py +19 -0
  34. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/explorer.py +2 -2
  35. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/__init__.py +2 -2
  36. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/external_state_events.py +1 -7
  37. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/hypercore.py +2 -2
  38. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/market_info.py +6 -3
  39. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/mcp/server.py +10 -3
  40. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/hypercore.py +11 -9
  41. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/hypercore_outcome.py +2 -5
  42. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/unpriceable.py +3 -2
  43. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/substrates.py +2 -2
  44. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/types.py +2 -0
  45. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_data.py +6 -6
  46. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_crosschain.py +1 -3
  47. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_crosschain_lifecycle.py +6 -4
  48. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_access_manager.py +14 -0
  49. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_commands.py +1 -1
  50. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_oracle_mapping.py +7 -4
  51. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_lane.py +110 -1
  52. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_error_hierarchy.py +12 -0
  53. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_explorer.py +11 -3
  54. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_external_state_executor.py +4 -4
  55. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_fuse_encoding.py +3 -3
  56. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_deployment.py +1 -2
  57. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_live.py +8 -6
  58. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_outcome.py +1 -2
  59. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_readers.py +6 -6
  60. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_simulation.py +0 -3
  61. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_mcp_server.py +7 -4
  62. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_public_api.py +10 -2
  63. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_borrow_aave_v3_mainnet.py +6 -4
  64. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_claim_merkl_wrapper_base.py +3 -2
  65. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_claim_morpho_rewards.py +3 -2
  66. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_crosschain_hypercore.py +1 -2
  67. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_crosschain_hyperevm.py +1 -3
  68. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_batch_base.py +3 -2
  69. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_credit_market_base.py +3 -2
  70. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_swap_deploy_base.py +3 -2
  71. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +3 -2
  72. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_euler_v2_swap_registry_base.py +3 -2
  73. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_external_state_executor_base.py +3 -2
  74. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_looping_aave_v3_base.py +3 -2
  75. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_looping_morpho_blue_base.py +3 -2
  76. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_morpho_blue.py +0 -3
  77. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_pilot_v3.py +12 -8
  78. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_pilot_v4_uniswap.py +15 -10
  79. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_pilot_v5_ramses.py +12 -8
  80. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_pilot_v5_scheduled.py +18 -12
  81. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_readers_integration.py +1 -3
  82. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_release_funds.py +6 -4
  83. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_swap_on_base.py +6 -4
  84. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_vault_from_scratch_base.py +3 -2
  85. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_unpriceable_tokens.py +5 -5
  86. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +1 -2
  87. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_web3_context.py +2 -4
  88. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/typed_client/consumer.py +62 -2
  89. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/typed_client/rejected.py +28 -0
  90. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.claude-plugin/marketplace.json +0 -0
  91. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.claude-plugin/plugin.json +0 -0
  92. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.env.example +0 -0
  93. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.gemini/settings.json +0 -0
  94. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.git-blame-ignore-revs +0 -0
  95. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.github/workflows/cd.yml +0 -0
  96. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.github/workflows/ci.yml +0 -0
  97. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.github/workflows/commit-lint.yml +0 -0
  98. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.github/workflows/notify-slack.yml +0 -0
  99. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.github/workflows/python-build.yml +0 -0
  100. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.github/workflows/python-publish.yml +0 -0
  101. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.github/workflows/release.yml +0 -0
  102. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.github/workflows/report-slack.yml +0 -0
  103. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.gitignore +0 -0
  104. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.mcp.json +0 -0
  105. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.pre-commit-config.yaml +0 -0
  106. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/.python-version +0 -0
  107. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/CHANGELOG.md +0 -0
  108. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/CLAUDE.md +0 -0
  109. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/CODEOWNERS +0 -0
  110. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/LICENSE +0 -0
  111. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/README.md +0 -0
  112. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/build.sh +0 -0
  113. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/examples/CONTRIBUTING.md +0 -0
  114. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/examples/README.md +0 -0
  115. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/pytest.ini +0 -0
  116. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/scripts/commit-check.sh +0 -0
  117. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/skills/ipor-deploy-vault/SKILL.md +0 -0
  118. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/about.py +0 -0
  119. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cache.py +0 -0
  120. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/chains.py +0 -0
  121. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/__init__.py +0 -0
  122. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  123. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/config_cmd.py +0 -0
  124. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/config_store.py +0 -0
  125. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/main.py +0 -0
  126. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  127. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_health.py +0 -0
  128. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  129. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  130. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  131. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/config/__init__.py +0 -0
  132. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/config/roles.py +0 -0
  133. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/__init__.py +0 -0
  134. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/address.py +0 -0
  135. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/erc20.py +0 -0
  136. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/fee_manager.py +0 -0
  137. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/fuse.py +0 -0
  138. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/fuse_action.py +0 -0
  139. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/fusion_factory.py +0 -0
  140. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/logs.py +0 -0
  141. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/rewards_manager.py +0 -0
  142. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/simulation.py +0 -0
  143. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  144. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  145. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  146. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  147. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/contracts.py +0 -0
  148. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/discovery.py +0 -0
  149. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/errors.py +0 -0
  150. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  151. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  152. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  153. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/logs.py +0 -0
  154. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/messages.py +0 -0
  155. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/simulation.py +0 -0
  156. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  157. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  158. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  159. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  160. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  161. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/crosschain/transport.py +0 -0
  162. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/entry_cli.py +0 -0
  163. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/entry_mcp.py +0 -0
  164. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/field_docs.py +0 -0
  165. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  166. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/async_action.py +0 -0
  167. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/base.py +0 -0
  168. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  169. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/erc4626.py +0 -0
  170. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  171. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/events.py +0 -0
  172. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/external_state.py +0 -0
  173. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  174. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  175. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  176. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/merkl.py +0 -0
  177. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/morpho.py +0 -0
  178. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  179. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  180. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/fuses/universal.py +0 -0
  181. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/__init__.py +0 -0
  182. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/architecture.md +0 -0
  183. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/glossary.md +0 -0
  184. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/invariants.md +0 -0
  185. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/guide/quickstart.md +0 -0
  186. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/market_ids.py +0 -0
  187. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/mcp/__init__.py +0 -0
  188. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/mcp/guide.py +0 -0
  189. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/mcp/models.py +0 -0
  190. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/morpho_api.py +0 -0
  191. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/py.typed +0 -0
  192. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/__init__.py +0 -0
  193. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/aave_v3.py +0 -0
  194. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/aave_v4.py +0 -0
  195. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/compound_v3.py +0 -0
  196. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/lending_health.py +0 -0
  197. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/morpho.py +0 -0
  198. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  199. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  200. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  201. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/position_manager.py +0 -0
  202. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  203. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  204. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/validation.py +0 -0
  205. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_erc20.py +0 -0
  206. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_info.py +0 -0
  207. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_managers.py +0 -0
  208. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_positions.py +0 -0
  209. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/src/ipor_fusion/vault_reads.py +0 -0
  210. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/UniswapV3UniversalRouter.py +0 -0
  211. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_crosschain_recovery.py +0 -0
  212. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_euler_v2.py +0 -0
  213. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_foundry.py +0 -0
  214. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_guide.py +0 -0
  215. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_hypercore_model.py +0 -0
  216. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_hypercore_shadow.py +0 -0
  217. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_multicall.py +0 -0
  218. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_simulate.py +0 -0
  219. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/_validation.py +0 -0
  220. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/addresses.py +0 -0
  221. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/conftest.py +0 -0
  222. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/constants.py +0 -0
  223. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  224. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/fixtures/hypercore_market55.json +0 -0
  225. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/fixtures/vault_info_golden/clean.json +0 -0
  226. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  227. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/fixtures/vault_info_golden/rich.json +0 -0
  228. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  229. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_about.py +0 -0
  230. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cache.py +0 -0
  231. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_chains.py +0 -0
  232. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_changelog.py +0 -0
  233. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_config_store.py +0 -0
  234. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_helpers.py +0 -0
  235. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_hypercore.py +0 -0
  236. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_lending_health.py +0 -0
  237. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_market_morpho_blue.py +0 -0
  238. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_pending_requests_scan.py +0 -0
  239. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_role_accounts.py +0 -0
  240. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_vault_info_golden.py +0 -0
  241. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_vault_predates_getters.py +0 -0
  242. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_cli_vault_validate.py +0 -0
  243. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_contract_call.py +0 -0
  244. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_contract_encoder.py +0 -0
  245. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_ccip_events.py +0 -0
  246. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_ccip_indexer.py +0 -0
  247. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_ccip_quote.py +0 -0
  248. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_chainlink.py +0 -0
  249. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_contracts.py +0 -0
  250. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_encoding.py +0 -0
  251. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_errors.py +0 -0
  252. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_layering.py +0 -0
  253. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_readiness.py +0 -0
  254. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_readiness_report.py +0 -0
  255. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_crosschain_transport.py +0 -0
  256. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_erc20.py +0 -0
  257. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_examples_composed_hypercore.py +0 -0
  258. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_examples_crosschain.py +0 -0
  259. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  260. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_examples_vaults.py +0 -0
  261. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_external_state_events.py +0 -0
  262. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_fee_manager.py +0 -0
  263. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_fusion_instance.py +0 -0
  264. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_guide.py +0 -0
  265. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_events.py +0 -0
  266. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_hypercore_flow.py +0 -0
  267. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_logs.py +0 -0
  268. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_mcp_hypercore.py +0 -0
  269. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_mcp_models.py +0 -0
  270. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_mcp_vault_validate.py +0 -0
  271. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_multicall.py +0 -0
  272. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_oracle.py +0 -0
  273. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_oracle_mapping.py +0 -0
  274. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_plasma_vault.py +0 -0
  275. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_public_type_reachability.py +0 -0
  276. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_reader_encoding.py +0 -0
  277. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_readiness_selection.py +0 -0
  278. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_revert_decoding.py +0 -0
  279. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_rewards_manager.py +0 -0
  280. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_roles.py +0 -0
  281. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_sdk_layering.py +0 -0
  282. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_crosschain_canary.py +0 -0
  283. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  284. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_examples_arbitrum.py +0 -0
  285. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_examples_base.py +0 -0
  286. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_examples_crosschain.py +0 -0
  287. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  288. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  289. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_solidity_compiler.py +0 -0
  290. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_solidity_mirrors.py +0 -0
  291. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_substrates.py +0 -0
  292. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_types_and_events.py +0 -0
  293. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_validation.py +0 -0
  294. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_vault_dep_graph.py +0 -0
  295. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_vault_info_onchain_base.py +0 -0
  296. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_vault_simulator_blocks.py +0 -0
  297. {ipor_fusion-4.0.0.dev129 → ipor_fusion-4.0.0.dev137}/tests/test_withdraw_manager.py +0 -0
  298. {ipor_fusion-4.0.0.dev129 → 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,21 @@ 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
282
  takes a `ProposalHash`; `BalanceProposal.proposal_hash` is a `ProposalHash`;
259
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.
260
288
  `ExternalStateExecutor.mark_nav` and `NavMark` are gone: send `propose_balance` and
261
289
  `confirm_balance` from separate custodians, then
262
290
  `PlasmaVault.update_markets_balances([IporFusionMarkets.EXTERNAL_STATE])`.
@@ -327,7 +355,8 @@ placeholder wrapper.
327
355
  `.decode(raw)` and `.map(f)` need no context. `Call(...)` no longer takes `ctx=`.
328
356
  - A wrapper method that executes to answer (storage, log scans, dependent reads) is
329
357
  named `read_*` and takes the context first (table below).
330
- - 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,
331
360
  every re-run of a failed sub-call and the per-call fallback run on its `ctx`, so a
332
361
  pinned context keeps them all at its block; it is no longer a `ContractWrapper`,
333
362
  `aggregate3(calls)` still returns the raw `Call`), `VaultSimulator`,
@@ -342,7 +371,14 @@ placeholder wrapper.
342
371
  `command_in_flight` and `transfer_in_flight` read on `hub_ctx`. The Calls a lane
343
372
  returns are unexecuted, and the executor and dispatcher share one address, so pick
344
373
  the context yourself: `lane.get_balance().call(lane.hub_ctx)`,
345
- `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)`
346
382
  and `.dispatcher(address)` no longer take a context.
347
383
  - Chain-bound decoding takes the chain explicitly:
348
384
  `ExternalStateExecutor.pending_proposals(account, *, chain_id)`,
@@ -380,6 +416,10 @@ placeholder wrapper.
380
416
  | `MorphoReader.rates_from(market, params)` | `MorphoReader.read_rates_from(ctx, market, params)` (it reads the IRM) |
381
417
  | `AaveV3Reader.position_breakdown(asset, user)` | `AaveV3Reader.read_position_breakdown(ctx, asset, user)` |
382
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)` |
383
423
  | `executor.pending_proposals(account)` | `executor.pending_proposals(account, chain_id=ctx.chain_id)` |
384
424
  | `executor.parse_balance_proposed(log)` / `find_balance_proposed(logs)` | `...(log, chain_id=...)` / `...(logs, chain_id=...)` |
385
425
  | `CcipLane(executor=, dispatcher=, spoke_chain_id=, ...)` | `CcipLane(hub_ctx=, spoke_ctx=, executor=, dispatcher=, ...)` (same for `StargateLane`) |
@@ -512,8 +552,12 @@ is the wrong one.
512
552
  - `MorphoApiError` (still a `RuntimeError`), `ExternalStateEventDecodeError` (still a
513
553
  `ValueError`) and `VerifierResultUnavailable` (still a `LookupError`) now derive
514
554
  from `IporFusionError`, so an `except IporFusionError` placed before their own
515
- handlers catches them first. `MorphoApiError` lives in `ipor_fusion.errors` and is
516
- 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`.
517
561
  - `EmptyCallResultError` is no longer an eth_abi `InsufficientDataBytes` (nor a
518
562
  `DecodingError`). Code that caught either around `.call()` to tolerate a missing
519
563
  contract or getter must catch `EmptyCallResultError`; return data that is present
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev129
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
@@ -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
 
@@ -931,7 +932,7 @@ def run_simulation(web3: Web3) -> SimulationResult:
931
932
  # to one custodian by either route, however separate the two keys are -- and
932
933
  # that is why a production deployment cannot drive both halves from one
933
934
  # process, whatever this example does under simulation.
934
- proposed_at = baseline_timestamp + PROPOSE_OFFSET
935
+ proposed_at = Timestamp(baseline_timestamp + PROPOSE_OFFSET)
935
936
  proposal_hash = ExternalStateExecutor.proposal_hash(
936
937
  executor=executor_address,
937
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.dev129"
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]:
@@ -144,6 +144,7 @@ from ipor_fusion.crosschain import (
144
144
  EVMTokenAmount,
145
145
  ExecutorInitParams,
146
146
  LaneFuses,
147
+ LaneHubState,
147
148
  LaneObservation,
148
149
  MessageExecutionState,
149
150
  MessageV1,
@@ -168,7 +169,6 @@ from ipor_fusion.crosschain import (
168
169
  TaxiMessage,
169
170
  TokenTransferV1,
170
171
  VerifierResult,
171
- VerifierResultUnavailable,
172
172
  ccip_events,
173
173
  ccip_receive_calldata,
174
174
  ccip_token_lane,
@@ -205,18 +205,21 @@ from ipor_fusion.errors import (
205
205
  CrosschainDiscoveryError,
206
206
  EmptyCallResultError,
207
207
  ExternalStateError,
208
+ ExternalStateEventDecodeError,
208
209
  HyperCoreValuationError,
209
210
  IporFusionError,
210
211
  LogScanError,
211
212
  LogSourceUnavailable,
212
213
  MorphoMarketNotFoundError,
213
214
  NotPlasmaVaultError,
215
+ OwnerNotFoundError,
214
216
  SimulationError,
215
217
  SimulationRunError,
216
218
  TransactionError,
217
219
  UnsupportedChainError,
218
220
  UnsupportedContractVersionError,
219
221
  UnsupportedVaultVersionError,
222
+ VerifierResultUnavailable,
220
223
  decode_custom_error,
221
224
  get_revert_reason,
222
225
  register_custom_errors,
@@ -300,7 +303,7 @@ from ipor_fusion.fuses import (
300
303
  is_hip3_asset,
301
304
  is_native_perp_asset,
302
305
  perp_dex_of,
303
- read_index_of,
306
+ precompile_index_of,
304
307
  system_address,
305
308
  )
306
309
  from ipor_fusion.fuses.external_state_events import (
@@ -308,7 +311,6 @@ from ipor_fusion.fuses.external_state_events import (
308
311
  EXTERNAL_STATE_EVENTS,
309
312
  EXTERNAL_STATE_TOPICS,
310
313
  ExternalStateEvent,
311
- ExternalStateEventDecodeError,
312
314
  ExternalStateEventSpec,
313
315
  decode_external_state_event,
314
316
  external_state_event_name,
@@ -411,6 +413,7 @@ from ipor_fusion.types import (
411
413
  Amount,
412
414
  AssetId,
413
415
  AssetSource,
416
+ CcipChainSelector,
414
417
  ChainId,
415
418
  Check,
416
419
  CommandId,
@@ -580,6 +583,7 @@ __all__ = [
580
583
  "StargateLane",
581
584
  "CcipLane",
582
585
  "LaneFuses",
586
+ "LaneHubState",
583
587
  "LaneObservation",
584
588
  "open_lane",
585
589
  "open_lanes",
@@ -717,7 +721,7 @@ __all__ = [
717
721
  "is_hip3_asset",
718
722
  "is_native_perp_asset",
719
723
  "perp_dex_of",
720
- "read_index_of",
724
+ "precompile_index_of",
721
725
  "UniversalTokenSwapperAbi",
722
726
  "UniversalTokenSwapperFuse",
723
727
  "UniversalTokenSwapperSubstrates",
@@ -754,6 +758,7 @@ __all__ = [
754
758
  "LogSourceUnavailable",
755
759
  "MorphoMarketNotFoundError",
756
760
  "NotPlasmaVaultError",
761
+ "OwnerNotFoundError",
757
762
  "TransactionError",
758
763
  "SimulationError",
759
764
  "SimulationRunError",
@@ -817,6 +822,7 @@ __all__ = [
817
822
  "ReturnRequestId",
818
823
  "StargateAssetId",
819
824
  "LayerZeroEid",
825
+ "CcipChainSelector",
820
826
  "TransferId",
821
827
  "Price",
822
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)
@@ -18,6 +18,7 @@ from ipor_fusion.errors import (
18
18
  ContractNotFoundError,
19
19
  EmptyCallResultError,
20
20
  NotPlasmaVaultError,
21
+ OwnerNotFoundError,
21
22
  )
22
23
  from ipor_fusion.types import AccessManagerAddress, Period, PlasmaVaultAddress, RoleId
23
24
 
@@ -112,7 +113,14 @@ class AccessManager(ContractWrapper):
112
113
  # ── Composite reads on ``ctx``: event replay + batched hasRole reads ───
113
114
 
114
115
  def read_owner(self, ctx: Web3Context) -> ChecksumAddress:
115
- return self.read_owners(ctx)[0]
116
+ """The first of :meth:`read_owners`; raises `OwnerNotFoundError` when no
117
+ account holds ``OWNER_ROLE``."""
118
+ owners = self.read_owners(ctx)
119
+ if not owners:
120
+ raise OwnerNotFoundError(
121
+ f"access manager {self._address} has no account with OWNER_ROLE"
122
+ )
123
+ return owners[0]
116
124
 
117
125
  def read_owners(self, ctx: Web3Context) -> list[ChecksumAddress]:
118
126
  return [
@@ -140,12 +140,10 @@ class Web3Context:
140
140
 
141
141
  @property
142
142
  def default_block(self) -> BlockIdentifier:
143
+ """The block reads default to: ``"latest"`` unless this context came
144
+ from `at_block`. Read-only; pin a copy with `at_block` instead."""
143
145
  return self._default_block
144
146
 
145
- @default_block.setter
146
- def default_block(self, value: BlockIdentifier) -> None:
147
- self._default_block = value
148
-
149
147
  def at_block(self, block: BlockIdentifier) -> Web3Context:
150
148
  """A copy of this context whose reads default to ``block``: same
151
149
  connection, signer, log source, cache and scan bounds. Pinning a copy leaves
@@ -134,7 +134,7 @@ class ContractWrapper:
134
134
 
135
135
  A method that must execute to answer (a storage read, a log scan, a read
136
136
  that depends on another read) is named ``read_*`` and takes the context as
137
- its first argument.
137
+ its first argument; no other method carries the prefix.
138
138
  """
139
139
 
140
140
  def __init__(self, address: ChecksumAddress):