ipor-fusion 4.0.0.dev303__tar.gz → 4.0.0.dev308__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 (359) hide show
  1. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/PKG-INFO +2 -2
  2. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/README.md +1 -1
  3. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/pyproject.toml +1 -1
  4. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/__init__.py +4 -0
  5. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/__init__.py +4 -0
  6. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/access.py +104 -1
  7. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_access_manager.py +98 -0
  8. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_public_api.py +4 -0
  9. ipor_fusion-4.0.0.dev308/tests/test_simulate_timelock_base.py +51 -0
  10. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.claude-plugin/marketplace.json +0 -0
  11. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.claude-plugin/plugin.json +0 -0
  12. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.env.example +0 -0
  13. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.gemini/settings.json +0 -0
  14. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.git-blame-ignore-revs +0 -0
  15. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.github/workflows/cd.yml +0 -0
  16. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.github/workflows/ci.yml +0 -0
  17. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.github/workflows/commit-lint.yml +0 -0
  18. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.github/workflows/notify-slack.yml +0 -0
  19. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.github/workflows/python-build.yml +0 -0
  20. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.github/workflows/python-publish.yml +0 -0
  21. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.github/workflows/release.yml +0 -0
  22. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.github/workflows/report-slack.yml +0 -0
  23. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.gitignore +0 -0
  24. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.mcp.json +0 -0
  25. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.pre-commit-config.yaml +0 -0
  26. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/.python-version +0 -0
  27. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/AGENTS.md +0 -0
  28. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/CHANGELOG.md +0 -0
  29. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/CLAUDE.md +0 -0
  30. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/CODEOWNERS +0 -0
  31. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/LICENSE +0 -0
  32. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/MIGRATION_4.md +0 -0
  33. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/build.sh +0 -0
  34. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/examples/CONTRIBUTING.md +0 -0
  35. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/examples/README.md +0 -0
  36. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/examples/advanced_euler_v2_credit_market_base.py +0 -0
  37. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
  38. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
  39. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
  40. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/examples/external_state_margin_leg_base.py +0 -0
  41. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/examples/simple_aave_v3_supply_base.py +0 -0
  42. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/pytest.ini +0 -0
  43. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/scripts/commit-check.sh +0 -0
  44. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/scripts/crosschain_readiness.py +0 -0
  45. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/skills/ipor-deploy-vault/SKILL.md +0 -0
  46. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/about.py +0 -0
  47. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cache.py +0 -0
  48. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/chains.py +0 -0
  49. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/__init__.py +0 -0
  50. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/address_cmd.py +0 -0
  51. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
  52. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/config_cmd.py +0 -0
  53. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/config_store.py +0 -0
  54. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/main.py +0 -0
  55. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/market_cmd.py +0 -0
  56. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/options.py +0 -0
  57. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
  58. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/preflight_cmd.py +0 -0
  59. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/vault_cmd.py +0 -0
  60. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/vault_health.py +0 -0
  61. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/vault_info_view.py +0 -0
  62. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
  63. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/cli/vault_rendering.py +0 -0
  64. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/config/__init__.py +0 -0
  65. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/config/roles.py +0 -0
  66. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/address.py +0 -0
  67. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/context.py +0 -0
  68. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/context_manager.py +0 -0
  69. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/contract.py +0 -0
  70. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/erc20.py +0 -0
  71. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/external_state_executor.py +0 -0
  72. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/fee_manager.py +0 -0
  73. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/fuse.py +0 -0
  74. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/fuse_action.py +0 -0
  75. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/fuse_decoding.py +0 -0
  76. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/fusion_factory.py +0 -0
  77. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/logs.py +0 -0
  78. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/multicall.py +0 -0
  79. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/oracle.py +0 -0
  80. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/plasma_vault.py +0 -0
  81. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/rewards_manager.py +0 -0
  82. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/simulation.py +0 -0
  83. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/simulation_result.py +0 -0
  84. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/core/withdraw_manager.py +0 -0
  85. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/__init__.py +0 -0
  86. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
  87. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
  88. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
  89. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
  90. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
  91. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
  92. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
  93. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
  94. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
  95. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/contracts.py +0 -0
  96. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/discovery.py +0 -0
  97. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/errors.py +0 -0
  98. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
  99. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
  100. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
  101. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
  102. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/lane.py +0 -0
  103. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/logs.py +0 -0
  104. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/messages.py +0 -0
  105. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/simulation.py +0 -0
  106. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
  107. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
  108. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
  109. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
  110. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
  111. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/crosschain/transport.py +0 -0
  112. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/dependency_graph.py +0 -0
  113. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/entry_cli.py +0 -0
  114. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/entry_mcp.py +0 -0
  115. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/errors.py +0 -0
  116. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/explorer.py +0 -0
  117. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/field_docs.py +0 -0
  118. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/__init__.py +0 -0
  119. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/aave_v3.py +0 -0
  120. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/async_action.py +0 -0
  121. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/base.py +0 -0
  122. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/compound_v3.py +0 -0
  123. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/erc4626.py +0 -0
  124. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/euler_v2.py +0 -0
  125. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/events.py +0 -0
  126. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/external_state.py +0 -0
  127. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
  128. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
  129. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
  130. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/hypercore.py +0 -0
  131. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
  132. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/merkl.py +0 -0
  133. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/morpho.py +0 -0
  134. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
  135. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
  136. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/fuses/universal.py +0 -0
  137. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/guide/__init__.py +0 -0
  138. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/guide/architecture.md +0 -0
  139. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/guide/glossary.md +0 -0
  140. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/guide/invariants.md +0 -0
  141. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/guide/quickstart.md +0 -0
  142. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/hypercore/__init__.py +0 -0
  143. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/market_ids.py +0 -0
  144. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/market_info.py +0 -0
  145. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/mcp/__init__.py +0 -0
  146. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/mcp/guide.py +0 -0
  147. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/mcp/models.py +0 -0
  148. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/mcp/server.py +0 -0
  149. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/morpho_api.py +0 -0
  150. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/preflight.py +0 -0
  151. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/py.typed +0 -0
  152. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/__init__.py +0 -0
  153. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/aave_v3.py +0 -0
  154. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/aave_v4.py +0 -0
  155. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/compound_v3.py +0 -0
  156. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/hypercore.py +0 -0
  157. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
  158. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/lending_health.py +0 -0
  159. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/morpho.py +0 -0
  160. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
  161. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
  162. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
  163. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/position_manager.py +0 -0
  164. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/ramses_v2.py +0 -0
  165. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
  166. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/readers/unpriceable.py +0 -0
  167. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/registry.py +0 -0
  168. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/substrates.py +0 -0
  169. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/types.py +0 -0
  170. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/validation.py +0 -0
  171. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/vault_data.py +0 -0
  172. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/vault_erc20.py +0 -0
  173. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/vault_info.py +0 -0
  174. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/vault_managers.py +0 -0
  175. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/vault_positions.py +0 -0
  176. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/src/ipor_fusion/vault_reads.py +0 -0
  177. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/UniswapV3UniversalRouter.py +0 -0
  178. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_crosschain.py +0 -0
  179. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_crosschain_lifecycle.py +0 -0
  180. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_crosschain_recovery.py +0 -0
  181. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_euler_v2.py +0 -0
  182. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_event_args.py +0 -0
  183. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_fake_rpc.py +0 -0
  184. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_foundry.py +0 -0
  185. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_golden.py +0 -0
  186. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_guide.py +0 -0
  187. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_hypercore_model.py +0 -0
  188. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_hypercore_shadow.py +0 -0
  189. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_live.py +0 -0
  190. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_multicall.py +0 -0
  191. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_oracle_mapping_fake.py +0 -0
  192. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_simulate.py +0 -0
  193. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/_validation.py +0 -0
  194. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/addresses.py +0 -0
  195. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/conftest.py +0 -0
  196. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/constants.py +0 -0
  197. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/hypercore_hip3_flow.json +0 -0
  198. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/hypercore_market55.json +0 -0
  199. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/hypercore_official.json +0 -0
  200. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
  201. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
  202. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
  203. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
  204. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
  205. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
  206. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
  207. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
  208. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_blue.json +0 -0
  209. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
  210. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
  211. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
  212. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
  213. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
  214. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
  215. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
  216. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/simulate_v1_request.json +0 -0
  217. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/clean.json +0 -0
  218. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
  219. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/clean.txt +0 -0
  220. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
  221. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
  222. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
  223. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
  224. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/fetched.json +0 -0
  225. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
  226. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/full.json +0 -0
  227. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
  228. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/full.txt +0 -0
  229. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/rich.json +0 -0
  230. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
  231. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/fixtures/vault_info_golden/rich.txt +0 -0
  232. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_about.py +0 -0
  233. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cache.py +0 -0
  234. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_chains.py +0 -0
  235. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_address.py +0 -0
  236. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_changelog.py +0 -0
  237. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_commands.py +0 -0
  238. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_config_store.py +0 -0
  239. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_helpers.py +0 -0
  240. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_hypercore.py +0 -0
  241. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_lending_health.py +0 -0
  242. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_market_golden.py +0 -0
  243. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_market_morpho_blue.py +0 -0
  244. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_mcp_parity.py +0 -0
  245. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_oracle_mapping.py +0 -0
  246. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_oracle_mapping_golden.py +0 -0
  247. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_pending_requests_scan.py +0 -0
  248. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_preflight.py +0 -0
  249. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_role_accounts.py +0 -0
  250. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_vault_info_golden.py +0 -0
  251. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_vault_predates_getters.py +0 -0
  252. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_cli_vault_validate.py +0 -0
  253. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_contract_call.py +0 -0
  254. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_contract_encoder.py +0 -0
  255. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_ccip_events.py +0 -0
  256. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_ccip_indexer.py +0 -0
  257. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_ccip_quote.py +0 -0
  258. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_chainlink.py +0 -0
  259. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_contracts.py +0 -0
  260. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_encoding.py +0 -0
  261. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_errors.py +0 -0
  262. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_lane.py +0 -0
  263. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_layering.py +0 -0
  264. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_readiness.py +0 -0
  265. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_readiness_report.py +0 -0
  266. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_crosschain_transport.py +0 -0
  267. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_docs_typing.py +0 -0
  268. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_erc20.py +0 -0
  269. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_error_hierarchy.py +0 -0
  270. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_examples_composed_hypercore.py +0 -0
  271. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_examples_crosschain.py +0 -0
  272. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_examples_euler_looping_arbitrum.py +0 -0
  273. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_examples_vaults.py +0 -0
  274. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_explorer.py +0 -0
  275. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_external_state_events.py +0 -0
  276. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_external_state_executor.py +0 -0
  277. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_fee_manager.py +0 -0
  278. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_fuse_decoding.py +0 -0
  279. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_fuse_encoding.py +0 -0
  280. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_fusion_instance.py +0 -0
  281. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_governance_wrappers.py +0 -0
  282. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_guide.py +0 -0
  283. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_hypercore_deployment.py +0 -0
  284. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_hypercore_events.py +0 -0
  285. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_hypercore_flow.py +0 -0
  286. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_hypercore_live.py +0 -0
  287. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_hypercore_outcome.py +0 -0
  288. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_hypercore_readers.py +0 -0
  289. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_hypercore_simulation.py +0 -0
  290. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_logs.py +0 -0
  291. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_mcp_address.py +0 -0
  292. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_mcp_hypercore.py +0 -0
  293. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_mcp_models.py +0 -0
  294. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_mcp_preflight.py +0 -0
  295. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_mcp_server.py +0 -0
  296. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_mcp_vault_validate.py +0 -0
  297. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_multicall.py +0 -0
  298. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_oracle.py +0 -0
  299. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_oracle_mapping.py +0 -0
  300. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_plasma_vault.py +0 -0
  301. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_preflight.py +0 -0
  302. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_public_type_reachability.py +0 -0
  303. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_reader_encoding.py +0 -0
  304. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_readiness_selection.py +0 -0
  305. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_registry.py +0 -0
  306. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_revert_decoding.py +0 -0
  307. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_rewards_manager.py +0 -0
  308. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_roles.py +0 -0
  309. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_sdk_layering.py +0 -0
  310. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
  311. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
  312. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_claim_morpho_rewards.py +0 -0
  313. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_crosschain_canary.py +0 -0
  314. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_crosschain_hypercore.py +0 -0
  315. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_crosschain_hyperevm.py +0 -0
  316. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_crosschain_lifecycle.py +0 -0
  317. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_euler_v2_batch_base.py +0 -0
  318. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
  319. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
  320. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
  321. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
  322. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_examples_arbitrum.py +0 -0
  323. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_examples_base.py +0 -0
  324. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_examples_crosschain.py +0 -0
  325. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_external_state_executor_base.py +0 -0
  326. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
  327. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_looping_aave_v3_base.py +0 -0
  328. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
  329. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_morpho_blue.py +0 -0
  330. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_pilot_v3.py +0 -0
  331. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
  332. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_pilot_v5_ramses.py +0 -0
  333. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
  334. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
  335. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_preflight_base.py +0 -0
  336. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_readers_integration.py +0 -0
  337. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_release_funds.py +0 -0
  338. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_swap_on_base.py +0 -0
  339. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulate_vault_from_scratch_base.py +0 -0
  340. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulation_errors.py +0 -0
  341. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_simulation_observation.py +0 -0
  342. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_solidity_compiler.py +0 -0
  343. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_solidity_mirrors.py +0 -0
  344. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_substrates.py +0 -0
  345. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_types_and_events.py +0 -0
  346. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_unpriceable_tokens.py +0 -0
  347. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_validation.py +0 -0
  348. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_vault_data_block.py +0 -0
  349. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_vault_dep_graph.py +0 -0
  350. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_vault_info_fetched_golden.py +0 -0
  351. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_vault_info_onchain_base.py +0 -0
  352. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
  353. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_vault_reconciliation_onchain_base.py +0 -0
  354. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_vault_simulator_blocks.py +0 -0
  355. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_web3_context.py +0 -0
  356. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/test_withdraw_manager.py +0 -0
  357. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/typed_client/consumer.py +0 -0
  358. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/tests/typed_client/rejected.py +0 -0
  359. {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev308}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ipor-fusion
3
- Version: 4.0.0.dev303
3
+ Version: 4.0.0.dev308
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
@@ -476,7 +476,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
476
476
  | `PlasmaVault` | ERC-4626 vault — execute, deposit, withdraw, previews, fuse, market, fee and limit configuration |
477
477
  | `VaultSimulator` | Batch `execute` + reads through `eth_simulateV1`, multi-block, no local node |
478
478
  | `FusionFactory` | Deploy a new vault (`clone`, `clone_supervised`) |
479
- | `AccessManager` | Role-based access control |
479
+ | `AccessManager` | Role-based access control; `read_call_plan(ctx, call, caller=)` says whether a restricted call goes now, must be scheduled (execution delay), waits, is ready or is forbidden, and gives the call to send now; `schedule`, `execute` and `cancel` build the timelock calls |
480
480
  | `RewardsManager` | Claim and vest rewards |
481
481
  | `WithdrawManager` | Time-windowed withdrawal requests |
482
482
  | `FeeManager` | Deposit, performance, and management fee configuration and harvesting |
@@ -427,7 +427,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
427
427
  | `PlasmaVault` | ERC-4626 vault — execute, deposit, withdraw, previews, fuse, market, fee and limit configuration |
428
428
  | `VaultSimulator` | Batch `execute` + reads through `eth_simulateV1`, multi-block, no local node |
429
429
  | `FusionFactory` | Deploy a new vault (`clone`, `clone_supervised`) |
430
- | `AccessManager` | Role-based access control |
430
+ | `AccessManager` | Role-based access control; `read_call_plan(ctx, call, caller=)` says whether a restricted call goes now, must be scheduled (execution delay), waits, is ready or is forbidden, and gives the call to send now; `schedule`, `execute` and `cancel` build the timelock calls |
431
431
  | `RewardsManager` | Claim and vest rewards |
432
432
  | `WithdrawManager` | Time-windowed withdrawal requests |
433
433
  | `FeeManager` | Deposit, performance, and management fee configuration and harvesting |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ipor-fusion"
3
- version = "4.0.0.dev303"
3
+ version = "4.0.0.dev308"
4
4
  description = "The IPOR Fusion SDK for Python"
5
5
  authors = [
6
6
  { name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
@@ -27,6 +27,8 @@ from ipor_fusion.config.roles import Roles
27
27
  from ipor_fusion.core.access import (
28
28
  AccessManager,
29
29
  CallPermission,
30
+ CallPlan,
31
+ CallPlanKind,
30
32
  RoleAccount,
31
33
  RoleAccountJson,
32
34
  RoleAccountsJson,
@@ -556,6 +558,8 @@ __all__ = [
556
558
  "RoleAccount",
557
559
  "RoleAccountJson",
558
560
  "CallPermission",
561
+ "CallPlan",
562
+ "CallPlanKind",
559
563
  "RoleStatus",
560
564
  "resolve_access_manager",
561
565
  "role_account_sort_key",
@@ -1,6 +1,8 @@
1
1
  from ipor_fusion.core.access import (
2
2
  AccessManager,
3
3
  CallPermission,
4
+ CallPlan,
5
+ CallPlanKind,
4
6
  RoleAccount,
5
7
  RoleAccountJson,
6
8
  RoleAccountsJson,
@@ -107,6 +109,8 @@ __all__ = [
107
109
  "RoleAccount",
108
110
  "RoleAccountJson",
109
111
  "CallPermission",
112
+ "CallPlan",
113
+ "CallPlanKind",
110
114
  "RoleStatus",
111
115
  "RewardsManager",
112
116
  "VestingData",
@@ -1,6 +1,6 @@
1
1
  from collections.abc import Callable, Sequence
2
2
  from dataclasses import dataclass
3
- from typing import TypedDict
3
+ from typing import Literal, TypedDict
4
4
 
5
5
  from eth_abi import decode, encode
6
6
  from eth_abi.exceptions import InsufficientDataBytes
@@ -68,6 +68,33 @@ class CallPermission:
68
68
  return self.immediate or self.delay > 0
69
69
 
70
70
 
71
+ # schedule(when=0): as soon as the sender's execution delay allows.
72
+ _ASAP = Timestamp(0)
73
+
74
+ CallPlanKind = Literal["immediate", "schedule", "waiting", "ready", "forbidden"]
75
+
76
+
77
+ @dataclass(frozen=True, slots=True)
78
+ class CallPlan:
79
+ """How ``caller`` gets a restricted call through the access manager now.
80
+
81
+ - ``immediate``: send the call itself (``next_call``).
82
+ - ``schedule``: the caller has an execution delay and nothing is scheduled;
83
+ ``next_call`` schedules it, executable ``delay`` seconds after it lands.
84
+ - ``waiting``: scheduled, executable at ``scheduled_at``; nothing to send yet.
85
+ - ``ready``: scheduled and executable; ``next_call`` is the call itself (or
86
+ the manager's ``execute`` with ``via_manager``), valid until the schedule
87
+ expires a week after ``scheduled_at``; an expired one plans ``schedule``.
88
+ - ``forbidden``: the caller has no role for it, or the target is closed.
89
+ """
90
+
91
+ kind: CallPlanKind
92
+ permission: CallPermission
93
+ operation: ScheduleId
94
+ scheduled_at: Timestamp | None
95
+ next_call: Call[None] | None
96
+
97
+
71
98
  def _call_permission_decoder(values: tuple[bool, int]) -> CallPermission:
72
99
  immediate, delay = values
73
100
  return CallPermission(immediate=immediate, delay=Period(delay))
@@ -204,6 +231,82 @@ class AccessManager(ContractWrapper):
204
231
  Web3.keccak(encode(["address", "address", "bytes"], [caller, target, data]))
205
232
  )
206
233
 
234
+ def schedule(
235
+ self, target: ChecksumAddress, data: bytes, when: Timestamp = _ASAP
236
+ ) -> Call[None]:
237
+ """Schedule ``data`` on ``target`` for the sender, executable at ``when``;
238
+ 0 means as soon as the sender's execution delay allows, and a nonzero
239
+ time before that reverts."""
240
+ return self._write("schedule(address,bytes,uint48)", target, data, when)
241
+
242
+ def execute(self, target: ChecksumAddress, data: bytes) -> Call[None]:
243
+ """Run ``data`` on ``target`` through the manager: an immediate call, or
244
+ the sender's scheduled operation once it is executable (it is consumed)."""
245
+ return self._write("execute(address,bytes)", target, data)
246
+
247
+ def cancel(
248
+ self, caller: ChecksumAddress, target: ChecksumAddress, data: bytes
249
+ ) -> Call[None]:
250
+ """Cancel ``caller``'s scheduled ``data`` on ``target``: the caller itself,
251
+ an ADMIN_ROLE member, or the guardian of the role ``target`` maps the
252
+ function's selector to may."""
253
+ return self._write("cancel(address,address,bytes)", caller, target, data)
254
+
255
+ def read_call_plan(
256
+ self,
257
+ ctx: Web3Context,
258
+ call: Call[object],
259
+ *,
260
+ caller: ChecksumAddress,
261
+ via_manager: bool = False,
262
+ ) -> CallPlan:
263
+ """What ``caller`` sends now to get ``call`` executed: an access and
264
+ timelock plan from ``canCall`` and, for a delayed caller, the schedule of
265
+ exactly ``call``'s calldata, all read at the block ``ctx.default_block``
266
+ names (a number, hash or tag, resolved once). It does not run the target's own checks
267
+ (redemption lock, pre-hooks, balances): simulate the call for those.
268
+
269
+ A ready operation is sent as ``call`` itself, from ``caller``: the target
270
+ consumes the schedule and still sees ``caller`` as the sender, which
271
+ deposits, withdrawals and other sender-dependent calls need.
272
+ ``via_manager`` sends it through the manager's ``execute`` instead, for a
273
+ target that does not consume schedules; the manager is then the sender.
274
+
275
+ Raises ``ValueError`` for a call on the access manager itself: its
276
+ authorization is function-specific (role grants depend on the role's
277
+ admin, target configuration on the target's admin delay, and the IPOR
278
+ manager's own restricted functions on ``canCall``), which this plan does
279
+ not model.
280
+ """
281
+ if call.to == self.address:
282
+ raise ValueError(
283
+ "read_call_plan does not plan calls on the access manager itself:"
284
+ " their authorization is function-specific"
285
+ )
286
+ block = ctx.web3.eth.get_block(ctx.default_block)
287
+ ctx = ctx.at_block(block["number"])
288
+ selector = bytes(call.data[:4])
289
+ permission = self.can_call(caller, call.to, selector).call(ctx)
290
+ operation = self.hash_operation(caller, call.to, call.data)
291
+ direct = Call[None](to=call.to, data=call.data)
292
+ if permission.immediate:
293
+ return CallPlan("immediate", permission, operation, None, direct)
294
+ if permission.delay == 0:
295
+ return CallPlan("forbidden", permission, operation, None, None)
296
+ scheduled_at = self.get_schedule(operation).call(ctx)
297
+ if scheduled_at == 0:
298
+ return CallPlan(
299
+ "schedule",
300
+ permission,
301
+ operation,
302
+ None,
303
+ self.schedule(call.to, call.data),
304
+ )
305
+ if scheduled_at > block["timestamp"]:
306
+ return CallPlan("waiting", permission, operation, scheduled_at, None)
307
+ ready = self.execute(call.to, call.data) if via_manager else direct
308
+ return CallPlan("ready", permission, operation, scheduled_at, ready)
309
+
207
310
  def can_call(
208
311
  self, caller: ChecksumAddress, target: ChecksumAddress, selector: bytes
209
312
  ) -> Call[CallPermission]:
@@ -15,8 +15,11 @@ from ipor_fusion import (
15
15
  ContractNotFoundError,
16
16
  NotPlasmaVaultError,
17
17
  OwnerNotFoundError,
18
+ PlasmaVault,
18
19
  RoleAccount,
20
+ Roles,
19
21
  parse_access_manager_address,
22
+ parse_plasma_vault_address,
20
23
  resolve_access_manager,
21
24
  role_account_sort_key,
22
25
  )
@@ -317,3 +320,98 @@ def test_hash_operation_matches_the_deployed_manager():
317
320
  assert AccessManager.hash_operation(caller, vault, data).hex() == (
318
321
  "449c1b0678d9666078c86be94b86c7f4bfe8fe012bdacb56246db911e78b7ebb"
319
322
  )
323
+
324
+
325
+ class TestCallPlan:
326
+ manager = AccessManager(parse_access_manager_address(MANAGER_ADDR))
327
+ target_call = PlasmaVault(parse_plasma_vault_address(VAULT_ADDR)).execute([])
328
+ NOW = 10_000
329
+
330
+ def _ctx(self, *, immediate: bool, delay: int, scheduled: int = 0):
331
+ def handler(_to: str, data: bytes) -> bytes:
332
+ selector = bytes(data[:4])
333
+ if selector == Web3.keccak(text="canCall(address,address,bytes4)")[:4]:
334
+ return encode(["bool", "uint32"], [immediate, delay])
335
+ assert selector == Web3.keccak(text="getSchedule(bytes32)")[:4]
336
+ return encode(["uint48"], [scheduled])
337
+
338
+ ctx = MagicMock()
339
+ ctx.default_block = "finalized"
340
+ ctx.at_block.return_value = ctx
341
+ ctx.call.side_effect = multicall_aware(handler)
342
+ ctx.web3.eth.get_block.return_value = {"number": 777, "timestamp": self.NOW}
343
+ return ctx
344
+
345
+ def _plan(self, *, via_manager: bool = False, **kwargs):
346
+ return self.manager.read_call_plan(
347
+ self._ctx(**kwargs), self.target_call, caller=ALICE, via_manager=via_manager
348
+ )
349
+
350
+ def test_immediate_sends_the_call_itself(self):
351
+ plan = self._plan(immediate=True, delay=0)
352
+ assert plan.kind == "immediate"
353
+ assert plan.next_call is not None
354
+ assert (plan.next_call.to, plan.next_call.data) == (
355
+ self.target_call.to,
356
+ self.target_call.data,
357
+ )
358
+
359
+ def test_the_block_the_context_names_is_resolved_once_and_pinned(self):
360
+ ctx = self._ctx(immediate=False, delay=3600, scheduled=self.NOW)
361
+ self.manager.read_call_plan(ctx, self.target_call, caller=ALICE)
362
+ ctx.web3.eth.get_block.assert_called_once_with("finalized")
363
+ ctx.at_block.assert_called_once_with(777)
364
+
365
+ def test_forbidden_sends_nothing(self):
366
+ plan = self._plan(immediate=False, delay=0)
367
+ assert (plan.kind, plan.next_call) == ("forbidden", None)
368
+
369
+ def test_a_delayed_caller_schedules_first(self):
370
+ plan = self._plan(immediate=False, delay=3600)
371
+ assert plan.kind == "schedule"
372
+ assert plan.next_call is not None
373
+ assert plan.next_call.data == Web3.keccak(
374
+ text="schedule(address,bytes,uint48)"
375
+ )[:4] + encode(
376
+ ["address", "bytes", "uint48"],
377
+ [self.target_call.to, self.target_call.data, 0],
378
+ )
379
+ assert plan.operation == AccessManager.hash_operation(
380
+ ALICE, self.target_call.to, self.target_call.data
381
+ )
382
+
383
+ def test_waiting_then_ready_through_the_manager(self):
384
+ waiting = self._plan(immediate=False, delay=3600, scheduled=self.NOW + 1)
385
+ assert (waiting.kind, waiting.scheduled_at, waiting.next_call) == (
386
+ "waiting",
387
+ self.NOW + 1,
388
+ None,
389
+ )
390
+ ready = self._plan(immediate=False, delay=3600, scheduled=self.NOW)
391
+ assert ready.kind == "ready"
392
+ assert ready.next_call is not None
393
+ # Sent as the call itself, so the target consumes the schedule and still
394
+ # sees the caller as the sender.
395
+ assert (ready.next_call.to, ready.next_call.data) == (
396
+ self.target_call.to,
397
+ self.target_call.data,
398
+ )
399
+ relayed = self._plan(
400
+ immediate=False, delay=3600, scheduled=self.NOW, via_manager=True
401
+ )
402
+ assert relayed.next_call is not None
403
+ assert relayed.next_call.to == MANAGER_ADDR
404
+ assert relayed.next_call.data == Web3.keccak(text="execute(address,bytes)")[
405
+ :4
406
+ ] + encode(["address", "bytes"], [self.target_call.to, self.target_call.data])
407
+
408
+ def test_calls_on_the_manager_itself_are_not_planned(self):
409
+ grant = self.manager.grant_role(Roles.ALPHA_ROLE, ALICE, Period(0))
410
+ with pytest.raises(ValueError, match="access manager itself"):
411
+ self.manager.read_call_plan(MagicMock(), grant, caller=ALICE)
412
+
413
+ def test_cancel_encodes_caller_target_and_data(self):
414
+ call = self.manager.cancel(ALICE, VAULT_ADDR, b"\x01")
415
+ assert call.data == Web3.keccak(text="cancel(address,address,bytes)")[
416
+ :4
417
+ ] + encode(["address", "address", "bytes"], [ALICE, VAULT_ADDR, b"\x01"])
@@ -82,6 +82,8 @@ _ALL_SNAPSHOT: dict[str, frozenset[str]] = {
82
82
  "CacheKey",
83
83
  "Call",
84
84
  "CallPermission",
85
+ "CallPlan",
86
+ "CallPlanKind",
85
87
  "ChainId",
86
88
  "ChainlinkDetailJson",
87
89
  "ChangelogEntry",
@@ -548,6 +550,8 @@ _ALL_SNAPSHOT: dict[str, frozenset[str]] = {
548
550
  "BlockOverrides",
549
551
  "Call",
550
552
  "CallPermission",
553
+ "CallPlan",
554
+ "CallPlanKind",
551
555
  "CloneArgs",
552
556
  "ContextManager",
553
557
  "DecodedFuseAction",
@@ -0,0 +1,51 @@
1
+ """A timelocked governance call on a live Base vault, end to end in one
2
+ eth_simulateV1 run: the plan says schedule, the scheduled call is not executable
3
+ before the delay, and runs through the manager after it. Skipped without an
4
+ archive ``BASE_PROVIDER_URL`` that serves ``eth_simulateV1``."""
5
+
6
+ from _live import archive_context
7
+ from web3 import Web3
8
+
9
+ from ipor_fusion import AccessManager, PlasmaVault, Shares, VaultSimulator
10
+ from ipor_fusion.types import parse_plasma_vault_address
11
+
12
+ VAULT = parse_plasma_vault_address("0xc4c00d8b323f37527eeda27c87412378be9f68ec")
13
+ # Holds the vault's ATOMIST role with a three-day execution delay.
14
+ ATOMIST = Web3.to_checksum_address("0xF6a9bd8F6DC537675D499Ac1CA14f2c55d8b5569")
15
+ BLOCK = 52973779
16
+ NEW_CAP = Shares(10**30)
17
+
18
+
19
+ def test_schedule_wait_and_execute_a_delayed_call(web3_base):
20
+ ctx = archive_context("BASE_PROVIDER_URL", BLOCK, VAULT)
21
+ vault = PlasmaVault(VAULT)
22
+ manager = AccessManager(vault.get_access_manager_address().call(ctx))
23
+ cap = vault.set_total_supply_cap(NEW_CAP)
24
+
25
+ plan = manager.read_call_plan(ctx, cap, caller=ATOMIST)
26
+ assert plan.kind == "schedule"
27
+ assert plan.permission.delay == 3 * 24 * 3600
28
+ assert plan.next_call is not None
29
+
30
+ early = VaultSimulator(web3_base, block=BLOCK)
31
+ early.add_call(plan.next_call, from_=ATOMIST, label="schedule")
32
+ early.next_block(3600)
33
+ early.add_call(cap, from_=ATOMIST, label="early")
34
+ scheduled, too_early = early.run().calls
35
+ assert scheduled.success
36
+ assert not too_early.success
37
+ assert too_early.error is not None
38
+ assert "0x18cb6b7a" in too_early.error # AccessManagerNotReady(bytes32)
39
+
40
+ # Once ready, the vault consumes the schedule on a direct call from the
41
+ # atomist, or the manager relays it.
42
+ for ready in (cap, manager.execute(cap.to, cap.data)):
43
+ sim = VaultSimulator(web3_base, block=BLOCK)
44
+ sim.add_call(plan.next_call, from_=ATOMIST)
45
+ when = sim.observe(manager.get_schedule(plan.operation))
46
+ sim.next_block(int(plan.permission.delay) + 1)
47
+ sim.add_call(ready, from_=ATOMIST)
48
+ after = sim.observe(vault.get_total_supply_cap())
49
+ assert sim.run().all_success
50
+ assert when.value > 0
51
+ assert after.value == NEW_CAP