ipor-fusion 4.2.0.dev11__tar.gz → 4.2.0.dev16__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 (374) hide show
  1. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/PKG-INFO +13 -2
  2. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/README.md +12 -1
  3. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/README.md +6 -0
  4. ipor_fusion-4.2.0.dev16/examples/advanced_compound_v3_institutional_usdc_ethereum.py +499 -0
  5. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/pyproject.toml +1 -1
  6. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/__init__.py +6 -0
  7. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_markets_view.py +3 -3
  8. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/__init__.py +9 -1
  9. ipor_fusion-4.2.0.dev16/src/ipor_fusion/readers/compound_v3.py +164 -0
  10. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/lending_health.py +132 -55
  11. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/validation.py +8 -2
  12. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_data.py +8 -4
  13. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_positions.py +28 -1
  14. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_lending_health.py +202 -1
  15. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_vault_predates_getters.py +1 -1
  16. ipor_fusion-4.2.0.dev16/tests/test_examples_compound_v3_ethereum.py +73 -0
  17. ipor_fusion-4.2.0.dev16/tests/test_lending_health_compound_v3_onchain.py +87 -0
  18. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_public_api.py +6 -0
  19. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_reader_encoding.py +7 -1
  20. ipor_fusion-4.2.0.dev16/tests/test_simulate_examples_ethereum.py +24 -0
  21. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_validation.py +8 -0
  22. ipor_fusion-4.2.0.dev11/src/ipor_fusion/readers/compound_v3.py +0 -32
  23. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.claude-plugin/marketplace.json +0 -0
  24. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.claude-plugin/plugin.json +0 -0
  25. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.env.example +0 -0
  26. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.gemini/settings.json +0 -0
  27. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.git-blame-ignore-revs +0 -0
  28. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/cd.yml +0 -0
  29. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/ci.yml +0 -0
  30. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/commit-lint.yml +0 -0
  31. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/notify-slack.yml +0 -0
  32. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/python-build.yml +0 -0
  33. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/python-publish.yml +0 -0
  34. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/release.yml +0 -0
  35. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/report-slack.yml +0 -0
  36. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.gitignore +0 -0
  37. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.mcp.json +0 -0
  38. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.pre-commit-config.yaml +0 -0
  39. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.python-version +0 -0
  40. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/AGENTS.md +0 -0
  41. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/CHANGELOG.md +0 -0
  42. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/CLAUDE.md +0 -0
  43. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/CODEOWNERS +0 -0
  44. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/LICENSE +0 -0
  45. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/MIGRATION_4.md +0 -0
  46. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/build.sh +0 -0
  47. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/CONTRIBUTING.md +0 -0
  48. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  49. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  50. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  51. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/external_state_margin_leg_base.py +0 -0
  52. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/hypercore_vault_official_deployment_hyperevm.py +0 -0
  53. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/simple_aave_v3_supply_base.py +0 -0
  54. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/pytest.ini +0 -0
  55. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/scripts/commit-check.sh +0 -0
  56. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/scripts/fusion_error_signatures.py +0 -0
  57. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/skills/ipor-deploy-vault/SKILL.md +0 -0
  58. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/about.py +0 -0
  59. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cache.py +0 -0
  60. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/chains.py +0 -0
  61. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/__init__.py +0 -0
  62. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/address_cmd.py +0 -0
  63. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  64. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/config_cmd.py +0 -0
  65. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/config_store.py +0 -0
  66. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/fuse_cmd.py +0 -0
  67. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/main.py +0 -0
  68. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/market_cmd.py +0 -0
  69. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/options.py +0 -0
  70. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  71. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/preflight_cmd.py +0 -0
  72. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  73. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_health.py +0 -0
  74. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  75. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  76. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/config/__init__.py +0 -0
  77. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/config/roles.py +0 -0
  78. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/__init__.py +0 -0
  79. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/access.py +0 -0
  80. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/address.py +0 -0
  81. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/context.py +0 -0
  82. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/context_manager.py +0 -0
  83. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/contract.py +0 -0
  84. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/erc20.py +0 -0
  85. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/external_state_executor.py +0 -0
  86. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fee_manager.py +0 -0
  87. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fuse.py +0 -0
  88. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fuse_action.py +0 -0
  89. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fuse_decoding.py +0 -0
  90. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fusion_factory.py +0 -0
  91. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/logs.py +0 -0
  92. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/multicall.py +0 -0
  93. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/oracle.py +0 -0
  94. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/plasma_vault.py +0 -0
  95. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/rewards_manager.py +0 -0
  96. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/simulation.py +0 -0
  97. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/simulation_result.py +0 -0
  98. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  99. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/__init__.py +0 -0
  100. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  101. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  102. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  103. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  104. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  105. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  106. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  107. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  108. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  109. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/contracts.py +0 -0
  110. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/discovery.py +0 -0
  111. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/errors.py +0 -0
  112. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  113. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  114. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  115. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  116. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/lane.py +0 -0
  117. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/logs.py +0 -0
  118. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/messages.py +0 -0
  119. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/simulation.py +0 -0
  120. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  121. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  122. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  123. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  124. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  125. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/transport.py +0 -0
  126. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/dependency_graph.py +0 -0
  127. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/entry_cli.py +0 -0
  128. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/entry_mcp.py +0 -0
  129. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/errors.py +0 -0
  130. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/explorer.py +0 -0
  131. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/field_docs.py +0 -0
  132. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuse_whitelist.py +0 -0
  133. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/__init__.py +0 -0
  134. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  135. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/async_action.py +0 -0
  136. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/base.py +0 -0
  137. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  138. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/erc4626.py +0 -0
  139. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  140. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/events.py +0 -0
  141. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/external_state.py +0 -0
  142. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
  143. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  144. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  145. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/hypercore.py +0 -0
  146. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
  147. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/hypercore_pre_hooks.py +0 -0
  148. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/merkl.py +0 -0
  149. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/morpho.py +0 -0
  150. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  151. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  152. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/universal.py +0 -0
  153. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fusion_errors.py +0 -0
  154. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/__init__.py +0 -0
  155. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/architecture.md +0 -0
  156. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/glossary.md +0 -0
  157. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/invariants.md +0 -0
  158. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/quickstart.md +0 -0
  159. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/hypercore/__init__.py +0 -0
  160. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/market_ids.py +0 -0
  161. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/market_info.py +0 -0
  162. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/mcp/__init__.py +0 -0
  163. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/mcp/guide.py +0 -0
  164. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/mcp/models.py +0 -0
  165. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/mcp/server.py +0 -0
  166. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/morpho_api.py +0 -0
  167. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/onchain_text.py +0 -0
  168. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/preflight.py +0 -0
  169. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/py.typed +0 -0
  170. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/aave_v3.py +0 -0
  171. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/aave_v4.py +0 -0
  172. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/euler_v2.py +0 -0
  173. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/hypercore.py +0 -0
  174. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  175. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/morpho.py +0 -0
  176. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  177. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  178. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  179. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/position_manager.py +0 -0
  180. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  181. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  182. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/unpriceable.py +0 -0
  183. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/registry.py +0 -0
  184. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/substrates.py +0 -0
  185. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/types.py +0 -0
  186. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_erc20.py +0 -0
  187. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_info.py +0 -0
  188. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_managers.py +0 -0
  189. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_reads.py +0 -0
  190. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/UniswapV3UniversalRouter.py +0 -0
  191. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_addresses.py +0 -0
  192. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_crosschain.py +0 -0
  193. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_crosschain_lifecycle.py +0 -0
  194. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_crosschain_recovery.py +0 -0
  195. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_euler_v2.py +0 -0
  196. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_event_args.py +0 -0
  197. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_fake_rpc.py +0 -0
  198. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_foundry.py +0 -0
  199. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_golden.py +0 -0
  200. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_guide.py +0 -0
  201. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_hypercore_model.py +0 -0
  202. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_hypercore_shadow.py +0 -0
  203. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_live.py +0 -0
  204. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_multicall.py +0 -0
  205. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_oracle_mapping_fake.py +0 -0
  206. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_simulate.py +0 -0
  207. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_validation.py +0 -0
  208. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/addresses.py +0 -0
  209. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/conftest.py +0 -0
  210. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/constants.py +0 -0
  211. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  212. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/hypercore_market55.json +0 -0
  213. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/hypercore_official.json +0 -0
  214. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
  215. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
  216. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
  217. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
  218. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
  219. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
  220. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
  221. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
  222. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue.json +0 -0
  223. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
  224. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
  225. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
  226. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
  227. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
  228. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
  229. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
  230. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/simulate_v1_request.json +0 -0
  231. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/clean.json +0 -0
  232. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
  233. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  234. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
  235. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
  236. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
  237. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
  238. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/fetched.json +0 -0
  239. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
  240. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/full.json +0 -0
  241. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
  242. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/full.txt +0 -0
  243. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/rich.json +0 -0
  244. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
  245. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  246. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_about.py +0 -0
  247. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_access_manager.py +0 -0
  248. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_addresses_helper.py +0 -0
  249. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cache.py +0 -0
  250. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_ccip_read_disabled.py +0 -0
  251. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_chains.py +0 -0
  252. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_address.py +0 -0
  253. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_changelog.py +0 -0
  254. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_commands.py +0 -0
  255. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_config_store.py +0 -0
  256. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_helpers.py +0 -0
  257. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_hypercore.py +0 -0
  258. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_market_golden.py +0 -0
  259. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_market_morpho_blue.py +0 -0
  260. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_mcp_fuse_whitelist.py +0 -0
  261. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_mcp_parity.py +0 -0
  262. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_oracle_mapping.py +0 -0
  263. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_oracle_mapping_golden.py +0 -0
  264. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_pending_requests_scan.py +0 -0
  265. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_preflight.py +0 -0
  266. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_role_accounts.py +0 -0
  267. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_vault_info_golden.py +0 -0
  268. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_vault_validate.py +0 -0
  269. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_contract_call.py +0 -0
  270. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_contract_encoder.py +0 -0
  271. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_ccip_events.py +0 -0
  272. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_ccip_indexer.py +0 -0
  273. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_ccip_quote.py +0 -0
  274. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_chainlink.py +0 -0
  275. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_contracts.py +0 -0
  276. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_encoding.py +0 -0
  277. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_errors.py +0 -0
  278. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_lane.py +0 -0
  279. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_layering.py +0 -0
  280. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_transport.py +0 -0
  281. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_docs_typing.py +0 -0
  282. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_erc20.py +0 -0
  283. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_error_hierarchy.py +0 -0
  284. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_examples_crosschain.py +0 -0
  285. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  286. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_examples_hypercore_hyperevm.py +0 -0
  287. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_examples_vaults.py +0 -0
  288. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_explorer.py +0 -0
  289. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_external_state_events.py +0 -0
  290. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_external_state_executor.py +0 -0
  291. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fee_manager.py +0 -0
  292. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fuse_decoding.py +0 -0
  293. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fuse_encoding.py +0 -0
  294. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fuse_whitelist.py +0 -0
  295. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fusion_errors.py +0 -0
  296. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fusion_instance.py +0 -0
  297. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_governance_wrappers.py +0 -0
  298. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_guide.py +0 -0
  299. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_deployment.py +0 -0
  300. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_events.py +0 -0
  301. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_flow.py +0 -0
  302. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_live.py +0 -0
  303. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_outcome.py +0 -0
  304. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_pre_hooks.py +0 -0
  305. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_readers.py +0 -0
  306. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_simulation.py +0 -0
  307. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_logs.py +0 -0
  308. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_address.py +0 -0
  309. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_hypercore.py +0 -0
  310. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_models.py +0 -0
  311. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_preflight.py +0 -0
  312. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_server.py +0 -0
  313. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_vault_validate.py +0 -0
  314. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_multicall.py +0 -0
  315. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_onchain_text.py +0 -0
  316. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_oracle.py +0 -0
  317. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_oracle_mapping.py +0 -0
  318. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_plasma_vault.py +0 -0
  319. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_preflight.py +0 -0
  320. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_public_type_reachability.py +0 -0
  321. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_registry.py +0 -0
  322. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_revert_decoding.py +0 -0
  323. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_rewards_manager.py +0 -0
  324. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_roles.py +0 -0
  325. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_sdk_layering.py +0 -0
  326. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  327. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  328. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  329. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_compound_v3_institutional_usdc.py +0 -0
  330. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  331. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  332. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  333. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  334. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  335. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  336. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_examples_arbitrum.py +0 -0
  337. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_examples_base.py +0 -0
  338. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_examples_crosschain.py +0 -0
  339. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_examples_hyperevm.py +0 -0
  340. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_external_state_executor_base.py +0 -0
  341. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  342. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  343. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  344. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_morpho_blue.py +0 -0
  345. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_pilot_v3.py +0 -0
  346. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  347. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  348. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  349. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  350. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_preflight_base.py +0 -0
  351. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_readers_integration.py +0 -0
  352. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_release_funds.py +0 -0
  353. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_swap_on_base.py +0 -0
  354. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_timelock_base.py +0 -0
  355. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  356. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulation_errors.py +0 -0
  357. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulation_observation.py +0 -0
  358. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_solidity_compiler.py +0 -0
  359. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_solidity_mirrors.py +0 -0
  360. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_substrates.py +0 -0
  361. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_types_and_events.py +0 -0
  362. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_unpriceable_tokens.py +0 -0
  363. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_data_block.py +0 -0
  364. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_dep_graph.py +0 -0
  365. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_info_fetched_golden.py +0 -0
  366. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_info_onchain_base.py +0 -0
  367. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  368. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_reconciliation_onchain_base.py +0 -0
  369. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_simulator_blocks.py +0 -0
  370. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_web3_context.py +0 -0
  371. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_withdraw_manager.py +0 -0
  372. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/typed_client/consumer.py +0 -0
  373. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/typed_client/rejected.py +0 -0
  374. {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.2.0.dev11
3
+ Version: 4.2.0.dev16
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
@@ -394,6 +394,10 @@ ever signed or broadcast.
394
394
  - [Advanced Euler V2 credit-market vault](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_euler_v2_credit_market_base.py) —
395
395
  continue here for composing multiple functional fuses, typed Euler substrates and sub-accounts,
396
396
  and an ordered collateral → borrow → repay → unwind lifecycle.
397
+ - [Compound V3 Institutional USDC borrow](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_compound_v3_institutional_usdc_ethereum.py) —
398
+ a WETH vault on market 57: collateral, borrow, repay and withdrawal with NAV at each step, the
399
+ USDC substrate as the borrow gate, and the ERC20 market valuing the borrowed USDC through a
400
+ dependency edge.
397
401
  - [External-state margin leg](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/external_state_margin_leg_base.py) — a vault whose capital
398
402
  sits off-chain: typed market-50 substrates, a dual-custodian propose/confirm marking the NAV,
399
403
  the scheduled-withdrawal prep an alpha performs, and a mismatched confirmation being rejected.
@@ -413,6 +417,13 @@ uv run python examples/advanced_euler_v2_credit_market_base.py
413
417
  uv run python examples/external_state_margin_leg_base.py
414
418
  ```
415
419
 
420
+ The Compound example needs an Ethereum archive RPC:
421
+
422
+ ```bash
423
+ export ETHEREUM_PROVIDER_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
424
+ uv run python examples/advanced_compound_v3_institutional_usdc_ethereum.py
425
+ ```
426
+
416
427
  The crosschain example needs Arbitrum and HyperEVM archive RPCs and the executor and dispatcher
417
428
  creation codes built with Foundry from the contracts release it pins; run without them, it prints
418
429
  the `forge inspect` commands that build them:
@@ -564,7 +575,7 @@ Read-only aggregators over positions and health, no fuses involved:
564
575
  |--------|---------|
565
576
  | `MorphoReader`, `AaveV3Reader`, `CompoundV3Reader` | Market params, rates and vault positions per lending protocol |
566
577
  | `UniswapV3Reader`, `RamsesV2Reader` | LP position details |
567
- | `fetch_vault_lending_health` | Health factor per lending market a vault is in (Morpho, Aave V3/V4, Euler V2; any other market that can borrow is listed as unreadable) |
578
+ | `fetch_vault_lending_health` | Health factor per lending market a vault is in (Morpho, Aave V3/V4, Euler V2, Compound V3; any other market that can borrow is listed as unreadable) |
568
579
  | `build_oracle_mapping` | How the vault prices every configured asset |
569
580
 
570
581
  ### Supported networks
@@ -345,6 +345,10 @@ ever signed or broadcast.
345
345
  - [Advanced Euler V2 credit-market vault](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_euler_v2_credit_market_base.py) —
346
346
  continue here for composing multiple functional fuses, typed Euler substrates and sub-accounts,
347
347
  and an ordered collateral → borrow → repay → unwind lifecycle.
348
+ - [Compound V3 Institutional USDC borrow](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_compound_v3_institutional_usdc_ethereum.py) —
349
+ a WETH vault on market 57: collateral, borrow, repay and withdrawal with NAV at each step, the
350
+ USDC substrate as the borrow gate, and the ERC20 market valuing the borrowed USDC through a
351
+ dependency edge.
348
352
  - [External-state margin leg](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/external_state_margin_leg_base.py) — a vault whose capital
349
353
  sits off-chain: typed market-50 substrates, a dual-custodian propose/confirm marking the NAV,
350
354
  the scheduled-withdrawal prep an alpha performs, and a mismatched confirmation being rejected.
@@ -364,6 +368,13 @@ uv run python examples/advanced_euler_v2_credit_market_base.py
364
368
  uv run python examples/external_state_margin_leg_base.py
365
369
  ```
366
370
 
371
+ The Compound example needs an Ethereum archive RPC:
372
+
373
+ ```bash
374
+ export ETHEREUM_PROVIDER_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
375
+ uv run python examples/advanced_compound_v3_institutional_usdc_ethereum.py
376
+ ```
377
+
367
378
  The crosschain example needs Arbitrum and HyperEVM archive RPCs and the executor and dispatcher
368
379
  creation codes built with Foundry from the contracts release it pins; run without them, it prints
369
380
  the `forge inspect` commands that build them:
@@ -515,7 +526,7 @@ Read-only aggregators over positions and health, no fuses involved:
515
526
  |--------|---------|
516
527
  | `MorphoReader`, `AaveV3Reader`, `CompoundV3Reader` | Market params, rates and vault positions per lending protocol |
517
528
  | `UniswapV3Reader`, `RamsesV2Reader` | LP position details |
518
- | `fetch_vault_lending_health` | Health factor per lending market a vault is in (Morpho, Aave V3/V4, Euler V2; any other market that can borrow is listed as unreadable) |
529
+ | `fetch_vault_lending_health` | Health factor per lending market a vault is in (Morpho, Aave V3/V4, Euler V2, Compound V3; any other market that can borrow is listed as unreadable) |
519
530
  | `build_oracle_mapping` | How the vault prices every configured asset |
520
531
 
521
532
  ### Supported networks
@@ -23,6 +23,12 @@ transaction.
23
23
  controller, borrow WETH, repay, then unwind (controller before collateral) and
24
24
  withdraw the collateral, reading the outstanding debt from the eVault's
25
25
  `debtOf` to confirm the position fully closed.
26
+ - [`advanced_compound_v3_institutional_usdc_ethereum.py`](advanced_compound_v3_institutional_usdc_ethereum.py) --
27
+ the same lifecycle on one Comet (market 57, Ethereum). Pairs the supply and borrow
28
+ fuses with the balance fuse, grants WETH as collateral and USDC as the borrow gate,
29
+ values the borrowed USDC through the ERC20 market and a dependency edge, then
30
+ supplies, borrows, repays part, lets interest accrue, repays in full and withdraws,
31
+ confirming NAV at each step.
26
32
  - [`advanced_euler_v2_looping_arbitrum.py`](advanced_euler_v2_looping_arbitrum.py) --
27
33
  builds a WBTC-denominated vault on Arbitrum and opens (but does not unwind) a
28
34
  leveraged Euler V2 position in one Morpho flash-loan callback: supply WBTC,
@@ -0,0 +1,499 @@
1
+ """Borrow USDC against WETH on Compound V3 Institutional USDC from a new Ethereum vault.
2
+
3
+ Continues from ``simple_aave_v3_supply_base.py``, which explains the vault
4
+ bootstrap at more length. This example shows the credit lifecycle on one Comet
5
+ (ciUSDCv3, market 57), end to end:
6
+
7
+ 1. preview a vault deployment and clone it, underlying WETH;
8
+ 2. bootstrap the vault's roles in the correct order;
9
+ 3. pair the supply and borrow fuses with the market's balance fuse;
10
+ 4. grant WETH as collateral and USDC as the borrow gate;
11
+ 5. value the borrowed USDC through the ERC20 market and a dependency edge;
12
+ 6. deposit WETH and supply it as collateral;
13
+ 7. borrow USDC, confirming NAV holds because the balance fuse nets the debt;
14
+ 8. repay part of the debt, let an hour of interest accrue, repay it in full;
15
+ 9. withdraw the collateral and confirm the position closed.
16
+
17
+ Everything runs inside one ``eth_simulateV1`` batch against a pinned Ethereum
18
+ block -- nothing is signed or broadcast. To put a flow on-chain, hand the
19
+ ``.calldata`` each builder produces to your own signer, each step signed by the
20
+ role authorized for it.
21
+
22
+ Reading the health of a real position does not fit in a batch, since the vault
23
+ here exists only inside the simulation. On a deployed vault use
24
+ ``fetch_vault_data(ctx, vault).lending_health`` or ``fusion vault validate``.
25
+
26
+ Rewards are out of scope: the market's claim fuse (``CompoundV3ClaimFuse``) goes
27
+ through ``RewardsManager.claim_rewards``, and this Comet has no reward token at
28
+ the pinned block, so a claim reverts ``NotSupported(address)``.
29
+
30
+ Run it (the shell snippets assume a POSIX shell -- bash or zsh):
31
+
32
+ export ETHEREUM_PROVIDER_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
33
+ uv run python examples/advanced_compound_v3_institutional_usdc_ethereum.py
34
+
35
+ The provider must be an archive node that implements ``eth_simulateV1`` (Alchemy
36
+ and other geth/reth-based providers do).
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ import logging
42
+ import os
43
+ import sys
44
+ from typing import TypedDict
45
+
46
+ from eth_typing import ChecksumAddress
47
+ from web3 import Web3
48
+
49
+ from ipor_fusion import (
50
+ ERC20,
51
+ AccessManager,
52
+ CompoundV3Reader,
53
+ FuseAction,
54
+ FusionFactory,
55
+ IporFusionMarkets,
56
+ PlasmaVault,
57
+ PriceOracleMiddlewareManager,
58
+ Roles,
59
+ SimulationResult,
60
+ VaultSimulator,
61
+ Web3Context,
62
+ erc20_balance_slot,
63
+ is_simulate_v1_supported,
64
+ parse_checksum_address,
65
+ parse_fuse_address,
66
+ )
67
+ from ipor_fusion.fuses import CompoundV3BorrowFuse, CompoundV3SupplyFuse
68
+ from ipor_fusion.types import MAX_UINT256, Amount, ChainId, Period
69
+
70
+ log = logging.getLogger("advanced_compound_v3_institutional_usdc_ethereum")
71
+
72
+ # ── Live IPOR / Ethereum infrastructure (do NOT change) ──────────────────────
73
+ # Every address below is a live, canonical deployment. Provenance for all IPOR
74
+ # Fusion addresses: https://github.com/IPOR-Labs/ipor-abi
75
+ # mainnet/mainnet-ethereum-fusion/addresses.json
76
+
77
+ ETHEREUM_CHAIN_ID = ChainId(1)
78
+
79
+ # IporFusionFactoryProxy on Ethereum.
80
+ ETHEREUM_FUSION_FACTORY = parse_checksum_address(
81
+ "0xcd05909C4A1F8E501e4ED554cEF4Ed5E48D9b852"
82
+ )
83
+
84
+ # Canonical WETH and Circle-issued USDC on Ethereum (18 and 6 decimals).
85
+ ETHEREUM_WETH = parse_checksum_address("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
86
+ ETHEREUM_USDC = parse_checksum_address("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
87
+
88
+ # The ciUSDCv3 Comet (registry name CompoundV3CometInstitutionalUsdc), base token
89
+ # USDC. It is a separate Comet from cUSDCv3, so the fuses below are their own
90
+ # deployments, bound to this Comet and market 57 by their immutables.
91
+ COMET = parse_checksum_address("0x207158a267CBD2598BB3d611D8CBdEE2709F2F8C")
92
+
93
+ COMPOUND_MARKET = IporFusionMarkets.COMPOUND_V3_INSTITUTIONAL_USDC
94
+ ERC20_MARKET = IporFusionMarkets.ERC20_VAULT_BALANCE
95
+
96
+ # Registry name SupplyFuseCompoundV3InstitutionalUsdc: supplies and withdraws
97
+ # collateral and base token.
98
+ SUPPLY_FUSE = parse_fuse_address("0xe7CaDa548534973b9d6A4124bBC476b586d1b3e4")
99
+ # Registry name BorrowFuseCompoundV3InstitutionalUsdc: borrows and repays the base
100
+ # token. A Comet lends only its base token, so neither action names an asset.
101
+ BORROW_FUSE = parse_fuse_address("0xff1762A6579787c7eab2E4f55A502F7c36714eF6")
102
+ # Registry name BalanceFuseCompoundV3WithPriceOracleMiddlewareInstitutionalUsdc:
103
+ # values the position as collateral minus debt, at the vault's oracle prices.
104
+ BALANCE_FUSE = parse_fuse_address("0x5dF0CB1cE5159De1efb8038B5185E4612fAd9168")
105
+ # Registry name BalanceFuseErc20: values the borrowed USDC sitting idle on the vault.
106
+ ERC20_BALANCE_FUSE = parse_fuse_address("0x6cEBf3e3392D0860Ed174402884b941DCBB30654")
107
+
108
+ # ── Simulation parameters (safe to adjust) ───────────────────────────────────
109
+ #
110
+ # OWNER, ALPHA, DEPOSITOR and USDC_SOURCE are impersonated simulation actors: the
111
+ # simulator runs with validation disabled, so it can act as any address without
112
+ # its key. They are NOT accounts you control -- there is nothing to replace and
113
+ # nothing to broadcast here.
114
+ #
115
+ # OWNER, ALPHA and DEPOSITOR are kept distinct on purpose, because they are three
116
+ # distinct roles that must NOT share an address in production:
117
+ # OWNER -- governance/atomist; can reconfigure the whole vault. A cold key
118
+ # or multisig.
119
+ # ALPHA -- the online operator; drives execute(), least-privilege, it cannot
120
+ # reconfigure the vault. Collapsing it into OWNER means a compromised
121
+ # alpha key becomes full governance control.
122
+ # DEPOSITOR-- an end user; only ever holds and deposits funds.
123
+
124
+ OWNER = parse_checksum_address("0x533ac556E288625B267bD71B7928E0a8B46DcE82")
125
+ # Anvil account #0, a well-known test address.
126
+ ALPHA = parse_checksum_address("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
127
+ # Anvil account #1, the natural sibling to the alpha's Anvil account #0.
128
+ DEPOSITOR = parse_checksum_address("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
129
+ # Anvil account #2. Holds the USDC that covers the interest; see run_simulation.
130
+ USDC_SOURCE = parse_checksum_address("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC")
131
+
132
+ # Pinned Ethereum block, after the fuses above were deployed. Pinning makes the
133
+ # simulation deterministic and immune to mainnet state drift. It requires an
134
+ # archive node that can serve state at this height -- if your provider cannot (an
135
+ # error mentioning missing trie/state, or a pruned block), BUMP THIS to a recent
136
+ # Ethereum block your node can reach.
137
+ PINNED_BLOCK = 26_348_000
138
+
139
+ WETH_COLLATERAL = Amount(40 * 10**18) # 40 WETH
140
+ USDC_BORROW = Amount(20_000 * 10**6) # 20,000 USDC
141
+ USDC_PARTIAL_REPAY = Amount(5_000 * 10**6) # 5,000 USDC
142
+ USDC_INTEREST_COVER = Amount(10 * 10**6) # 10 USDC, far above an hour of interest
143
+ ONE_HOUR_SECONDS = 3600
144
+ # A clone deploys the whole vault stack, more than a node's default gas cap for a
145
+ # simulated call allows.
146
+ CLONE_GAS = 30_000_000
147
+ # Comet rounds a base balance through its principal and index, by a few base units.
148
+ USDC_DUST_TOLERANCE = Amount(10)
149
+ # The same rounding and the WAD flooring of the balance fuse, valued in WETH.
150
+ NAV_DUST_TOLERANCE = Amount(10**13) # 0.00001 WETH
151
+
152
+
153
+ # ── Inlined helpers (kept local so this file is self-contained) ──────────────
154
+
155
+
156
+ def _address_substrate(addr: str) -> bytes:
157
+ """Pad an EVM address into a 32-byte substrate value (left-zero-padded)."""
158
+ return bytes.fromhex(addr.removeprefix("0x").lower().rjust(64, "0"))
159
+
160
+
161
+ def _check(condition: bool, message: str) -> None:
162
+ """Fail loud unconditionally -- unlike ``assert``, survives ``python -O``."""
163
+ if not condition:
164
+ raise AssertionError(message)
165
+
166
+
167
+ def _assert_all_success(result: SimulationResult) -> None:
168
+ """Raise with a readable summary if any simulated call reverted."""
169
+ if result.all_success:
170
+ return
171
+ failed = [(c.label, c.error) for c in result.failed_calls]
172
+ raise AssertionError(f"simulation calls failed: {failed}")
173
+
174
+
175
+ def _connected_web3() -> Web3:
176
+ """Build a Web3 client from ETHEREUM_PROVIDER_URL, or exit with a clear message."""
177
+ url = os.environ.get("ETHEREUM_PROVIDER_URL")
178
+ if not url:
179
+ sys.exit("ETHEREUM_PROVIDER_URL is not set -- export your RPC URL first.")
180
+ web3 = Web3(Web3.HTTPProvider(url))
181
+ if not web3.is_connected():
182
+ sys.exit("cannot reach the RPC at ETHEREUM_PROVIDER_URL -- check the endpoint.")
183
+ if not is_simulate_v1_supported(web3):
184
+ sys.exit(
185
+ "the provider does not implement eth_simulateV1 -- use an archive node."
186
+ )
187
+ return web3
188
+
189
+
190
+ # ── Pure builders (no chain access -- safe to import and unit-test offline) ───
191
+
192
+
193
+ class CloneKwargs(TypedDict):
194
+ """The keyword arguments of ``FusionFactory.clone``."""
195
+
196
+ asset_name: str
197
+ asset_symbol: str
198
+ underlying_token: ChecksumAddress
199
+ redemption_delay_seconds: Period
200
+ owner: ChecksumAddress
201
+ dao_fee_package_index: int
202
+
203
+
204
+ def clone_args() -> CloneKwargs:
205
+ """Arguments for FusionFactory.clone(...).
206
+
207
+ The preview and the in-batch create MUST use identical args: same args plus
208
+ the same factory state yields the same CREATE addresses in one simulation.
209
+ """
210
+ return {
211
+ "asset_name": "IPOR ciUSDCv3 Borrow Vault (example)",
212
+ "asset_symbol": "ipCIBex",
213
+ "underlying_token": ETHEREUM_WETH,
214
+ "redemption_delay_seconds": Period(0),
215
+ "owner": OWNER,
216
+ "dao_fee_package_index": 0,
217
+ }
218
+
219
+
220
+ def compound_market_substrates() -> list[bytes]:
221
+ """WETH is the collateral the supply and balance fuses accept. USDC is the gate
222
+ the borrow fuse checks on both borrow and repay: without it, a borrow reverts
223
+ ``CompoundV3BorrowFuseUnsupportedAsset`` even though the fuse's target is fixed."""
224
+ return [_address_substrate(ETHEREUM_WETH), _address_substrate(ETHEREUM_USDC)]
225
+
226
+
227
+ def erc20_market_substrates() -> list[bytes]:
228
+ """The idle token the ERC20 market values: the borrowed USDC."""
229
+ return [_address_substrate(ETHEREUM_USDC)]
230
+
231
+
232
+ def build_supply_collateral_action(amount: Amount) -> FuseAction:
233
+ return CompoundV3SupplyFuse(SUPPLY_FUSE).supply(asset=ETHEREUM_WETH, amount=amount)
234
+
235
+
236
+ def build_withdraw_collateral_action(amount: Amount) -> FuseAction:
237
+ return CompoundV3SupplyFuse(SUPPLY_FUSE).withdraw(
238
+ asset=ETHEREUM_WETH, amount=amount
239
+ )
240
+
241
+
242
+ def build_borrow_action(amount: Amount) -> FuseAction:
243
+ return CompoundV3BorrowFuse(BORROW_FUSE).borrow(amount=amount)
244
+
245
+
246
+ def build_repay_action(amount: Amount) -> FuseAction:
247
+ """A repay is capped at the debt: ``MAX_UINT256`` clears it, interest included,
248
+ without opening a base supply position with the rest of the idle USDC. The
249
+ vault must hold the USDC, or the token transfer reverts."""
250
+ return CompoundV3BorrowFuse(BORROW_FUSE).repay(amount=amount)
251
+
252
+
253
+ # ── Simulation (the whole build + strategy, no broadcast) ────────────────────
254
+
255
+
256
+ def run_simulation(web3: Web3) -> SimulationResult:
257
+ """Build the vault and run the credit lifecycle in one eth_simulateV1 batch."""
258
+ ctx = Web3Context(web3=web3, chain_id=ETHEREUM_CHAIN_ID, signer=OWNER).at_block(
259
+ PINNED_BLOCK
260
+ )
261
+ factory = FusionFactory(ETHEREUM_FUSION_FACTORY)
262
+
263
+ # Preview the deterministic addresses (eth_call, no gas, no state change).
264
+ preview = factory.clone(**clone_args()).call(ctx)
265
+ log.info("predicted vault=%s", preview.plasma_vault)
266
+
267
+ vault_address = preview.plasma_vault
268
+ plasma_vault = PlasmaVault(vault_address)
269
+ access_manager = AccessManager(preview.access_manager)
270
+ price_manager = PriceOracleMiddlewareManager(preview.price_manager)
271
+ comet = CompoundV3Reader(COMET)
272
+ weth = ERC20(ETHEREUM_WETH)
273
+ usdc = ERC20(ETHEREUM_USDC)
274
+
275
+ # Each simulator primitive stands in for a production action, read as
276
+ # "simulation -> production":
277
+ # - sim.add_call(call=X, from_=OWNER) -> X.send(ctx) by the owner/atomist
278
+ # (or X.calldata to a multisig / timelock).
279
+ # - sim.execute([action]) -> plasma_vault.execute([action]).send(ctx) by the alpha.
280
+ # - read = sim.observe(X), then read.value -> X.call(ctx), a plain read.
281
+ sim = VaultSimulator(
282
+ web3=web3, vault=vault_address, alpha=ALPHA, block=PINNED_BLOCK
283
+ )
284
+ plan: list[str] = []
285
+
286
+ # SIMULATION ONLY (storage overrides): these balances stand in for tokens the
287
+ # holders already have. Production has no equivalent -- a real depositor holds
288
+ # their WETH.
289
+ sim.with_erc20_balance(
290
+ ETHEREUM_WETH,
291
+ DEPOSITOR,
292
+ WETH_COLLATERAL,
293
+ slot=erc20_balance_slot(web3, ETHEREUM_WETH, block=PINNED_BLOCK),
294
+ )
295
+ sim.with_erc20_balance(
296
+ ETHEREUM_USDC,
297
+ USDC_SOURCE,
298
+ USDC_INTEREST_COVER,
299
+ slot=erc20_balance_slot(web3, ETHEREUM_USDC, block=PINNED_BLOCK),
300
+ )
301
+
302
+ # The clone MUST be the first queued call -- any earlier call that bumps the
303
+ # factory index would change the addresses previewed above.
304
+ sim.add_call(
305
+ call=factory.clone(**clone_args()), from_=OWNER, label="clone", gas=CLONE_GAS
306
+ )
307
+ plan.append("deploy the vault stack (clone)")
308
+
309
+ # Role bootstrap order: a fresh clone grants OWNER only OWNER_ROLE. The admin
310
+ # chain is OWNER -> ATOMIST -> {ALPHA, FUSE_MANAGER, WHITELIST,
311
+ # UPDATE_MARKETS_BALANCES}, so OWNER must self-grant ATOMIST before it can
312
+ # grant the rest. Each grant uses Period(0) (no execution delay) to keep the
313
+ # simulation legible; production governance often puts a nonzero delay -- a
314
+ # timelock -- on the sensitive roles.
315
+ for role, holder in (
316
+ (Roles.ATOMIST_ROLE, OWNER),
317
+ (Roles.FUSE_MANAGER_ROLE, OWNER),
318
+ (Roles.ALPHA_ROLE, ALPHA),
319
+ (Roles.WHITELIST_ROLE, DEPOSITOR),
320
+ ):
321
+ sim.add_call(
322
+ call=access_manager.grant_role(role, holder, Period(0)), from_=OWNER
323
+ )
324
+ plan.append("grant roles: ATOMIST, FUSE_MANAGER, ALPHA, WHITELIST")
325
+
326
+ # Wire market 57. There is no separate collateral fuse: Comet counts every
327
+ # supplied collateral towards the borrow limit on its own.
328
+ sim.add_call(call=plasma_vault.add_fuses([SUPPLY_FUSE, BORROW_FUSE]), from_=OWNER)
329
+ sim.add_call(
330
+ call=plasma_vault.add_balance_fuse(COMPOUND_MARKET, BALANCE_FUSE), from_=OWNER
331
+ )
332
+ sim.add_call(
333
+ call=plasma_vault.grant_market_substrates(
334
+ COMPOUND_MARKET, compound_market_substrates()
335
+ ),
336
+ from_=OWNER,
337
+ )
338
+ # A borrow lowers this market's value and raises the vault's idle USDC in one
339
+ # step. The ERC20 market values that USDC, and the dependency edge refreshes it
340
+ # whenever the Compound market changes. Without the edge, NAV falls by the
341
+ # borrowed amount until someone refreshes the ERC20 market.
342
+ sim.add_call(
343
+ call=plasma_vault.add_balance_fuse(ERC20_MARKET, ERC20_BALANCE_FUSE),
344
+ from_=OWNER,
345
+ )
346
+ sim.add_call(
347
+ call=plasma_vault.grant_market_substrates(
348
+ ERC20_MARKET, erc20_market_substrates()
349
+ ),
350
+ from_=OWNER,
351
+ )
352
+ sim.add_call(
353
+ call=plasma_vault.update_dependency_balance_graphs(
354
+ [COMPOUND_MARKET], [[ERC20_MARKET]]
355
+ ),
356
+ from_=OWNER,
357
+ )
358
+ plan.append("wire market 57 and the ERC20 market with a dependency edge")
359
+
360
+ # Oracle coverage: the balance fuse prices both tokens through the vault's
361
+ # price manager, which delegates to the global middleware.
362
+ weth_price = sim.observe(price_manager.get_asset_price(ETHEREUM_WETH))
363
+ usdc_price = sim.observe(price_manager.get_asset_price(ETHEREUM_USDC))
364
+ plan.append("verify oracle coverage for WETH and USDC")
365
+
366
+ # The approve + deposit are exactly what a real depositor signs.
367
+ sim.add_call(call=weth.approve(vault_address, WETH_COLLATERAL), from_=DEPOSITOR)
368
+ sim.add_call(call=plasma_vault.deposit(WETH_COLLATERAL, DEPOSITOR), from_=DEPOSITOR)
369
+ plan.append("deposit 40 WETH")
370
+
371
+ sim.execute([build_supply_collateral_action(WETH_COLLATERAL)])
372
+ collateral = sim.observe(comet.collateral_balance_of(vault_address, ETHEREUM_WETH))
373
+ nav_after_supply = sim.observe(plasma_vault.total_assets())
374
+ plan.append("alpha supplies 40 WETH as collateral")
375
+
376
+ # Comet enforces the limit: a borrow above the collateral factor reverts
377
+ # NotCollateralized in the Comet, not in the fuse.
378
+ sim.execute([build_borrow_action(USDC_BORROW)])
379
+ idle_usdc_after_borrow = sim.observe(usdc.balance_of(vault_address))
380
+ debt_after_borrow = sim.observe(comet.borrow_balance_of(vault_address))
381
+ market_after_borrow = sim.observe(
382
+ plasma_vault.total_assets_in_market(COMPOUND_MARKET)
383
+ )
384
+ nav_after_borrow = sim.observe(plasma_vault.total_assets())
385
+ plan.append("alpha borrows 20,000 USDC")
386
+
387
+ sim.execute([build_repay_action(USDC_PARTIAL_REPAY)])
388
+ debt_after_partial = sim.observe(comet.borrow_balance_of(vault_address))
389
+ nav_after_partial = sim.observe(plasma_vault.total_assets())
390
+ plan.append("alpha repays 5,000 USDC")
391
+
392
+ # SIMULATION ONLY (time advancement): next_block shifts block.timestamp so
393
+ # interest accrues inside one batch. There is no production equivalent -- the
394
+ # debt simply grows between transactions.
395
+ sim.next_block(time_shift_seconds=ONE_HOUR_SECONDS)
396
+ debt_with_interest = sim.observe(comet.borrow_balance_of(vault_address))
397
+ # SIMULATION ONLY (the impersonated transfer): the debt now exceeds the USDC the
398
+ # vault borrowed. Production earns or swaps that difference in through a fuse.
399
+ # The repay and withdrawal below are NOT simulation-only.
400
+ sim.add_call(
401
+ call=usdc.transfer(vault_address, USDC_INTEREST_COVER), from_=USDC_SOURCE
402
+ )
403
+ plan.append("an hour of interest accrues; USDC arrives to cover it")
404
+
405
+ sim.execute([build_repay_action(Amount(MAX_UINT256))])
406
+ debt_after_full = sim.observe(comet.borrow_balance_of(vault_address))
407
+ base_supply_after_full = sim.observe(comet.balance_of(vault_address))
408
+ idle_usdc_after_full = sim.observe(usdc.balance_of(vault_address))
409
+ plan.append("alpha repays the debt in full")
410
+
411
+ # Comet blocks withdrawing collateral that still backs debt, so the order is
412
+ # repay first, then withdraw.
413
+ sim.execute([build_withdraw_collateral_action(WETH_COLLATERAL)])
414
+ collateral_after_exit = sim.observe(
415
+ comet.collateral_balance_of(vault_address, ETHEREUM_WETH)
416
+ )
417
+ idle_weth_after_exit = sim.observe(weth.balance_of(vault_address))
418
+ market_after_exit = sim.observe(
419
+ plasma_vault.total_assets_in_market(COMPOUND_MARKET)
420
+ )
421
+ plan.append("alpha withdraws the 40 WETH")
422
+
423
+ log.info("transaction plan:")
424
+ for i, step in enumerate(plan, start=1):
425
+ log.info(" %d. %s", i, step)
426
+
427
+ result = sim.run()
428
+ _assert_all_success(result)
429
+
430
+ # ── Simulation done. From here the recorded observations are read back and the
431
+ # outcome verified, failing loud on any mismatch. ──────────────────────────
432
+ _check(weth_price.value.amount > 0, "WETH has no oracle price")
433
+ _check(usdc_price.value.amount > 0, "USDC has no oracle price")
434
+ _check(collateral.value == WETH_COLLATERAL, "collateral did not reach the Comet")
435
+
436
+ # NAV is preserved across supply, borrow and repay: value only moves between the
437
+ # Compound and ERC20 markets. A balance fuse ignoring the debt would add the
438
+ # 20,000 USDC to NAV here; compare against the deposit, not a lower bound.
439
+ for step, nav in (
440
+ ("supply", nav_after_supply),
441
+ ("borrow", nav_after_borrow),
442
+ ("partial repay", nav_after_partial),
443
+ ):
444
+ _check(
445
+ abs(nav.value - WETH_COLLATERAL) <= NAV_DUST_TOLERANCE,
446
+ f"{step} moved NAV beyond dust: {nav.value}",
447
+ )
448
+
449
+ _check(
450
+ idle_usdc_after_borrow.value == USDC_BORROW,
451
+ "the borrowed USDC did not reach the vault",
452
+ )
453
+ _check(
454
+ 0 <= debt_after_borrow.value - USDC_BORROW <= USDC_DUST_TOLERANCE,
455
+ f"unexpected debt after the borrow: {debt_after_borrow.value}",
456
+ )
457
+ _check(
458
+ market_after_borrow.value < WETH_COLLATERAL,
459
+ "the Compound market value did not net the debt",
460
+ )
461
+ remaining = USDC_BORROW - USDC_PARTIAL_REPAY
462
+ _check(
463
+ abs(debt_after_partial.value - remaining) <= USDC_DUST_TOLERANCE,
464
+ f"unexpected debt after the partial repay: {debt_after_partial.value}",
465
+ )
466
+ _check(
467
+ debt_with_interest.value > debt_after_partial.value,
468
+ "no interest accrued over the hour",
469
+ )
470
+
471
+ # The full repay paid exactly the debt, and the rest of the USDC stayed idle.
472
+ _check(debt_after_full.value == 0, "the debt did not clear")
473
+ _check(base_supply_after_full.value == 0, "the repay opened a base supply position")
474
+ expected_idle = remaining + USDC_INTEREST_COVER - debt_with_interest.value
475
+ _check(
476
+ abs(idle_usdc_after_full.value - expected_idle) <= USDC_DUST_TOLERANCE,
477
+ f"unexpected idle USDC after the full repay: {idle_usdc_after_full.value}",
478
+ )
479
+
480
+ _check(collateral_after_exit.value == 0, "collateral left on the Comet")
481
+ _check(idle_weth_after_exit.value == WETH_COLLATERAL, "the WETH did not come back")
482
+ _check(market_after_exit.value == 0, "the Compound market still has value")
483
+
484
+ log.info(
485
+ "OK -- borrowed %s USDC, paid %s of debt in full, gas_used=%s",
486
+ USDC_BORROW / 10**6,
487
+ debt_with_interest.value / 10**6,
488
+ result.gas_used,
489
+ )
490
+ return result
491
+
492
+
493
+ def main() -> None:
494
+ logging.basicConfig(level=logging.INFO, format="%(message)s")
495
+ run_simulation(_connected_web3())
496
+
497
+
498
+ if __name__ == "__main__":
499
+ main()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.2.0.dev11"
3
+ version = "4.2.0.dev16"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -312,6 +312,9 @@ from ipor_fusion.readers import (
312
312
  AaveV4OracleReader,
313
313
  AaveV4SpokeReader,
314
314
  AaveV4UserAccountData,
315
+ CometAccountValue,
316
+ CometAssetInfo,
317
+ CompoundV3FuseReader,
315
318
  CompoundV3Reader,
316
319
  LendingMarketHealth,
317
320
  MiddlewarePricedToken,
@@ -788,6 +791,9 @@ __all__ = [
788
791
  "AaveV4OracleReader",
789
792
  "AaveV4SpokeReader",
790
793
  "AaveV4UserAccountData",
794
+ "CometAccountValue",
795
+ "CometAssetInfo",
796
+ "CompoundV3FuseReader",
791
797
  "CompoundV3Reader",
792
798
  "HYPEREVM_CHAIN_ID",
793
799
  "LendingMarketHealth",
@@ -116,8 +116,8 @@ def _index_lending_health(
116
116
  """Split lending health rows by protocol for fast lookup during rendering.
117
117
 
118
118
  Morpho rows are keyed by morpho substrate id (one row per substrate).
119
- Aave V3 rows are keyed by IPOR market id (account-aggregated, one row per
120
- market); Aave V4 rows by Spoke address (account-aggregated per Spoke) and
119
+ Aave V3 and Compound V3 rows are keyed by IPOR market id (account-aggregated,
120
+ one row per market); Aave V4 rows by Spoke address (account-aggregated per Spoke) and
121
121
  Euler V2 rows by sub-account address, in the same account-level map.
122
122
  """
123
123
  morpho: dict[str, LendingMarketHealth] = {}
@@ -127,7 +127,7 @@ def _index_lending_health(
127
127
  for m in lh.markets:
128
128
  if m.protocol == "morpho" and m.substrate_id:
129
129
  morpho[str(m.substrate_id).lower().removeprefix("0x")] = m
130
- elif m.protocol == "aave_v3":
130
+ elif m.protocol in ("aave_v3", "compound_v3"):
131
131
  aave[m.market_id] = m
132
132
  elif m.protocol in ("aave_v4", "euler_v2") and m.substrate_id:
133
133
  aave[m.substrate_id] = m
@@ -11,7 +11,12 @@ from ipor_fusion.readers.aave_v4 import (
11
11
  AaveV4SpokeReader,
12
12
  AaveV4UserAccountData,
13
13
  )
14
- from ipor_fusion.readers.compound_v3 import CompoundV3Reader
14
+ from ipor_fusion.readers.compound_v3 import (
15
+ CometAccountValue,
16
+ CometAssetInfo,
17
+ CompoundV3FuseReader,
18
+ CompoundV3Reader,
19
+ )
15
20
  from ipor_fusion.readers.hypercore import (
16
21
  HYPEREVM_CHAIN_ID,
17
22
  )
@@ -79,6 +84,9 @@ __all__ = [
79
84
  "AaveV4OracleReader",
80
85
  "AaveV4SpokeReader",
81
86
  "AaveV4UserAccountData",
87
+ "CometAccountValue",
88
+ "CometAssetInfo",
89
+ "CompoundV3FuseReader",
82
90
  "CompoundV3Reader",
83
91
  "HYPEREVM_CHAIN_ID",
84
92
  "LendingMarketHealth",