ipor-fusion 4.0.0.dev63__tar.gz → 4.0.0.dev66__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 (291) hide show
  1. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/MIGRATION_4.md +12 -4
  2. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/PKG-INFO +1 -1
  3. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/pyproject.toml +1 -1
  4. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/vault_info_view.py +2 -2
  5. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/external_state_events.py +6 -1
  6. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/mcp/server.py +2 -2
  7. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/substrates.py +21 -16
  8. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/types.py +16 -9
  9. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_vault_info_golden.py +2 -2
  10. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_external_state_events.py +7 -0
  11. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_types_and_events.py +16 -5
  12. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/typed_client/consumer.py +1 -1
  13. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.claude-plugin/marketplace.json +0 -0
  14. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.claude-plugin/plugin.json +0 -0
  15. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.env.example +0 -0
  16. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.gemini/settings.json +0 -0
  17. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.git-blame-ignore-revs +0 -0
  18. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.github/workflows/cd.yml +0 -0
  19. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.github/workflows/ci.yml +0 -0
  20. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.github/workflows/commit-lint.yml +0 -0
  21. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.github/workflows/notify-slack.yml +0 -0
  22. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.github/workflows/python-build.yml +0 -0
  23. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.github/workflows/python-publish.yml +0 -0
  24. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.github/workflows/release.yml +0 -0
  25. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.github/workflows/report-slack.yml +0 -0
  26. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.gitignore +0 -0
  27. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.mcp.json +0 -0
  28. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.pre-commit-config.yaml +0 -0
  29. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/.python-version +0 -0
  30. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/AGENTS.md +0 -0
  31. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/CHANGELOG.md +0 -0
  32. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/CLAUDE.md +0 -0
  33. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/CODEOWNERS +0 -0
  34. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/LICENSE +0 -0
  35. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/README.md +0 -0
  36. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/build.sh +0 -0
  37. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/examples/CONTRIBUTING.md +0 -0
  38. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/examples/README.md +0 -0
  39. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  40. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  41. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  42. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  43. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/examples/external_state_margin_leg_base.py +0 -0
  44. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/examples/simple_aave_v3_supply_base.py +0 -0
  45. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/pytest.ini +0 -0
  46. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/scripts/commit-check.sh +0 -0
  47. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/scripts/crosschain_readiness.py +0 -0
  48. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/skills/ipor-deploy-vault/SKILL.md +0 -0
  49. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/__init__.py +0 -0
  50. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/about.py +0 -0
  51. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cache.py +0 -0
  52. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/chains.py +0 -0
  53. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/__init__.py +0 -0
  54. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  55. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/config_cmd.py +0 -0
  56. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/config_store.py +0 -0
  57. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/main.py +0 -0
  58. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/market_cmd.py +0 -0
  59. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/options.py +0 -0
  60. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  61. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  62. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/vault_health.py +0 -0
  63. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  64. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  65. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/config/__init__.py +0 -0
  66. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/config/roles.py +0 -0
  67. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/__init__.py +0 -0
  68. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/access.py +0 -0
  69. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/address.py +0 -0
  70. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/context.py +0 -0
  71. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/contract.py +0 -0
  72. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/erc20.py +0 -0
  73. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/external_state_executor.py +0 -0
  74. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/fee_manager.py +0 -0
  75. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/fuse_action.py +0 -0
  76. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/fusion_factory.py +0 -0
  77. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/logs.py +0 -0
  78. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/multicall.py +0 -0
  79. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/oracle.py +0 -0
  80. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/plasma_vault.py +0 -0
  81. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/rewards_manager.py +0 -0
  82. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/simulation.py +0 -0
  83. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  84. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/__init__.py +0 -0
  85. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  86. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  87. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  88. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  89. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  90. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  91. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  92. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  93. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  94. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/contracts.py +0 -0
  95. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/discovery.py +0 -0
  96. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/errors.py +0 -0
  97. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/lane.py +0 -0
  98. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/logs.py +0 -0
  99. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/messages.py +0 -0
  100. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/simulation.py +0 -0
  101. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  102. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  103. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  104. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  105. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  106. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/crosschain/transport.py +0 -0
  107. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/dependency_graph.py +0 -0
  108. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/entry_cli.py +0 -0
  109. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/entry_mcp.py +0 -0
  110. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/errors.py +0 -0
  111. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/explorer.py +0 -0
  112. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/field_docs.py +0 -0
  113. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/__init__.py +0 -0
  114. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  115. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/async_action.py +0 -0
  116. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/base.py +0 -0
  117. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  118. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
  119. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
  120. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
  121. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
  122. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/erc4626.py +0 -0
  123. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  124. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/events.py +0 -0
  125. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/external_state.py +0 -0
  126. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  127. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  128. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/hypercore.py +0 -0
  129. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  130. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/merkl.py +0 -0
  131. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/morpho.py +0 -0
  132. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  133. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  134. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/fuses/universal.py +0 -0
  135. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/guide/__init__.py +0 -0
  136. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/guide/architecture.md +0 -0
  137. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/guide/glossary.md +0 -0
  138. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/guide/invariants.md +0 -0
  139. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/guide/quickstart.md +0 -0
  140. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/market_ids.py +0 -0
  141. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/market_info.py +0 -0
  142. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/mcp/__init__.py +0 -0
  143. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/mcp/guide.py +0 -0
  144. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/mcp/models.py +0 -0
  145. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/morpho_api.py +0 -0
  146. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/py.typed +0 -0
  147. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/__init__.py +0 -0
  148. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/aave_v3.py +0 -0
  149. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/aave_v4.py +0 -0
  150. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/compound_v3.py +0 -0
  151. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/hypercore.py +0 -0
  152. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  153. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/lending_health.py +0 -0
  154. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/morpho.py +0 -0
  155. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  156. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  157. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  158. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/position_manager.py +0 -0
  159. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  160. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  161. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/readers/unpriceable.py +0 -0
  162. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/validation.py +0 -0
  163. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/vault_data.py +0 -0
  164. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/vault_info.py +1 -1
  165. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/vault_managers.py +0 -0
  166. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/vault_positions.py +0 -0
  167. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/src/ipor_fusion/vault_reads.py +0 -0
  168. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/UniswapV3UniversalRouter.py +0 -0
  169. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_crosschain.py +0 -0
  170. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_crosschain_lifecycle.py +0 -0
  171. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_crosschain_recovery.py +0 -0
  172. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_euler_v2.py +0 -0
  173. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_foundry.py +0 -0
  174. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_guide.py +0 -0
  175. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_hypercore_model.py +0 -0
  176. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_hypercore_shadow.py +0 -0
  177. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_multicall.py +0 -0
  178. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_simulate.py +0 -0
  179. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/_validation.py +0 -0
  180. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/addresses.py +0 -0
  181. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/conftest.py +0 -0
  182. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/constants.py +0 -0
  183. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  184. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/fixtures/hypercore_market55.json +0 -0
  185. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/fixtures/vault_info_golden/clean.json +0 -0
  186. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  187. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/fixtures/vault_info_golden/rich.json +0 -0
  188. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  189. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_about.py +0 -0
  190. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_access_manager.py +0 -0
  191. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cache.py +0 -0
  192. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_chains.py +0 -0
  193. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_changelog.py +0 -0
  194. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_commands.py +0 -0
  195. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_config_store.py +0 -0
  196. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_helpers.py +0 -0
  197. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_hypercore.py +0 -0
  198. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_lending_health.py +0 -0
  199. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_market_morpho_blue.py +0 -0
  200. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_oracle_mapping.py +0 -0
  201. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_pending_requests_scan.py +0 -0
  202. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_role_accounts.py +0 -0
  203. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_vault_predates_getters.py +0 -0
  204. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_cli_vault_validate.py +0 -0
  205. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_contract_call.py +0 -0
  206. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_contract_encoder.py +0 -0
  207. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_ccip_events.py +0 -0
  208. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_ccip_indexer.py +0 -0
  209. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_ccip_quote.py +0 -0
  210. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_chainlink.py +0 -0
  211. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_contracts.py +0 -0
  212. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_encoding.py +0 -0
  213. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_errors.py +0 -0
  214. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_lane.py +0 -0
  215. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_layering.py +0 -0
  216. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_readiness.py +0 -0
  217. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_readiness_report.py +0 -0
  218. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_crosschain_transport.py +0 -0
  219. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_erc20.py +0 -0
  220. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_examples_composed_hypercore.py +0 -0
  221. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_examples_crosschain.py +0 -0
  222. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  223. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_examples_vaults.py +0 -0
  224. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_explorer.py +0 -0
  225. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_external_state_executor.py +0 -0
  226. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_fee_manager.py +0 -0
  227. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_fuse_encoding.py +0 -0
  228. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_guide.py +0 -0
  229. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_hypercore_deployment.py +0 -0
  230. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_hypercore_events.py +0 -0
  231. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_hypercore_flow.py +0 -0
  232. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_hypercore_live.py +0 -0
  233. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_hypercore_outcome.py +0 -0
  234. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_hypercore_readers.py +0 -0
  235. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_hypercore_simulation.py +0 -0
  236. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_logs.py +0 -0
  237. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_mcp_hypercore.py +0 -0
  238. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_mcp_models.py +0 -0
  239. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_mcp_server.py +0 -0
  240. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_mcp_vault_validate.py +0 -0
  241. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_multicall.py +0 -0
  242. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_oracle.py +0 -0
  243. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_oracle_mapping.py +0 -0
  244. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_plasma_vault.py +0 -0
  245. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_reader_encoding.py +0 -0
  246. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_readiness_selection.py +0 -0
  247. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_revert_decoding.py +0 -0
  248. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_rewards_manager.py +0 -0
  249. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_roles.py +0 -0
  250. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_sdk_layering.py +0 -0
  251. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  252. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  253. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  254. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_crosschain_canary.py +0 -0
  255. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_crosschain_hypercore.py +0 -0
  256. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  257. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  258. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  259. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  260. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  261. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  262. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  263. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_examples_arbitrum.py +0 -0
  264. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_examples_base.py +0 -0
  265. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_examples_crosschain.py +0 -0
  266. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_external_state_executor_base.py +0 -0
  267. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  268. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  269. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  270. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_morpho_blue.py +0 -0
  271. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_pilot_v3.py +0 -0
  272. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  273. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  274. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  275. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  276. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_readers_integration.py +0 -0
  277. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_release_funds.py +0 -0
  278. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_swap_on_base.py +0 -0
  279. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  280. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_solidity_compiler.py +0 -0
  281. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_solidity_mirrors.py +0 -0
  282. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_substrates.py +0 -0
  283. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_unpriceable_tokens.py +0 -0
  284. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_validation.py +0 -0
  285. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_vault_dep_graph.py +0 -0
  286. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_vault_info_onchain_base.py +0 -0
  287. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  288. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_vault_simulator_blocks.py +0 -0
  289. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_web3_context.py +0 -0
  290. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/tests/test_withdraw_manager.py +0 -0
  291. {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev66}/uv.lock +0 -0
@@ -29,15 +29,22 @@ widths or address spelling.
29
29
  | `RoleId` | a `Roles` member (accepted as is), or `RoleId(n)`; `Roles.resolve(name)` returns one |
30
30
  | `Decimals`, `Period`, `Amount`, ... | `Decimals(8)`, `Period(0)`, `Amount(n)` (unchanged) |
31
31
  | `ChecksumAddress` | `Web3.to_checksum_address(value)` |
32
- | `HexAddress` | any `ChecksumAddress`, or `parse_hex_address(value)` (20 bytes or hex in any spelling → lowercase `0x` form) |
32
+ | `HexAddress` | any `ChecksumAddress`, or `parse_hex_address(value)` (20 bytes or `0x`/`0X`-prefixed hex in any case → lowercase `0x` form) |
33
33
  | `MorphoBlueMarketId` | `parse_morpho_market_id(value)`: 32 bytes or hex in any spelling, always `0x` + 64 lowercase digits |
34
- | `TxHash` | `parse_tx_hash(value)` (32 bytes or hex in any spelling) |
34
+ | `TxHash` | `parse_tx_hash(value)` (32 bytes or `0x`/`0X`-prefixed hex in any case) |
35
35
  | `PlasmaVaultAddress`, `FuseAddress`, `AccessManagerAddress` | from the SDK (`PlasmaVault.address`, `get_fuses()`, `get_access_manager_address()`, `FusionInstance`, discovery), or `parse_plasma_vault_address` / `parse_fuse_address` / `parse_access_manager_address(value)` for a raw address; they label the role and check nothing on chain |
36
36
  | crosschain ids | `CommandId(b)`, `MessageId(b)`, `AssetId(b)`, `OperationId(b)`, `TransferId(b)`, `ReturnRequestId(b)`, `PositionSetHash(b)` (bytes32); `ProposalId(n)`, `DeploymentRequestId(n)`, `StargateAssetId(n)` (int) |
37
37
  | HyperCore ids | `HyperCoreTokenIndex(n)` (or `USDC_TOKEN_INDEX`), `HyperCoreAsset(n)` (or `hip3_action_asset(dex, index)`), `HyperCoreOrderId(n)`, `HyperCoreClientOrderId(n)`, `MarginTableId(n)`, `HyperCoreActionId(n)`, `PayloadHash(b)` |
38
38
  | external state | `ProposalHash(b)` |
39
39
  | `BlockIdentifier` | a block number, `"latest"` / `"pending"` / ..., or a block hash |
40
40
 
41
+ The address and hash parsers (`parse_hex_address`, `parse_tx_hash`,
42
+ `parse_plasma_vault_address`, `parse_fuse_address`, `parse_access_manager_address`)
43
+ validate strictly: a string needs exactly one `0x`/`0X` prefix and the exact number of
44
+ hex digits, with no surrounding whitespace; anything else raises `ValueError`. Strip
45
+ user input yourself before parsing it. `parse_morpho_market_id` still accepts hex
46
+ without `0x`.
47
+
41
48
  `MessageId` covers both a CCIP `messageId` and a LayerZero `guid`. `AssetId` is the
42
49
  crosschain executor's `bytes32` asset id; a Stargate pool's `uint16` asset id is
43
50
  `StargateAssetId`. `DeploymentRequestId` is a factory's `uint256` dispatcher request;
@@ -49,8 +56,9 @@ a `bytes32` return (recall) request is `ReturnRequestId`.
49
56
  `build_vault_info_json`, `ensure_supported_chain` and `fetch_vault_lending_health`
50
57
  take `ChainId`. The vault is a `PlasmaVaultAddress` (see *Address roles*) in
51
58
  `validate_vault` and `fetch_deployment_info`; `build_vault_info_json(ctx, plasma_vault,
52
- data, chain_id, chain_label)` takes no address and labels the document with
53
- `plasma_vault.address`. `fetch_unpriceable_priced_tokens` takes the oracle and
59
+ data, *, chain_id, chain_label)` takes no address, takes the chain by keyword, and
60
+ labels the document with `plasma_vault.address` (a test that mocks `PlasmaVault`
61
+ sets `.address`). `fetch_unpriceable_priced_tokens` takes the oracle and
54
62
  underlying as `ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
55
63
  `get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
56
64
  fits), and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev63
3
+ Version: 4.0.0.dev66
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.dev63"
3
+ version = "4.0.0.dev66"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -98,8 +98,8 @@ def _print_vault_info(
98
98
  ctx,
99
99
  plasma_vault,
100
100
  data,
101
- chain_id,
102
- chain_label,
101
+ chain_id=chain_id,
102
+ chain_label=chain_label,
103
103
  resolve_name=partial(get_contract_name, chain_id, api_key=api_key),
104
104
  )
105
105
  click.echo(json.dumps(result, indent=2))
@@ -196,7 +196,12 @@ def external_state_event_name(topic: bytes | str) -> str | None:
196
196
  def _hex(value: Any) -> TxHash | None:
197
197
  if value is None:
198
198
  return None
199
- return parse_tx_hash(_as_bytes(value, "transactionHash"))
199
+ raw = _as_bytes(value, "transactionHash")
200
+ if len(raw) != 32:
201
+ raise ExternalStateEventDecodeError(
202
+ f"transactionHash: {len(raw)} bytes, expected 32"
203
+ )
204
+ return parse_tx_hash(raw)
200
205
 
201
206
 
202
207
  def decode_external_state_event(log: Mapping[str, Any]) -> ExternalStateEvent | None:
@@ -216,8 +216,8 @@ def vault_info(
216
216
  ctx,
217
217
  plasma_vault,
218
218
  data,
219
- chain_id,
220
- chain_label,
219
+ chain_id=chain_id,
220
+ chain_label=chain_label,
221
221
  resolve_name=partial(get_contract_name, chain_id, api_key=api_key),
222
222
  )
223
223
  return VaultInfoResponse.model_validate(result)
@@ -47,10 +47,15 @@ class SubstrateInfo:
47
47
  # Slippage variant: [2 type chars][62 value chars]
48
48
 
49
49
 
50
+ def _address(hex_digits: str) -> HexAddress:
51
+ """The address in 40 hex digits sliced out of a substrate word."""
52
+ return parse_hex_address("0x" + hex_digits)
53
+
54
+
50
55
  def _decode_type_lshift160(hex_str: str, types: dict[int, str]) -> SubstrateInfo:
51
56
  """Decode type<<160 | address (Ebisu, Midas, Balancer, Velodrome)."""
52
57
  type_byte = int(hex_str[22:24], 16)
53
- addr = parse_hex_address(hex_str[24:])
58
+ addr = _address(hex_str[24:])
54
59
  label = types.get(type_byte, f"type={type_byte}")
55
60
  return SubstrateInfo(address=addr, type_label=label)
56
61
 
@@ -63,13 +68,13 @@ def _decode_type_lshift248(hex_str: str, types: dict[int, str]) -> SubstrateInfo
63
68
  return SubstrateInfo(
64
69
  raw_hex=f"0x{hex_str}", type_label=label, extra={"slippage": str(value)}
65
70
  )
66
- addr = parse_hex_address(hex_str[24:])
71
+ addr = _address(hex_str[24:])
67
72
  return SubstrateInfo(address=addr, type_label=label)
68
73
 
69
74
 
70
75
  def _decode_plain_address(hex_str: str) -> SubstrateInfo:
71
76
  """Decode zero-padded address: 12 zero bytes + 20 address bytes."""
72
- return SubstrateInfo(address=parse_hex_address(hex_str[24:]))
77
+ return SubstrateInfo(address=_address(hex_str[24:]))
73
78
 
74
79
 
75
80
  def _decode_morpho(hex_str: str) -> SubstrateInfo:
@@ -84,14 +89,14 @@ def _decode_uniswap_v4(hex_str: str) -> SubstrateInfo:
84
89
  substrate-as-asset (12 zero bytes + address)."""
85
90
  if hex_str[:24] == "0" * 24:
86
91
  return SubstrateInfo(
87
- address=parse_hex_address(hex_str[24:]), type_label="uniswap_v4_token"
92
+ address=_address(hex_str[24:]), type_label="uniswap_v4_token"
88
93
  )
89
94
  return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label="uniswap_v4_pool_id")
90
95
 
91
96
 
92
97
  def _decode_enso(hex_str: str) -> SubstrateInfo:
93
98
  """Decode address<<96 | selector<<64 (Enso)."""
94
- addr = parse_hex_address(hex_str[0:40])
99
+ addr = _address(hex_str[0:40])
95
100
  selector = f"0x{hex_str[40:48]}"
96
101
  return SubstrateInfo(
97
102
  address=addr, type_label="ENSO_TARGET", extra={"selector": selector}
@@ -100,7 +105,7 @@ def _decode_enso(hex_str: str) -> SubstrateInfo:
100
105
 
101
106
  def _decode_dolomite(hex_str: str) -> SubstrateInfo:
102
107
  """Decode asset<<96 | subAccountId<<88 | canBorrow<<80 (Dolomite)."""
103
- addr = parse_hex_address(hex_str[0:40])
108
+ addr = _address(hex_str[0:40])
104
109
  sub_account_id = int(hex_str[40:42], 16)
105
110
  can_borrow = (int(hex_str[42:44], 16) & 0x01) == 1
106
111
  return SubstrateInfo(
@@ -127,7 +132,7 @@ def _decode_aave_v4(hex_str: str) -> SubstrateInfo:
127
132
  return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label=label)
128
133
  flags = int(hex_str[50:52], 16)
129
134
  return SubstrateInfo(
130
- address=parse_hex_address(hex_str[2:42]),
135
+ address=_address(hex_str[2:42]),
131
136
  type_label="AAVE_V4_RESERVE",
132
137
  extra={
133
138
  "reserve_id": str(int(hex_str[42:50], 16)),
@@ -145,7 +150,7 @@ def _decode_euler_v2(hex_str: str) -> SubstrateInfo:
145
150
  plain-address path silently produces a malformed address (last 20 bytes are
146
151
  flag bytes + zero padding).
147
152
  """
148
- addr = parse_hex_address(hex_str[0:40])
153
+ addr = _address(hex_str[0:40])
149
154
  is_collateral = (int(hex_str[40:42], 16) & 0x01) == 1
150
155
  can_borrow = (int(hex_str[42:44], 16) & 0x01) == 1
151
156
  sub_account = f"0x{hex_str[44:46]}"
@@ -174,13 +179,13 @@ def _decode_async_action(hex_str: str) -> SubstrateInfo:
174
179
  type_byte = int(hex_str[0:2], 16)
175
180
  if type_byte == 0:
176
181
  return SubstrateInfo(
177
- address=parse_hex_address(hex_str[2:42]),
182
+ address=_address(hex_str[2:42]),
178
183
  type_label="ALLOWED_AMOUNT_TO_OUTSIDE",
179
184
  extra={"amount": str(int(hex_str[42:64], 16))},
180
185
  )
181
186
  if type_byte == 1:
182
187
  return SubstrateInfo(
183
- address=parse_hex_address(hex_str[16:56]),
188
+ address=_address(hex_str[16:56]),
184
189
  type_label="ALLOWED_TARGETS",
185
190
  extra={"selector": f"0x{hex_str[56:64]}"},
186
191
  )
@@ -225,10 +230,10 @@ def _decode_external_state(hex_str: str) -> SubstrateInfo:
225
230
  if type_byte == 0:
226
231
  return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label="UNDEFINED")
227
232
  if label := _EXTERNAL_STATE_ADDRESS_TYPES.get(type_byte):
228
- return SubstrateInfo(address=parse_hex_address(hex_str[24:]), type_label=label)
233
+ return SubstrateInfo(address=_address(hex_str[24:]), type_label=label)
229
234
  if type_byte == 2:
230
235
  return SubstrateInfo(
231
- address=parse_hex_address(hex_str[24:]),
236
+ address=_address(hex_str[24:]),
232
237
  type_label="TARGET",
233
238
  extra={"selector": f"0x{hex_str[16:24]}"},
234
239
  )
@@ -266,7 +271,7 @@ def _decode_crosschain(hex_str: str) -> SubstrateInfo:
266
271
  is_error=True,
267
272
  extra={"chain_id": str(chain_id)},
268
273
  )
269
- info = SubstrateInfo(address=parse_hex_address(hex_str[24:]), type_label=label)
274
+ info = SubstrateInfo(address=_address(hex_str[24:]), type_label=label)
270
275
  if type_byte == 2:
271
276
  info.extra = {"chain_id": str(chain_id)}
272
277
  return info
@@ -326,7 +331,7 @@ def _decode_hypercore(hex_str: str) -> SubstrateInfo:
326
331
  return _hypercore_invalid(hex_str, label)
327
332
  if type_byte == 1:
328
333
  return SubstrateInfo(
329
- address=parse_hex_address(hex_str[24:]),
334
+ address=_address(hex_str[24:]),
330
335
  type_label=label,
331
336
  extra={"token_index": str(int(hex_str[8:24], 16))},
332
337
  )
@@ -347,10 +352,10 @@ def _decode_hypercore(hex_str: str) -> SubstrateInfo:
347
352
  extra["read_index"] = str(read_index_of(asset))
348
353
  return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label=label, extra=extra)
349
354
  if type_byte == 3:
350
- return SubstrateInfo(address=parse_hex_address(hex_str[24:]), type_label=label)
355
+ return SubstrateInfo(address=_address(hex_str[24:]), type_label=label)
351
356
  if type_byte == 4:
352
357
  return SubstrateInfo(
353
- address=parse_hex_address(hex_str[24:]),
358
+ address=_address(hex_str[24:]),
354
359
  type_label=label,
355
360
  extra={"max_fee_rate_decibps": str(int(hex_str[8:24], 16))},
356
361
  )
@@ -145,22 +145,28 @@ def _hex_digits(value: str | bytes, size: int, what: str) -> str:
145
145
  if len(value) != size:
146
146
  raise ValueError(f"invalid {what} (expected {size} bytes): {value!r}")
147
147
  return value.hex()
148
- digits = value.strip().removeprefix("0x").removeprefix("0X").lower()
149
- if len(digits) != 2 * size or any(c not in "0123456789abcdef" for c in digits):
150
- raise ValueError(f"invalid {what} (expected {size}-byte hex): {value}")
148
+ digits = value[2:].lower()
149
+ if (
150
+ value[:2] not in ("0x", "0X")
151
+ or len(digits) != 2 * size
152
+ or any(c not in "0123456789abcdef" for c in digits)
153
+ ):
154
+ raise ValueError(f"invalid {what} (expected 0x and {size}-byte hex): {value!r}")
151
155
  return digits
152
156
 
153
157
 
154
158
  def parse_hex_address(value: str | bytes) -> HexAddress:
155
- """An address as ``0x`` and 40 lowercase hex digits, from 20 raw bytes or hex
156
- in any spelling; raises ``ValueError`` on anything else. For a checksummed
159
+ """An address as ``0x`` and 40 lowercase hex digits, from 20 raw bytes or
160
+ ``0x``-prefixed hex in any case; raises ``ValueError`` on anything else,
161
+ surrounding whitespace included. For a checksummed
157
162
  address use ``Web3.to_checksum_address``."""
158
163
  return HexAddress(HexStr("0x" + _hex_digits(value, 20, "address")))
159
164
 
160
165
 
161
166
  def parse_tx_hash(value: str | bytes) -> TxHash:
162
167
  """A transaction hash as ``0x`` and 64 lowercase hex digits, from 32 raw bytes
163
- or hex in any spelling; raises ``ValueError`` on anything else."""
168
+ or ``0x``-prefixed hex in any case; raises ``ValueError`` on anything else,
169
+ surrounding whitespace included."""
164
170
  return TxHash(HexStr("0x" + _hex_digits(value, 32, "transaction hash")))
165
171
 
166
172
 
@@ -169,9 +175,10 @@ def _checksum(value: str | bytes) -> ChecksumAddress:
169
175
 
170
176
 
171
177
  def parse_plasma_vault_address(value: str | bytes) -> PlasmaVaultAddress:
172
- """A Plasma Vault's address, checksummed from 20 raw bytes or hex in any
173
- spelling; raises ``ValueError`` on anything else. It labels the role and
174
- checks nothing on chain."""
178
+ """A Plasma Vault's address, checksummed from 20 raw bytes or ``0x``/``0X``
179
+ and 40 hex digits in any case; raises ``ValueError`` on anything else,
180
+ surrounding whitespace included. It labels the role and checks nothing on
181
+ chain."""
175
182
  return PlasmaVaultAddress(_checksum(value))
176
183
 
177
184
 
@@ -378,8 +378,8 @@ def test_public_builder_renders_the_cli_json(scenario: str) -> None:
378
378
  ctx,
379
379
  PlasmaVault(ctx, VAULT), # type: ignore[arg-type]
380
380
  data,
381
- 1,
382
- "ethereum",
381
+ chain_id=1,
382
+ chain_label="ethereum",
383
383
  resolve_name=lambda address: _NAMES_BY_ADDRESS.get(address.lower(), ""),
384
384
  )
385
385
  expected = (FIXTURES / f"{scenario}.json").read_text(encoding="utf-8")
@@ -96,6 +96,13 @@ def test_hex_text_data_and_topics_from_an_indexer_decode_too():
96
96
  assert event.transaction_hash == "0x" + "fb" * 32
97
97
 
98
98
 
99
+ def test_a_short_transaction_hash_is_a_decode_error():
100
+ log = _log("ExternalStateAssetRescued", (SIGNER,))
101
+ log["transactionHash"] = bytes(31)
102
+ with pytest.raises(ExternalStateEventDecodeError, match="transactionHash"):
103
+ decode_external_state_event(log)
104
+
105
+
99
106
  def test_an_unknown_topic_is_none_and_a_malformed_listed_one_raises():
100
107
  assert (
101
108
  decode_external_state_event(
@@ -64,23 +64,34 @@ class TestExtractEvents:
64
64
 
65
65
 
66
66
  class TestParsers:
67
- def test_hex_address_is_lowercase_0x_from_any_spelling(self):
67
+ def test_hex_address_is_lowercase_0x_from_either_prefix_case(self):
68
68
  from ipor_fusion import parse_hex_address
69
69
 
70
70
  checksum = "0xAbCdEf0123456789aBcDeF0123456789AbCdEf01"
71
71
  lower = checksum.lower()
72
72
  assert parse_hex_address(checksum) == lower
73
- assert parse_hex_address(lower[2:]) == lower
73
+ assert parse_hex_address("0X" + lower[2:]) == lower
74
74
  assert parse_hex_address(bytes.fromhex(lower[2:])) == lower
75
75
 
76
- def test_tx_hash_is_lowercase_0x_from_any_spelling(self):
76
+ def test_tx_hash_is_lowercase_0x_from_either_prefix_case(self):
77
77
  from ipor_fusion import parse_tx_hash
78
78
 
79
79
  digest = "AB" * 32
80
- assert parse_tx_hash(digest) == "0x" + digest.lower()
80
+ assert parse_tx_hash("0x" + digest) == "0x" + digest.lower()
81
81
  assert parse_tx_hash(bytes.fromhex(digest)) == "0x" + digest.lower()
82
82
 
83
- @pytest.mark.parametrize("value", ["0x1234", "0x" + "zz" * 20, b"\x00" * 19])
83
+ @pytest.mark.parametrize(
84
+ "value",
85
+ [
86
+ "0x1234",
87
+ "0x" + "zz" * 20,
88
+ b"\x00" * 19,
89
+ "ab" * 20,
90
+ " 0x" + "ab" * 20,
91
+ "0x" + "ab" * 20 + "\n",
92
+ "0x0X" + "ab" * 19,
93
+ ],
94
+ )
84
95
  def test_hex_address_rejects_anything_else(self, value):
85
96
  from ipor_fusion import parse_hex_address
86
97
 
@@ -78,7 +78,7 @@ def vault_reads(ctx: Web3Context, vault: PlasmaVault) -> None:
78
78
  ensure_supported_chain(ETHEREUM)
79
79
  data = fetch_vault_data(ctx, vault, chain_id=ETHEREUM)
80
80
  fetch_deployment_info(ctx, ETHEREUM, VAULT, None)
81
- build_vault_info_json(ctx, vault, data, ETHEREUM, "ethereum")
81
+ build_vault_info_json(ctx, vault, data, chain_id=ETHEREUM, chain_label="ethereum")
82
82
  validate_vault(ctx, VAULT)
83
83
  resolve_token_symbol(ctx, VAULT)
84
84
  fetch_unpriceable_priced_tokens(ctx, VAULT, VAULT, [], {}, None)