ipor-fusion 4.0.0.dev255__tar.gz → 4.0.0.dev263__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 (342) hide show
  1. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/AGENTS.md +3 -1
  2. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/PKG-INFO +5 -2
  3. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/README.md +4 -1
  4. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/pyproject.toml +1 -1
  5. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/skills/ipor-deploy-vault/SKILL.md +26 -16
  6. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/__init__.py +8 -0
  7. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/fusion_factory.py +6 -6
  8. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/guide/quickstart.md +26 -16
  9. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/types.py +79 -0
  10. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_guide.py +48 -1
  11. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_public_api.py +8 -0
  12. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_types_and_events.py +117 -1
  13. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.claude-plugin/marketplace.json +0 -0
  14. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.claude-plugin/plugin.json +0 -0
  15. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.env.example +0 -0
  16. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.gemini/settings.json +0 -0
  17. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.git-blame-ignore-revs +0 -0
  18. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.github/workflows/cd.yml +0 -0
  19. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.github/workflows/ci.yml +0 -0
  20. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.github/workflows/commit-lint.yml +0 -0
  21. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.github/workflows/notify-slack.yml +0 -0
  22. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.github/workflows/python-build.yml +0 -0
  23. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.github/workflows/python-publish.yml +0 -0
  24. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.github/workflows/release.yml +0 -0
  25. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.github/workflows/report-slack.yml +0 -0
  26. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.gitignore +0 -0
  27. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.mcp.json +0 -0
  28. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.pre-commit-config.yaml +0 -0
  29. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/.python-version +0 -0
  30. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/CHANGELOG.md +0 -0
  31. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/CLAUDE.md +0 -0
  32. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/CODEOWNERS +0 -0
  33. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/LICENSE +0 -0
  34. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/MIGRATION_4.md +0 -0
  35. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/build.sh +0 -0
  36. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/examples/CONTRIBUTING.md +0 -0
  37. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/examples/README.md +0 -0
  38. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  39. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  40. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  41. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  42. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/examples/external_state_margin_leg_base.py +0 -0
  43. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/examples/simple_aave_v3_supply_base.py +0 -0
  44. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/pytest.ini +0 -0
  45. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/scripts/commit-check.sh +0 -0
  46. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/scripts/crosschain_readiness.py +0 -0
  47. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/about.py +0 -0
  48. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cache.py +0 -0
  49. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/chains.py +0 -0
  50. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/__init__.py +0 -0
  51. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  52. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/config_cmd.py +0 -0
  53. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/config_store.py +0 -0
  54. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/main.py +0 -0
  55. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/market_cmd.py +0 -0
  56. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/options.py +0 -0
  57. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  58. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  59. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/vault_health.py +0 -0
  60. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  61. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  62. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  63. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/config/__init__.py +0 -0
  64. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/config/roles.py +0 -0
  65. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/__init__.py +0 -0
  66. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/access.py +0 -0
  67. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/address.py +0 -0
  68. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/context.py +0 -0
  69. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/contract.py +0 -0
  70. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/erc20.py +0 -0
  71. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/external_state_executor.py +0 -0
  72. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/fee_manager.py +0 -0
  73. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/fuse.py +0 -0
  74. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/fuse_action.py +0 -0
  75. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/logs.py +0 -0
  76. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/multicall.py +0 -0
  77. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/oracle.py +0 -0
  78. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/plasma_vault.py +0 -0
  79. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/rewards_manager.py +0 -0
  80. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/simulation.py +0 -0
  81. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/simulation_result.py +0 -0
  82. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  83. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/__init__.py +0 -0
  84. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  85. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  86. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  87. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  88. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  89. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  90. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  91. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  92. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  93. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/contracts.py +0 -0
  94. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/discovery.py +0 -0
  95. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/errors.py +0 -0
  96. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  97. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  98. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  99. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  100. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/lane.py +0 -0
  101. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/logs.py +0 -0
  102. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/messages.py +0 -0
  103. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/simulation.py +0 -0
  104. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  105. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  106. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  107. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  108. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  109. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/crosschain/transport.py +0 -0
  110. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/dependency_graph.py +0 -0
  111. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/entry_cli.py +0 -0
  112. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/entry_mcp.py +0 -0
  113. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/errors.py +0 -0
  114. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/explorer.py +0 -0
  115. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/field_docs.py +0 -0
  116. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/__init__.py +0 -0
  117. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  118. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/async_action.py +0 -0
  119. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/base.py +0 -0
  120. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  121. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/erc4626.py +0 -0
  122. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  123. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/events.py +0 -0
  124. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/external_state.py +0 -0
  125. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
  126. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  127. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  128. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/hypercore.py +0 -0
  129. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
  130. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/merkl.py +0 -0
  131. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/morpho.py +0 -0
  132. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  133. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  134. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/fuses/universal.py +0 -0
  135. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/guide/__init__.py +0 -0
  136. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/guide/architecture.md +0 -0
  137. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/guide/glossary.md +0 -0
  138. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/guide/invariants.md +0 -0
  139. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/hypercore/__init__.py +0 -0
  140. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/market_ids.py +0 -0
  141. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/market_info.py +0 -0
  142. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/mcp/__init__.py +0 -0
  143. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/mcp/guide.py +0 -0
  144. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/mcp/models.py +0 -0
  145. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/mcp/server.py +0 -0
  146. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/morpho_api.py +0 -0
  147. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/py.typed +0 -0
  148. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/__init__.py +0 -0
  149. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/aave_v3.py +0 -0
  150. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/aave_v4.py +0 -0
  151. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/compound_v3.py +0 -0
  152. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/hypercore.py +0 -0
  153. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  154. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/lending_health.py +0 -0
  155. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/morpho.py +0 -0
  156. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  157. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  158. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  159. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/position_manager.py +0 -0
  160. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  161. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  162. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/readers/unpriceable.py +0 -0
  163. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/substrates.py +0 -0
  164. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/validation.py +0 -0
  165. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/vault_data.py +0 -0
  166. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/vault_erc20.py +0 -0
  167. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/vault_info.py +0 -0
  168. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/vault_managers.py +0 -0
  169. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/vault_positions.py +0 -0
  170. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/src/ipor_fusion/vault_reads.py +0 -0
  171. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/UniswapV3UniversalRouter.py +0 -0
  172. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_crosschain.py +0 -0
  173. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_crosschain_lifecycle.py +0 -0
  174. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_crosschain_recovery.py +0 -0
  175. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_euler_v2.py +0 -0
  176. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_event_args.py +0 -0
  177. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_fake_rpc.py +0 -0
  178. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_foundry.py +0 -0
  179. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_golden.py +0 -0
  180. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_guide.py +0 -0
  181. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_hypercore_model.py +0 -0
  182. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_hypercore_shadow.py +0 -0
  183. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_live.py +0 -0
  184. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_multicall.py +0 -0
  185. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_oracle_mapping_fake.py +0 -0
  186. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_simulate.py +0 -0
  187. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/_validation.py +0 -0
  188. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/addresses.py +0 -0
  189. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/conftest.py +0 -0
  190. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/constants.py +0 -0
  191. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  192. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/hypercore_market55.json +0 -0
  193. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
  194. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
  195. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
  196. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
  197. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
  198. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
  199. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
  200. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
  201. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_blue.json +0 -0
  202. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
  203. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
  204. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
  205. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
  206. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
  207. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
  208. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
  209. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/simulate_v1_request.json +0 -0
  210. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/clean.json +0 -0
  211. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
  212. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  213. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
  214. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
  215. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
  216. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
  217. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/fetched.json +0 -0
  218. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
  219. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/full.json +0 -0
  220. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
  221. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/full.txt +0 -0
  222. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/rich.json +0 -0
  223. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
  224. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  225. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_about.py +0 -0
  226. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_access_manager.py +0 -0
  227. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cache.py +0 -0
  228. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_chains.py +0 -0
  229. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_changelog.py +0 -0
  230. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_commands.py +0 -0
  231. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_config_store.py +0 -0
  232. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_helpers.py +0 -0
  233. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_hypercore.py +0 -0
  234. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_lending_health.py +0 -0
  235. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_market_golden.py +0 -0
  236. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_market_morpho_blue.py +0 -0
  237. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_mcp_parity.py +0 -0
  238. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_oracle_mapping.py +0 -0
  239. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_oracle_mapping_golden.py +0 -0
  240. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_pending_requests_scan.py +0 -0
  241. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_role_accounts.py +0 -0
  242. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_vault_info_golden.py +0 -0
  243. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_vault_predates_getters.py +0 -0
  244. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_cli_vault_validate.py +0 -0
  245. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_contract_call.py +0 -0
  246. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_contract_encoder.py +0 -0
  247. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_ccip_events.py +0 -0
  248. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_ccip_indexer.py +0 -0
  249. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_ccip_quote.py +0 -0
  250. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_chainlink.py +0 -0
  251. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_contracts.py +0 -0
  252. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_encoding.py +0 -0
  253. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_errors.py +0 -0
  254. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_lane.py +0 -0
  255. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_layering.py +0 -0
  256. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_readiness.py +0 -0
  257. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_readiness_report.py +0 -0
  258. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_crosschain_transport.py +0 -0
  259. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_docs_typing.py +0 -0
  260. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_erc20.py +0 -0
  261. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_error_hierarchy.py +0 -0
  262. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_examples_composed_hypercore.py +0 -0
  263. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_examples_crosschain.py +0 -0
  264. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  265. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_examples_vaults.py +0 -0
  266. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_explorer.py +0 -0
  267. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_external_state_events.py +0 -0
  268. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_external_state_executor.py +0 -0
  269. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_fee_manager.py +0 -0
  270. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_fuse_encoding.py +0 -0
  271. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_fusion_instance.py +0 -0
  272. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_hypercore_deployment.py +0 -0
  273. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_hypercore_events.py +0 -0
  274. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_hypercore_flow.py +0 -0
  275. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_hypercore_live.py +0 -0
  276. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_hypercore_outcome.py +0 -0
  277. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_hypercore_readers.py +0 -0
  278. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_hypercore_simulation.py +0 -0
  279. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_logs.py +0 -0
  280. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_mcp_hypercore.py +0 -0
  281. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_mcp_models.py +0 -0
  282. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_mcp_server.py +0 -0
  283. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_mcp_vault_validate.py +0 -0
  284. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_multicall.py +0 -0
  285. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_oracle.py +0 -0
  286. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_oracle_mapping.py +0 -0
  287. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_plasma_vault.py +0 -0
  288. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_public_type_reachability.py +0 -0
  289. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_reader_encoding.py +0 -0
  290. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_readiness_selection.py +0 -0
  291. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_revert_decoding.py +0 -0
  292. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_rewards_manager.py +0 -0
  293. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_roles.py +0 -0
  294. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_sdk_layering.py +0 -0
  295. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  296. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  297. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  298. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_crosschain_canary.py +0 -0
  299. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_crosschain_hypercore.py +0 -0
  300. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  301. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  302. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  303. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  304. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  305. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  306. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  307. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_examples_arbitrum.py +0 -0
  308. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_examples_base.py +0 -0
  309. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_examples_crosschain.py +0 -0
  310. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_external_state_executor_base.py +0 -0
  311. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  312. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  313. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  314. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_morpho_blue.py +0 -0
  315. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_pilot_v3.py +0 -0
  316. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  317. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  318. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  319. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  320. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_readers_integration.py +0 -0
  321. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_release_funds.py +0 -0
  322. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_swap_on_base.py +0 -0
  323. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  324. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulation_errors.py +0 -0
  325. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_simulation_observation.py +0 -0
  326. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_solidity_compiler.py +0 -0
  327. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_solidity_mirrors.py +0 -0
  328. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_substrates.py +0 -0
  329. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_unpriceable_tokens.py +0 -0
  330. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_validation.py +0 -0
  331. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_vault_data_block.py +0 -0
  332. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_vault_dep_graph.py +0 -0
  333. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_vault_info_fetched_golden.py +0 -0
  334. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_vault_info_onchain_base.py +0 -0
  335. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  336. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_vault_reconciliation_onchain_base.py +0 -0
  337. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_vault_simulator_blocks.py +0 -0
  338. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_web3_context.py +0 -0
  339. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/test_withdraw_manager.py +0 -0
  340. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/typed_client/consumer.py +0 -0
  341. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/tests/typed_client/rejected.py +0 -0
  342. {ipor_fusion-4.0.0.dev255 → ipor_fusion-4.0.0.dev263}/uv.lock +0 -0
@@ -419,7 +419,9 @@ or examples.
419
419
  round re-resolves it. Labels only name calls in `result.calls` and failures.
420
420
  Integration tests and examples use it instead of a local node.
421
421
  - Amounts are raw on-chain integers; `types.py` `NewType`s (`Amount`, `Shares`,
422
- `MarketId`, ...) name the unit, and nothing scales by decimals.
422
+ `MarketId`, ...) name the unit, and nothing scales by decimals except the exact
423
+ boundary converters `parse_amount` / `format_amount` (and the `*_shares` pair):
424
+ decimal text in, raw integer out, no float ever.
423
425
  - Three address roles are nominal: `PlasmaVaultAddress`, `FuseAddress` and
424
426
  `AccessManagerAddress` (NewTypes of `ChecksumAddress`). Their wrappers, getters and
425
427
  records return them, so a vault, a fuse and an access manager cannot be passed for
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev255
3
+ Version: 4.0.0.dev263
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
@@ -170,7 +170,10 @@ Fuse, factory and manager addresses per chain are published in
170
170
  [ipor-abi](https://github.com/IPOR-Labs/ipor-abi) (`mainnet/mainnet-<chain>-fusion/addresses.json`).
171
171
  A fuse must also be registered on the vault; `vault.get_fuses().call(ctx)` lists the registered ones.
172
172
 
173
- Amounts are raw on-chain integers (`Amount`, `Shares` in `ipor_fusion.types`); the SDK never scales by decimals.
173
+ Amounts are raw on-chain integers (`Amount`, `Shares` in `ipor_fusion.types`); the SDK scales by decimals
174
+ only in explicit boundary converters.
175
+ `parse_amount("1.5", Decimals(6))` and `format_amount(Amount(1_500_000), Decimals(6))` convert exactly at the
176
+ boundary (no floats, no rounding; `parse_shares` / `format_shares` for shares at the share token's decimals).
174
177
 
175
178
  ### Read, send, or simulate
176
179
 
@@ -122,7 +122,10 @@ Fuse, factory and manager addresses per chain are published in
122
122
  [ipor-abi](https://github.com/IPOR-Labs/ipor-abi) (`mainnet/mainnet-<chain>-fusion/addresses.json`).
123
123
  A fuse must also be registered on the vault; `vault.get_fuses().call(ctx)` lists the registered ones.
124
124
 
125
- Amounts are raw on-chain integers (`Amount`, `Shares` in `ipor_fusion.types`); the SDK never scales by decimals.
125
+ Amounts are raw on-chain integers (`Amount`, `Shares` in `ipor_fusion.types`); the SDK scales by decimals
126
+ only in explicit boundary converters.
127
+ `parse_amount("1.5", Decimals(6))` and `format_amount(Amount(1_500_000), Decimals(6))` convert exactly at the
128
+ boundary (no floats, no rounding; `parse_shares` / `format_shares` for shares at the share token's decimals).
126
129
 
127
130
  ### Read, send, or simulate
128
131
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev255"
3
+ version = "4.0.0.dev263"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -204,22 +204,32 @@ Notes on the walk:
204
204
  so you recognise and avoid it. Verify against `ipor-abi` before use; the
205
205
  registry is the source of truth and is regenerated on release.
206
206
 
207
- | Chain | Chain id | `IporFusionFactoryProxy` (use) | `IporFusionFactoryImpl` (reverts) | Verified |
208
- |---|---|---|---|---|
209
- | Ethereum | 1 | `0xcd05909C4A1F8E501e4ED554cEF4Ed5E48D9b852` | `0xf19C1E9f6616F6056AF1e322A86fDaAaAf0263f5` | implementation slot |
210
- | Unichain | 130 | `0xC9b4E839e86284C558791C13Bda97999fc49aF63` | `0xf5Bc40C192D51ED93ADD6557884766772FA72589` | implementation slot |
211
- | TAC | 239 | `0x26b5561E2E0e7d7977431E92740903Ef3cB7194b` | `0x7DDd0bBB59eFB4b7791cE5E1Cf1587F083aE4Ef5` | implementation slot |
212
- | Botanix | 3637 | `0x7c9857f22D0cc6523C8e88BFDCbf411AB8300fF5` | `0xddb0EFB2a7a5C02e01b5A1f85C0a1110d68EeC26` | registry only |
213
- | Base | 8453 | `0x1455717668fA96534f675856347A973fA907e922` | `0x610152A79BE7F2Aa3aA70520c9331c18fe8D33b7` | clone executed on a fork |
214
- | Plasma | 9745 | `0x70d41759FBF90fB63c88197217cB08F93a1c65CB` | `0x3A42bD6fEa94421025Ae2ed01CE6f137ac785abD` | implementation slot |
215
- | Arbitrum | 42161 | `0x134fCAce7a2C7Ef3dF2479B62f03ddabAEa922d5` | `0x87f94ac9aF79261F0BC73582114f805F55Cd0b25` | clone executed on a fork |
216
- | Avalanche | 43114 | `0xa00b6379833D77fA9C9497b32dae00dF39Ac751e` | `0x399596cFccFEbBb60f8652bece8586a6Bb4199ab` | implementation slot |
217
- | Ink | 57073 | `0xEC53f69Bd1D991a2F99e96DE66E81D0E42A61D8D` | `0xe7372BC46b79e0c87AfE5286c41c20687018E425` | implementation slot |
218
- | Katana | 747474 | `0xc29b8D591d6a3f109Ca7ba384F2e00162866D37B` | `0x4a4De6e86AD0d0546F55B595cA75f55803D940bA` | registry only |
219
-
220
- "Implementation slot" means the proxy's EIP-1967 implementation slot was read
221
- and matches the registry's `IporFusionFactoryImpl`; "registry only" means the
222
- pair was taken from `ipor-abi` without an on-chain read.
207
+ | Chain | Chain id | `IporFusionFactoryProxy` (use) | `IporFusionFactoryImpl` (reverts) | Vault tooling | Verified |
208
+ |---|---|---|---|---|---|
209
+ | Ethereum | 1 | `0xcd05909C4A1F8E501e4ED554cEF4Ed5E48D9b852` | `0x14d120C365Debd920eaDf0a7AC31e659Bc58217b` | validated | implementation slot |
210
+ | Flare | 14 | `0xE75F3565D67645775351A9886F2b598f07d1860B` | `0x6C1ff6eBf5e92Dbc5591E7cCF455829De9C46579` | factory only | implementation slot |
211
+ | Unichain | 130 | `0xC9b4E839e86284C558791C13Bda97999fc49aF63` | `0x73c7CA802403BB21e9608C937c556C04d5eeBAD6` | validated | implementation slot |
212
+ | Monad | 143 | `0x62DFfe8bCaaf7960f858a4f7dDC83cc98F04cEDC` | `0x1017f0032460B3c8FBB92Bfd350F708Ff6229137` | validated | implementation slot |
213
+ | TAC | 239 | `0x26b5561E2E0e7d7977431E92740903Ef3cB7194b` | `0x7DDd0bBB59eFB4b7791cE5E1Cf1587F083aE4Ef5` | factory only | implementation slot |
214
+ | HyperEVM | 999 | `0x6bBc827F34b4e862d15215ABd4AC0E3886181665` | `0x010430cbdE2Bb8ACC48D7aDfbEb99812E83f1156` | validated | implementation slot |
215
+ | Botanix | 3637 | `0x7c9857f22D0cc6523C8e88BFDCbf411AB8300fF5` | `0xddb0EFB2a7a5C02e01b5A1f85C0a1110d68EeC26` | factory only | registry only |
216
+ | Robinhood | 4663 | `0xDF7A590b60072476E355A8EfF33e4872440bDc7C` | `0x9d897beA78F8747d8f9C45d5734089ca7225AE90` | validated | implementation slot |
217
+ | Base | 8453 | `0x1455717668fA96534f675856347A973fA907e922` | `0x378fEb2E96082FB965E2BB922AEE484d07c14014` | validated | implementation slot |
218
+ | Plasma | 9745 | `0x70d41759FBF90fB63c88197217cB08F93a1c65CB` | `0x346DA190c60d6101B77DA613a252c7aE25A7a120` | validated | implementation slot |
219
+ | Arbitrum | 42161 | `0x134fCAce7a2C7Ef3dF2479B62f03ddabAEa922d5` | `0x86C8Ea8800EB6bc8C846c3e0230178520a0ED721` | validated | implementation slot |
220
+ | Avalanche | 43114 | `0xa00b6379833D77fA9C9497b32dae00dF39Ac751e` | `0x0f358Bb3d14AB911dBfAF4d9061097e7F5Cf123F` | validated | implementation slot |
221
+ | Ink | 57073 | `0xEC53f69Bd1D991a2F99e96DE66E81D0E42A61D8D` | `0xEF774B9767cC9FC813A2b595BE870A13D5B367C0` | validated | implementation slot |
222
+ | Katana | 747474 | `0xc29b8D591d6a3f109Ca7ba384F2e00162866D37B` | `0x4a4De6e86AD0d0546F55B595cA75f55803D940bA` | validated | implementation slot |
223
+
224
+ A deployed factory is not the same as supported tooling. "Validated" chains are
225
+ `SUPPORTED_CHAIN_IDS` (`ipor_fusion.chains`): `fusion vault info`/`validate`
226
+ and `fetch_vault_data` run there. On a "factory only" chain `clone()` works,
227
+ but the vault tooling raises `UnsupportedChainError` until the chain is
228
+ validated. "Implementation slot" means the proxy's EIP-1967 implementation
229
+ slot was read on chain and matches the registry's `IporFusionFactoryImpl`;
230
+ "registry only" means the pair comes from `ipor-abi` without an on-chain read.
231
+ The factory implementations are upgraded in place, so the implementation
232
+ column changes with releases while the proxy stays.
223
233
 
224
234
  Fuse addresses are not in the SDK. Resolve them by registry name and chain
225
235
  in `ipor-abi` (`SupplyFuseAaveV3`, `BalanceFuseAaveV3`, ...), or with the
@@ -333,11 +333,15 @@ from ipor_fusion.types import (
333
333
  TokenId,
334
334
  TxHash,
335
335
  Verdict,
336
+ format_amount,
337
+ format_shares,
336
338
  parse_access_manager_address,
339
+ parse_amount,
337
340
  parse_fuse_address,
338
341
  parse_hex_address,
339
342
  parse_morpho_market_id,
340
343
  parse_plasma_vault_address,
344
+ parse_shares,
341
345
  parse_tx_hash,
342
346
  )
343
347
  from ipor_fusion.validation import (
@@ -617,6 +621,10 @@ __all__ = [
617
621
  "parse_plasma_vault_address",
618
622
  "parse_morpho_market_id",
619
623
  "parse_tx_hash",
624
+ "parse_amount",
625
+ "parse_shares",
626
+ "format_amount",
627
+ "format_shares",
620
628
  "Price",
621
629
  "Period",
622
630
  "MAX_UINT256",
@@ -1,16 +1,16 @@
1
1
  """IporFusionFactoryProxy wrapper — deploys a full Fusion vault stack
2
2
  (plasmaVault + accessManager + 6 managers) in a single tx.
3
3
 
4
- Deployed signature on BASE (verified 2026-05-12 against impl bytecode):
4
+ Deployed signature on BASE (verified against the impl bytecode on 2026-05-12,
5
+ and on 2026-10-23 for the upgraded impl 0x378fEb2E96082FB965E2BB922AEE484d07c14014):
5
6
  clone(string assetName_, string assetSymbol_, address underlyingToken_,
6
7
  uint256 redemptionDelayInSeconds_, address owner_,
7
8
  uint256 daoFeePackageIndex_) → FusionInstance
8
9
 
9
- Note: the ipor-abi registry publishes a 5-arg shape (no daoFeePackageIndex);
10
- that's stale — the deployed factory impl at
11
- 0x610152a79be7f2aa3aa70520c9331c18fe8d33b7 only exposes the 6-arg variant
12
- (selector 0x8697b10a). Always trust the impl bytecode over the registry
13
- when they disagree.
10
+ Note: the ipor-abi registry has published a 5-arg shape (no
11
+ daoFeePackageIndex); both the earlier impl 0x610152a79be7f2aa3aa70520c9331c18fe8d33b7
12
+ and the current one expose only the 6-arg variant (selector 0x8697b10a). Always
13
+ trust the impl bytecode over the registry when they disagree.
14
14
 
15
15
  `clone()` is a write — call `.send(ctx)` for a real tx, then decode the
16
16
  created addresses from its receipt with `factory.decode_clone_receipt(receipt)`.
@@ -119,22 +119,32 @@ Notes on the walk:
119
119
  so you recognise and avoid it. Verify against `ipor-abi` before use; the
120
120
  registry is the source of truth and is regenerated on release.
121
121
 
122
- | Chain | Chain id | `IporFusionFactoryProxy` (use) | `IporFusionFactoryImpl` (reverts) | Verified |
123
- |---|---|---|---|---|
124
- | Ethereum | 1 | `0xcd05909C4A1F8E501e4ED554cEF4Ed5E48D9b852` | `0xf19C1E9f6616F6056AF1e322A86fDaAaAf0263f5` | implementation slot |
125
- | Unichain | 130 | `0xC9b4E839e86284C558791C13Bda97999fc49aF63` | `0xf5Bc40C192D51ED93ADD6557884766772FA72589` | implementation slot |
126
- | TAC | 239 | `0x26b5561E2E0e7d7977431E92740903Ef3cB7194b` | `0x7DDd0bBB59eFB4b7791cE5E1Cf1587F083aE4Ef5` | implementation slot |
127
- | Botanix | 3637 | `0x7c9857f22D0cc6523C8e88BFDCbf411AB8300fF5` | `0xddb0EFB2a7a5C02e01b5A1f85C0a1110d68EeC26` | registry only |
128
- | Base | 8453 | `0x1455717668fA96534f675856347A973fA907e922` | `0x610152A79BE7F2Aa3aA70520c9331c18fe8D33b7` | clone executed on a fork |
129
- | Plasma | 9745 | `0x70d41759FBF90fB63c88197217cB08F93a1c65CB` | `0x3A42bD6fEa94421025Ae2ed01CE6f137ac785abD` | implementation slot |
130
- | Arbitrum | 42161 | `0x134fCAce7a2C7Ef3dF2479B62f03ddabAEa922d5` | `0x87f94ac9aF79261F0BC73582114f805F55Cd0b25` | clone executed on a fork |
131
- | Avalanche | 43114 | `0xa00b6379833D77fA9C9497b32dae00dF39Ac751e` | `0x399596cFccFEbBb60f8652bece8586a6Bb4199ab` | implementation slot |
132
- | Ink | 57073 | `0xEC53f69Bd1D991a2F99e96DE66E81D0E42A61D8D` | `0xe7372BC46b79e0c87AfE5286c41c20687018E425` | implementation slot |
133
- | Katana | 747474 | `0xc29b8D591d6a3f109Ca7ba384F2e00162866D37B` | `0x4a4De6e86AD0d0546F55B595cA75f55803D940bA` | registry only |
134
-
135
- "Implementation slot" means the proxy's EIP-1967 implementation slot was read
136
- and matches the registry's `IporFusionFactoryImpl`; "registry only" means the
137
- pair was taken from `ipor-abi` without an on-chain read.
122
+ | Chain | Chain id | `IporFusionFactoryProxy` (use) | `IporFusionFactoryImpl` (reverts) | Vault tooling | Verified |
123
+ |---|---|---|---|---|---|
124
+ | Ethereum | 1 | `0xcd05909C4A1F8E501e4ED554cEF4Ed5E48D9b852` | `0x14d120C365Debd920eaDf0a7AC31e659Bc58217b` | validated | implementation slot |
125
+ | Flare | 14 | `0xE75F3565D67645775351A9886F2b598f07d1860B` | `0x6C1ff6eBf5e92Dbc5591E7cCF455829De9C46579` | factory only | implementation slot |
126
+ | Unichain | 130 | `0xC9b4E839e86284C558791C13Bda97999fc49aF63` | `0x73c7CA802403BB21e9608C937c556C04d5eeBAD6` | validated | implementation slot |
127
+ | Monad | 143 | `0x62DFfe8bCaaf7960f858a4f7dDC83cc98F04cEDC` | `0x1017f0032460B3c8FBB92Bfd350F708Ff6229137` | validated | implementation slot |
128
+ | TAC | 239 | `0x26b5561E2E0e7d7977431E92740903Ef3cB7194b` | `0x7DDd0bBB59eFB4b7791cE5E1Cf1587F083aE4Ef5` | factory only | implementation slot |
129
+ | HyperEVM | 999 | `0x6bBc827F34b4e862d15215ABd4AC0E3886181665` | `0x010430cbdE2Bb8ACC48D7aDfbEb99812E83f1156` | validated | implementation slot |
130
+ | Botanix | 3637 | `0x7c9857f22D0cc6523C8e88BFDCbf411AB8300fF5` | `0xddb0EFB2a7a5C02e01b5A1f85C0a1110d68EeC26` | factory only | registry only |
131
+ | Robinhood | 4663 | `0xDF7A590b60072476E355A8EfF33e4872440bDc7C` | `0x9d897beA78F8747d8f9C45d5734089ca7225AE90` | validated | implementation slot |
132
+ | Base | 8453 | `0x1455717668fA96534f675856347A973fA907e922` | `0x378fEb2E96082FB965E2BB922AEE484d07c14014` | validated | implementation slot |
133
+ | Plasma | 9745 | `0x70d41759FBF90fB63c88197217cB08F93a1c65CB` | `0x346DA190c60d6101B77DA613a252c7aE25A7a120` | validated | implementation slot |
134
+ | Arbitrum | 42161 | `0x134fCAce7a2C7Ef3dF2479B62f03ddabAEa922d5` | `0x86C8Ea8800EB6bc8C846c3e0230178520a0ED721` | validated | implementation slot |
135
+ | Avalanche | 43114 | `0xa00b6379833D77fA9C9497b32dae00dF39Ac751e` | `0x0f358Bb3d14AB911dBfAF4d9061097e7F5Cf123F` | validated | implementation slot |
136
+ | Ink | 57073 | `0xEC53f69Bd1D991a2F99e96DE66E81D0E42A61D8D` | `0xEF774B9767cC9FC813A2b595BE870A13D5B367C0` | validated | implementation slot |
137
+ | Katana | 747474 | `0xc29b8D591d6a3f109Ca7ba384F2e00162866D37B` | `0x4a4De6e86AD0d0546F55B595cA75f55803D940bA` | validated | implementation slot |
138
+
139
+ A deployed factory is not the same as supported tooling. "Validated" chains are
140
+ `SUPPORTED_CHAIN_IDS` (`ipor_fusion.chains`): `fusion vault info`/`validate`
141
+ and `fetch_vault_data` run there. On a "factory only" chain `clone()` works,
142
+ but the vault tooling raises `UnsupportedChainError` until the chain is
143
+ validated. "Implementation slot" means the proxy's EIP-1967 implementation
144
+ slot was read on chain and matches the registry's `IporFusionFactoryImpl`;
145
+ "registry only" means the pair comes from `ipor-abi` without an on-chain read.
146
+ The factory implementations are upgraded in place, so the implementation
147
+ column changes with releases while the proxy stays.
138
148
 
139
149
  Fuse addresses are not in the SDK. Resolve them by registry name and chain
140
150
  in `ipor-abi` (`SupplyFuseAaveV3`, `BalanceFuseAaveV3`, ...), or with the
@@ -1,3 +1,4 @@
1
+ import re
1
2
  from dataclasses import dataclass
2
3
  from typing import ClassVar, Literal, NewType, TypeAlias
3
4
 
@@ -196,3 +197,81 @@ def parse_fuse_address(value: str | bytes) -> FuseAddress:
196
197
  def parse_access_manager_address(value: str | bytes) -> AccessManagerAddress:
197
198
  """An access manager's address, checksummed; see `parse_plasma_vault_address`."""
198
199
  return AccessManagerAddress(_checksum(value))
200
+
201
+
202
+ # ASCII digits only: ``\d`` would also take other scripts' digits, which int() reads.
203
+ _DECIMAL_TEXT = re.compile(r"([0-9]*)(?:\.([0-9]*))?")
204
+
205
+
206
+ _UINT256_DIGITS = len(str(MAX_UINT256))
207
+
208
+
209
+ def _check_decimals(decimals: Decimals) -> None:
210
+ if not 0 <= decimals <= 255:
211
+ raise ValueError(f"decimals must be 0..255 (ERC-20 uint8), got {decimals}")
212
+
213
+
214
+ def _parse_units(text: str, decimals: Decimals, what: str) -> int:
215
+ if not isinstance(text, str):
216
+ raise TypeError(
217
+ f"{what} must be text, not {type(text).__name__}: floats lose precision"
218
+ )
219
+ _check_decimals(decimals)
220
+ match = _DECIMAL_TEXT.fullmatch(text.strip())
221
+ if match is None or not (match.group(1) or match.group(2)):
222
+ raise ValueError(f"not a decimal {what}: {text!r}")
223
+ whole, fraction = match.group(1).lstrip("0"), match.group(2) or ""
224
+ if len(fraction) > decimals:
225
+ raise ValueError(
226
+ f"{what} {text!r} has {len(fraction)} fractional digits,"
227
+ f" more than the {decimals} the token carries"
228
+ )
229
+ # Bounded before int(): a longer whole part overflows uint256 at any scale.
230
+ if len(whole) > _UINT256_DIGITS:
231
+ raise ValueError(f"{what} {text!r} exceeds uint256")
232
+ value = int(whole + fraction.ljust(decimals, "0") or "0")
233
+ if value > MAX_UINT256:
234
+ raise ValueError(f"{what} {text!r} exceeds uint256")
235
+ return value
236
+
237
+
238
+ def _format_units(value: int, decimals: Decimals, what: str) -> str:
239
+ if not 0 <= value <= MAX_UINT256:
240
+ raise ValueError(f"{what} must be within uint256, got {value}")
241
+ _check_decimals(decimals)
242
+ whole, fraction = divmod(value, 10**decimals)
243
+ if fraction == 0:
244
+ return str(whole)
245
+ return f"{whole}.{str(fraction).zfill(decimals).rstrip('0')}"
246
+
247
+
248
+ def parse_amount(text: str, decimals: Decimals) -> Amount:
249
+ """The raw `Amount` of ``text`` token units at ``decimals``, exactly:
250
+ ``parse_amount("1.5", Decimals(6))`` is ``Amount(1_500_000)``.
251
+
252
+ ``text`` is digits with at most one ``.`` (surrounding whitespace ignored);
253
+ no sign, exponent or separators. ``decimals`` is 0..255, an ERC-20 uint8.
254
+ Raises ``ValueError`` for anything else, for more fractional digits than
255
+ ``decimals`` (nothing is rounded) and for a value above uint256; ``TypeError`` for a non-string, so a float is never
256
+ accepted. Inverse of `format_amount`.
257
+ """
258
+ return Amount(_parse_units(text, decimals, "amount"))
259
+
260
+
261
+ def parse_shares(text: str, decimals: Decimals) -> Shares:
262
+ """`parse_amount` for vault shares at the share token's ``decimals``
263
+ (`PlasmaVault.decimals`, not the asset's)."""
264
+ return Shares(_parse_units(text, decimals, "shares"))
265
+
266
+
267
+ def format_amount(amount: Amount, decimals: Decimals) -> str:
268
+ """``amount`` in token units as exact decimal text, no trailing zeros:
269
+ ``format_amount(Amount(1_500_000), Decimals(6))`` is ``"1.5"``; a whole
270
+ number has no ``.``. Raises ``ValueError`` on an amount outside uint256 or
271
+ ``decimals`` outside 0..255. Inverse of `parse_amount`."""
272
+ return _format_units(amount, decimals, "amount")
273
+
274
+
275
+ def format_shares(shares: Shares, decimals: Decimals) -> str:
276
+ """`format_amount` for vault shares."""
277
+ return _format_units(shares, decimals, "shares")
@@ -10,7 +10,14 @@ from _euler_v2 import BASE_FUSION_FACTORY
10
10
  from _guide import GUIDE_ADDRESSES
11
11
  from addresses import BASE_USDC
12
12
 
13
- from ipor_fusion import ERC20, AccessManager, Call, PlasmaVault
13
+ from ipor_fusion import (
14
+ CHAIN_NAMES,
15
+ ERC20,
16
+ SUPPORTED_CHAIN_IDS,
17
+ AccessManager,
18
+ Call,
19
+ PlasmaVault,
20
+ )
14
21
  from ipor_fusion.core import FusionFactory
15
22
  from ipor_fusion.fuses import AaveV3SupplyFuse
16
23
  from ipor_fusion.guide import (
@@ -21,6 +28,7 @@ from ipor_fusion.guide import (
21
28
  RESOURCES,
22
29
  guide_text,
23
30
  )
31
+ from ipor_fusion.types import ChainId
24
32
 
25
33
  REPO_ROOT = Path(__file__).resolve().parents[1]
26
34
  SKILL_DIR = REPO_ROOT / "skills" / "ipor-deploy-vault"
@@ -49,6 +57,28 @@ PUBLIC_REPOS = {
49
57
  _URL = re.compile(r"https?://([A-Za-z0-9.-]+)(?::\d+)?(/[^\s)`>\"']*)?")
50
58
  _FRONTMATTER = re.compile(r"\A---\n(.*?)\n---\n", re.DOTALL)
51
59
  _PY_BLOCK = re.compile(r"```python\n(.*?)```", re.DOTALL)
60
+ # Chains with an IporFusionFactoryProxy in ipor-abi (mainnet-<chain>-fusion).
61
+ FACTORY_CHAINS = {
62
+ 1,
63
+ 14,
64
+ 130,
65
+ 143,
66
+ 239,
67
+ 999,
68
+ 3637,
69
+ 4663,
70
+ 8453,
71
+ 9745,
72
+ 42161,
73
+ 43114,
74
+ 57073,
75
+ 747474,
76
+ }
77
+ _FACTORY_ROW = re.compile(
78
+ r"^\| (\w+) \| (\d+) \| `(0x[0-9a-fA-F]{40})` \| `(0x[0-9a-fA-F]{40})` \| "
79
+ r"(validated|factory only) \| (implementation slot|registry only) \|$",
80
+ re.MULTILINE,
81
+ )
52
82
 
53
83
 
54
84
  def _public_texts() -> dict[str, str]:
@@ -89,6 +119,23 @@ class TestResources:
89
119
  assert GUIDE_ADDRESSES["USDC"] == BASE_USDC
90
120
  assert {"SUPPLY_FUSE", "BALANCE_FUSE"} <= set(GUIDE_ADDRESSES)
91
121
 
122
+ def test_factory_table_tooling_column_is_the_supported_chain_set(self):
123
+ section = QUICKSTART.text.split("## Factory proxies per chain", 1)[1]
124
+ body = [
125
+ line
126
+ for line in section.splitlines()
127
+ if line.startswith("| ") and not line.startswith("| Chain |")
128
+ ]
129
+ rows = _FACTORY_ROW.findall("\n".join(body))
130
+ assert len(rows) == len(body), "every table row parses"
131
+ assert {int(row[1]) for row in rows} == FACTORY_CHAINS
132
+ ids = [int(row[1]) for row in rows]
133
+ assert ids == sorted(set(ids)), "one row per chain, by chain id"
134
+ for name, chain_id, *_ in rows:
135
+ assert CHAIN_NAMES[ChainId(int(chain_id))] == name.lower()
136
+ validated = {int(row[1]) for row in rows if row[4] == "validated"}
137
+ assert validated == set(SUPPORTED_CHAIN_IDS)
138
+
92
139
  def test_invariants_name_every_deploy_path_revert(self):
93
140
  text = INVARIANTS.text
94
141
  for selector, name in [
@@ -367,7 +367,9 @@ _ALL_SNAPSHOT: dict[str, frozenset[str]] = {
367
367
  "find_creation_block",
368
368
  "find_markets_missing_erc20_dependency",
369
369
  "find_orphan_fuse_markets",
370
+ "format_amount",
370
371
  "format_market_label",
372
+ "format_shares",
371
373
  "get_contract_name",
372
374
  "get_deployment_tx",
373
375
  "get_logs_adaptive",
@@ -377,10 +379,12 @@ _ALL_SNAPSHOT: dict[str, frozenset[str]] = {
377
379
  "morpho_blue_address",
378
380
  "package_version",
379
381
  "parse_access_manager_address",
382
+ "parse_amount",
380
383
  "parse_fuse_address",
381
384
  "parse_hex_address",
382
385
  "parse_morpho_market_id",
383
386
  "parse_plasma_vault_address",
387
+ "parse_shares",
384
388
  "parse_tx_hash",
385
389
  "price_oracle_middleware",
386
390
  "read_changelog",
@@ -1107,11 +1111,15 @@ _MODULE_PATH_SNAPSHOT: dict[str, frozenset[str]] = {
1107
1111
  "TxHash",
1108
1112
  "Verdict",
1109
1113
  "ZERO_ADDRESS",
1114
+ "format_amount",
1115
+ "format_shares",
1110
1116
  "parse_access_manager_address",
1117
+ "parse_amount",
1111
1118
  "parse_fuse_address",
1112
1119
  "parse_hex_address",
1113
1120
  "parse_morpho_market_id",
1114
1121
  "parse_plasma_vault_address",
1122
+ "parse_shares",
1115
1123
  "parse_tx_hash",
1116
1124
  }
1117
1125
  ),
@@ -6,7 +6,7 @@ from hexbytes import HexBytes
6
6
  from web3 import Web3
7
7
 
8
8
  from ipor_fusion.fuses.events import extract_events
9
- from ipor_fusion.types import Amount, Decimals, Price
9
+ from ipor_fusion.types import MAX_UINT256, Amount, Decimals, Price, Shares
10
10
 
11
11
  ADDR = Web3.to_checksum_address("0x1111111111111111111111111111111111111111")
12
12
 
@@ -155,3 +155,119 @@ class TestRoleParsers:
155
155
  assert parser(bytes.fromhex(self.LOWER[2:])) == checksum
156
156
  with pytest.raises(ValueError):
157
157
  parser("0x1234")
158
+
159
+
160
+ class TestAmountText:
161
+ @pytest.mark.parametrize(
162
+ ("text", "decimals", "raw"),
163
+ [
164
+ ("1.5", 6, 1_500_000),
165
+ ("0.000001", 6, 1),
166
+ ("1", 18, 10**18),
167
+ ("1.", 6, 1_000_000),
168
+ (".5", 6, 500_000),
169
+ (" 42 ", 0, 42),
170
+ ("0", 6, 0),
171
+ ("1.000000", 6, 1_000_000),
172
+ (str(MAX_UINT256), 0, MAX_UINT256),
173
+ (
174
+ "115792089237316195423570985008687907853269984665640564039457.584007913129639935",
175
+ 18,
176
+ MAX_UINT256,
177
+ ),
178
+ ],
179
+ )
180
+ def test_parse_amount_is_exact(self, text, decimals, raw):
181
+ from ipor_fusion import parse_amount
182
+
183
+ assert parse_amount(text, Decimals(decimals)) == raw
184
+
185
+ @pytest.mark.parametrize(
186
+ "text",
187
+ [
188
+ "",
189
+ ".",
190
+ "-1",
191
+ "+1",
192
+ "1e6",
193
+ "1,000",
194
+ "1_000",
195
+ "1.5.0",
196
+ "0x10",
197
+ "1 0",
198
+ "nan",
199
+ "\u0663",
200
+ "\uff11.5",
201
+ ],
202
+ )
203
+ def test_parse_amount_rejects_anything_but_plain_decimal_text(self, text):
204
+ from ipor_fusion import parse_amount
205
+
206
+ with pytest.raises(ValueError, match="not a decimal amount"):
207
+ parse_amount(text, Decimals(6))
208
+
209
+ def test_parse_amount_never_rounds(self):
210
+ from ipor_fusion import parse_amount
211
+
212
+ with pytest.raises(ValueError, match="7 fractional digits"):
213
+ parse_amount("1.0000001", Decimals(6))
214
+
215
+ def test_parse_amount_rejects_uint256_overflow_and_floats(self):
216
+ from ipor_fusion import parse_amount
217
+
218
+ with pytest.raises(ValueError, match="exceeds uint256"):
219
+ parse_amount(str(MAX_UINT256 + 1), Decimals(0))
220
+ with pytest.raises(TypeError, match="floats lose precision"):
221
+ parse_amount(1.5, Decimals(6))
222
+ with pytest.raises(ValueError, match="decimals"):
223
+ parse_amount("1", Decimals(-1))
224
+ with pytest.raises(ValueError, match="0..255"):
225
+ parse_amount("1", Decimals(256))
226
+
227
+ def test_parse_amount_bounds_digits_before_converting(self):
228
+ from ipor_fusion import parse_amount
229
+
230
+ # Leading zeros are not significant, past Python's int() digit limit too.
231
+ assert parse_amount("0" * 5000 + "1", Decimals(0)) == 1
232
+ with pytest.raises(ValueError, match="exceeds uint256"):
233
+ parse_amount("1" * 5000, Decimals(0))
234
+ assert parse_amount("0." + "0" * 254 + "1", Decimals(255)) == 1
235
+
236
+ @pytest.mark.parametrize(
237
+ ("raw", "decimals", "text"),
238
+ [
239
+ (1_500_000, 6, "1.5"),
240
+ (1, 6, "0.000001"),
241
+ (10**18, 18, "1"),
242
+ (0, 6, "0"),
243
+ (42, 0, "42"),
244
+ (123_456_789, 4, "12345.6789"),
245
+ (
246
+ MAX_UINT256,
247
+ 18,
248
+ "115792089237316195423570985008687907853269984665640564039457.584007913129639935",
249
+ ),
250
+ ],
251
+ )
252
+ def test_format_amount_round_trips(self, raw, decimals, text):
253
+ from ipor_fusion import format_amount, parse_amount
254
+
255
+ assert format_amount(Amount(raw), Decimals(decimals)) == text
256
+ assert parse_amount(text, Decimals(decimals)) == raw
257
+
258
+ @pytest.mark.parametrize("raw", [-1, MAX_UINT256 + 1])
259
+ def test_format_amount_rejects_values_outside_uint256(self, raw):
260
+ from ipor_fusion import format_amount, format_shares
261
+
262
+ with pytest.raises(ValueError, match="within uint256"):
263
+ format_amount(Amount(raw), Decimals(6))
264
+ with pytest.raises(ValueError, match="within uint256"):
265
+ format_shares(Shares(raw), Decimals(6))
266
+ with pytest.raises(ValueError, match="0..255"):
267
+ format_amount(Amount(1), Decimals(256))
268
+
269
+ def test_shares_pair_mirrors_amounts(self):
270
+ from ipor_fusion import format_shares, parse_shares
271
+
272
+ assert parse_shares("2.5", Decimals(8)) == Shares(250_000_000)
273
+ assert format_shares(Shares(250_000_000), Decimals(8)) == "2.5"