ipor-fusion 4.0.0.dev116__tar.gz → 4.0.0.dev120__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.dev116 → ipor_fusion-4.0.0.dev120}/AGENTS.md +3 -1
  2. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/MIGRATION_4.md +27 -10
  3. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/PKG-INFO +1 -1
  4. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/pyproject.toml +1 -1
  5. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/vault_cmd.py +3 -1
  6. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/vault_info_view.py +1 -1
  7. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/access.py +2 -4
  8. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/context.py +22 -2
  9. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/external_state_executor.py +9 -10
  10. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/mcp/server.py +1 -1
  11. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/hypercore.py +14 -14
  12. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/hypercore_outcome.py +12 -12
  13. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/lending_health.py +5 -5
  14. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/oracle_mapping.py +7 -7
  15. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/validation.py +11 -6
  16. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/vault_data.py +38 -29
  17. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/vault_erc20.py +59 -34
  18. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/vault_info.py +1 -1
  19. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_helpers.py +25 -17
  20. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_lending_health.py +4 -4
  21. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_vault_info_golden.py +19 -18
  22. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_vault_predates_getters.py +3 -3
  23. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_vault_validate.py +2 -2
  24. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_mcp_vault_validate.py +2 -2
  25. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_validation.py +85 -13
  26. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_web3_context.py +98 -0
  27. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.claude-plugin/marketplace.json +0 -0
  28. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.claude-plugin/plugin.json +0 -0
  29. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.env.example +0 -0
  30. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.gemini/settings.json +0 -0
  31. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.git-blame-ignore-revs +0 -0
  32. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.github/workflows/cd.yml +0 -0
  33. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.github/workflows/ci.yml +0 -0
  34. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.github/workflows/commit-lint.yml +0 -0
  35. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.github/workflows/notify-slack.yml +0 -0
  36. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.github/workflows/python-build.yml +0 -0
  37. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.github/workflows/python-publish.yml +0 -0
  38. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.github/workflows/release.yml +0 -0
  39. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.github/workflows/report-slack.yml +0 -0
  40. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.gitignore +0 -0
  41. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.mcp.json +0 -0
  42. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.pre-commit-config.yaml +0 -0
  43. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/.python-version +0 -0
  44. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/CHANGELOG.md +0 -0
  45. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/CLAUDE.md +0 -0
  46. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/CODEOWNERS +0 -0
  47. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/LICENSE +0 -0
  48. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/README.md +0 -0
  49. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/build.sh +0 -0
  50. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/examples/CONTRIBUTING.md +0 -0
  51. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/examples/README.md +0 -0
  52. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  53. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  54. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  55. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  56. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/examples/external_state_margin_leg_base.py +0 -0
  57. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/examples/simple_aave_v3_supply_base.py +0 -0
  58. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/pytest.ini +0 -0
  59. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/scripts/commit-check.sh +0 -0
  60. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/scripts/crosschain_readiness.py +0 -0
  61. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/skills/ipor-deploy-vault/SKILL.md +0 -0
  62. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/__init__.py +0 -0
  63. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/about.py +0 -0
  64. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cache.py +0 -0
  65. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/chains.py +0 -0
  66. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/__init__.py +0 -0
  67. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  68. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/config_cmd.py +0 -0
  69. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/config_store.py +0 -0
  70. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/main.py +0 -0
  71. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/market_cmd.py +0 -0
  72. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/options.py +0 -0
  73. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  74. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/vault_health.py +0 -0
  75. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  76. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  77. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/config/__init__.py +0 -0
  78. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/config/roles.py +0 -0
  79. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/__init__.py +0 -0
  80. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/address.py +0 -0
  81. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/contract.py +0 -0
  82. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/erc20.py +0 -0
  83. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/fee_manager.py +0 -0
  84. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/fuse.py +0 -0
  85. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/fuse_action.py +0 -0
  86. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/fusion_factory.py +0 -0
  87. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/logs.py +0 -0
  88. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/multicall.py +0 -0
  89. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/oracle.py +0 -0
  90. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/plasma_vault.py +0 -0
  91. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/rewards_manager.py +0 -0
  92. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/simulation.py +0 -0
  93. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  94. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/__init__.py +0 -0
  95. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  96. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  97. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  98. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  99. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  100. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  101. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  102. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  103. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  104. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/contracts.py +0 -0
  105. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/discovery.py +0 -0
  106. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/errors.py +0 -0
  107. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  108. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  109. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  110. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  111. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/lane.py +0 -0
  112. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/logs.py +0 -0
  113. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/messages.py +0 -0
  114. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/simulation.py +0 -0
  115. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  116. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  117. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  118. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  119. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  120. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/crosschain/transport.py +0 -0
  121. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/dependency_graph.py +0 -0
  122. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/entry_cli.py +0 -0
  123. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/entry_mcp.py +0 -0
  124. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/errors.py +0 -0
  125. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/explorer.py +0 -0
  126. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/field_docs.py +0 -0
  127. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/__init__.py +0 -0
  128. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  129. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/async_action.py +0 -0
  130. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/base.py +0 -0
  131. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  132. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/erc4626.py +0 -0
  133. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  134. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/events.py +0 -0
  135. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/external_state.py +0 -0
  136. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  137. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  138. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  139. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/hypercore.py +0 -0
  140. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  141. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/merkl.py +0 -0
  142. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/morpho.py +0 -0
  143. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  144. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  145. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/fuses/universal.py +0 -0
  146. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/guide/__init__.py +0 -0
  147. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/guide/architecture.md +0 -0
  148. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/guide/glossary.md +0 -0
  149. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/guide/invariants.md +0 -0
  150. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/guide/quickstart.md +0 -0
  151. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/market_ids.py +0 -0
  152. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/market_info.py +0 -0
  153. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/mcp/__init__.py +0 -0
  154. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/mcp/guide.py +0 -0
  155. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/mcp/models.py +0 -0
  156. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/morpho_api.py +0 -0
  157. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/py.typed +0 -0
  158. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/__init__.py +0 -0
  159. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/aave_v3.py +0 -0
  160. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/aave_v4.py +0 -0
  161. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/compound_v3.py +0 -0
  162. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/morpho.py +0 -0
  163. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  164. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  165. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/position_manager.py +0 -0
  166. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  167. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  168. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/readers/unpriceable.py +0 -0
  169. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/substrates.py +0 -0
  170. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/types.py +0 -0
  171. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/vault_managers.py +0 -0
  172. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/vault_positions.py +0 -0
  173. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/src/ipor_fusion/vault_reads.py +0 -0
  174. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/UniswapV3UniversalRouter.py +0 -0
  175. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_crosschain.py +0 -0
  176. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_crosschain_lifecycle.py +0 -0
  177. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_crosschain_recovery.py +0 -0
  178. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_euler_v2.py +0 -0
  179. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_foundry.py +0 -0
  180. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_guide.py +0 -0
  181. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_hypercore_model.py +0 -0
  182. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_hypercore_shadow.py +0 -0
  183. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_multicall.py +0 -0
  184. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_simulate.py +0 -0
  185. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/_validation.py +0 -0
  186. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/addresses.py +0 -0
  187. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/conftest.py +0 -0
  188. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/constants.py +0 -0
  189. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  190. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/fixtures/hypercore_market55.json +0 -0
  191. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/fixtures/vault_info_golden/clean.json +0 -0
  192. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  193. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/fixtures/vault_info_golden/rich.json +0 -0
  194. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  195. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_about.py +0 -0
  196. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_access_manager.py +0 -0
  197. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cache.py +0 -0
  198. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_chains.py +0 -0
  199. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_changelog.py +0 -0
  200. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_commands.py +0 -0
  201. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_config_store.py +0 -0
  202. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_hypercore.py +0 -0
  203. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_market_morpho_blue.py +0 -0
  204. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_oracle_mapping.py +0 -0
  205. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_pending_requests_scan.py +0 -0
  206. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_cli_role_accounts.py +0 -0
  207. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_contract_call.py +0 -0
  208. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_contract_encoder.py +0 -0
  209. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_ccip_events.py +0 -0
  210. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_ccip_indexer.py +0 -0
  211. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_ccip_quote.py +0 -0
  212. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_chainlink.py +0 -0
  213. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_contracts.py +0 -0
  214. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_encoding.py +0 -0
  215. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_errors.py +0 -0
  216. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_lane.py +0 -0
  217. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_layering.py +0 -0
  218. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_readiness.py +0 -0
  219. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_readiness_report.py +0 -0
  220. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_crosschain_transport.py +0 -0
  221. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_erc20.py +0 -0
  222. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_error_hierarchy.py +0 -0
  223. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_examples_composed_hypercore.py +0 -0
  224. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_examples_crosschain.py +0 -0
  225. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  226. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_examples_vaults.py +0 -0
  227. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_explorer.py +0 -0
  228. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_external_state_events.py +0 -0
  229. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_external_state_executor.py +0 -0
  230. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_fee_manager.py +0 -0
  231. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_fuse_encoding.py +0 -0
  232. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_fusion_instance.py +0 -0
  233. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_guide.py +0 -0
  234. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_hypercore_deployment.py +0 -0
  235. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_hypercore_events.py +0 -0
  236. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_hypercore_flow.py +0 -0
  237. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_hypercore_live.py +0 -0
  238. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_hypercore_outcome.py +0 -0
  239. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_hypercore_readers.py +0 -0
  240. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_hypercore_simulation.py +0 -0
  241. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_logs.py +0 -0
  242. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_mcp_hypercore.py +0 -0
  243. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_mcp_models.py +0 -0
  244. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_mcp_server.py +0 -0
  245. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_multicall.py +0 -0
  246. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_oracle.py +0 -0
  247. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_oracle_mapping.py +0 -0
  248. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_plasma_vault.py +0 -0
  249. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_public_api.py +0 -0
  250. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_reader_encoding.py +0 -0
  251. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_readiness_selection.py +0 -0
  252. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_revert_decoding.py +0 -0
  253. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_rewards_manager.py +0 -0
  254. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_roles.py +0 -0
  255. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_sdk_layering.py +0 -0
  256. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  257. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  258. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  259. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_crosschain_canary.py +0 -0
  260. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_crosschain_hypercore.py +0 -0
  261. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  262. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  263. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  264. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  265. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  266. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  267. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  268. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_examples_arbitrum.py +0 -0
  269. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_examples_base.py +0 -0
  270. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_examples_crosschain.py +0 -0
  271. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_external_state_executor_base.py +0 -0
  272. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  273. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  274. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  275. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_morpho_blue.py +0 -0
  276. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_pilot_v3.py +0 -0
  277. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  278. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  279. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  280. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  281. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_readers_integration.py +0 -0
  282. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_release_funds.py +0 -0
  283. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_swap_on_base.py +0 -0
  284. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  285. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_solidity_compiler.py +0 -0
  286. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_solidity_mirrors.py +0 -0
  287. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_substrates.py +0 -0
  288. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_types_and_events.py +0 -0
  289. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_unpriceable_tokens.py +0 -0
  290. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_vault_dep_graph.py +0 -0
  291. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_vault_info_onchain_base.py +0 -0
  292. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  293. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_vault_simulator_blocks.py +0 -0
  294. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/test_withdraw_manager.py +0 -0
  295. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/typed_client/consumer.py +0 -0
  296. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/tests/typed_client/rejected.py +0 -0
  297. {ipor_fusion-4.0.0.dev116 → ipor_fusion-4.0.0.dev120}/uv.lock +0 -0
@@ -429,7 +429,9 @@ are not validated.
429
429
  everything validation and the ``vault info`` document need, so neither reads the
430
430
  chain. The chain is `ctx.chain_id`; ``lending=False`` skips lending health, the
431
431
  position breakdowns, their prices and the unpriceable check; ``metadata=False``
432
- skips token symbols/decimals. Add a new chain-state input to the snapshot, never a
432
+ reads no token symbol or decimals at all (`validate_vault`'s default), and
433
+ ``metadata=True`` reads them in one cached Multicall3 batch that also names the
434
+ ERC20 holdings. Add a new chain-state input to the snapshot, never a
433
435
  read inside a renderer or validator.
434
436
  - PlasmaVault serves governance/view functions via `fallback()` delegatecall to
435
437
  PlasmaVaultBase, so Etherscan's ABI for the vault address omits callable
@@ -14,10 +14,11 @@ type-checked or not. In particular, `fetch_market` and
14
14
  (pyright, mypy, Pylance) also wraps literals and raw values as below. Values the SDK
15
15
  returns already carry their type and go back in unwrapped.
16
16
 
17
- All types are importable from `ipor_fusion` (and `ipor_fusion.types`), as are
18
- `USDC_TOKEN_INDEX` and `hip3_action_asset`; `ChecksumAddress` and `HexAddress` come
19
- from `eth_typing` and `BlockIdentifier` from `web3.types`. The wrappers label values;
20
- they do not validate byte widths or address spelling.
17
+ The SDK's own types are importable from `ipor_fusion` (and `ipor_fusion.types`),
18
+ as are `USDC_TOKEN_INDEX` and `hip3_action_asset`. Third-party types are not
19
+ re-exported: `ChecksumAddress` and `HexAddress` come from `eth_typing`, and
20
+ `BlockIdentifier`, `Timestamp` and `TxParams` from `web3.types`. The wrappers label
21
+ values; they do not validate byte widths or address spelling.
21
22
 
22
23
  ## Building the types
23
24
 
@@ -64,12 +65,15 @@ chain is always the context's:
64
65
  the vault address (not a `PlasmaVault`) and no `chain_id`. It resolves one block
65
66
  (``block``, else a numeric `ctx.default_block`, else the latest) and runs every
66
67
  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`,
68
+ ``chain_id=0`` switch is ``lending=False``; ``metadata=False`` reads no token
69
+ symbol or decimals (the ERC20 holdings' symbols are then `"?"`), and with
70
+ ``metadata=True`` the holdings are named by the same cached, batched read as
71
+ `token_symbols`. `VaultData` gains `vault`, `chain_id`, `market_balances`, `erc20`,
69
72
  `token_symbols` and `token_decimals`; `WithdrawManagerData` gains
70
73
  `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
74
+ - `validate_vault(ctx, vault, *, block=None, metadata=False)` fetches (by default
75
+ without token metadata) and validates; it no longer takes ``resolve_symbol``.
76
+ ``metadata=True`` names the ERC20 holdings in the result and its findings. `validate_vault_state(state)` is
73
77
  pure: a custom `VaultState` also provides `vault`, `chain_id`, `market_balances`,
74
78
  `erc20` and the queue's `shares_to_release_assets`. `read_erc20_holdings` is gone:
75
79
  the holdings are `VaultData.erc20`.
@@ -92,6 +96,10 @@ are `Fee`, `management_fee_last_update` a `Timestamp`, `unrealized_management_fe
92
96
  `Amount`; `WithdrawManagerData.withdraw_window` is a `Period`, its fees `Fee`,
93
97
  `shares_to_release` `Shares`, `last_release_funds_timestamp` a `Timestamp` and the
94
98
  converted amounts `Amount`. A custom `VaultState` annotates the same.
99
+ `VaultValidation.block_timestamp` is a `Timestamp` and `asset_decimals` `Decimals`;
100
+ `Erc20Holding.decimals` / `balance` are `Decimals | None` / `Amount | None` and
101
+ `Erc20Holdings.value_in_asset` / `market_cached_value` `Amount`;
102
+ `VaultData.token_decimals` values and `resolve_token_decimals` are `Decimals | None`.
95
103
  `PlasmaVault.total_supply()`, `balance_of()` and `get_total_supply_cap()` return
96
104
  `Shares` (vault shares), and `transfer` / `approve` / `transfer_from` /
97
105
  `set_total_supply_cap` take `Shares`; `ERC20` keeps `Amount`. The supply cap is
@@ -114,13 +122,15 @@ typed for `str` still fits). Market-keyed data is keyed by `MarketId`:
114
122
  and the `dependency_graph` helpers (`compute_update_reach`, `compute_update_groups`,
115
123
  `find_orphan_fuse_markets`, `find_markets_missing_erc20_dependency`).
116
124
 
117
- `VaultData.asset`, `price_oracle_addr`, `rewards_manager`, `withdraw_manager`,
125
+ `VaultData.price_oracle_addr` is renamed `price_oracle`, like its sibling
126
+ address fields (a custom `VaultState` renames it too).
127
+ `VaultData.asset`, `price_oracle`, `rewards_manager`, `withdraw_manager`,
118
128
  `FeeData.fee_manager` / `ipor_dao_fee_recipient` and `VaultValidation.vault` /
119
129
  `asset` are `ChecksumAddress` (`VaultValidation.vault` a `PlasmaVaultAddress`);
120
130
  `VaultData.access_manager` is an `AccessManagerAddress`, `fuses` / `instant_fuses`
121
131
  and the `fuse_markets` keys are `FuseAddress`, `balance_fuses` is
122
132
  `list[BalanceFuse]`, and `token_prices_usd` is keyed by lowercase `HexAddress`. A
123
- custom `VaultState` implementation annotates `asset` and `price_oracle_addr` as
133
+ custom `VaultState` implementation annotates `asset` and `price_oracle` as
124
134
  `ChecksumAddress`, `fuse_markets` keys as `FuseAddress`, and its
125
135
  `unpriceable_priced_tokens` items' `market_id` as `MarketId` (`UnpriceableToken`).
126
136
  The Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient` are
@@ -155,6 +165,13 @@ Every fuse constructor, `FuseAction.fuse`, `BalanceFuse.fuse`,
155
165
  `FusionInstance.access_manager` return one. `resolve_access_manager` still takes any
156
166
  `ChecksumAddress`: it is what checks that the address is a vault.
157
167
 
168
+ The vault parameter is named `vault` throughout: `validate_vault`,
169
+ `fetch_deployment_info`, `fetch_vault_lending_health`, `build_oracle_mapping`,
170
+ `read_external_state`, `ExternalStateExecutor.for_vault`, `read_hypercore_nav`,
171
+ `read_hypercore_vault_state`, `read_hypercore_transfer_snapshot`,
172
+ `read_hypercore_evm_exit_ceiling` and `resolve_access_manager` renamed
173
+ ``vault_address`` to ``vault``; only a call that passes it by keyword changes.
174
+
158
175
  **Simulation and factory records.** `VaultSimulator(web3, *, vault=None, alpha=None,
159
176
  block=...)` takes every argument after `web3` by keyword, and `vault` and `alpha` are
160
177
  optional: a simulator that only observes, deploys or sends `execute_call(call,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev116
3
+ Version: 4.0.0.dev120
4
4
  Summary: The IPOR Fusion SDK for Python
5
5
  Project-URL: homepage, https://ipor.io
6
6
  Project-URL: repository, https://github.com/IPOR-Labs/ipor-fusion.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev116"
3
+ version = "4.0.0.dev120"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -257,7 +257,9 @@ def validate(
257
257
  raise click.UsageError(str(exc)) from exc
258
258
 
259
259
  try:
260
- validation = validate_vault(ctx, vault_address, block=block_number)
260
+ validation = validate_vault(
261
+ ctx, vault_address, block=block_number, metadata=True
262
+ )
261
263
  except UnsupportedVaultVersionError as exc:
262
264
  raise click.ClickException(str(exc)) from exc
263
265
 
@@ -191,7 +191,7 @@ def _print_supply(data: VaultData) -> None:
191
191
 
192
192
  def _print_managers(data: VaultData) -> None:
193
193
  click.echo(f"Access Manager: {data.access_manager}")
194
- click.echo(f"Price Oracle: {data.price_oracle_addr}")
194
+ click.echo(f"Price Oracle: {data.price_oracle}")
195
195
  click.echo(f"Rewards Manager: {data.rewards_manager or 'N/A'}")
196
196
  click.echo(f"Withdraw Manager: {data.withdraw_manager or 'N/A'}")
197
197
  if data.withdraw_manager_data:
@@ -182,9 +182,7 @@ class AccessManager(ContractWrapper):
182
182
  )
183
183
 
184
184
 
185
- def resolve_access_manager(
186
- ctx: Web3Context, vault_address: ChecksumAddress
187
- ) -> AccessManager:
185
+ def resolve_access_manager(ctx: Web3Context, vault: ChecksumAddress) -> AccessManager:
188
186
  """Resolve a vault address to its AccessManager, with typed guards. An
189
187
  eager resolver: it reads on ``ctx`` and returns a context-free wrapper.
190
188
 
@@ -195,7 +193,7 @@ def resolve_access_manager(
195
193
  (InsufficientDataBytes) or a revert (ContractLogicError).
196
194
  Provider/transport errors propagate unchanged.
197
195
  """
198
- checksum = Web3.to_checksum_address(vault_address)
196
+ checksum = Web3.to_checksum_address(vault)
199
197
  # Same block as the eth_call below, or a pre-deployment pin would pass
200
198
  # this guard and get misdiagnosed as "not a vault".
201
199
  code = ctx.web3.eth.get_code(checksum, block_identifier=ctx.default_block)
@@ -21,6 +21,7 @@ from web3.providers.rpc.utils import (
21
21
  from web3.types import (
22
22
  BlockIdentifier,
23
23
  LogReceipt,
24
+ MakeBatchRequestFn,
24
25
  MakeRequestFn,
25
26
  RPCEndpoint,
26
27
  RPCResponse,
@@ -192,7 +193,14 @@ class Web3Context:
192
193
  # and leaves every other block untouched.
193
194
  web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
194
195
  if max_concurrent_requests is not None:
195
- web3.middleware_onion.add(_in_flight_limit(max_concurrent_requests))
196
+ # Innermost (layer 0, injected last): a slot covers only the provider
197
+ # call, so a middleware that issues its own request (the gas price
198
+ # strategy reads a block) never waits on the slot it already holds.
199
+ web3.middleware_onion.inject(
200
+ _in_flight_limit(max_concurrent_requests),
201
+ name="in_flight_limit",
202
+ layer=0,
203
+ )
196
204
  chain_id = ChainId(web3.eth.chain_id)
197
205
 
198
206
  return cls(
@@ -411,7 +419,8 @@ class Web3Context:
411
419
 
412
420
  def _in_flight_limit(limit: int) -> type[Web3Middleware]:
413
421
  """A middleware class (web3 instantiates it per Web3) holding a semaphore
414
- slot for the duration of each provider request."""
422
+ slot for the duration of each provider call; a JSON-RPC batch is one HTTP
423
+ request, so it takes one slot whatever its item count."""
415
424
  if limit < 1:
416
425
  raise ValueError(f"max_concurrent_requests must be at least 1, got {limit}")
417
426
  slots = threading.BoundedSemaphore(limit)
@@ -424,4 +433,15 @@ def _in_flight_limit(limit: int) -> type[Web3Middleware]:
424
433
 
425
434
  return limited
426
435
 
436
+ def wrap_make_batch_request(
437
+ self, make_batch_request: MakeBatchRequestFn
438
+ ) -> MakeBatchRequestFn:
439
+ def limited(
440
+ requests_info: list[tuple[RPCEndpoint, Any]],
441
+ ) -> list[RPCResponse] | RPCResponse:
442
+ with slots:
443
+ return make_batch_request(requests_info)
444
+
445
+ return limited
446
+
427
447
  return InFlightLimit
@@ -234,7 +234,7 @@ def _snapshot_block(ctx: Web3Context) -> tuple[BlockIdentifier, int]:
234
234
 
235
235
 
236
236
  def read_external_state(
237
- ctx: Web3Context, vault_address: PlasmaVaultAddress
237
+ ctx: Web3Context, vault: PlasmaVaultAddress
238
238
  ) -> ExternalStateVaultState:
239
239
  """Read the external-state market's vault state as one coherent snapshot.
240
240
 
@@ -249,19 +249,19 @@ def read_external_state(
249
249
  executor word is ``None`` (no executor deployed), which is a state, while
250
250
  an RPC or decoding failure raises.
251
251
  """
252
- vault = Web3.to_checksum_address(vault_address)
252
+ checksum = Web3.to_checksum_address(vault)
253
253
  read_at, block = _snapshot_block(ctx)
254
254
  base = ExternalStateExecutor._EXECUTOR_STORAGE_SLOT
255
255
  words = [
256
- _storage_word(ctx.get_storage_at(vault, base + i, read_at)) for i in range(4)
256
+ _storage_word(ctx.get_storage_at(checksum, base + i, read_at)) for i in range(4)
257
257
  ]
258
258
  if words[0] >> 160:
259
259
  raise ExternalStateError(
260
- f"malformed executor word in the external-state storage of {vault}"
260
+ f"malformed executor word in the external-state storage of {checksum}"
261
261
  )
262
262
  if words[3] not in (0, 1):
263
263
  raise ExternalStateError(
264
- f"malformed pause flag in the external-state storage of {vault}"
264
+ f"malformed pause flag in the external-state storage of {checksum}"
265
265
  )
266
266
  return ExternalStateVaultState(
267
267
  executor=Web3.to_checksum_address(f"0x{words[0]:040x}") if words[0] else None,
@@ -321,7 +321,7 @@ class ExternalStateExecutor(ContractWrapper):
321
321
 
322
322
  @classmethod
323
323
  def for_vault(
324
- cls, ctx: Web3Context, vault_address: PlasmaVaultAddress
324
+ cls, ctx: Web3Context, vault: PlasmaVaultAddress
325
325
  ) -> ExternalStateExecutor:
326
326
  """Resolve the per-vault executor from vault storage (at
327
327
  ``ctx.default_block``) and wrap it. An eager resolver: the wrapper it
@@ -334,17 +334,16 @@ class ExternalStateExecutor(ContractWrapper):
334
334
  """
335
335
  word = _storage_word(
336
336
  ctx.get_storage_at(
337
- Web3.to_checksum_address(vault_address), cls._EXECUTOR_STORAGE_SLOT
337
+ Web3.to_checksum_address(vault), cls._EXECUTOR_STORAGE_SLOT
338
338
  )
339
339
  )
340
340
  if word >> 160:
341
341
  raise ExternalStateError(
342
- f"malformed executor word in the external-state storage of "
343
- f"{vault_address}"
342
+ f"malformed executor word in the external-state storage of {vault}"
344
343
  )
345
344
  if word == 0:
346
345
  raise ExternalStateError(
347
- f"no ExternalStateExecutor deployed for vault {vault_address}"
346
+ f"no ExternalStateExecutor deployed for vault {vault}"
348
347
  )
349
348
  return cls(Web3.to_checksum_address(f"0x{word:040x}"))
350
349
 
@@ -245,7 +245,7 @@ def vault_validate(
245
245
  # Same cheap probe as vault_info: "not a vault" fails before the fetch.
246
246
  resolve_access_manager(ctx, checksum)
247
247
 
248
- validation = validate_vault(ctx, checksum, block=effective_block)
248
+ validation = validate_vault(ctx, checksum, block=effective_block, metadata=True)
249
249
  return VaultValidationResponse.from_validation(
250
250
  validation, CHAIN_NAMES.get(chain_id, str(chain_id))
251
251
  )
@@ -575,7 +575,7 @@ def _perp_leg(
575
575
 
576
576
  def read_hypercore_nav(
577
577
  ctx: Web3Context,
578
- vault_address: PlasmaVaultAddress,
578
+ vault: PlasmaVaultAddress,
579
579
  market_id: MarketId = HYPERCORE_MARKET,
580
580
  ) -> HyperCoreNav:
581
581
  """Reproduce ``HyperCoreValuationLib.liveValueWad(market_id)`` for the
@@ -589,17 +589,17 @@ def read_hypercore_nav(
589
589
  library reverts on (no price, overflow, a failed precompile) raise
590
590
  :class:`~ipor_fusion.errors.HyperCoreValuationError`.
591
591
  """
592
- vault = PlasmaVault(vault_address)
592
+ pv = PlasmaVault(vault)
593
593
  reader = HyperCoreReader()
594
594
  multicall = Multicall3(ctx)
595
595
  substrates, oracle_address, exists, native = multicall.aggregate(
596
596
  cast(
597
597
  list[Call[Any]],
598
598
  [
599
- vault.get_market_substrates(market_id),
600
- vault.get_price_oracle_middleware_address(),
601
- reader.core_user_exists(vault_address),
602
- reader.account_margin_summary(NATIVE_PERP_DEX, vault_address),
599
+ pv.get_market_substrates(market_id),
600
+ pv.get_price_oracle_middleware_address(),
601
+ reader.core_user_exists(vault),
602
+ reader.account_margin_summary(NATIVE_PERP_DEX, vault),
603
603
  ],
604
604
  )
605
605
  )
@@ -611,11 +611,11 @@ def read_hypercore_nav(
611
611
  calls: list[Call[Any]] = []
612
612
  for index, evm_asset in tokens:
613
613
  calls += [
614
- reader.spot_balance(vault_address, index),
614
+ reader.spot_balance(vault, index),
615
615
  reader.token_info(index),
616
616
  oracle.get_asset_price(evm_asset),
617
617
  ]
618
- calls += [reader.account_margin_summary(dex, vault_address) for dex in dexes]
618
+ calls += [reader.account_margin_summary(dex, vault) for dex in dexes]
619
619
  results = multicall.try_aggregate(calls)
620
620
  spot = tuple(
621
621
  _spot_leg(index, evm_asset, *results[3 * i : 3 * i + 3])
@@ -685,7 +685,7 @@ def _granted_perp_markets(
685
685
 
686
686
  def read_hypercore_vault_state(
687
687
  ctx: Web3Context,
688
- vault_address: PlasmaVaultAddress,
688
+ vault: PlasmaVaultAddress,
689
689
  balance_fuse: FuseAddress,
690
690
  market_id: MarketId = HYPERCORE_MARKET,
691
691
  *,
@@ -695,14 +695,14 @@ def read_hypercore_vault_state(
695
695
  the vault's context, the pending state (when a reader is known for the
696
696
  chain or given) and the granted perp markets with their coin names, all
697
697
  at ``ctx.default_block``."""
698
- nav = read_hypercore_nav(ctx, vault_address, market_id)
699
- vault = PlasmaVault(vault_address)
698
+ nav = read_hypercore_nav(ctx, vault, market_id)
699
+ pv = PlasmaVault(vault)
700
700
  reader = HyperCoreReader()
701
701
  pending_reader = pending_reader or HYPERCORE_PENDING_READERS.get(ctx.chain_id)
702
- granted = _granted_perp_markets(vault.get_market_substrates(market_id).call(ctx))
703
- calls: list[Call[Any]] = [vault.balance_fuse_value(balance_fuse)]
702
+ granted = _granted_perp_markets(pv.get_market_substrates(market_id).call(ctx))
703
+ calls: list[Call[Any]] = [pv.balance_fuse_value(balance_fuse)]
704
704
  if pending_reader is not None:
705
- calls.append(HyperCorePendingReader(vault, pending_reader).pending_state())
705
+ calls.append(HyperCorePendingReader(pv, pending_reader).pending_state())
706
706
  calls += [reader.perp_asset_info(read_index_of(asset)) for asset, _, _ in granted]
707
707
  results = Multicall3(ctx).try_aggregate(calls)
708
708
  fuse_value = results[0]
@@ -110,7 +110,7 @@ def _pinned_context(ctx: Web3Context) -> tuple[Web3Context, int]:
110
110
 
111
111
  def read_hypercore_transfer_snapshot(
112
112
  ctx: Web3Context,
113
- vault_address: PlasmaVaultAddress,
113
+ vault: PlasmaVaultAddress,
114
114
  evm_token: ChecksumAddress,
115
115
  *,
116
116
  token_index: HyperCoreTokenIndex = USDC_TOKEN_INDEX,
@@ -128,21 +128,21 @@ def read_hypercore_transfer_snapshot(
128
128
  raise UnsupportedChainError(
129
129
  f"no HyperCorePendingReader known for chain {ctx.chain_id}"
130
130
  )
131
- vault = PlasmaVault(vault_address)
131
+ pv = PlasmaVault(vault)
132
132
  reader = HyperCoreReader()
133
133
  calls = cast(
134
134
  list[Call[Any]],
135
135
  [
136
- reader.core_user_exists(vault_address),
137
- reader.spot_balance(vault_address, token_index),
138
- ERC20(evm_token).balance_of(vault_address),
139
- HyperCorePendingReader(vault, pending_reader).pending_state(),
136
+ reader.core_user_exists(vault),
137
+ reader.spot_balance(vault, token_index),
138
+ ERC20(evm_token).balance_of(vault),
139
+ HyperCorePendingReader(pv, pending_reader).pending_state(),
140
140
  ],
141
141
  )
142
142
  exists, spot, evm_balance, pending = Multicall3(pinned).aggregate(calls)
143
143
  return HyperCoreTransferSnapshot(
144
144
  block_number=block,
145
- vault=vault_address,
145
+ vault=vault,
146
146
  token_index=token_index,
147
147
  evm_token=evm_token,
148
148
  action_nonce=pending.action_nonce,
@@ -258,26 +258,26 @@ def read_hypercore_transfer_outcome(
258
258
 
259
259
 
260
260
  def read_hypercore_evm_exit_ceiling(
261
- ctx: Web3Context, vault_address: PlasmaVaultAddress, holder: ChecksumAddress
261
+ ctx: Web3Context, vault: PlasmaVaultAddress, holder: ChecksumAddress
262
262
  ) -> HyperCoreEvmExitCeiling:
263
263
  """Read the holder's upper bound for an immediate EVM underlying exit.
264
264
 
265
265
  This uses idle ERC-20 only and is a ceiling, not an executable quote.
266
266
  """
267
267
  pinned, _ = _pinned_context(ctx)
268
- vault = PlasmaVault(vault_address)
268
+ pv = PlasmaVault(vault)
269
269
  shares, asset = Multicall3(pinned).aggregate(
270
270
  cast(
271
271
  list[Call[Any]],
272
- [vault.balance_of(holder), vault.underlying_asset_address()],
272
+ [pv.balance_of(holder), pv.underlying_asset_address()],
273
273
  )
274
274
  )
275
275
  share_assets, idle = Multicall3(pinned).aggregate(
276
276
  cast(
277
277
  list[Call[Any]],
278
278
  [
279
- vault.convert_to_assets(shares),
280
- ERC20(asset).balance_of(vault_address),
279
+ pv.convert_to_assets(shares),
280
+ ERC20(asset).balance_of(vault),
281
281
  ],
282
282
  )
283
283
  )
@@ -392,7 +392,7 @@ def _collect_lending_markets(
392
392
 
393
393
  def fetch_vault_lending_health(
394
394
  ctx: Web3Context,
395
- vault_address: PlasmaVaultAddress,
395
+ vault: PlasmaVaultAddress,
396
396
  *,
397
397
  balance_fuse_market_ids: list[MarketId],
398
398
  market_substrates: dict[MarketId, list[bytes]],
@@ -402,7 +402,7 @@ def fetch_vault_lending_health(
402
402
 
403
403
  Args:
404
404
  ctx: Web3 context for on-chain calls.
405
- vault_address: The Plasma Vault address.
405
+ vault: The Plasma Vault address.
406
406
  balance_fuse_market_ids: List of market IDs from balance fuses.
407
407
  market_substrates: Map of market_id -> list of raw substrate bytes.
408
408
  Morpho market ids and Aave V4 Spokes are read from it.
@@ -434,7 +434,7 @@ def fetch_vault_lending_health(
434
434
  ctx,
435
435
  morpho_reader,
436
436
  morpho_mid,
437
- vault_address,
437
+ vault,
438
438
  ipor_mid,
439
439
  name,
440
440
  )
@@ -447,7 +447,7 @@ def fetch_vault_lending_health(
447
447
  _compute_aave_market_health,
448
448
  ctx,
449
449
  AaveV3Reader(aave_pool),
450
- vault_address,
450
+ vault,
451
451
  ipor_mid,
452
452
  name,
453
453
  )
@@ -459,7 +459,7 @@ def fetch_vault_lending_health(
459
459
  _compute_aave_v4_market_health,
460
460
  ctx,
461
461
  spoke,
462
- vault_address,
462
+ vault,
463
463
  ipor_mid,
464
464
  name,
465
465
  )
@@ -427,23 +427,23 @@ def _enumerate_assets(
427
427
 
428
428
  def build_oracle_mapping(
429
429
  ctx: Web3Context,
430
- vault_address: PlasmaVaultAddress,
430
+ vault: PlasmaVaultAddress,
431
431
  effective_block: int,
432
432
  max_depth: int = 6,
433
433
  ) -> OracleMapping:
434
- """Build the full oracle mapping for ``vault_address``.
434
+ """Build the full oracle mapping for ``vault``.
435
435
 
436
436
  ``ctx`` must already be pinned to ``effective_block`` (the caller sets
437
437
  ``ctx.default_block``); the block is echoed in the output. Raises
438
438
  `UnsupportedVaultVersionError` for a vault that exposes no price-oracle
439
439
  getter.
440
440
  """
441
- vault = PlasmaVault(vault_address)
442
- oracle_addr = vault.read_price_oracle_address(ctx)
443
- asset_addr = vault.underlying_asset_address().call(ctx)
441
+ pv = PlasmaVault(vault)
442
+ oracle_addr = pv.read_price_oracle_address(ctx)
443
+ asset_addr = pv.underlying_asset_address().call(ctx)
444
444
 
445
445
  try:
446
- vault_name = vault.name().call(ctx)
446
+ vault_name = pv.name().call(ctx)
447
447
  except Exception:
448
448
  vault_name = None
449
449
 
@@ -468,7 +468,7 @@ def build_oracle_mapping(
468
468
  )
469
469
 
470
470
  return OracleMapping(
471
- vault=vault_address,
471
+ vault=vault,
472
472
  vault_name=vault_name,
473
473
  asset=OracleAsset(
474
474
  address=asset_addr,
@@ -120,7 +120,7 @@ class VaultState(Protocol):
120
120
  @property
121
121
  def underlying_balance_on_vault(self) -> Amount: ...
122
122
  @property
123
- def price_oracle_addr(self) -> ChecksumAddress: ...
123
+ def price_oracle(self) -> ChecksumAddress: ...
124
124
  @property
125
125
  def balance_fuses(self) -> Sequence[BalanceFuse]: ...
126
126
  @property
@@ -202,10 +202,10 @@ class VaultValidation:
202
202
  vault: PlasmaVaultAddress
203
203
  chain_id: ChainId
204
204
  block_number: int
205
- block_timestamp: int
205
+ block_timestamp: Timestamp
206
206
  asset: ChecksumAddress
207
207
  asset_symbol: str
208
- asset_decimals: int
208
+ asset_decimals: Decimals
209
209
  asset_price_usd: float | None
210
210
  findings: tuple[Finding, ...]
211
211
  reconciliation: Reconciliation
@@ -236,15 +236,20 @@ class VaultValidation:
236
236
 
237
237
  def validate_vault(
238
238
  ctx: Web3Context,
239
- vault_address: PlasmaVaultAddress,
239
+ vault: PlasmaVaultAddress,
240
240
  *,
241
241
  block: int | None = None,
242
+ metadata: bool = False,
242
243
  ) -> VaultValidation:
243
244
  """Fetch the vault's snapshot at one block (`fetch_vault_data`: ``block``,
244
245
  else the context's numeric default block, else the latest) and validate
245
- it; ``ctx`` itself is not modified."""
246
+ it; ``ctx`` itself is not modified.
247
+
248
+ The checks need no token metadata, so none is read by default and the
249
+ ERC20 holdings are named ``"?"``; ``metadata=True`` adds one batched,
250
+ cached symbol read that names them in the holdings and findings."""
246
251
  return validate_vault_state(
247
- fetch_vault_data(ctx, vault_address, block=block, metadata=False)
252
+ fetch_vault_data(ctx, vault, block=block, metadata=metadata)
248
253
  )
249
254
 
250
255