ipor-fusion 4.0.0.dev144__tar.gz → 4.0.0.dev149__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 (300) hide show
  1. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/AGENTS.md +2 -1
  2. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/MIGRATION_4.md +66 -18
  3. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/PKG-INFO +1 -1
  4. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +5 -4
  5. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +5 -4
  6. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/pyproject.toml +1 -1
  7. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/vault_cmd.py +3 -1
  8. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/external_state_executor.py +9 -5
  9. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/fusion_factory.py +11 -10
  10. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/simulation.py +3 -3
  11. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/ccip/codec.py +4 -4
  12. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/ccip/contracts.py +26 -15
  13. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/ccip/lane.py +2 -2
  14. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/ccip/transport.py +3 -3
  15. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/contracts.py +32 -18
  16. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/lane.py +17 -8
  17. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/stargate/contracts.py +27 -15
  18. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/stargate/lane.py +2 -2
  19. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/stargate/layerzero.py +9 -3
  20. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/stargate/transport.py +5 -5
  21. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/transport.py +3 -3
  22. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/external_state.py +3 -3
  23. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/mcp/server.py +1 -1
  24. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/morpho_api.py +66 -59
  25. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/aave_v4.py +3 -2
  26. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/hypercore.py +9 -7
  27. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/lending_health.py +1 -1
  28. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/oracle_mapping.py +3 -1
  29. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/oracle_mapping_nodes.py +7 -6
  30. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/oracle_mapping_reader.py +12 -10
  31. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/validation.py +16 -16
  32. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/vault_data.py +14 -4
  33. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_commands.py +9 -0
  34. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_oracle_mapping.py +4 -4
  35. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_mcp_server.py +2 -2
  36. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_oracle_mapping.py +3 -3
  37. ipor_fusion-4.0.0.dev149/tests/test_vault_data_block.py +34 -0
  38. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.claude-plugin/marketplace.json +0 -0
  39. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.claude-plugin/plugin.json +0 -0
  40. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.env.example +0 -0
  41. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.gemini/settings.json +0 -0
  42. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.git-blame-ignore-revs +0 -0
  43. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.github/workflows/cd.yml +0 -0
  44. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.github/workflows/ci.yml +0 -0
  45. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.github/workflows/commit-lint.yml +0 -0
  46. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.github/workflows/notify-slack.yml +0 -0
  47. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.github/workflows/python-build.yml +0 -0
  48. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.github/workflows/python-publish.yml +0 -0
  49. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.github/workflows/release.yml +0 -0
  50. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.github/workflows/report-slack.yml +0 -0
  51. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.gitignore +0 -0
  52. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.mcp.json +0 -0
  53. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.pre-commit-config.yaml +0 -0
  54. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/.python-version +0 -0
  55. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/CHANGELOG.md +0 -0
  56. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/CLAUDE.md +0 -0
  57. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/CODEOWNERS +0 -0
  58. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/LICENSE +0 -0
  59. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/README.md +0 -0
  60. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/build.sh +0 -0
  61. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/examples/CONTRIBUTING.md +0 -0
  62. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/examples/README.md +0 -0
  63. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  64. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  65. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/examples/external_state_margin_leg_base.py +0 -0
  66. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/examples/simple_aave_v3_supply_base.py +0 -0
  67. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/pytest.ini +0 -0
  68. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/scripts/commit-check.sh +0 -0
  69. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/scripts/crosschain_readiness.py +0 -0
  70. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/skills/ipor-deploy-vault/SKILL.md +0 -0
  71. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/__init__.py +0 -0
  72. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/about.py +0 -0
  73. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cache.py +0 -0
  74. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/chains.py +0 -0
  75. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/__init__.py +0 -0
  76. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  77. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/config_cmd.py +0 -0
  78. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/config_store.py +0 -0
  79. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/main.py +0 -0
  80. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/market_cmd.py +0 -0
  81. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/options.py +0 -0
  82. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  83. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/vault_health.py +0 -0
  84. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  85. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  86. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  87. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/config/__init__.py +0 -0
  88. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/config/roles.py +0 -0
  89. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/__init__.py +0 -0
  90. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/access.py +0 -0
  91. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/address.py +0 -0
  92. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/context.py +0 -0
  93. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/contract.py +0 -0
  94. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/erc20.py +0 -0
  95. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/fee_manager.py +0 -0
  96. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/fuse.py +0 -0
  97. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/fuse_action.py +0 -0
  98. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/logs.py +0 -0
  99. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/multicall.py +0 -0
  100. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/oracle.py +0 -0
  101. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/plasma_vault.py +0 -0
  102. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/rewards_manager.py +0 -0
  103. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  104. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/__init__.py +0 -0
  105. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  106. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  107. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  108. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  109. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  110. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/discovery.py +0 -0
  111. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/errors.py +0 -0
  112. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  113. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  114. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  115. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  116. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/logs.py +0 -0
  117. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/messages.py +0 -0
  118. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/simulation.py +0 -0
  119. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  120. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/dependency_graph.py +0 -0
  121. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/entry_cli.py +0 -0
  122. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/entry_mcp.py +0 -0
  123. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/errors.py +0 -0
  124. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/explorer.py +0 -0
  125. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/field_docs.py +0 -0
  126. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/__init__.py +0 -0
  127. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  128. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/async_action.py +0 -0
  129. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/base.py +0 -0
  130. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  131. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/erc4626.py +0 -0
  132. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  133. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/events.py +0 -0
  134. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/external_state_events.py +0 -0
  135. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  136. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  137. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/hypercore.py +0 -0
  138. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
  139. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/merkl.py +0 -0
  140. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/morpho.py +0 -0
  141. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  142. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  143. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/fuses/universal.py +0 -0
  144. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/guide/__init__.py +0 -0
  145. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/guide/architecture.md +0 -0
  146. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/guide/glossary.md +0 -0
  147. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/guide/invariants.md +0 -0
  148. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/guide/quickstart.md +0 -0
  149. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/market_ids.py +0 -0
  150. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/market_info.py +0 -0
  151. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/mcp/__init__.py +0 -0
  152. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/mcp/guide.py +0 -0
  153. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/mcp/models.py +0 -0
  154. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/py.typed +0 -0
  155. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/__init__.py +0 -0
  156. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/aave_v3.py +0 -0
  157. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/compound_v3.py +0 -0
  158. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  159. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/morpho.py +0 -0
  160. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/position_manager.py +0 -0
  161. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  162. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  163. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/readers/unpriceable.py +0 -0
  164. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/substrates.py +0 -0
  165. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/types.py +0 -0
  166. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/vault_erc20.py +0 -0
  167. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/vault_info.py +0 -0
  168. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/vault_managers.py +0 -0
  169. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/vault_positions.py +0 -0
  170. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/src/ipor_fusion/vault_reads.py +0 -0
  171. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/UniswapV3UniversalRouter.py +0 -0
  172. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_crosschain.py +0 -0
  173. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_crosschain_lifecycle.py +0 -0
  174. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_crosschain_recovery.py +0 -0
  175. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_euler_v2.py +0 -0
  176. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_foundry.py +0 -0
  177. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_guide.py +0 -0
  178. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_hypercore_model.py +0 -0
  179. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_hypercore_shadow.py +0 -0
  180. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_multicall.py +0 -0
  181. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_simulate.py +0 -0
  182. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/_validation.py +0 -0
  183. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/addresses.py +0 -0
  184. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/conftest.py +0 -0
  185. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/constants.py +0 -0
  186. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  187. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/fixtures/hypercore_market55.json +0 -0
  188. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/fixtures/vault_info_golden/clean.json +0 -0
  189. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  190. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/fixtures/vault_info_golden/rich.json +0 -0
  191. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  192. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_about.py +0 -0
  193. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_access_manager.py +0 -0
  194. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cache.py +0 -0
  195. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_chains.py +0 -0
  196. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_changelog.py +0 -0
  197. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_config_store.py +0 -0
  198. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_helpers.py +0 -0
  199. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_hypercore.py +0 -0
  200. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_lending_health.py +0 -0
  201. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_market_morpho_blue.py +0 -0
  202. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_pending_requests_scan.py +0 -0
  203. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_role_accounts.py +0 -0
  204. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_vault_info_golden.py +0 -0
  205. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_vault_predates_getters.py +0 -0
  206. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_cli_vault_validate.py +0 -0
  207. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_contract_call.py +0 -0
  208. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_contract_encoder.py +0 -0
  209. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_ccip_events.py +0 -0
  210. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_ccip_indexer.py +0 -0
  211. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_ccip_quote.py +0 -0
  212. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_chainlink.py +0 -0
  213. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_contracts.py +0 -0
  214. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_encoding.py +0 -0
  215. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_errors.py +0 -0
  216. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_lane.py +0 -0
  217. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_layering.py +0 -0
  218. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_readiness.py +0 -0
  219. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_readiness_report.py +0 -0
  220. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_crosschain_transport.py +0 -0
  221. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_docs_typing.py +0 -0
  222. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_erc20.py +0 -0
  223. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_error_hierarchy.py +0 -0
  224. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_examples_composed_hypercore.py +0 -0
  225. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_examples_crosschain.py +0 -0
  226. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  227. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_examples_vaults.py +0 -0
  228. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_explorer.py +0 -0
  229. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_external_state_events.py +0 -0
  230. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_external_state_executor.py +0 -0
  231. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_fee_manager.py +0 -0
  232. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_fuse_encoding.py +0 -0
  233. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_fusion_instance.py +0 -0
  234. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_guide.py +0 -0
  235. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_hypercore_deployment.py +0 -0
  236. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_hypercore_events.py +0 -0
  237. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_hypercore_flow.py +0 -0
  238. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_hypercore_live.py +0 -0
  239. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_hypercore_outcome.py +0 -0
  240. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_hypercore_readers.py +0 -0
  241. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_hypercore_simulation.py +0 -0
  242. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_logs.py +0 -0
  243. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_mcp_hypercore.py +0 -0
  244. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_mcp_models.py +0 -0
  245. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_mcp_vault_validate.py +0 -0
  246. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_multicall.py +0 -0
  247. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_oracle.py +0 -0
  248. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_plasma_vault.py +0 -0
  249. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_public_api.py +0 -0
  250. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_public_type_reachability.py +0 -0
  251. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_reader_encoding.py +0 -0
  252. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_readiness_selection.py +0 -0
  253. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_revert_decoding.py +0 -0
  254. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_rewards_manager.py +0 -0
  255. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_roles.py +0 -0
  256. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_sdk_layering.py +0 -0
  257. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  258. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  259. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  260. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_crosschain_canary.py +0 -0
  261. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_crosschain_hypercore.py +0 -0
  262. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  263. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  264. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  265. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  266. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  267. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  268. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  269. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_examples_arbitrum.py +0 -0
  270. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_examples_base.py +0 -0
  271. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_examples_crosschain.py +0 -0
  272. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_external_state_executor_base.py +0 -0
  273. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  274. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  275. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  276. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_morpho_blue.py +0 -0
  277. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_pilot_v3.py +0 -0
  278. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  279. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  280. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  281. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  282. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_readers_integration.py +0 -0
  283. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_release_funds.py +0 -0
  284. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_swap_on_base.py +0 -0
  285. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  286. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_solidity_compiler.py +0 -0
  287. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_solidity_mirrors.py +0 -0
  288. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_substrates.py +0 -0
  289. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_types_and_events.py +0 -0
  290. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_unpriceable_tokens.py +0 -0
  291. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_validation.py +0 -0
  292. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_vault_dep_graph.py +0 -0
  293. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_vault_info_onchain_base.py +0 -0
  294. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  295. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_vault_simulator_blocks.py +0 -0
  296. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_web3_context.py +0 -0
  297. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/test_withdraw_manager.py +0 -0
  298. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/typed_client/consumer.py +0 -0
  299. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/tests/typed_client/rejected.py +0 -0
  300. {ipor_fusion-4.0.0.dev144 → ipor_fusion-4.0.0.dev149}/uv.lock +0 -0
@@ -441,7 +441,8 @@ are not validated.
441
441
  ## Domain rules (verified against the contracts)
442
442
 
443
443
  - `fetch_vault_data(ctx, vault, *, block=, lending=, metadata=)` is the snapshot:
444
- it resolves one block (``block``, else a numeric `ctx.default_block`, else latest),
444
+ it resolves one block (``block``, else `ctx.default_block` with a tag or hash resolved to its
445
+ number, else latest),
445
446
  runs every read on `ctx.at_block(block)` (log scans end there too) and returns
446
447
  everything validation and the ``vault info`` document need, so neither reads the
447
448
  chain. The chain is `ctx.chain_id`; ``lending=False`` skips lending health, the
@@ -94,7 +94,7 @@ placeholder wrapper.
94
94
  `aggregate3(calls)` still returns the raw `Call`), `VaultSimulator`,
95
95
  `CrosschainSimulator` and `OracleMappingReader(ctx, oracle)`.
96
96
  - Resolvers that probe the chain stay eager and return context-free wrappers:
97
- `price_oracle_middleware(ctx, oracle)`, `resolve_access_manager(ctx, vault)`,
97
+ `price_oracle_middleware(ctx, address)`, `resolve_access_manager(ctx, vault)`,
98
98
  `ExternalStateExecutor.for_vault(ctx, vault)`. A resolved oracle carries the ABI
99
99
  found on that chain and block; resolve again for another one.
100
100
  - Crosschain lanes own both contexts: `CcipLane` / `StargateLane` take `hub_ctx=` and
@@ -113,9 +113,9 @@ placeholder wrapper.
113
113
  do not see a CCIP recall or tell a parked command from a pending one. `CrosschainTransport.executor(address)`
114
114
  and `.dispatcher(address)` no longer take a context.
115
115
  - Chain-bound decoding takes the chain explicitly:
116
- `ExternalStateExecutor.pending_proposals(account, *, chain_id)`,
116
+ `ExternalStateExecutor.pending_proposals(balance_account, *, chain_id)`,
117
117
  `parse_balance_proposed(log, *, chain_id)` and
118
- `find_balance_proposed(logs, account=None, *, chain_id)` require it (3.x fell back
118
+ `find_balance_proposed(logs, balance_account=None, *, chain_id)` require it (3.x fell back
119
119
  to the wrapper's context). The decoder captures it, so `.call(ctx)`, `Multicall3`
120
120
  and `VaultSimulator.observe` stamp the same hash for the same bytes, whatever
121
121
  context executes them. Pass `ctx.chain_id` for the chain you read from.
@@ -134,7 +134,7 @@ placeholder wrapper.
134
134
  | `call.build_transaction(block=b)`, `call.estimate_gas(block=b)` | `call.build_transaction(ctx, block=b)`, `call.estimate_gas(ctx, block=b)` |
135
135
  | `call.ctx` | gone: keep the context yourself |
136
136
  | `Multicall3(ctx, address)` | `Multicall3(ctx, address=address)` |
137
- | `executor.pending_proposals(account)` | `executor.pending_proposals(account, chain_id=ctx.chain_id)` |
137
+ | `executor.pending_proposals(balance_account)` | `executor.pending_proposals(balance_account, chain_id=ctx.chain_id)` |
138
138
  | `executor.parse_balance_proposed(log)` / `find_balance_proposed(logs)` | `...(log, chain_id=...)` / `...(logs, chain_id=...)` |
139
139
  | `CcipLane(executor=, dispatcher=, spoke_chain_id=, ...)` | `CcipLane(hub_ctx=, spoke_ctx=, executor=, dispatcher=, ...)` (same for `StargateLane`) |
140
140
  | `transport.executor(ctx, address)` / `transport.dispatcher(ctx, address)` | `transport.executor(address)` / `transport.dispatcher(address)` |
@@ -174,7 +174,8 @@ The vault read is one pinned snapshot, and the chain is always the context's:
174
174
 
175
175
  - `fetch_vault_data(ctx, vault, *, block=None, lending=True, metadata=True)` takes
176
176
  the vault address (not a `PlasmaVault`) and no `chain_id`. It resolves one block
177
- (`block`, else a numeric `ctx.default_block`, else the latest) and runs every
177
+ (`block`, else `ctx.default_block`, with a `safe` / `finalized` tag or a block hash
178
+ resolved to its number, else the latest) and runs every
178
179
  read, log scans included, on `ctx.at_block(block)`; `ctx` is not modified. In 3.x
179
180
  `block_number` only selected the block reported in `VaultData`, and the reads ran
180
181
  wherever `ctx` pointed. The old `chain_id=0` switch is `lending=False`;
@@ -289,6 +290,8 @@ fails at run time:
289
290
  keyword.
290
291
  - `encode_oft_compose_msg(*, nonce=, src_eid=, amount_ld=, compose_msg=)` takes
291
292
  keyword arguments only.
293
+ - `build_oracle_mapping(ctx, vault, *, effective_block, max_depth=6)` takes
294
+ `effective_block` and `max_depth` by keyword.
292
295
  - The vault parameter is named `vault` throughout: `validate_vault`,
293
296
  `fetch_deployment_info`, `fetch_vault_lending_health`, `build_oracle_mapping`,
294
297
  `read_external_state`, `ExternalStateExecutor.for_vault`, `read_hypercore_nav`,
@@ -434,8 +437,8 @@ are cached in a `ContractCache`, injected instead of always written to `~/.cache
434
437
  explicitly (default: the file cache); pass `cache=ctx.cache` to share the
435
438
  context's policy. The CLI's text and JSON output name contracts through the
436
439
  context's cache.
437
- - `build_vault_info_json(data)` without `resolve_name` names no contract (every
438
- `contract` is `"?"`) instead of reading names from the on-disk cache; pass
440
+ - `build_vault_info_json(data)` without `resolve_name` names no contract (fuse and
441
+ balance-fuse entries show `contract` as `"?"`, substrate entries omit it) instead of reading names from the on-disk cache; pass
439
442
  `resolve_name=partial(get_contract_name, chain_id, api_key=..., cache=ctx.cache)`
440
443
  for the CLI's document.
441
444
  - `ipor_fusion.cache` is a public module now and exports `ContractCache`,
@@ -536,13 +539,19 @@ converted amounts `Amount`. A custom `VaultState` annotates the same.
536
539
  `PlasmaVault.total_supply()`, `balance_of()` and `get_total_supply_cap()` return
537
540
  `Shares` (vault shares), and `transfer` / `approve` / `transfer_from` /
538
541
  `set_total_supply_cap` take `Shares`; `ERC20` keeps `Amount`.
542
+ `Reconciliation` amounts (`balance_fuses_total`, `underlying_on_vault`,
543
+ `erc20_direct_total`, `sum`, `on_chain_total_assets`, `delta`,
544
+ `pending_withdrawals`, `implied_market_total`, `market_storage_divergence`) are
545
+ `Amount`. `FusionInstance.asset_decimals` / `underlying_token_decimals` are
546
+ `Decimals`, and `CloneArgs.redemption_delay_seconds` is a `Period`.
547
+ `VaultSimulator.current_time` is a `Timestamp`.
539
548
 
540
549
  **Vault data and helpers.** `ensure_supported_chain` takes `ChainId`.
541
550
  `fetch_unpriceable_priced_tokens` takes the oracle and underlying as
542
551
  `ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
543
552
  `get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
544
- fits), and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
545
- typed for `str` still fits). Market-keyed data is keyed by `MarketId`:
553
+ fits), and the `resolve_name` callback receives one (a callback typed for `str`
554
+ still fits). Market-keyed data is keyed by `MarketId`:
546
555
  `VaultData.market_substrates`, `dependency_graph`, `morpho_positions`,
547
556
  `aave_positions`, `market_balances`; the `VaultState` protocol,
548
557
  `fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens` and the
@@ -566,9 +575,13 @@ The Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient`
566
575
  `MarketId`; `Erc20Holding.address` is `HexAddress`, and `SubstrateInfo.address` is
567
576
  `HexAddress | None`. `LegacyPriceOracleMiddleware` takes `base_currency_decimals:
568
577
  Decimals`, and `PriceOracleMiddleware.base_currency_decimals()` returns
569
- `Call[Decimals]`. `get_logs_adaptive(chain_id=)` and `LogSource.get_logs` take
570
- `ChainId`; `quote_ccip_native_fee(block=)` and
578
+ `Call[Decimals]`. `get_logs_adaptive(chain_id=)` takes `ChainId | None` and
579
+ `LogSource.get_logs` a `ChainId`; `quote_ccip_native_fee(block=)` and
571
580
  `OracleMappingReader.asset_source_events` take a `BlockIdentifier`.
581
+ `OracleMappingReader.token_decimals`, `feed_decimals` and `vault_decimals` return
582
+ `Decimals | None` and `vault_convert_to_assets` an `Amount | None`;
583
+ `AaveV4OracleReader.decimals()` returns `Call[Decimals]`; and `OraclePrice.decimals`, `OracleNode.decimals` and
584
+ `OracleAsset.decimals` are `Decimals` (`| None` on the nodes).
572
585
 
573
586
  **Address roles.** `PlasmaVault(address)`,
574
587
  `validate_vault`, `fetch_deployment_info`, the HyperCore, lending-health and
@@ -602,7 +615,8 @@ by `ChainId` (`HYPEREVM_CHAIN_ID` is one) and `explorer.SUPPORTED_CHAINS` is a
602
615
  type-checks; `PUBLIC_ALLOCATOR_ADDRESSES` values are `ChecksumAddress`.
603
616
 
604
617
  **Markets, substrates and access.** Every `IporFusionMarkets` member is a `MarketId`.
605
- `decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
618
+ `market_name` and `format_market_label` take `MarketId`, `decode_substrate` a
619
+ `MarketId | None`.
606
620
  `AccessManager.has_role`, `grant_role`, `revoke_role` and `read_accounts_with_role`
607
621
  take `RoleId | Roles`; `RoleAccount.role_id` is `RoleId`.
608
622
 
@@ -619,6 +633,12 @@ takes a `ChecksumAddress`, and `build_morpho_blue_market_json` takes
619
633
  records is `ChecksumAddress`; optional ones (`curator`, `guardian`, `fee_recipient`,
620
634
  `performance_fee_recipient`, `management_fee_recipient`, `inner_vault`,
621
635
  `VaultFlowCap.admin`, the `VaultV2Cap` market params) are `ChecksumAddress | None`.
636
+ Token amounts on those records are `Amount` (`VaultFlowCap.max_in` / `max_out`, the
637
+ `VaultAllocation`, `VaultV1Info`, `VaultV1MarketAllocation`, `VaultV2Info` and
638
+ `MorphoApiMarket` asset amounts and caps, `VaultV2Cap.absolute_cap` / `allocation`
639
+ / `room`, `VaultV2Adapter.assets`, `cap_room`), token decimals are `Decimals` and
640
+ `MorphoApiMarket.timestamp` is a `Timestamp`. `lltv`, `fee_wad`,
641
+ `relative_cap_wad` and `fee_wei` stay `int`.
622
642
 
623
643
  **Crosschain.** The executor, dispatcher and factory wrappers, the lanes and
624
644
  `StargateCrosschainCommandFuse.enter(request_id=)` take the crosschain ids above, and
@@ -630,7 +650,25 @@ on CCIP, `active_return` → `OperationId`, `asset_id`, `tracked_position_set_ha
630
650
  `CcipObservation`, `OutboundMessage`, `CcipMessageSent`, `Any2EVMMessage`,
631
651
  `VerifierResult`, `Packet.guid`, `TaxiMessage`, `StargateChain`; and the helpers
632
652
  `fetch_verifier_result(message_id=)`, `lz_receive_calldata(guid=)` and
633
- `lz_compose_calldata(guid=)` take a `MessageId`. Decoded event values
653
+ `lz_compose_calldata(guid=)` take a `MessageId`. Lane times are typed:
654
+ `last_approved_observed_at()` returns a `Timestamp` and `staleness_max()` (with
655
+ `STALENESS_MAX` / `BALANCE_STALENESS_MAX` on the executors) a `Period`, and so do
656
+ the `LaneHubState` fields; `attestation(remote_timestamp=, expiry=)` and
657
+ `BalanceObservation` take `Timestamp`, `needs_attestation(now=, margin=)` a
658
+ `Timestamp` and a `Period`. The other durations are `Period` too: the executors'
659
+ `min_update_interval()`, `transfer_staleness_max()` and `deployment_ttl()`, both
660
+ factories' `config_delay()`, and the time fields of `ExecutorInitParams` and
661
+ `SafetyConfig` (`rescue_delay`, the staleness fields, `min_update_interval`,
662
+ `deployment_ttl`); `in_flight_since()`, `last_approval_at()`, `oldest_pending_at()`
663
+ and `pending_deployment_created_at()` return a `Timestamp`. Decimals are
664
+ `Decimals`: `local_decimals()` / `shared_decimals()` on the executor and dispatcher,
665
+ `CcipChain.local_decimals`, `StargateChain.local_decimals` / `shared_decimals`, the
666
+ `shared_decimals` of `CcipCrosschainFactory.asset_config()`, `schedule_asset` and
667
+ `execute_asset`. Token amounts are `Amount` (`EVMTokenAmount.amount`,
668
+ `TokenTransferV1.amount`, `TaxiMessage.amount_sd`, `OutboundMessage.token_amount`,
669
+ `StargateChain.to_local_decimals`), and `pending_request_shares()` returns
670
+ `Shares`. Counters, sequences, epochs, versions, bps and gas limits stay `int`.
671
+ Decoded event values
634
672
  (`ccip_events` → `CcipEvent.values`) stay a generic mapping: convert a value
635
673
  before passing it on.
636
674
 
@@ -653,25 +691,35 @@ fuse's `eid=` take it (`LayerZeroEid(30101)`). CCIP chain selectors are
653
691
  `read_hypercore_transfer_snapshot` and the helpers `system_address`,
654
692
  `precompile_index_of`, `perp_dex_of`, `is_native_perp_asset`, `is_hip3_asset` take the
655
693
  HyperCore ids; `HyperCorePerpAssetInfo`, `HyperCorePerpMarket`, `HyperCoreSpotLeg`,
656
- `HyperCorePendingState` and `HyperCoreTransferSnapshot` carry them.
694
+ `HyperCorePendingState` and `HyperCoreTransferSnapshot` carry them. Token decimals
695
+ (`HyperCoreTokenInfo.sz_decimals` / `wei_decimals` / `evm_extra_wei_decimals`,
696
+ `HyperCorePerpAssetInfo.sz_decimals`, `HyperCoreSpotLeg.wei_decimals`) are
697
+ `Decimals`, and `HyperCorePendingState.pending_until` / `current_timestamp` are
698
+ `Timestamp`; Core amounts, positions, WAD values and L1 block numbers stay `int`.
657
699
 
658
700
  **External state.** `ExternalStateExecutor.proposal_hash` returns and `confirm_balance`
659
701
  takes a `ProposalHash`; `BalanceProposal.proposal_hash` is a `ProposalHash`;
660
- `ExternalStateEvent.transaction_hash` is a `TxHash`.
702
+ `ExternalStateEvent.transaction_hash` is a `TxHash | None`.
661
703
  `ExternalStateVaultState.last_checked_custodian_timestamp`,
662
704
  `BalanceFuseSnapshot.last_custodian_update_timestamp`, `BalanceProposal.proposed_at`,
663
705
  `last_updated()` and `get_oldest_update_timestamp()` are web3's `Timestamp`, and
664
706
  `ExternalStateExecutor.proposal_hash(proposed_at=)` takes one.
707
+ `ExternalStateExecutor.staleness_max()` / `min_update_interval()` return a `Period`,
708
+ and `ExternalStateSubstrates.staleness_max` / `min_update_interval` take one
709
+ (`Period.DAY`).
665
710
 
666
- **Errors and explorer.** `TransactionError(tx_hash=)` takes a `TxHash`,
711
+ **Errors and explorer.** `TransactionError(tx_hash=)` takes a `TxHash | None`,
667
712
  `LogScanError(address=)` a `ChecksumAddress`; `get_revert_reason` takes a `TxHash`;
668
713
  `get_deployment_tx` returns a `TxHash` (a malformed hash from Etherscan is reported
669
714
  as the error `malformed-tx-hash`).
670
715
 
671
716
  **MCP response models** (Python constructors only; the JSON schemas are unchanged
672
717
  except as listed under [Behavior changes](#behavior-changes)): `chain_id` fields are
673
- `ChainId`, `role_id` `RoleId`, `market_id` `MarketId`. Calling an MCP tool function
674
- directly from Python takes `ChainId`.
718
+ `ChainId`, `role_id` `RoleId`, `market_id` `MarketId` (except
719
+ `LendingMarketHealth.market_id`, which stays `str | int`: a Morpho market id is a
720
+ string). Calling an MCP tool function
721
+ directly from Python takes `ChainId`. Their amount, decimals and time fields stay
722
+ `int`: they are JSON output models, and the SDK values above fit them unchanged.
675
723
 
676
724
  ## Behavior changes
677
725
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev144
3
+ Version: 4.0.0.dev149
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
@@ -68,7 +68,7 @@ from typing import Any, NamedTuple
68
68
  from eth_abi.abi import encode as abi_encode
69
69
  from eth_utils import keccak
70
70
  from web3 import Web3
71
- from web3.types import BlockIdentifier, RPCEndpoint
71
+ from web3.types import BlockIdentifier, RPCEndpoint, Timestamp
72
72
 
73
73
  from ipor_fusion import (
74
74
  ERC20,
@@ -110,6 +110,7 @@ from ipor_fusion.market_ids import IporFusionMarkets
110
110
  from ipor_fusion.types import (
111
111
  Amount,
112
112
  ChainId,
113
+ Decimals,
113
114
  HyperCoreAsset,
114
115
  HyperCoreClientOrderId,
115
116
  MarketId,
@@ -649,14 +650,14 @@ def _transport() -> CcipTransport:
649
650
  ARBITRUM_CCIP_SELECTOR,
650
651
  ARBITRUM_CCIP_ROUTER,
651
652
  ARBITRUM_USDC,
652
- 6,
653
+ Decimals(6),
653
654
  ),
654
655
  CcipChain(
655
656
  HYPEREVM_CHAIN_ID,
656
657
  HYPEREVM_CCIP_SELECTOR,
657
658
  HYPEREVM_CCIP_ROUTER,
658
659
  HYPEREVM_USDC,
659
- 6,
660
+ Decimals(6),
660
661
  ),
661
662
  )
662
663
  )
@@ -791,7 +792,7 @@ def _attest(flow: _Tail, tag: str, observation_label: str, *, hub_idle: int = 0)
791
792
  observation,
792
793
  remote_block=flow.spoke.current_block_number,
793
794
  remote_timestamp=flow.spoke.current_time,
794
- expiry=flow.hub.current_time + PROPOSAL_TTL,
795
+ expiry=Timestamp(flow.hub.current_time + PROPOSAL_TTL),
795
796
  )
796
797
  # SIMULATION ONLY: two keyless addresses driven from one process; live
797
798
  # they are two signers, which is the point of the two-key design.
@@ -66,6 +66,7 @@ import sys
66
66
  from typing import NamedTuple
67
67
 
68
68
  from web3 import Web3
69
+ from web3.types import Timestamp
69
70
 
70
71
  from ipor_fusion import (
71
72
  ERC20,
@@ -85,7 +86,7 @@ from ipor_fusion import (
85
86
  parse_plasma_vault_address,
86
87
  )
87
88
  from ipor_fusion.market_ids import IporFusionMarkets
88
- from ipor_fusion.types import Amount, ChainId, MarketId, Period, Shares
89
+ from ipor_fusion.types import Amount, ChainId, Decimals, MarketId, Period, Shares
89
90
 
90
91
  log = logging.getLogger("crosschain_ccip_usdc_arbitrum_hyperevm")
91
92
 
@@ -222,14 +223,14 @@ def transport() -> CcipTransport:
222
223
  ARBITRUM_CCIP_SELECTOR,
223
224
  ARBITRUM_CCIP_ROUTER,
224
225
  ARBITRUM_USDC,
225
- 6,
226
+ Decimals(6),
226
227
  ),
227
228
  CcipChain(
228
229
  HYPEREVM_CHAIN_ID,
229
230
  HYPEREVM_CCIP_SELECTOR,
230
231
  HYPEREVM_CCIP_ROUTER,
231
232
  HYPEREVM_USDC,
232
- 6,
233
+ Decimals(6),
233
234
  ),
234
235
  )
235
236
  )
@@ -431,7 +432,7 @@ def _attest(flow: _Flow, tag: str, observation_label: str, *, hub_idle: int = 0)
431
432
  observation,
432
433
  remote_block=flow.spoke.current_block_number,
433
434
  remote_timestamp=flow.spoke.current_time,
434
- expiry=flow.hub.current_time + PROPOSAL_TTL,
435
+ expiry=Timestamp(flow.hub.current_time + PROPOSAL_TTL),
435
436
  )
436
437
  # SIMULATION ONLY: the proposer and the approver are two addresses with no
437
438
  # keys here, driven from one process. Live they are two separate signers,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev144"
3
+ version = "4.0.0.dev149"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -449,7 +449,9 @@ def oracle_mapping(
449
449
  raise click.UsageError(str(exc)) from exc
450
450
 
451
451
  try:
452
- mapping = build_oracle_mapping(ctx, vault_address, effective_block, max_depth)
452
+ mapping = build_oracle_mapping(
453
+ ctx, vault_address, effective_block=effective_block, max_depth=max_depth
454
+ )
453
455
  except UnsupportedVaultVersionError as exc:
454
456
  raise click.ClickException(str(exc)) from exc
455
457
 
@@ -35,6 +35,7 @@ from ipor_fusion.types import (
35
35
  ZERO_ADDRESS,
36
36
  Amount,
37
37
  ChainId,
38
+ Period,
38
39
  PlasmaVaultAddress,
39
40
  ProposalHash,
40
41
  )
@@ -371,10 +372,10 @@ class ExternalStateExecutor(ContractWrapper):
371
372
  "getOldestUpdateTimestamp()", output_types=["uint256"], decoder=Timestamp
372
373
  )
373
374
 
374
- def staleness_max(self) -> Call[int]:
375
+ def staleness_max(self) -> Call[Period]:
375
376
  """Seconds after which a custodian report counts as stale: the pre-hook
376
377
  blocks the vault's gated user operations past it."""
377
- return self._view("stalenessMax()", output_types=["uint256"])
378
+ return self._view("stalenessMax()", output_types=["uint256"], decoder=Period)
378
379
 
379
380
  def big_change_bps(self) -> Call[int]:
380
381
  """Basis points of total-balance change between two custodian updates
@@ -382,13 +383,16 @@ class ExternalStateExecutor(ContractWrapper):
382
383
  return self._view("bigChangeBps()", output_types=["uint256"])
383
384
 
384
385
  def dust_threshold(self) -> Call[int]:
385
- """Underlying units below which a leftover is dust, not a balance."""
386
+ """Dust threshold as a percent of one underlying token (100 = one whole
387
+ token); a leftover below it is dust, not a balance."""
386
388
  return self._view("dustThreshold()", output_types=["uint256"])
387
389
 
388
- def min_update_interval(self) -> Call[int]:
390
+ def min_update_interval(self) -> Call[Period]:
389
391
  """Seconds a balance account must wait between two custodian
390
392
  updates."""
391
- return self._view("minUpdateInterval()", output_types=["uint256"])
393
+ return self._view(
394
+ "minUpdateInterval()", output_types=["uint256"], decoder=Period
395
+ )
392
396
 
393
397
  def nonce(self) -> Call[int]:
394
398
  """Monotonic proposal nonce, incremented on every `proposeBalance`.
@@ -35,6 +35,7 @@ from web3.types import TxReceipt
35
35
  from ipor_fusion.core.contract import Call, ContractWrapper
36
36
  from ipor_fusion.types import (
37
37
  AccessManagerAddress,
38
+ Decimals,
38
39
  Period,
39
40
  PlasmaVaultAddress,
40
41
  parse_access_manager_address,
@@ -51,7 +52,7 @@ class CloneArgs:
51
52
  asset_name: str
52
53
  asset_symbol: str
53
54
  underlying_token: ChecksumAddress
54
- redemption_delay_seconds: int
55
+ redemption_delay_seconds: Period
55
56
  owner: ChecksumAddress
56
57
  dao_fee_package_index: int
57
58
 
@@ -64,10 +65,10 @@ class FusionInstance:
64
65
  version: int
65
66
  asset_name: str
66
67
  asset_symbol: str
67
- asset_decimals: int
68
+ asset_decimals: Decimals
68
69
  underlying_token: ChecksumAddress
69
70
  underlying_token_symbol: str
70
- underlying_token_decimals: int
71
+ underlying_token_decimals: Decimals
71
72
  initial_owner: ChecksumAddress
72
73
  plasma_vault: PlasmaVaultAddress
73
74
  plasma_vault_base: ChecksumAddress
@@ -118,10 +119,10 @@ _INSTANCE_FIELDS: dict[str, Callable[[Any], Any]] = {
118
119
  "version": _integer,
119
120
  "asset_name": _text,
120
121
  "asset_symbol": _text,
121
- "asset_decimals": _integer,
122
+ "asset_decimals": lambda value: Decimals(_integer(value)),
122
123
  "underlying_token": _address,
123
124
  "underlying_token_symbol": _text,
124
- "underlying_token_decimals": _integer,
125
+ "underlying_token_decimals": lambda value: Decimals(_integer(value)),
125
126
  "initial_owner": _address,
126
127
  "plasma_vault": lambda value: parse_plasma_vault_address(_text(value)),
127
128
  "plasma_vault_base": _address,
@@ -185,10 +186,10 @@ def _fusion_instance_decoder(values: tuple) -> FusionInstance:
185
186
  version=version,
186
187
  asset_name=asset_name,
187
188
  asset_symbol=asset_symbol,
188
- asset_decimals=asset_decimals,
189
+ asset_decimals=Decimals(asset_decimals),
189
190
  underlying_token=addr(underlying_token),
190
191
  underlying_token_symbol=underlying_token_symbol,
191
- underlying_token_decimals=underlying_token_decimals,
192
+ underlying_token_decimals=Decimals(underlying_token_decimals),
192
193
  initial_owner=addr(initial_owner),
193
194
  plasma_vault=PlasmaVaultAddress(addr(plasma_vault)),
194
195
  plasma_vault_base=addr(plasma_vault_base),
@@ -347,10 +348,10 @@ class FusionFactory(ContractWrapper):
347
348
  version=int(values[1]),
348
349
  asset_name=values[2],
349
350
  asset_symbol=values[3],
350
- asset_decimals=int(values[4]),
351
+ asset_decimals=Decimals(int(values[4])),
351
352
  underlying_token=Web3.to_checksum_address(values[5]),
352
353
  underlying_token_symbol=values[6],
353
- underlying_token_decimals=int(values[7]),
354
+ underlying_token_decimals=Decimals(int(values[7])),
354
355
  initial_owner=Web3.to_checksum_address(values[8]),
355
356
  plasma_vault=PlasmaVaultAddress(Web3.to_checksum_address(values[9])),
356
357
  plasma_vault_base=Web3.to_checksum_address(values[10]),
@@ -390,7 +391,7 @@ class FusionFactory(ContractWrapper):
390
391
  asset_name=asset_name,
391
392
  asset_symbol=asset_symbol,
392
393
  underlying_token=Web3.to_checksum_address(underlying_token),
393
- redemption_delay_seconds=int(redemption_delay_seconds),
394
+ redemption_delay_seconds=Period(redemption_delay_seconds),
394
395
  owner=Web3.to_checksum_address(owner),
395
396
  dao_fee_package_index=int(dao_fee_package_index),
396
397
  )
@@ -10,7 +10,7 @@ from eth_typing import ChecksumAddress
10
10
  from eth_utils import keccak
11
11
  from hexbytes import HexBytes
12
12
  from web3 import Web3
13
- from web3.types import BlockIdentifier, Nonce, RPCEndpoint
13
+ from web3.types import BlockIdentifier, Nonce, RPCEndpoint, Timestamp
14
14
  from web3.utils.address import get_create_address
15
15
 
16
16
  from ipor_fusion.core.contract import Call, _encode_calldata
@@ -221,13 +221,13 @@ class VaultSimulator:
221
221
  return any(block.calls for block in self._blocks)
222
222
 
223
223
  @property
224
- def current_time(self) -> int:
224
+ def current_time(self) -> Timestamp:
225
225
  """Timestamp of the latest sent block or an explicit pending time.
226
226
 
227
227
  A pending block without an explicit time will run 12 seconds later
228
228
  once it contains a call.
229
229
  """
230
- return self._current_time()
230
+ return Timestamp(self._current_time())
231
231
 
232
232
  @property
233
233
  def current_block_number(self) -> int:
@@ -21,7 +21,7 @@ from web3 import Web3
21
21
 
22
22
  from ipor_fusion.core.contract import _encode_calldata
23
23
  from ipor_fusion.crosschain.logs import log_bytes
24
- from ipor_fusion.types import CcipChainSelector, MessageId
24
+ from ipor_fusion.types import Amount, CcipChainSelector, MessageId
25
25
 
26
26
  #: topic0 of ``OnRamp 2.0.0``'s ``CCIPMessageSent``; ``destChainSelector``,
27
27
  #: ``sender`` and ``messageId`` are indexed.
@@ -72,7 +72,7 @@ class TokenTransferV1:
72
72
  """``MessageV1Codec.TokenTransferV1``. Address fields are raw bytes as
73
73
  encoded for the destination chain (20 bytes on EVM)."""
74
74
 
75
- amount: int
75
+ amount: Amount
76
76
  source_pool_address: bytes
77
77
  source_token_address: bytes
78
78
  dest_token_address: bytes
@@ -84,7 +84,7 @@ class TokenTransferV1:
84
84
  if reader.uint(1) != _MESSAGE_VERSION:
85
85
  raise ValueError("unsupported CCIP token transfer version")
86
86
  return cls(
87
- amount=reader.uint(32),
87
+ amount=Amount(reader.uint(32)),
88
88
  source_pool_address=reader.prefixed(1),
89
89
  source_token_address=reader.prefixed(1),
90
90
  dest_token_address=reader.prefixed(1),
@@ -207,7 +207,7 @@ class EVMTokenAmount:
207
207
  """``CcipClient.EVMTokenAmount``."""
208
208
 
209
209
  token: ChecksumAddress
210
- amount: int
210
+ amount: Amount
211
211
 
212
212
 
213
213
  @dataclass(frozen=True, slots=True)
@@ -8,6 +8,7 @@ from dataclasses import dataclass
8
8
  from eth_abi import encode
9
9
  from eth_typing import ChecksumAddress
10
10
  from eth_utils import keccak
11
+ from web3.types import Timestamp
11
12
 
12
13
  from ipor_fusion.core.contract import Call
13
14
  from ipor_fusion.crosschain.contracts import (
@@ -24,8 +25,10 @@ from ipor_fusion.types import (
24
25
  CcipChainSelector,
25
26
  ChainId,
26
27
  CommandId,
28
+ Decimals,
27
29
  MessageId,
28
30
  OperationId,
31
+ Period,
29
32
  PlasmaVaultAddress,
30
33
  PositionSetHash,
31
34
  ProposalId,
@@ -92,11 +95,11 @@ class SafetyConfig:
92
95
  rescue_guardian: ChecksumAddress
93
96
  balance_proposer: ChecksumAddress
94
97
  balance_approver: ChecksumAddress
95
- rescue_delay: int
96
- balance_staleness_max: int
98
+ rescue_delay: Period
99
+ balance_staleness_max: Period
97
100
  big_change_bps: int
98
- min_update_interval: int
99
- transfer_staleness_max: int
101
+ min_update_interval: Period
102
+ transfer_staleness_max: Period
100
103
 
101
104
  def as_tuple(self) -> tuple:
102
105
  return (
@@ -143,8 +146,10 @@ class CcipCrosschainExecutor(CrosschainExecutor):
143
146
  def decimal_conversion_rate(self) -> Call[int]:
144
147
  return self._view("DECIMAL_CONVERSION_RATE()", output_types=["uint256"])
145
148
 
146
- def balance_staleness_max(self) -> Call[int]:
147
- return self._view("BALANCE_STALENESS_MAX()", output_types=["uint256"])
149
+ def balance_staleness_max(self) -> Call[Period]:
150
+ return self._view(
151
+ "BALANCE_STALENESS_MAX()", output_types=["uint256"], decoder=Period
152
+ )
148
153
 
149
154
  def ccip_route(self, chain_id: ChainId) -> Call[CcipRouteConfig]:
150
155
  return self._view(
@@ -228,8 +233,13 @@ class CcipCrosschainExecutor(CrosschainExecutor):
228
233
  decoder=CommandStatus,
229
234
  )
230
235
 
231
- def oldest_pending_at(self, chain_id: ChainId) -> Call[int]:
232
- return self._view("oldestPendingAt(uint256)", chain_id, output_types=["uint64"])
236
+ def oldest_pending_at(self, chain_id: ChainId) -> Call[Timestamp]:
237
+ return self._view(
238
+ "oldestPendingAt(uint256)",
239
+ chain_id,
240
+ output_types=["uint64"],
241
+ decoder=Timestamp,
242
+ )
233
243
 
234
244
  def pending_transfer_count(self, chain_id: ChainId) -> Call[int]:
235
245
  return self._view(
@@ -387,13 +397,13 @@ class CcipCrosschainFactory(CrosschainFactory):
387
397
 
388
398
  def asset_config(
389
399
  self, asset_id: AssetId
390
- ) -> Call[tuple[ChecksumAddress, int, bool]]:
400
+ ) -> Call[tuple[ChecksumAddress, Decimals, bool]]:
391
401
  """``(token, sharedDecimals, enabled)`` for an asset id."""
392
402
  return self._view(
393
403
  "assetConfig(bytes32)",
394
404
  asset_id,
395
405
  output_types=["address", "uint8", "bool"],
396
- decoder=lambda v: (_address(v[0]), int(v[1]), bool(v[2])),
406
+ decoder=lambda v: (_address(v[0]), Decimals(v[1]), bool(v[2])),
397
407
  )
398
408
 
399
409
  def is_dispatcher(self, dispatcher: ChecksumAddress) -> Call[bool]:
@@ -412,22 +422,23 @@ class CcipCrosschainFactory(CrosschainFactory):
412
422
 
413
423
  def pending_deployment_created_at(
414
424
  self, executor: ChecksumAddress, dst_chain_id: ChainId
415
- ) -> Call[int]:
425
+ ) -> Call[Timestamp]:
416
426
  return self._view(
417
427
  "pendingDeploymentCreatedAt(address,uint256)",
418
428
  executor,
419
429
  dst_chain_id,
420
430
  output_types=["uint64"],
431
+ decoder=Timestamp,
421
432
  )
422
433
 
423
434
  def owner(self) -> Call[ChecksumAddress]:
424
435
  return self._view("OWNER()", output_types=["address"], decoder=_address)
425
436
 
426
- def config_delay(self) -> Call[int]:
437
+ def config_delay(self) -> Call[Period]:
427
438
  """Timelock of every scheduled asset, factory route and dispatcher
428
439
  deployment gas limit: 24 hours in source, 300 seconds on the test
429
440
  builds."""
430
- return self._view("CONFIG_DELAY()", output_types=["uint256"])
441
+ return self._view("CONFIG_DELAY()", output_types=["uint256"], decoder=Period)
431
442
 
432
443
  def executor_creation_code_hash(self) -> Call[bytes]:
433
444
  """keccak of the executor creation code the factory stores. It tells
@@ -466,7 +477,7 @@ class CcipCrosschainFactory(CrosschainFactory):
466
477
  )
467
478
 
468
479
  def schedule_asset(
469
- self, asset_id: AssetId, token: ChecksumAddress, shared_decimals: int
480
+ self, asset_id: AssetId, token: ChecksumAddress, shared_decimals: Decimals
470
481
  ) -> Call[None]:
471
482
  """Owner-only; executable after ``config_delay`` with the same arguments."""
472
483
  return self._write(
@@ -474,7 +485,7 @@ class CcipCrosschainFactory(CrosschainFactory):
474
485
  )
475
486
 
476
487
  def execute_asset(
477
- self, asset_id: AssetId, token: ChecksumAddress, shared_decimals: int
488
+ self, asset_id: AssetId, token: ChecksumAddress, shared_decimals: Decimals
478
489
  ) -> Call[None]:
479
490
  return self._write(
480
491
  "executeAsset(bytes32,address,uint8)", asset_id, token, shared_decimals
@@ -22,7 +22,7 @@ from ipor_fusion.crosschain.fuses.ccip import (
22
22
  )
23
23
  from ipor_fusion.crosschain.lane import CrosschainLane, LaneFuses, LaneObservation
24
24
  from ipor_fusion.crosschain.messages import CommandStatus, CrosschainTransportKind
25
- from ipor_fusion.types import Amount, ProposalId
25
+ from ipor_fusion.types import Amount, Period, ProposalId
26
26
 
27
27
 
28
28
  class CcipLane(CrosschainLane):
@@ -89,7 +89,7 @@ class CcipLane(CrosschainLane):
89
89
  ),
90
90
  )
91
91
 
92
- def staleness_max(self) -> Call[int]:
92
+ def staleness_max(self) -> Call[Period]:
93
93
  return self.executor.balance_staleness_max()
94
94
 
95
95
  def default_send(self, *, token: bool) -> CcipSendParams: