ipor-fusion 4.0.0.dev200__tar.gz → 4.0.0.dev206__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.github/workflows/cd.yml +2 -1
  2. ipor_fusion-4.0.0.dev206/.github/workflows/ci.yml +55 -0
  3. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.github/workflows/python-build.yml +15 -4
  4. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/AGENTS.md +17 -4
  5. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/PKG-INFO +1 -1
  6. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/pyproject.toml +2 -4
  7. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/context.py +1 -1
  8. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/fusion_factory.py +1 -3
  9. ipor_fusion-4.0.0.dev206/tests/_live.py +67 -0
  10. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/conftest.py +11 -13
  11. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_readiness_selection.py +2 -1
  12. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_solidity_mirrors.py +3 -2
  13. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_vault_info_onchain_base.py +11 -23
  14. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +4 -19
  15. ipor_fusion-4.0.0.dev200/.github/workflows/ci.yml +0 -32
  16. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.claude-plugin/marketplace.json +0 -0
  17. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.claude-plugin/plugin.json +0 -0
  18. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.env.example +0 -0
  19. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.gemini/settings.json +0 -0
  20. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.git-blame-ignore-revs +0 -0
  21. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.github/workflows/commit-lint.yml +0 -0
  22. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.github/workflows/notify-slack.yml +0 -0
  23. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.github/workflows/python-publish.yml +0 -0
  24. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.github/workflows/release.yml +0 -0
  25. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.github/workflows/report-slack.yml +0 -0
  26. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.gitignore +0 -0
  27. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.mcp.json +0 -0
  28. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.pre-commit-config.yaml +0 -0
  29. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/.python-version +0 -0
  30. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/CHANGELOG.md +0 -0
  31. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/CLAUDE.md +0 -0
  32. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/CODEOWNERS +0 -0
  33. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/LICENSE +0 -0
  34. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/MIGRATION_4.md +0 -0
  35. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/README.md +0 -0
  36. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/build.sh +0 -0
  37. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/examples/CONTRIBUTING.md +0 -0
  38. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/examples/README.md +0 -0
  39. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  40. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  41. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  42. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  43. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/examples/external_state_margin_leg_base.py +0 -0
  44. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/examples/simple_aave_v3_supply_base.py +0 -0
  45. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/pytest.ini +0 -0
  46. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/scripts/commit-check.sh +0 -0
  47. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/scripts/crosschain_readiness.py +0 -0
  48. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/skills/ipor-deploy-vault/SKILL.md +0 -0
  49. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/__init__.py +0 -0
  50. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/about.py +0 -0
  51. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cache.py +0 -0
  52. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/chains.py +0 -0
  53. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/__init__.py +0 -0
  54. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  55. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/config_cmd.py +0 -0
  56. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/config_store.py +0 -0
  57. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/main.py +0 -0
  58. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/market_cmd.py +0 -0
  59. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/options.py +0 -0
  60. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  61. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  62. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/vault_health.py +0 -0
  63. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  64. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  65. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  66. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/config/__init__.py +0 -0
  67. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/config/roles.py +0 -0
  68. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/__init__.py +0 -0
  69. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/access.py +0 -0
  70. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/address.py +0 -0
  71. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/contract.py +0 -0
  72. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/erc20.py +0 -0
  73. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/external_state_executor.py +0 -0
  74. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/fee_manager.py +0 -0
  75. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/fuse.py +0 -0
  76. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/fuse_action.py +0 -0
  77. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/logs.py +0 -0
  78. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/multicall.py +0 -0
  79. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/oracle.py +0 -0
  80. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/plasma_vault.py +0 -0
  81. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/rewards_manager.py +0 -0
  82. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/simulation.py +0 -0
  83. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/simulation_result.py +0 -0
  84. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  85. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/__init__.py +0 -0
  86. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  87. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  88. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  89. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  90. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  91. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  92. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  93. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  94. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  95. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/contracts.py +0 -0
  96. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/discovery.py +0 -0
  97. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/errors.py +0 -0
  98. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  99. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  100. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  101. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  102. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/lane.py +0 -0
  103. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/logs.py +0 -0
  104. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/messages.py +0 -0
  105. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/simulation.py +0 -0
  106. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  107. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  108. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  109. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  110. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  111. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/crosschain/transport.py +0 -0
  112. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/dependency_graph.py +0 -0
  113. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/entry_cli.py +0 -0
  114. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/entry_mcp.py +0 -0
  115. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/errors.py +0 -0
  116. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/explorer.py +0 -0
  117. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/field_docs.py +0 -0
  118. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/__init__.py +0 -0
  119. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  120. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/async_action.py +0 -0
  121. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/base.py +0 -0
  122. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  123. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/erc4626.py +0 -0
  124. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  125. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/events.py +0 -0
  126. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/external_state.py +0 -0
  127. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
  128. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  129. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  130. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/hypercore.py +0 -0
  131. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
  132. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/merkl.py +0 -0
  133. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/morpho.py +0 -0
  134. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  135. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  136. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/fuses/universal.py +0 -0
  137. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/guide/__init__.py +0 -0
  138. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/guide/architecture.md +0 -0
  139. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/guide/glossary.md +0 -0
  140. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/guide/invariants.md +0 -0
  141. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/guide/quickstart.md +0 -0
  142. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/market_ids.py +0 -0
  143. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/market_info.py +0 -0
  144. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/mcp/__init__.py +0 -0
  145. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/mcp/guide.py +0 -0
  146. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/mcp/models.py +0 -0
  147. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/mcp/server.py +0 -0
  148. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/morpho_api.py +0 -0
  149. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/py.typed +0 -0
  150. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/__init__.py +0 -0
  151. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/aave_v3.py +0 -0
  152. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/aave_v4.py +0 -0
  153. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/compound_v3.py +0 -0
  154. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/hypercore.py +0 -0
  155. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  156. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/lending_health.py +0 -0
  157. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/morpho.py +0 -0
  158. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  159. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  160. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  161. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/position_manager.py +0 -0
  162. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  163. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  164. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/readers/unpriceable.py +0 -0
  165. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/substrates.py +0 -0
  166. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/types.py +0 -0
  167. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/validation.py +0 -0
  168. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/vault_data.py +0 -0
  169. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/vault_erc20.py +0 -0
  170. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/vault_info.py +0 -0
  171. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/vault_managers.py +0 -0
  172. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/vault_positions.py +0 -0
  173. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/src/ipor_fusion/vault_reads.py +0 -0
  174. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/UniswapV3UniversalRouter.py +0 -0
  175. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_crosschain.py +0 -0
  176. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_crosschain_lifecycle.py +0 -0
  177. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_crosschain_recovery.py +0 -0
  178. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_euler_v2.py +0 -0
  179. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_event_args.py +0 -0
  180. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_fake_rpc.py +0 -0
  181. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_foundry.py +0 -0
  182. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_golden.py +0 -0
  183. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_guide.py +0 -0
  184. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_hypercore_model.py +0 -0
  185. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_hypercore_shadow.py +0 -0
  186. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_multicall.py +0 -0
  187. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_oracle_mapping_fake.py +0 -0
  188. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_simulate.py +0 -0
  189. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/_validation.py +0 -0
  190. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/addresses.py +0 -0
  191. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/constants.py +0 -0
  192. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  193. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/hypercore_market55.json +0 -0
  194. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
  195. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
  196. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
  197. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
  198. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
  199. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
  200. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
  201. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
  202. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_blue.json +0 -0
  203. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
  204. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
  205. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
  206. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
  207. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
  208. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
  209. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
  210. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/simulate_v1_request.json +0 -0
  211. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/clean.json +0 -0
  212. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
  213. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  214. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
  215. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
  216. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
  217. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
  218. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/fetched.json +0 -0
  219. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
  220. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/full.json +0 -0
  221. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
  222. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/full.txt +0 -0
  223. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/rich.json +0 -0
  224. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
  225. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  226. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_about.py +0 -0
  227. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_access_manager.py +0 -0
  228. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cache.py +0 -0
  229. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_chains.py +0 -0
  230. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_changelog.py +0 -0
  231. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_commands.py +0 -0
  232. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_config_store.py +0 -0
  233. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_helpers.py +0 -0
  234. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_hypercore.py +0 -0
  235. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_lending_health.py +0 -0
  236. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_market_golden.py +0 -0
  237. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_market_morpho_blue.py +0 -0
  238. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_oracle_mapping.py +0 -0
  239. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_oracle_mapping_golden.py +0 -0
  240. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_pending_requests_scan.py +0 -0
  241. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_role_accounts.py +0 -0
  242. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_vault_info_golden.py +0 -0
  243. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_vault_predates_getters.py +0 -0
  244. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_cli_vault_validate.py +0 -0
  245. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_contract_call.py +0 -0
  246. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_contract_encoder.py +0 -0
  247. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_ccip_events.py +0 -0
  248. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_ccip_indexer.py +0 -0
  249. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_ccip_quote.py +0 -0
  250. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_chainlink.py +0 -0
  251. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_contracts.py +0 -0
  252. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_encoding.py +0 -0
  253. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_errors.py +0 -0
  254. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_lane.py +0 -0
  255. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_layering.py +0 -0
  256. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_readiness.py +0 -0
  257. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_readiness_report.py +0 -0
  258. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_crosschain_transport.py +0 -0
  259. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_docs_typing.py +0 -0
  260. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_erc20.py +0 -0
  261. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_error_hierarchy.py +0 -0
  262. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_examples_composed_hypercore.py +0 -0
  263. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_examples_crosschain.py +0 -0
  264. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  265. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_examples_vaults.py +0 -0
  266. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_explorer.py +0 -0
  267. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_external_state_events.py +0 -0
  268. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_external_state_executor.py +0 -0
  269. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_fee_manager.py +0 -0
  270. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_fuse_encoding.py +0 -0
  271. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_fusion_instance.py +0 -0
  272. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_guide.py +0 -0
  273. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_hypercore_deployment.py +0 -0
  274. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_hypercore_events.py +0 -0
  275. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_hypercore_flow.py +0 -0
  276. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_hypercore_live.py +0 -0
  277. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_hypercore_outcome.py +0 -0
  278. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_hypercore_readers.py +0 -0
  279. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_hypercore_simulation.py +0 -0
  280. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_logs.py +0 -0
  281. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_mcp_hypercore.py +0 -0
  282. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_mcp_models.py +0 -0
  283. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_mcp_server.py +0 -0
  284. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_mcp_vault_validate.py +0 -0
  285. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_multicall.py +0 -0
  286. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_oracle.py +0 -0
  287. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_oracle_mapping.py +0 -0
  288. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_plasma_vault.py +0 -0
  289. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_public_api.py +0 -0
  290. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_public_type_reachability.py +0 -0
  291. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_reader_encoding.py +0 -0
  292. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_revert_decoding.py +0 -0
  293. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_rewards_manager.py +0 -0
  294. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_roles.py +0 -0
  295. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_sdk_layering.py +0 -0
  296. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  297. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  298. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  299. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_crosschain_canary.py +0 -0
  300. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_crosschain_hypercore.py +0 -0
  301. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  302. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  303. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  304. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  305. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  306. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  307. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  308. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_examples_arbitrum.py +0 -0
  309. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_examples_base.py +0 -0
  310. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_examples_crosschain.py +0 -0
  311. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_external_state_executor_base.py +0 -0
  312. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  313. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  314. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  315. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_morpho_blue.py +0 -0
  316. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_pilot_v3.py +0 -0
  317. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  318. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  319. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  320. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  321. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_readers_integration.py +0 -0
  322. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_release_funds.py +0 -0
  323. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_swap_on_base.py +0 -0
  324. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  325. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulation_errors.py +0 -0
  326. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_simulation_observation.py +0 -0
  327. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_solidity_compiler.py +0 -0
  328. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_substrates.py +0 -0
  329. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_types_and_events.py +0 -0
  330. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_unpriceable_tokens.py +0 -0
  331. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_validation.py +0 -0
  332. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_vault_data_block.py +0 -0
  333. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_vault_dep_graph.py +0 -0
  334. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_vault_info_fetched_golden.py +0 -0
  335. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_vault_reconciliation_onchain_base.py +0 -0
  336. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_vault_simulator_blocks.py +0 -0
  337. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_web3_context.py +0 -0
  338. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/test_withdraw_manager.py +0 -0
  339. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/typed_client/consumer.py +0 -0
  340. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/tests/typed_client/rejected.py +0 -0
  341. {ipor_fusion-4.0.0.dev200 → ipor_fusion-4.0.0.dev206}/uv.lock +0 -0
@@ -12,12 +12,13 @@ jobs:
12
12
  SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
13
13
 
14
14
  build:
15
- needs: notify
16
15
  uses: ./.github/workflows/python-build.yml
17
16
  secrets:
18
17
  ETHEREUM_PROVIDER_URL: ${{ secrets.ETHEREUM_PROVIDER_URL }}
19
18
  ARBITRUM_PROVIDER_URL: ${{ secrets.ARBITRUM_PROVIDER_URL }}
20
19
  BASE_PROVIDER_URL: ${{ secrets.BASE_PROVIDER_URL }}
20
+ with:
21
+ require-live: ETHEREUM_PROVIDER_URL,ARBITRUM_PROVIDER_URL,BASE_PROVIDER_URL,GITHUB
21
22
 
22
23
  report:
23
24
  if: ${{ always() }}
@@ -0,0 +1,55 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - "**"
7
+
8
+ jobs:
9
+ notify:
10
+ uses: ./.github/workflows/notify-slack.yml
11
+ secrets:
12
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
13
+
14
+ # No secrets: what the code proves offline, on every supported Python.
15
+ offline:
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ python-version: ["3.11", "3.12", "3.13"]
20
+ uses: ./.github/workflows/python-build.yml
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ format-code-enabled: ${{ matrix.python-version == '3.12' }}
24
+ static-code-analysis-enabled: ${{ matrix.python-version == '3.12' }}
25
+ build-enabled: ${{ matrix.python-version == '3.12' }}
26
+ coverage-artifact-name: coverage-offline-${{ matrix.python-version }}
27
+
28
+ # Live providers, for pull requests from this repository only (a fork gets
29
+ # no secrets); a provider or GitHub outage fails it instead of skipping.
30
+ live:
31
+ if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
32
+ uses: ./.github/workflows/python-build.yml
33
+ secrets:
34
+ ETHEREUM_PROVIDER_URL: ${{ secrets.ETHEREUM_PROVIDER_URL }}
35
+ ARBITRUM_PROVIDER_URL: ${{ secrets.ARBITRUM_PROVIDER_URL }}
36
+ BASE_PROVIDER_URL: ${{ secrets.BASE_PROVIDER_URL }}
37
+ with:
38
+ format-code-enabled: false
39
+ static-code-analysis-enabled: false
40
+ build-enabled: false
41
+ coverage-artifact-name: coverage-live
42
+ require-live: ETHEREUM_PROVIDER_URL,ARBITRUM_PROVIDER_URL,BASE_PROVIDER_URL,GITHUB
43
+
44
+ report:
45
+ if: ${{ always() }}
46
+ needs:
47
+ - notify
48
+ - offline
49
+ - live
50
+ uses: ./.github/workflows/report-slack.yml
51
+ secrets:
52
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
53
+ with:
54
+ success: ${{ needs.offline.result == 'success' && contains(fromJSON('["success", "skipped"]'), needs.live.result) }}
55
+ slack-status-msg-id: ${{ needs.notify.outputs.slack-status-msg-id }}
@@ -5,11 +5,11 @@ on:
5
5
 
6
6
  secrets:
7
7
  ARBITRUM_PROVIDER_URL:
8
- required: true
8
+ required: false
9
9
  BASE_PROVIDER_URL:
10
- required: true
10
+ required: false
11
11
  ETHEREUM_PROVIDER_URL:
12
- required: true
12
+ required: false
13
13
 
14
14
  inputs:
15
15
  python-version:
@@ -42,6 +42,16 @@ on:
42
42
  type: boolean
43
43
  default: true
44
44
  required: false
45
+ coverage-artifact-name:
46
+ description: "Name of the uploaded coverage artifact (unique per call in a run)"
47
+ type: string
48
+ default: coverage-report
49
+ required: false
50
+ require-live:
51
+ description: "Comma-separated live sources whose tests must run, not skip (tests/_live.py)"
52
+ type: string
53
+ default: ""
54
+ required: false
45
55
 
46
56
  jobs:
47
57
  python:
@@ -93,6 +103,7 @@ jobs:
93
103
  ETHEREUM_PROVIDER_URL: ${{ secrets.ETHEREUM_PROVIDER_URL }}
94
104
  ARBITRUM_PROVIDER_URL: ${{ secrets.ARBITRUM_PROVIDER_URL }}
95
105
  BASE_PROVIDER_URL: ${{ secrets.BASE_PROVIDER_URL }}
106
+ IPOR_FUSION_REQUIRE_LIVE: ${{ inputs.require-live }}
96
107
  id: tests
97
108
 
98
109
  - name: Coverage summary
@@ -104,6 +115,6 @@ jobs:
104
115
  if: inputs.test-enabled && always() && steps.tests.outcome != 'skipped'
105
116
  uses: actions/upload-artifact@v4
106
117
  with:
107
- name: coverage-report
118
+ name: ${{ inputs.coverage-artifact-name }}
108
119
  path: coverage.xml
109
120
 
@@ -27,7 +27,7 @@ uv sync --all-extras --locked # install, same as
27
27
  uv run ruff format ./ # format (CI runs --check)
28
28
  uv run ruff check ./ # lint: bandit S, C901 max-complexity 10, PLR limits
29
29
  uv run pyright # types, basic mode, src + tests + examples + scripts
30
- uv run pytest # all tests + coverage gate (fail_under = 95)
30
+ uv run pytest # all tests + coverage gate (fail_under = 98, mcp/ included)
31
31
  uv run pytest -m "cli or mcp" --no-cov # fast offline subset
32
32
  uv run pytest --run-readiness -m readiness --no-cov -rA # opt-in live Ethereum-hub POC checks
33
33
  uv run python scripts/crosschain_readiness.py --output /tmp/crosschain-readiness.json # live Arbitrum/HyperEVM report
@@ -54,13 +54,26 @@ the sequence in `.github/workflows/python-build.yml`. When ruff `C901` trips,
54
54
  extract helpers, never suppress.
55
55
 
56
56
  **Offline vs network.** `test_cli_*` and `test_mcp_*` need no network
57
- (auto-tagged `cli` / `mcp` in `conftest.py`; everything else is `sdk`).
57
+ (auto-tagged `cli` / `mcp` in `conftest.py`, `test_examples_*` `examples`;
58
+ everything else is `sdk`). Most `sdk` tests are offline too: the suite with no
59
+ provider set still clears the coverage gate.
58
60
  Integration tests (`test_simulate_*`, `test_vault_info_onchain_*`) run against
59
61
  live RPCs via `eth_simulateV1`, no Docker or Anvil (`ANVIL_WALLET` is just a
60
62
  named address constant). They auto-skip unless `ETHEREUM_PROVIDER_URL`,
61
63
  `BASE_PROVIDER_URL` and `ARBITRUM_PROVIDER_URL` are set (`.env` is loaded via
62
- python-dotenv) and the provider supports `eth_simulateV1`. CI has all three as
63
- secrets. Never print `.env` or a provider URL: they embed API keys.
64
+ python-dotenv) and the provider supports `eth_simulateV1`. Every such gate goes
65
+ through `tests/_live.py` (`provider_url`, `archive_context`, `unavailable`): it
66
+ skips locally and fails when the source is named in `IPOR_FUSION_REQUIRE_LIVE`
67
+ (comma-separated env var names, `GITHUB` for the Solidity mirrors). CI
68
+ (`ci.yml`) runs an offline job without secrets on Python 3.11, 3.12 and 3.13
69
+ and, for pull requests from this repository, a live job with the three
70
+ provider secrets that requires all of them and GitHub: a skip for one of those
71
+ sources being unavailable is a failure there (state-dependent skips such as a
72
+ pilot vault's preconditions remain skips, and the offline job still reads the
73
+ public Solidity sources from GitHub). Skip only for an unavailable source; an
74
+ exception from the
75
+ SDK is a test failure, never a skip. Never print `.env` or a provider URL: they
76
+ embed API keys, so skip and fail messages carry the exception type only.
64
77
  `HYPEREVM_PROVIDER_URL` is used by the HyperEVM lifecycle/deployment tests and
65
78
  the readiness script; general PR CI does not supply it.
66
79
  `test_simulate_crosschain_hyperevm.py` rehearses Arbitrum → HyperEVM on pinned blocks,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev200
3
+ Version: 4.0.0.dev206
4
4
  Summary: The IPOR Fusion SDK for Python
5
5
  Project-URL: homepage, https://ipor.io
6
6
  Project-URL: repository, https://github.com/IPOR-Labs/ipor-fusion.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev200"
3
+ version = "4.0.0.dev206"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -123,12 +123,10 @@ executionEnvironments = [
123
123
  [tool.coverage.run]
124
124
  source = ["src/ipor_fusion"]
125
125
  data_file = ".coverage_data/.coverage"
126
- omit = ["src/ipor_fusion/mcp/*"]
127
126
 
128
127
  [tool.coverage.report]
129
128
  show_missing = true
130
- fail_under = 95
131
- omit = ["src/ipor_fusion/mcp/*"]
129
+ fail_under = 98
132
130
 
133
131
  [tool.semantic_release]
134
132
  # Runs inside the python-semantic-release Docker container, not on the runner
@@ -156,7 +156,7 @@ class Web3Context:
156
156
  return self._signer
157
157
 
158
158
  @classmethod
159
- def from_url( # proctor: ignore[primitives-too-many-params]
159
+ def from_url(
160
160
  cls,
161
161
  url: str,
162
162
  *,
@@ -79,9 +79,7 @@ class FusionInstance:
79
79
  price_manager: ChecksumAddress
80
80
 
81
81
  @classmethod
82
- def from_dict( # proctor: ignore[primitives-untyped-dict]
83
- cls, data: Mapping[str, object]
84
- ) -> FusionInstance:
82
+ def from_dict(cls, data: Mapping[str, object]) -> FusionInstance:
85
83
  """Rebuild an instance from its JSON form (``dataclasses.asdict``),
86
84
  parsing every field: role addresses through their parsers, the rest
87
85
  checksummed, integers and strings type-checked. Raises ``ValueError``
@@ -0,0 +1,67 @@
1
+ """Gates of the tests that need a live provider or network source.
2
+
3
+ Locally a missing or unreachable source skips the test. A run that names the
4
+ source in ``IPOR_FUSION_REQUIRE_LIVE`` (comma-separated: provider env var names
5
+ such as ``BASE_PROVIDER_URL``, or ``GITHUB`` for the Solidity mirrors) fails
6
+ instead, so a green CI run means the live checks ran rather than skipped.
7
+ Messages carry the exception type only: provider URLs embed API keys.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ from typing import NoReturn
14
+
15
+ import pytest
16
+ import requests
17
+ from web3 import Web3
18
+ from web3.exceptions import Web3Exception
19
+
20
+ from ipor_fusion import Web3Context
21
+
22
+
23
+ def required() -> frozenset[str]:
24
+ return frozenset(
25
+ name.strip()
26
+ for name in os.environ.get("IPOR_FUSION_REQUIRE_LIVE", "").split(",")
27
+ if name.strip()
28
+ )
29
+
30
+
31
+ def unavailable(source: str, reason: str) -> NoReturn:
32
+ """Skip, or fail when this run requires ``source``."""
33
+ if source in required():
34
+ pytest.fail(f"{source} is required in this run: {reason}", pytrace=False)
35
+ pytest.skip(reason)
36
+
37
+
38
+ def provider_url(env_var: str) -> str:
39
+ url = os.environ.get(env_var)
40
+ if not url:
41
+ unavailable(env_var, f"{env_var} not set")
42
+ return url
43
+
44
+
45
+ # A provider that cannot be reached, refuses the key, or has pruned the state.
46
+ _INFRA_ERRORS = (Web3Exception, requests.RequestException, OSError)
47
+
48
+
49
+ def archive_context(env_var: str, block: int, contract: str) -> Web3Context:
50
+ """A context pinned to ``block`` on the provider in ``env_var``, once it
51
+ has connected and served ``contract``'s code at that block (historical
52
+ state, not just the header); later SDK errors are test failures, not
53
+ infrastructure."""
54
+ url = provider_url(env_var)
55
+ # The failure is reported outside the except block: a chained traceback
56
+ # would print the request URL, which embeds the API key.
57
+ failure: str | None = None
58
+ try:
59
+ ctx = Web3Context.from_url(url)
60
+ code = ctx.web3.eth.get_code(Web3.to_checksum_address(contract), block)
61
+ except _INFRA_ERRORS as exc:
62
+ failure = type(exc).__name__
63
+ if failure is not None:
64
+ unavailable(env_var, f"{env_var} cannot serve state at {block}: {failure}")
65
+ if not code:
66
+ unavailable(env_var, f"{env_var} returned no code for {contract} at {block}")
67
+ return ctx.at_block(block)
@@ -1,11 +1,11 @@
1
1
  import importlib.util
2
- import os
3
2
  from collections.abc import Callable
4
3
  from pathlib import Path
5
4
  from types import ModuleType
6
5
  from unittest.mock import patch
7
6
 
8
7
  import pytest
8
+ from _live import provider_url, unavailable
9
9
  from _multicall import SequentialMulticall
10
10
  from dotenv import load_dotenv
11
11
  from web3 import Web3
@@ -72,40 +72,38 @@ def pytest_collection_modifyitems(config, items):
72
72
 
73
73
 
74
74
  def _connected_web3(env_var: str) -> Web3:
75
- """Build a Web3 client from `env_var`; skip the test if missing/unreachable."""
76
- url = os.environ.get(env_var)
77
- if not url:
78
- pytest.skip(f"{env_var} not set")
79
- w3 = Web3(Web3.HTTPProvider(url))
75
+ """Build a Web3 client from `env_var`; skip the test if missing/unreachable
76
+ (fail when the run requires it, see `_live`)."""
77
+ w3 = Web3(Web3.HTTPProvider(provider_url(env_var)))
80
78
  if not w3.is_connected():
81
- pytest.skip(f"cannot reach RPC at {env_var}")
79
+ unavailable(env_var, f"cannot reach RPC at {env_var}")
82
80
  return w3
83
81
 
84
82
 
85
- def _ensure_simulate_v1(web3: Web3) -> None:
83
+ def _ensure_simulate_v1(web3: Web3, env_var: str) -> None:
86
84
  """Skip if the provider does not implement `eth_simulateV1`."""
87
85
  if not is_simulate_v1_supported(web3):
88
- pytest.skip("provider does not support eth_simulateV1")
86
+ unavailable(env_var, f"{env_var} does not support eth_simulateV1")
89
87
 
90
88
 
91
89
  @pytest.fixture(scope="session")
92
90
  def web3_eth() -> Web3:
93
91
  w3 = _connected_web3("ETHEREUM_PROVIDER_URL")
94
- _ensure_simulate_v1(w3)
92
+ _ensure_simulate_v1(w3, "ETHEREUM_PROVIDER_URL")
95
93
  return w3
96
94
 
97
95
 
98
96
  @pytest.fixture(scope="session")
99
97
  def web3_base() -> Web3:
100
98
  w3 = _connected_web3("BASE_PROVIDER_URL")
101
- _ensure_simulate_v1(w3)
99
+ _ensure_simulate_v1(w3, "BASE_PROVIDER_URL")
102
100
  return w3
103
101
 
104
102
 
105
103
  @pytest.fixture(scope="session")
106
104
  def web3_arb() -> Web3:
107
105
  w3 = _connected_web3("ARBITRUM_PROVIDER_URL")
108
- _ensure_simulate_v1(w3)
106
+ _ensure_simulate_v1(w3, "ARBITRUM_PROVIDER_URL")
109
107
  return w3
110
108
 
111
109
 
@@ -113,5 +111,5 @@ def web3_arb() -> Web3:
113
111
  def web3_hyperevm() -> Web3:
114
112
  """HyperEVM simulation fixture; general PR CI does not supply its RPC secret."""
115
113
  w3 = _connected_web3("HYPEREVM_PROVIDER_URL")
116
- _ensure_simulate_v1(w3)
114
+ _ensure_simulate_v1(w3, "HYPEREVM_PROVIDER_URL")
117
115
  return w3
@@ -22,7 +22,8 @@ pytest_plugins = ["pytester"]
22
22
  def test_readiness_requires_explicit_opt_in(pytester, args, passed, deselected):
23
23
  pytester.makeconftest(Path(__file__).with_name("conftest.py").read_text())
24
24
  pytester.makepyfile(
25
- _multicall=Path(__file__).with_name("_multicall.py").read_text()
25
+ _multicall=Path(__file__).with_name("_multicall.py").read_text(),
26
+ _live=Path(__file__).with_name("_live.py").read_text(),
26
27
  )
27
28
  pytester.makeini("[pytest]\nmarkers =\n sdk\n cli\n mcp\n readiness\n")
28
29
  pytester.makepyfile(
@@ -14,6 +14,7 @@ from functools import cache
14
14
 
15
15
  import pytest
16
16
  import requests
17
+ from _live import unavailable
17
18
  from eth_utils import function_signature_to_4byte_selector
18
19
 
19
20
  from ipor_fusion.config.roles import Roles
@@ -58,11 +59,11 @@ def _solidity_source(path: str) -> str:
58
59
  try:
59
60
  resp = requests.get(url, timeout=15)
60
61
  except requests.RequestException as exc:
61
- pytest.skip(f"cannot fetch {path} from GitHub: {exc}")
62
+ unavailable("GITHUB", f"cannot fetch {path} from GitHub: {type(exc).__name__}")
62
63
  if resp.status_code == 404:
63
64
  pytest.fail(f"{url} not found — wrong pinned ref or moved file")
64
65
  if resp.status_code != 200:
65
- pytest.skip(f"GitHub returned HTTP {resp.status_code} for {path}")
66
+ unavailable("GITHUB", f"GitHub returned HTTP {resp.status_code} for {path}")
66
67
  return resp.text
67
68
 
68
69
 
@@ -17,11 +17,10 @@ historical block requires an archive node.
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- import os
21
20
  from collections import Counter
22
21
  from unittest.mock import patch
23
22
 
24
- import pytest
23
+ from _live import archive_context, provider_url
25
24
  from web3 import Web3
26
25
  from web3.providers.rpc import HTTPProvider
27
26
 
@@ -35,15 +34,8 @@ EXPECTED_IDLE_USDC = 10 * 10**6 # 10 USDC (6 decimals) sitting idle on the vaul
35
34
 
36
35
 
37
36
  def test_idle_underlying_reconciles_on_real_base_vault():
38
- url = os.environ.get("BASE_PROVIDER_URL")
39
- if not url:
40
- pytest.skip("BASE_PROVIDER_URL not set")
41
-
42
- try: # pylint: disable=broad-except
43
- validation = validate_vault(Web3Context.from_url(url), VAULT, block=BLOCK)
44
- except Exception as exc: # pylint: disable=broad-except
45
- # Infra problem (provider unreachable or non-archive) — skip, don't fail.
46
- pytest.skip(f"BASE on-chain read failed (provider missing/non-archive?): {exc}")
37
+ ctx = archive_context("BASE_PROVIDER_URL", BLOCK, VAULT)
38
+ validation = validate_vault(ctx, VAULT, block=BLOCK)
47
39
 
48
40
  # Sanity: confirm we are reading the expected vault/chain state at this block.
49
41
  recon = validation.reconciliation
@@ -64,9 +56,8 @@ MAX_ETH_CALLS = 20
64
56
 
65
57
 
66
58
  def test_fetch_stays_within_round_trip_budget():
67
- url = os.environ.get("BASE_PROVIDER_URL")
68
- if not url:
69
- pytest.skip("BASE_PROVIDER_URL not set")
59
+ archive_context("BASE_PROVIDER_URL", BLOCK, VAULT) # skips without an archive
60
+ url = provider_url("BASE_PROVIDER_URL")
70
61
 
71
62
  methods: Counter[str] = Counter()
72
63
  make_request = HTTPProvider.make_request
@@ -75,15 +66,12 @@ def test_fetch_stays_within_round_trip_budget():
75
66
  methods[method] += 1
76
67
  return make_request(provider, method, params)
77
68
 
78
- try: # pylint: disable=broad-except
79
- # web3 binds make_request when the provider is built, so the context
80
- # must be created under the patch for any request to be counted.
81
- with patch.object(HTTPProvider, "make_request", counting):
82
- ctx = Web3Context.from_url(url)
83
- methods.clear()
84
- data = fetch_vault_data(ctx, VAULT, block=BLOCK)
85
- except Exception as exc: # pylint: disable=broad-except
86
- pytest.skip(f"BASE on-chain read failed (provider missing/non-archive?): {exc}")
69
+ # web3 binds make_request when the provider is built, so the context
70
+ # must be created under the patch for any request to be counted.
71
+ with patch.object(HTTPProvider, "make_request", counting):
72
+ ctx = Web3Context.from_url(url)
73
+ methods.clear()
74
+ data = fetch_vault_data(ctx, VAULT, block=BLOCK)
87
75
 
88
76
  assert data.total_assets == EXPECTED_IDLE_USDC
89
77
  assert methods["eth_call"] > 0, "requests were not counted"
@@ -16,12 +16,10 @@ code under test runs unguarded, so a regression fails instead of skipping.
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- import os
20
-
21
- import pytest
19
+ from _live import archive_context
22
20
  from web3 import Web3
23
21
 
24
- from ipor_fusion import PlasmaVault, Web3Context
22
+ from ipor_fusion import PlasmaVault
25
23
  from ipor_fusion.vault_data import fetch_vault_data
26
24
 
27
25
  VAULT = Web3.to_checksum_address("0x45aa96f0b3188D47a1DaFdbefCE1db6B37f58216")
@@ -30,22 +28,9 @@ CHAIN_ID = 8453
30
28
  BLOCK = 50788912
31
29
 
32
30
 
33
- def _connect(url: str) -> tuple[Web3Context, PlasmaVault]:
34
- """Skip on infra problems only (unreachable or non-archive provider)."""
35
- try:
36
- ctx = Web3Context.from_url(url).at_block(BLOCK)
37
- pv = PlasmaVault(VAULT)
38
- pv.underlying_asset_address().call(ctx) # state read at BLOCK needs archive
39
- except Exception as exc: # noqa: BLE001
40
- pytest.skip(f"BASE on-chain read failed (provider missing/non-archive?): {exc}")
41
- return ctx, pv
42
-
43
-
44
31
  def test_vault_without_withdraw_manager_fetches_on_real_base_vault():
45
- url = os.environ.get("BASE_PROVIDER_URL")
46
- if not url:
47
- pytest.skip("BASE_PROVIDER_URL not set")
48
- ctx, pv = _connect(url)
32
+ ctx = archive_context("BASE_PROVIDER_URL", BLOCK, VAULT)
33
+ pv = PlasmaVault(VAULT)
49
34
 
50
35
  withdraw_manager = pv.read_withdraw_manager_address(ctx)
51
36
  data = fetch_vault_data(ctx, pv.address, block=BLOCK)
@@ -1,32 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- pull_request:
5
- branches:
6
- - "**"
7
-
8
- jobs:
9
- notify:
10
- uses: ./.github/workflows/notify-slack.yml
11
- secrets:
12
- SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
13
-
14
- build:
15
- needs: notify
16
- uses: ./.github/workflows/python-build.yml
17
- secrets:
18
- ETHEREUM_PROVIDER_URL: ${{ secrets.ETHEREUM_PROVIDER_URL }}
19
- ARBITRUM_PROVIDER_URL: ${{ secrets.ARBITRUM_PROVIDER_URL }}
20
- BASE_PROVIDER_URL: ${{ secrets.BASE_PROVIDER_URL }}
21
-
22
- report:
23
- if: ${{ always() }}
24
- needs:
25
- - notify
26
- - build
27
- uses: ./.github/workflows/report-slack.yml
28
- secrets:
29
- SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
30
- with:
31
- success: ${{ needs.build.result == 'success' }}
32
- slack-status-msg-id: ${{ needs.notify.outputs.slack-status-msg-id }}