ipor-fusion 4.0.0.dev86__tar.gz → 4.0.0.dev93__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 (297) hide show
  1. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/AGENTS.md +27 -12
  2. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/MIGRATION_4.md +65 -10
  3. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/PKG-INFO +1 -1
  4. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/examples/advanced_euler_v2_credit_market_base.py +2 -2
  5. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/examples/advanced_euler_v2_looping_arbitrum.py +2 -2
  6. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/examples/external_state_margin_leg_base.py +4 -4
  7. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/examples/simple_aave_v3_supply_base.py +2 -2
  8. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/pyproject.toml +1 -1
  9. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/__init__.py +0 -2
  10. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_cmd.py +4 -16
  11. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_health.py +1 -15
  12. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_info_view.py +16 -37
  13. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_markets_view.py +44 -96
  14. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/access.py +3 -8
  15. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/context.py +18 -5
  16. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/contract.py +26 -33
  17. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/external_state_executor.py +10 -11
  18. ipor_fusion-4.0.0.dev86/src/ipor_fusion/fuses/base.py → ipor_fusion-4.0.0.dev93/src/ipor_fusion/core/fuse.py +17 -8
  19. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/multicall.py +6 -4
  20. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/oracle.py +2 -2
  21. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/plasma_vault.py +5 -9
  22. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/withdraw_manager.py +4 -4
  23. ipor_fusion-4.0.0.dev93/src/ipor_fusion/fuses/base.py +17 -0
  24. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/crosschain/base.py +3 -6
  25. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/crosschain/ccip.py +3 -2
  26. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/crosschain/stargate.py +2 -1
  27. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/universal.py +0 -10
  28. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/mcp/server.py +4 -17
  29. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/aave_v3.py +5 -3
  30. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/lending_health.py +2 -3
  31. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/morpho.py +3 -3
  32. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/unpriceable.py +5 -1
  33. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/validation.py +36 -273
  34. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_data.py +204 -75
  35. ipor_fusion-4.0.0.dev93/src/ipor_fusion/vault_erc20.py +204 -0
  36. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_info.py +59 -98
  37. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_managers.py +27 -4
  38. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_crosschain_recovery.py +1 -1
  39. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_validation.py +6 -7
  40. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/conftest.py +1 -0
  41. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_commands.py +30 -14
  42. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_helpers.py +155 -116
  43. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_hypercore.py +8 -7
  44. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_lending_health.py +9 -7
  45. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_pending_requests_scan.py +11 -4
  46. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_vault_info_golden.py +51 -16
  47. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_vault_predates_getters.py +7 -6
  48. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_vault_validate.py +5 -2
  49. ipor_fusion-4.0.0.dev93/tests/test_contract_encoder.py +118 -0
  50. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_layering.py +13 -0
  51. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_external_state_executor.py +27 -27
  52. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_fee_manager.py +2 -2
  53. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_fuse_encoding.py +17 -0
  54. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_flow.py +3 -3
  55. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_mcp_vault_validate.py +3 -8
  56. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_plasma_vault.py +1 -1
  57. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_public_api.py +0 -1
  58. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_fusion_factory_clone_base.py +7 -23
  59. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_plasma_vault_setters_base.py +1 -4
  60. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_solidity_mirrors.py +1 -1
  61. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_validation.py +40 -43
  62. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_vault_info_onchain_base.py +3 -4
  63. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +1 -1
  64. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_web3_context.py +24 -0
  65. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_withdraw_manager.py +24 -2
  66. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/typed_client/consumer.py +15 -4
  67. ipor_fusion-4.0.0.dev86/tests/test_contract_encoder.py +0 -118
  68. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.claude-plugin/marketplace.json +0 -0
  69. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.claude-plugin/plugin.json +0 -0
  70. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.env.example +0 -0
  71. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.gemini/settings.json +0 -0
  72. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.git-blame-ignore-revs +0 -0
  73. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.github/workflows/cd.yml +0 -0
  74. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.github/workflows/ci.yml +0 -0
  75. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.github/workflows/commit-lint.yml +0 -0
  76. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.github/workflows/notify-slack.yml +0 -0
  77. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.github/workflows/python-build.yml +0 -0
  78. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.github/workflows/python-publish.yml +0 -0
  79. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.github/workflows/release.yml +0 -0
  80. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.github/workflows/report-slack.yml +0 -0
  81. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.gitignore +0 -0
  82. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.mcp.json +0 -0
  83. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.pre-commit-config.yaml +0 -0
  84. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/.python-version +0 -0
  85. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/CHANGELOG.md +0 -0
  86. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/CLAUDE.md +0 -0
  87. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/CODEOWNERS +0 -0
  88. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/LICENSE +0 -0
  89. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/README.md +0 -0
  90. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/build.sh +0 -0
  91. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/examples/CONTRIBUTING.md +0 -0
  92. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/examples/README.md +0 -0
  93. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  94. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  95. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/pytest.ini +0 -0
  96. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/scripts/commit-check.sh +0 -0
  97. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/scripts/crosschain_readiness.py +0 -0
  98. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/skills/ipor-deploy-vault/SKILL.md +0 -0
  99. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/about.py +0 -0
  100. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cache.py +0 -0
  101. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/chains.py +0 -0
  102. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/__init__.py +0 -0
  103. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  104. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/config_cmd.py +0 -0
  105. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/config_store.py +0 -0
  106. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/main.py +0 -0
  107. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/market_cmd.py +0 -0
  108. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/options.py +0 -0
  109. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  110. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  111. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/config/__init__.py +0 -0
  112. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/config/roles.py +0 -0
  113. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/__init__.py +0 -0
  114. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/address.py +0 -0
  115. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/erc20.py +0 -0
  116. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/fee_manager.py +0 -0
  117. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/fuse_action.py +0 -0
  118. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/fusion_factory.py +0 -0
  119. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/logs.py +0 -0
  120. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/rewards_manager.py +0 -0
  121. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/simulation.py +0 -0
  122. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/__init__.py +0 -0
  123. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  124. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  125. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  126. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  127. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  128. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  129. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  130. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  131. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  132. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/contracts.py +0 -0
  133. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/discovery.py +0 -0
  134. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/errors.py +0 -0
  135. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/lane.py +0 -0
  136. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/logs.py +0 -0
  137. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/messages.py +0 -0
  138. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/simulation.py +0 -0
  139. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  140. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  141. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  142. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  143. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  144. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/transport.py +0 -0
  145. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/dependency_graph.py +0 -0
  146. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/entry_cli.py +0 -0
  147. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/entry_mcp.py +0 -0
  148. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/errors.py +0 -0
  149. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/explorer.py +0 -0
  150. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/field_docs.py +0 -0
  151. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/__init__.py +0 -0
  152. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  153. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/async_action.py +0 -0
  154. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  155. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
  156. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/erc4626.py +0 -0
  157. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  158. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/events.py +0 -0
  159. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/external_state.py +0 -0
  160. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  161. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  162. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  163. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/hypercore.py +0 -0
  164. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  165. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/merkl.py +0 -0
  166. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/morpho.py +0 -0
  167. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  168. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  169. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/__init__.py +0 -0
  170. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/architecture.md +0 -0
  171. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/glossary.md +0 -0
  172. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/invariants.md +0 -0
  173. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/quickstart.md +0 -0
  174. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/market_ids.py +0 -0
  175. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/market_info.py +0 -0
  176. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/mcp/__init__.py +0 -0
  177. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/mcp/guide.py +0 -0
  178. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/mcp/models.py +0 -0
  179. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/morpho_api.py +0 -0
  180. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/py.typed +0 -0
  181. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/__init__.py +0 -0
  182. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/aave_v4.py +0 -0
  183. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/compound_v3.py +0 -0
  184. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/hypercore.py +0 -0
  185. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  186. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  187. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  188. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  189. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/position_manager.py +0 -0
  190. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  191. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  192. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/substrates.py +0 -0
  193. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/types.py +0 -0
  194. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_positions.py +0 -0
  195. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_reads.py +0 -0
  196. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/UniswapV3UniversalRouter.py +0 -0
  197. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_crosschain.py +0 -0
  198. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_crosschain_lifecycle.py +0 -0
  199. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_euler_v2.py +0 -0
  200. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_foundry.py +0 -0
  201. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_guide.py +0 -0
  202. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_hypercore_model.py +0 -0
  203. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_hypercore_shadow.py +0 -0
  204. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_multicall.py +0 -0
  205. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/_simulate.py +0 -0
  206. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/addresses.py +0 -0
  207. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/constants.py +0 -0
  208. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  209. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/fixtures/hypercore_market55.json +0 -0
  210. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/fixtures/vault_info_golden/clean.json +0 -0
  211. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  212. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/fixtures/vault_info_golden/rich.json +0 -0
  213. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  214. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_about.py +0 -0
  215. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_access_manager.py +0 -0
  216. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cache.py +0 -0
  217. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_chains.py +0 -0
  218. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_changelog.py +0 -0
  219. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_config_store.py +0 -0
  220. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_market_morpho_blue.py +0 -0
  221. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_oracle_mapping.py +0 -0
  222. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_cli_role_accounts.py +0 -0
  223. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_contract_call.py +0 -0
  224. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_ccip_events.py +0 -0
  225. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_ccip_indexer.py +0 -0
  226. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_ccip_quote.py +0 -0
  227. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_chainlink.py +0 -0
  228. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_contracts.py +0 -0
  229. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_encoding.py +0 -0
  230. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_errors.py +0 -0
  231. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_lane.py +0 -0
  232. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_readiness.py +0 -0
  233. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_readiness_report.py +0 -0
  234. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_transport.py +0 -0
  235. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_erc20.py +0 -0
  236. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_error_hierarchy.py +0 -0
  237. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_examples_composed_hypercore.py +0 -0
  238. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_examples_crosschain.py +0 -0
  239. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  240. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_examples_vaults.py +0 -0
  241. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_explorer.py +0 -0
  242. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_external_state_events.py +0 -0
  243. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_fusion_instance.py +0 -0
  244. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_guide.py +0 -0
  245. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_deployment.py +0 -0
  246. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_events.py +0 -0
  247. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_live.py +0 -0
  248. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_outcome.py +0 -0
  249. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_readers.py +0 -0
  250. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_simulation.py +0 -0
  251. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_logs.py +0 -0
  252. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_mcp_hypercore.py +0 -0
  253. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_mcp_models.py +0 -0
  254. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_mcp_server.py +0 -0
  255. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_multicall.py +0 -0
  256. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_oracle.py +0 -0
  257. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_oracle_mapping.py +0 -0
  258. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_reader_encoding.py +0 -0
  259. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_readiness_selection.py +0 -0
  260. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_revert_decoding.py +0 -0
  261. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_rewards_manager.py +0 -0
  262. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_roles.py +0 -0
  263. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_sdk_layering.py +0 -0
  264. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  265. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  266. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  267. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_crosschain_canary.py +0 -0
  268. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_crosschain_hypercore.py +0 -0
  269. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  270. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  271. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  272. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  273. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  274. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  275. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  276. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_examples_arbitrum.py +0 -0
  277. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_examples_base.py +0 -0
  278. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_examples_crosschain.py +0 -0
  279. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_external_state_executor_base.py +0 -0
  280. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  281. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  282. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_morpho_blue.py +0 -0
  283. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_pilot_v3.py +0 -0
  284. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  285. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  286. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  287. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_readers_integration.py +0 -0
  288. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_release_funds.py +0 -0
  289. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_swap_on_base.py +0 -0
  290. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  291. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_solidity_compiler.py +0 -0
  292. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_substrates.py +0 -0
  293. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_types_and_events.py +0 -0
  294. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_unpriceable_tokens.py +0 -0
  295. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_vault_dep_graph.py +0 -0
  296. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/tests/test_vault_simulator_blocks.py +0 -0
  297. {ipor_fusion-4.0.0.dev86 → ipor_fusion-4.0.0.dev93}/uv.lock +0 -0
@@ -188,8 +188,9 @@ the lifecycle matrix follows that.
188
188
  - `substrates.py` — public per-market bytes32 substrate decoding (`decode_substrate`,
189
189
  `SubstrateInfo`, `format_market_label`, `market_name`; re-exported from `ipor_fusion`),
190
190
  used by the CLI, MCP and `readers/lending_health`
191
- - `validation.py` — public vault validation (`validate_vault`; `read_erc20_holdings` +
192
- `validate_vault_state` for an already-fetched state typed by the `VaultState` protocol):
191
+ - `validation.py` — public vault validation (`validate_vault` fetches and validates;
192
+ `validate_vault_state` is a pure function of a snapshot typed by the `VaultState`
193
+ protocol):
193
194
  `VaultValidation` with ordered `Finding`s, an ok/warning/critical verdict, the
194
195
  `Reconciliation`, `Erc20Holdings` and lending health; `dependency_graph.py` holds the
195
196
  balance-graph checks
@@ -197,9 +198,11 @@ the lifecycle matrix follows that.
197
198
  `WithdrawManagerData`), `fetch_deployment_info`, cached `resolve_token_symbol` /
198
199
  `resolve_token_decimals`; it composes `vault_reads.py` (the shared Multicall3 batch
199
200
  and degrade-to-``None`` helpers), `vault_managers.py` (fee and WithdrawManager
200
- snapshots) and `vault_positions.py` (Morpho/Aave position breakdowns and their token
201
- prices); `vault_info.py` — `build_vault_info_json`, the
202
- `fusion vault info --json` document (contract names via an injected `resolve_name`),
201
+ snapshots), `vault_positions.py` (Morpho/Aave position breakdowns and their token
202
+ prices) and `vault_erc20.py` (the ERC20_VAULT_BALANCE holdings);
203
+ `vault_info.py` — `build_vault_info_json(data)`, the `fusion vault info --json`
204
+ document rendered from the snapshot alone (contract names via an injected
205
+ `resolve_name`),
203
206
  and `health_check_lists`; `explorer.py` Etherscan V2 (single endpoint, needs API key);
204
207
  `cache.py` the on-disk contract and deployment caches (`~/.cache/ipor-fusion/`).
205
208
  `morpho_api.py` the Morpho public GraphQL API client (`fetch_market`, `fetch_vault`,
@@ -218,12 +221,14 @@ the lifecycle matrix follows that.
218
221
  `simulation` (`VaultSimulator`, eth_simulateV1, `erc20_balance_slot` and ERC-20 balance
219
222
  overrides), `oracle`, `fusion_factory`,
220
223
  `external_state_executor` (NAV marks for market 50), `erc20`, `fuse_action`
221
- (`FuseAction`), `address` (`_validate_not_zero_address`). `core` imports neither
224
+ (`FuseAction`), `fuse` (the `Fuse` base, `StakeFuse`, the shared validators and
225
+ substrate packers), `address` (`_validate_not_zero_address`). `core` imports neither
222
226
  `fuses` nor `crosschain` (`tests/test_sdk_layering.py`)
223
227
  - `fuses/` — per-protocol fuse encoders (aave_v3, async_action, compound_v3, crosschain/,
224
228
  erc4626, euler_v2, external_state, fluid_instadapp, gearbox_v3, merkl, morpho, ramses_v2,
225
- uniswap_v3, universal, `events.py`); `base.py` holds `Fuse` and the
226
- shared validators and re-exports `FuseAction` and `ZERO_ADDRESS` (from `types`)
229
+ uniswap_v3, universal, `events.py`); `base.py` re-exports the `core/fuse.py` base and validators,
230
+ `FuseAction` and `ZERO_ADDRESS` (from `types`). A fuse is a value: equality and
231
+ hash cover its class and every attribute it stores
227
232
  - `crosschain/` — crosschain Plasma Vaults (executor on the hub chain, dispatcher at
228
233
  the same address on each spoke chain), split like the contracts: shared `messages`
229
234
  (`Command`, `MsgType`, envelopes), `contracts` (wrapper bases), `transport`
@@ -276,7 +281,9 @@ or examples.
276
281
  - `Call[T]` (`core/contract.py`): every `ContractWrapper` method (one per
277
282
  Solidity function) returns a `Call` instead of executing. `.call()` runs
278
283
  `eth_call` and decodes to `T`; `.send()` signs and submits, returning a
279
- `TxReceipt`; `.calldata` hands the bytes to an external signer;
284
+ `TxReceipt`; `.calldata` hands the bytes to an external signer (a wrapper
285
+ built with `ctx=None`, e.g. `PlasmaVault(None, address)`, builds calldata
286
+ offline; there is no separate encoder factory);
280
287
  `.build_transaction()` / `Web3Context.estimate_gas` preview without signing.
281
288
  A `Call` without `output_types` (a write) raises on `.call()`. The same
282
289
  `Call` feeds `VaultSimulator.observe`, so reads, sends and simulations share
@@ -291,6 +298,9 @@ or examples.
291
298
  via `self._action_raw(solidity_signature, values)`; the shared `_validate_*`
292
299
  helpers raise before encoding. `PlasmaVault.execute([actions])` runs the
293
300
  batch atomically.
301
+ - Pin reads, never mutate a shared context: `ctx.at_block(n)` is a copy whose
302
+ reads (and `get_logs` upper bound) default to block ``n``; `Call.call(block=n)`
303
+ pins one read.
294
304
  - Simulate before sending: `VaultSimulator(web3, vault=, alpha=)` batches
295
305
  `execute` and `observe` calls into one `eth_simulateV1` round trip, across
296
306
  blocks via `next_block`. Integration tests and examples use it instead of a
@@ -379,9 +389,14 @@ are not validated.
379
389
 
380
390
  ## Domain rules (verified against the contracts)
381
391
 
382
- - `fetch_vault_data(..., chain_id=0)` (the default) skips lending_health,
383
- Morpho/Aave position breakdowns and token prices, all guarded by `if chain_id:`.
384
- Pass a real chain_id or those come back null/empty, silently.
392
+ - `fetch_vault_data(ctx, vault, *, block=, lending=, metadata=)` is the snapshot:
393
+ it resolves one block (``block``, else a numeric `ctx.default_block`, else latest),
394
+ runs every read on `ctx.at_block(block)` (log scans end there too) and returns
395
+ everything validation and the ``vault info`` document need, so neither reads the
396
+ chain. The chain is `ctx.chain_id`; ``lending=False`` skips lending health, the
397
+ position breakdowns, their prices and the unpriceable check; ``metadata=False``
398
+ skips token symbols/decimals. Add a new chain-state input to the snapshot, never a
399
+ read inside a renderer or validator.
385
400
  - PlasmaVault serves governance/view functions via `fallback()` delegatecall to
386
401
  PlasmaVaultBase, so Etherscan's ABI for the vault address omits callable
387
402
  view functions such as `getDependencyBalanceGraph`. An empty dependency graph
@@ -57,20 +57,40 @@ names earlier reachable only by module path (`FusionFactory`, `FusionInstance`,
57
57
  `ipor_fusion`: a module path outside the README's list (*Public API and stability*)
58
58
  still works but is internal.
59
59
 
60
- **Vault reads and validation.** `fetch_vault_data(chain_id=)`, `fetch_deployment_info`,
61
- `build_vault_info_json`, `ensure_supported_chain` and `fetch_vault_lending_health`
62
- take `ChainId`. The vault is a `PlasmaVaultAddress` (see *Address roles*) in
63
- `validate_vault` and `fetch_deployment_info`; `build_vault_info_json(ctx, plasma_vault,
64
- data, *, chain_id, chain_label)` takes no address, takes the chain by keyword, and
65
- labels the document with `plasma_vault.address` (a test that mocks `PlasmaVault`
66
- sets `.address`). `fetch_unpriceable_priced_tokens` takes the oracle and
60
+ **Vault reads and validation.** The vault read is one pinned snapshot, and the
61
+ chain is always the context's:
62
+
63
+ - `fetch_vault_data(ctx, vault, *, block=None, lending=True, metadata=True)` takes
64
+ the vault address (not a `PlasmaVault`) and no `chain_id`. It resolves one block
65
+ (``block``, else a numeric `ctx.default_block`, else the latest) and runs every
66
+ read, log scans included, on `ctx.at_block(block)`; `ctx` is not modified. The old
67
+ ``chain_id=0`` switch is ``lending=False``; ``metadata=False`` skips token symbols
68
+ and decimals. `VaultData` gains `vault`, `chain_id`, `market_balances`, `erc20`,
69
+ `token_symbols` and `token_decimals`; `WithdrawManagerData` gains
70
+ `shares_to_release_assets` and `pending_request_assets`.
71
+ - `validate_vault(ctx, vault, *, block=None)` fetches with ``metadata=False`` and
72
+ validates; it no longer takes ``resolve_symbol``. `validate_vault_state(state)` is
73
+ pure: a custom `VaultState` also provides `vault`, `chain_id`, `market_balances`,
74
+ `erc20` and the queue's `shares_to_release_assets`. `read_erc20_holdings` is gone:
75
+ the holdings are `VaultData.erc20`.
76
+ - `build_vault_info_json(data, *, resolve_name=None)` renders from the snapshot and
77
+ reads nothing; the chain and its label come from `data.chain_id`.
78
+ - `fetch_deployment_info(ctx, vault, *, api_key=None)` and
79
+ `fetch_vault_lending_health(ctx, vault, *, balance_fuse_market_ids,
80
+ market_substrates, aave_pools=None)` take the chain from `ctx`.
81
+ - `Web3Context(web3, chain_id, *, ...)` and `Web3Context.from_url(url, *, ...)` take
82
+ their options by keyword. New: `Web3Context.at_block(block)` (a pinned copy) and
83
+ `Call.call(ctx=None, *, block=None)`. `Web3Context.get_logs(to_block=None)` ends at
84
+ the context's default block and `Web3Context.get_block(block=None)` returns it,
85
+ both instead of ``"latest"``.
86
+
87
+ `ensure_supported_chain` takes `ChainId`. `fetch_unpriceable_priced_tokens` takes the oracle and
67
88
  underlying as `ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
68
89
  `get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
69
90
  fits), and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
70
91
  typed for `str` still fits). Market-keyed data is keyed by `MarketId`:
71
92
  `VaultData.market_substrates`, `dependency_graph`, `morpho_positions`,
72
- `aave_positions`; the `VaultState` protocol, `validate_vault_state`
73
- (`market_balances`), `fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens`
93
+ `aave_positions`, `market_balances`; the `VaultState` protocol, `fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens`
74
94
  and the `dependency_graph` helpers (`compute_update_reach`, `compute_update_groups`,
75
95
  `find_orphan_fuse_markets`, `find_markets_missing_erc20_dependency`).
76
96
 
@@ -93,7 +113,7 @@ The Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient`
93
113
  `quote_ccip_native_fee(block=)` and `OracleMappingReader.asset_source_events` take a
94
114
  `BlockIdentifier`.
95
115
 
96
- **Address roles.** `PlasmaVault(ctx, address)`, `PlasmaVault.encoder(address)`,
116
+ **Address roles.** `PlasmaVault(ctx, address)` (`ctx` may be `None`, see below),
97
117
  `validate_vault`, `fetch_deployment_info`, the HyperCore, lending-health and
98
118
  oracle-mapping readers, `read_external_state`, `VaultSimulator(vault=)`,
99
119
  `CrosschainSimulator`, crosschain discovery, the hub-manager inputs
@@ -194,6 +214,35 @@ are `ProposalHash`; `ExternalStateEvent.transaction_hash` is a `TxHash`.
194
214
  except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
195
215
  `MarketId`. Calling an MCP tool function directly from Python takes `ChainId`.
196
216
 
217
+ **Offline wrappers.** `ContractWrapper.encoder()` (and the `PlasmaVault` /
218
+ `AccessManager` overrides) is gone. Every wrapper constructor takes
219
+ `ctx: Web3Context | None`, so build an offline wrapper with the normal
220
+ constructor and an address: `PlasmaVault(None, vault)`, `ERC20(None, token)`,
221
+ `FusionFactory(None, factory)`, `LegacyPriceOracleMiddleware(None, oracle,
222
+ Decimals(8))`. The address is required (there is no zero-address placeholder) and
223
+ subclass parameters stay required, so the wrapper is fully initialized:
224
+ `.encoder()` skipped the constructor, and a legacy oracle built that way failed to
225
+ decode `get_asset_price`. `.calldata` still needs no context; `.call()`, `.send()`,
226
+ `.build_transaction()` and `.estimate_gas()` raise `ValueError` until one is passed
227
+ to them, and a method that reads the chain itself (log scans such as
228
+ `get_balance_fuses` or `get_pending_requests`, `withdraw_manager_address`,
229
+ `Multicall3.aggregate` / `try_aggregate`, the Aave V3 and Morpho position reads)
230
+ raises `ValueError("Web3Context required: <wrapper> at <address> ...")` instead of
231
+ an `AttributeError` on `None`. `ExternalStateExecutor` still refuses the zero
232
+ address for its hash and event readers.
233
+
234
+ | 3.x | 4.0 |
235
+ |---|---|
236
+ | `PlasmaVault.encoder(vault)` | `PlasmaVault(None, vault)` |
237
+ | `ERC20.encoder()` (no address) | `ERC20(None, token)`: pass the real address |
238
+ | `Cls.__new__(Cls)` + `_ctx` / `_address` | `Cls(None, address)` |
239
+
240
+ **Fuse base.** `Fuse`, `StakeFuse` and the shared validators and substrate packers
241
+ live in `ipor_fusion.core.fuse`; `ipor_fusion.fuses.base`, `ipor_fusion.fuses` and
242
+ `ipor_fusion` re-export them, so existing imports keep working. Fuse equality and
243
+ hash now cover every attribute a fuse stores, not only its class and address (see
244
+ [Behavior changes](#behavior-changes)).
245
+
197
246
  **Moved, still importable.** `FuseAction` lives in `ipor_fusion.core.fuse_action` and
198
247
  `ZERO_ADDRESS` in `ipor_fusion.types`; both are re-exported from
199
248
  `ipor_fusion.fuses.base` and `FuseAction` from `ipor_fusion`. The CLI's internal
@@ -202,6 +251,12 @@ except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
202
251
 
203
252
  ## Behavior changes
204
253
 
254
+ - Two fuses are equal (and hash alike) only when they have the same class and the
255
+ same stored state. `StakeFuse` subclasses (`GearboxStakeFuse`,
256
+ `FluidInstadappStakingFuse`) with the same fuse address but different
257
+ `staking_address` were equal and collapsed in a set or dict key; they are now
258
+ distinct. A custom `Fuse` subclass is compared on its instance attributes, which
259
+ must be hashable; drop an `__eq__` / `__hash__` override that only added them.
205
260
  - The MCP `vault_info` document reports the vault address checksummed (the CLI
206
261
  already did).
207
262
  - `SubstrateInfo.address` is `None` instead of `""` for a substrate without an
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev86
3
+ Version: 4.0.0.dev93
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
@@ -261,12 +261,12 @@ def clone_args() -> dict:
261
261
  def unsigned_clone_calldata() -> bytes:
262
262
  """Unsigned creation calldata (selector + ABI-encoded args) for the deploy.
263
263
 
264
- Built with a ctx-less encoder, so it needs no provider. Hand these bytes to
264
+ Built on a wrapper with ctx=None, so it needs no provider. Hand these bytes to
265
265
  an external signer / multisig instead of calling ``.send()``. NOTE: the owner
266
266
  encoded here is the simulation ``OWNER`` (a throwaway EOA) -- set your real
267
267
  owner in ``clone_args()`` before deploying, or the vault is owned by it.
268
268
  """
269
- return FusionFactory.encoder(BASE_FUSION_FACTORY).clone(**clone_args()).calldata
269
+ return FusionFactory(None, BASE_FUSION_FACTORY).clone(**clone_args()).calldata
270
270
 
271
271
 
272
272
  def market_substrates() -> list[bytes]:
@@ -208,7 +208,7 @@ def clone_args() -> dict:
208
208
 
209
209
 
210
210
  def unsigned_clone_calldata() -> bytes:
211
- return FusionFactory.encoder(ARBITRUM_FUSION_FACTORY).clone(**clone_args()).calldata
211
+ return FusionFactory(None, ARBITRUM_FUSION_FACTORY).clone(**clone_args()).calldata
212
212
 
213
213
 
214
214
  def euler_market_substrates() -> list[bytes]:
@@ -260,7 +260,7 @@ def enable_euler_actions() -> list[FuseAction]:
260
260
  def build_swap_action() -> FuseAction:
261
261
  """Swap the borrowed USDC through Uniswap V3 and require flash repayment."""
262
262
  transfer_data = (
263
- ERC20.encoder(ARBITRUM_USDC)
263
+ ERC20(None, ARBITRUM_USDC)
264
264
  .transfer(UNISWAP_UNIVERSAL_ROUTER, BORROW_USDC)
265
265
  .calldata
266
266
  )
@@ -438,12 +438,12 @@ def clone_args() -> dict:
438
438
  def unsigned_clone_calldata() -> bytes:
439
439
  """Unsigned creation calldata (selector + ABI-encoded args) for the deploy.
440
440
 
441
- Built with a ctx-less encoder, so it needs no provider. Hand these bytes to
441
+ Built on a wrapper with ctx=None, so it needs no provider. Hand these bytes to
442
442
  an external signer / multisig instead of calling ``.send()``. NOTE: the owner
443
443
  encoded here is the simulation ``OWNER`` (a throwaway EOA) -- set your real
444
444
  owner in ``clone_args()`` before deploying, or the vault is owned by it.
445
445
  """
446
- return FusionFactory.encoder(BASE_FUSION_FACTORY).clone(**clone_args()).calldata
446
+ return FusionFactory(None, BASE_FUSION_FACTORY).clone(**clone_args()).calldata
447
447
 
448
448
 
449
449
  def market_substrates() -> list[bytes]:
@@ -534,9 +534,9 @@ def market_substrates() -> list[bytes]:
534
534
  def venue_transfer_calldata(amount: Amount) -> bytes:
535
535
  """``USDC.transfer(VENUE_DEPOSIT_ADDRESS, amount)`` calldata, run from the executor.
536
536
 
537
- Encoded with a ctx-less ERC20 wrapper -- pure bytes, no chain access.
537
+ Encoded with an ERC20 wrapper built with ctx=None -- pure bytes, no chain access.
538
538
  """
539
- return ERC20.encoder(BASE_USDC).transfer(VENUE_DEPOSIT_ADDRESS, amount).calldata
539
+ return ERC20(None, BASE_USDC).transfer(VENUE_DEPOSIT_ADDRESS, amount).calldata
540
540
 
541
541
 
542
542
  def build_enter_action(amount: Amount) -> FuseAction:
@@ -201,12 +201,12 @@ def clone_args() -> dict:
201
201
  def unsigned_clone_calldata() -> bytes:
202
202
  """Unsigned creation calldata (selector + ABI-encoded args) for the deploy.
203
203
 
204
- Built with a ctx-less encoder, so it needs no provider. Hand these bytes to
204
+ Built on a wrapper with ctx=None, so it needs no provider. Hand these bytes to
205
205
  an external signer / multisig instead of calling ``.send()``. NOTE: the owner
206
206
  encoded here is the simulation ``OWNER`` (a throwaway EOA) -- set your real
207
207
  owner in ``clone_args()`` before deploying, or the vault is owned by it.
208
208
  """
209
- return FusionFactory.encoder(BASE_FUSION_FACTORY).clone(**clone_args()).calldata
209
+ return FusionFactory(None, BASE_FUSION_FACTORY).clone(**clone_args()).calldata
210
210
 
211
211
 
212
212
  def build_supply_action(amount: Amount) -> FuseAction:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev86"
3
+ version = "4.0.0.dev93"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -445,7 +445,6 @@ from ipor_fusion.validation import (
445
445
  VaultState,
446
446
  VaultValidation,
447
447
  WithdrawQueue,
448
- read_erc20_holdings,
449
448
  validate_vault,
450
449
  validate_vault_state,
451
450
  )
@@ -861,7 +860,6 @@ __all__ = [
861
860
  "VaultState",
862
861
  "VaultValidation",
863
862
  "WithdrawQueue",
864
- "read_erc20_holdings",
865
863
  "validate_vault",
866
864
  "validate_vault_state",
867
865
  "compute_update_groups",
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import sys
5
- from functools import partial
6
5
  from typing import Any
7
6
 
8
7
  import click
@@ -50,7 +49,6 @@ from ipor_fusion.validation import (
50
49
  from ipor_fusion.vault_data import (
51
50
  _ROLE_SCAN_ERRORS,
52
51
  fetch_vault_data,
53
- resolve_token_symbol,
54
52
  )
55
53
  from ipor_fusion.vault_info import (
56
54
  _erc20_balances_json,
@@ -207,7 +205,6 @@ def info(
207
205
  """
208
206
  cfg = load_config()
209
207
  chain_id, ctx = _build_ctx(cfg, vault_address, chain_id, block_number)
210
- checksum_address = vault_address
211
208
 
212
209
  # Cheap single-call probe: friendly errors for "nothing deployed here" and
213
210
  # "not a Plasma Vault" (revert and empty-return flavors alike) before the
@@ -217,17 +214,13 @@ def info(
217
214
  except (ContractNotFoundError, NotPlasmaVaultError) as exc:
218
215
  raise click.UsageError(str(exc)) from exc
219
216
 
220
- plasma_vault = PlasmaVault(ctx, checksum_address)
221
-
222
- _auto_save_vault(cfg, vault_address, chain_id, plasma_vault)
217
+ _auto_save_vault(cfg, vault_address, chain_id, PlasmaVault(ctx, vault_address))
223
218
 
224
219
  try:
225
- data = fetch_vault_data(ctx, plasma_vault, block_number, chain_id=chain_id)
220
+ data = fetch_vault_data(ctx, vault_address, block=block_number)
226
221
  except UnsupportedVaultVersionError as exc:
227
222
  raise click.ClickException(str(exc)) from exc
228
- _print_vault_info(
229
- ctx, plasma_vault, cfg, data, vault_address, chain_id, json_output
230
- )
223
+ _print_vault_info(ctx, cfg, data, json_output)
231
224
 
232
225
 
233
226
  @vault.command("validate")
@@ -264,12 +257,7 @@ def validate(
264
257
  raise click.UsageError(str(exc)) from exc
265
258
 
266
259
  try:
267
- validation = validate_vault(
268
- ctx,
269
- vault_address,
270
- block_number,
271
- resolve_symbol=partial(resolve_token_symbol, ctx),
272
- )
260
+ validation = validate_vault(ctx, vault_address, block=block_number)
273
261
  except UnsupportedVaultVersionError as exc:
274
262
  raise click.ClickException(str(exc)) from exc
275
263
 
@@ -3,32 +3,18 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
- from functools import partial
7
-
8
6
  import click
9
7
 
10
8
  from ipor_fusion.cli.vault_rendering import _format_amount, _format_usd, _print_table
11
- from ipor_fusion.core.context import Web3Context
12
- from ipor_fusion.core.plasma_vault import PlasmaVault
13
9
  from ipor_fusion.substrates import market_name
14
10
  from ipor_fusion.validation import (
15
11
  Erc20Holdings,
16
12
  VaultValidation,
17
- read_erc20_holdings,
18
13
  )
19
- from ipor_fusion.vault_data import VaultData, resolve_token_symbol
14
+ from ipor_fusion.vault_data import VaultData
20
15
  from ipor_fusion.vault_info import health_check_lists
21
16
 
22
17
 
23
- def _read_erc20_holdings(
24
- ctx: Web3Context, plasma_vault: PlasmaVault, data: VaultData
25
- ) -> Erc20Holdings:
26
- """ERC20 holdings with token symbols from the CLI's contract cache."""
27
- return read_erc20_holdings(
28
- ctx, plasma_vault, data, partial(resolve_token_symbol, ctx)
29
- )
30
-
31
-
32
18
  def _print_erc20_balances(data: VaultData, erc20: Erc20Holdings) -> None:
33
19
  if not erc20.tokens:
34
20
  has_erc20_market = any(
@@ -14,7 +14,6 @@ from ipor_fusion.cli.vault_health import (
14
14
  _print_erc20_balances,
15
15
  _print_health_check,
16
16
  _print_reconciliation,
17
- _read_erc20_holdings,
18
17
  )
19
18
  from ipor_fusion.cli.vault_markets_view import _print_lending_health, _print_markets
20
19
  from ipor_fusion.cli.vault_rendering import (
@@ -26,7 +25,6 @@ from ipor_fusion.cli.vault_rendering import (
26
25
  )
27
26
  from ipor_fusion.core.context import Web3Context
28
27
  from ipor_fusion.core.fee_manager import RecipientFee
29
- from ipor_fusion.core.plasma_vault import PlasmaVault
30
28
  from ipor_fusion.explorer import get_contract_name
31
29
  from ipor_fusion.readers.hypercore import HyperCorePendingState
32
30
  from ipor_fusion.substrates import (
@@ -48,7 +46,6 @@ from ipor_fusion.vault_info import (
48
46
  _wad_to_usd,
49
47
  build_vault_info_json,
50
48
  )
51
- from ipor_fusion.vault_reads import _safe_call
52
49
 
53
50
 
54
51
  def _print_role_accounts_table(role_accounts: list[dict[str, Any]]) -> None:
@@ -76,38 +73,28 @@ def _print_role_accounts(role_accounts: list[dict[str, Any]] | None) -> None:
76
73
 
77
74
 
78
75
  def _print_vault_info(
79
- ctx: Web3Context,
80
- plasma_vault: PlasmaVault,
81
- cfg: FusionConfig,
82
- data: VaultData,
83
- vault_address: PlasmaVaultAddress,
84
- chain_id: ChainId,
85
- json_output: bool = False,
76
+ ctx: Web3Context, cfg: FusionConfig, data: VaultData, json_output: bool = False
86
77
  ) -> None:
87
78
  api_key = cfg.etherscan_api_key
79
+ chain_id = data.chain_id
88
80
  chain_label = CHAIN_NAMES.get(chain_id, str(chain_id))
89
81
 
90
82
  (
91
83
  data.deployment_block,
92
84
  data.deployment_timestamp,
93
85
  data.deployment_error,
94
- ) = fetch_deployment_info(ctx, chain_id, vault_address, api_key)
86
+ ) = fetch_deployment_info(ctx, data.vault, api_key=api_key)
95
87
 
96
88
  if json_output:
97
89
  result = build_vault_info_json(
98
- ctx,
99
- plasma_vault,
100
- data,
101
- chain_id=chain_id,
102
- chain_label=chain_label,
103
- resolve_name=partial(get_contract_name, chain_id, api_key=api_key),
90
+ data, resolve_name=partial(get_contract_name, chain_id, api_key=api_key)
104
91
  )
105
92
  click.echo(json.dumps(result, indent=2))
106
93
  return
107
94
 
108
- _print_identity(data, vault_address, chain_id, chain_label)
95
+ _print_identity(data, data.vault, chain_id, chain_label)
109
96
  _print_supply(data)
110
- _print_managers(data, plasma_vault)
97
+ _print_managers(data)
111
98
  click.echo()
112
99
 
113
100
  _print_fees(data)
@@ -115,15 +102,9 @@ def _print_vault_info(
115
102
 
116
103
  _print_role_accounts(_role_accounts_json(data))
117
104
 
118
- market_balances = _print_markets(ctx, plasma_vault, data, chain_id, api_key)
105
+ _print_markets(data, api_key)
119
106
 
120
- validation = validate_vault_state(
121
- ctx,
122
- plasma_vault,
123
- data,
124
- market_balances,
125
- _read_erc20_holdings(ctx, plasma_vault, data),
126
- )
107
+ validation = validate_vault_state(data)
127
108
  click.echo("ERC20 Balances (vault holdings):")
128
109
  _print_erc20_balances(data, validation.erc20)
129
110
  click.echo()
@@ -131,7 +112,7 @@ def _print_vault_info(
131
112
  _print_reconciliation(data, validation)
132
113
  click.echo()
133
114
 
134
- _print_lending_health(ctx, data)
115
+ _print_lending_health(data)
135
116
  click.echo()
136
117
  _print_hypercore(data)
137
118
 
@@ -207,7 +188,7 @@ def _print_supply(data: VaultData) -> None:
207
188
  )
208
189
 
209
190
 
210
- def _print_managers(data: VaultData, plasma_vault: PlasmaVault) -> None:
191
+ def _print_managers(data: VaultData) -> None:
211
192
  click.echo(f"Access Manager: {data.access_manager}")
212
193
  click.echo(f"Price Oracle: {data.price_oracle_addr}")
213
194
  click.echo(f"Rewards Manager: {data.rewards_manager or 'N/A'}")
@@ -222,7 +203,7 @@ def _print_managers(data: VaultData, plasma_vault: PlasmaVault) -> None:
222
203
  if wmd.last_release_funds_timestamp > 0:
223
204
  last_iso = _unix_to_iso(wmd.last_release_funds_timestamp)
224
205
  click.echo(f" Last release: {last_iso}")
225
- _print_pending_requests(data, plasma_vault)
206
+ _print_pending_requests(data)
226
207
 
227
208
 
228
209
  def _format_fee_percent(value: float | None) -> str:
@@ -375,7 +356,7 @@ def _print_hypercore_pending(pending: HyperCorePendingState | None) -> None:
375
356
  )
376
357
 
377
358
 
378
- def _print_pending_requests(data: VaultData, plasma_vault: PlasmaVault) -> None:
359
+ def _print_pending_requests(data: VaultData) -> None:
379
360
  if (wmd := data.withdraw_manager_data) is None:
380
361
  return
381
362
  if wmd.pending_requests is None:
@@ -389,16 +370,14 @@ def _print_pending_requests(data: VaultData, plasma_vault: PlasmaVault) -> None:
389
370
  return
390
371
 
391
372
  total_shares = Shares(sum(r.shares for r in requests))
392
- total_assets: int | None = _safe_call(
393
- lambda ts=total_shares: plasma_vault.convert_to_assets(ts).call()
373
+ request_assets = [wmd.pending_request_assets.get(r.account) for r in requests]
374
+ total_assets = (
375
+ None if None in request_assets else sum(a for a in request_assets if a)
394
376
  )
395
377
 
396
378
  click.echo(f" Pending requests ({len(requests)}):")
397
379
  rows: list[tuple[str, ...]] = []
398
- for req in requests:
399
- assets: int | None = _safe_call(
400
- lambda s=req.shares: plasma_vault.convert_to_assets(s).call()
401
- )
380
+ for req, assets in zip(requests, request_assets, strict=True):
402
381
  assets_str = (
403
382
  f"{_format_amount(assets, data.asset_decimals)} {data.asset_symbol}"
404
383
  if assets is not None