ipor-fusion 4.0.0.dev234__tar.gz → 4.0.0.dev244__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.
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/AGENTS.md +43 -22
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/MIGRATION_4.md +19 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/PKG-INFO +13 -7
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/README.md +12 -6
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/__init__.py +4 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/config_cmd.py +1 -1
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/config_store.py +18 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/market_cmd.py +13 -10
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/options.py +43 -10
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_cmd.py +21 -18
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/__init__.py +4 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/access.py +40 -2
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/mcp/models.py +2 -35
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/mcp/server.py +104 -67
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_helpers.py +8 -2
- ipor_fusion-4.0.0.dev244/tests/test_cli_mcp_parity.py +157 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_oracle_mapping.py +6 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_role_accounts.py +6 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_mcp_models.py +21 -12
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_mcp_server.py +10 -3
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_mcp_vault_validate.py +8 -2
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_public_api.py +24 -20
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/simulation_result.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/hypercore/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_event_args.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_fake_rpc.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_golden.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_live.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_oracle_mapping_fake.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/simulate_v1_request.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/fetched.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/full.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/full.txt +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_market_golden.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_oracle_mapping_golden.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_docs_typing.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulation_errors.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_simulation_observation.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_data_block.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_info_fetched_golden.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_reconciliation_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev234 → ipor_fusion-4.0.0.dev244}/uv.lock +0 -0
|
@@ -28,7 +28,7 @@ uv run ruff format ./ # format (CI runs
|
|
|
28
28
|
uv run ruff check ./ # lint: bandit S, C901 max-complexity 10, PLR limits
|
|
29
29
|
uv run pyright # types, basic mode, src + tests + examples + scripts
|
|
30
30
|
uv run pytest # all tests + coverage gate (fail_under = 98, mcp/ included)
|
|
31
|
-
uv run pytest -m "cli or mcp" --no-cov #
|
|
31
|
+
uv run pytest -m "cli or mcp" --no-cov # CLI/MCP subset (the full suite is mostly offline too)
|
|
32
32
|
uv run pytest --run-readiness -m readiness --no-cov -rA # opt-in live Ethereum-hub POC checks
|
|
33
33
|
uv run python scripts/crosschain_readiness.py --output /tmp/crosschain-readiness.json # live Arbitrum/HyperEVM report
|
|
34
34
|
uv run pytest tests/test_fuse_encoding.py -k aave --no-cov # single file / test
|
|
@@ -59,9 +59,9 @@ everything else is `sdk`). Most `sdk` tests are offline too: the suite with no
|
|
|
59
59
|
provider set still clears the coverage gate.
|
|
60
60
|
Integration tests (`test_simulate_*`, `test_vault_info_onchain_*`) run against
|
|
61
61
|
live RPCs via `eth_simulateV1`, no Docker or Anvil (`ANVIL_WALLET` is just a
|
|
62
|
-
named address constant).
|
|
63
|
-
`BASE_PROVIDER_URL`
|
|
64
|
-
python-dotenv)
|
|
62
|
+
named address constant). Each skips unless the provider it needs
|
|
63
|
+
(`ETHEREUM_PROVIDER_URL`, `BASE_PROVIDER_URL` or `ARBITRUM_PROVIDER_URL`; `.env` is
|
|
64
|
+
loaded via python-dotenv) is set and supports `eth_simulateV1`. Every such gate goes
|
|
65
65
|
through `tests/_live.py` (`provider_url`, `archive_context`, `unavailable`): it
|
|
66
66
|
skips locally and fails when the source is named in `IPOR_FUSION_REQUIRE_LIVE`
|
|
67
67
|
(comma-separated env var names, `GITHUB` for the Solidity mirrors). CI
|
|
@@ -84,9 +84,11 @@ executors from the creation code they store; `source` builds a factory pair from
|
|
|
84
84
|
that revision (the fuses are compiled from it); a checkout without `node_modules` also
|
|
85
85
|
needs `IPOR_FUSION_FOUNDRY_REMAPPINGS` for OpenZeppelin. `test_crosschain_ccip_events.py`
|
|
86
86
|
mirrors the CCIP event registry from a checkout at its own `CONTRACTS_REVISION`.
|
|
87
|
-
`test_solidity_compiler.py`
|
|
88
|
-
|
|
89
|
-
external prerequisites are unavailable.
|
|
87
|
+
`test_solidity_compiler.py` unit-tests that compiler helper (`tests/_foundry.py`)
|
|
88
|
+
offline, with `forge` and `git` mocked. The checkout-based tests above skip when their
|
|
89
|
+
external prerequisites are unavailable. `test_hypercore_live.py`,
|
|
90
|
+
`test_hypercore_deployment.py` and `test_hypercore_simulation.py` need
|
|
91
|
+
`HYPEREVM_PROVIDER_URL` as well.
|
|
90
92
|
`test_simulate_crosschain_lifecycle.py` drives the mainnet POC deployment from
|
|
91
93
|
Ethereum to every spoke on every transport through `CrosschainLane` and
|
|
92
94
|
`CrosschainSimulator`; the fixtures (`Chain`, `Deployment`, `Spoke`, pinned
|
|
@@ -110,8 +112,9 @@ the deployed runtimes, and `tests/_hypercore_model.py` answers those addresses t
|
|
|
110
112
|
tests prove EVM validation under supplied Core state. `test_hypercore_simulation.py`
|
|
111
113
|
runs one vault cycle; `test_simulate_crosschain_hypercore.py` runs the composed flow
|
|
112
114
|
(v3 canary hub → CCIP → the market-55 vault → a trade → recall → claim) and holds every
|
|
113
|
-
CCIP delivery to its route gas limit. Both skip without the source directory
|
|
114
|
-
|
|
115
|
+
CCIP delivery to its route gas limit. Both skip without the source directory;
|
|
116
|
+
`test_hypercore_simulation.py` also needs `HYPEREVM_PROVIDER_URL`, the composed flow
|
|
117
|
+
`ARBITRUM_PROVIDER_URL` as well.
|
|
115
118
|
`test_crosschain_readiness.py` checks the Ethereum-hub POC's live bridge and IPOR
|
|
116
119
|
preconditions. Its `readiness` marker requires `--run-readiness`: ordinary runs
|
|
117
120
|
exclude it even with `-m sdk`. Known HyperEVM gaps are non-strict xfails restricted
|
|
@@ -218,7 +221,7 @@ the lifecycle matrix follows that.
|
|
|
218
221
|
| `IporFusionMarkets`, `Roles` | `market_ids.py`, `config/roles.py` mirror `IporFusionMarkets.sol`, `Roles.sol` in `ipor-fusion/contracts/libraries/`; drift-gated by `tests/test_solidity_mirrors.py` — bump its pinned ref in the same change that syncs the mirrors An id sourced from a contracts feature branch ahead of the pinned ref goes in the test's `_AHEAD_OF_UPSTREAM` allowlist and is removed when the ref catches up |
|
|
219
222
|
| substrate decoders | `substrates.py` registry mirrors each market's `contracts/fuses/<protocol>/*SubstrateLib.sol` or `*FuseLib.sol` |
|
|
220
223
|
| `vault_info` / `fusion market` JSON shapes | the builders and their TypedDicts (`VaultInfoJson`, `MorphoBlueMarketJson`, `MetaMorphoV1Json` / `MetaMorphoV2Json`) in `vault_info.py` / `market_info.py`, models in `mcp/models.py` (`extra="forbid"`, the published schema), the `VaultInfoJson`-typed `_full_vault_info_dict` fixture in `test_mcp_models.py` |
|
|
221
|
-
| CLI command set | every CLI command has a matching tool in `mcp/server.py` (`changelog` maps to `server_info`) |
|
|
224
|
+
| CLI command set | every CLI command has a matching tool in `mcp/server.py` with the same arguments (`changelog` maps to `server_info`; `--json` and display filters are CLI-only); `tests/test_cli_mcp_parity.py` enforces it, and both resolve chain and provider through `cli/config_store.resolve_chain_id` |
|
|
222
225
|
|
|
223
226
|
## Layout (`src/ipor_fusion/`)
|
|
224
227
|
|
|
@@ -243,8 +246,10 @@ the lifecycle matrix follows that.
|
|
|
243
246
|
snapshots), `vault_positions.py` (Morpho/Aave position breakdowns and their token
|
|
244
247
|
prices) and `vault_erc20.py` (the ERC20_VAULT_BALANCE holdings);
|
|
245
248
|
`vault_info.py` — `build_vault_info_json(data)`, the `fusion vault info --json`
|
|
246
|
-
document rendered from the snapshot
|
|
247
|
-
|
|
249
|
+
document rendered from the snapshot, plus three inputs that are not chain state at
|
|
250
|
+
its block: contract names through the injected `resolve_name` (Etherscan I/O on a
|
|
251
|
+
pool), the deployment fields the caller writes into the snapshot from
|
|
252
|
+
`fetch_deployment_info`, and `age_days` from the wall clock; typed block by block (`VaultInfoJson` and its nested
|
|
248
253
|
TypedDicts), and `health_check_lists`; `explorer.py` Etherscan V2 (single endpoint, needs API key);
|
|
249
254
|
`cache.py` the `ContractCache` protocol (`get`/`put` of immutable facts keyed
|
|
250
255
|
`CacheKey(chain_id, address, field)`), `FileContractCache` (one JSON file, atomic
|
|
@@ -284,13 +289,16 @@ the lifecycle matrix follows that.
|
|
|
284
289
|
uniswap_v3, universal); `events.py` (`extract_events`, `abi_value` and the raw-log
|
|
285
290
|
event decoder both event families share), `external_state_event_decoding.py` and
|
|
286
291
|
`hypercore_event_decoding.py` (the ExternalState and HyperCore event specs, classes
|
|
287
|
-
and decoders; internal modules, the names are exported from
|
|
292
|
+
and decoders; internal modules, the ExternalState names are exported from
|
|
293
|
+
`ipor_fusion.fuses`, the HyperCore ones from `ipor_fusion.hypercore`), `hypercore.py`
|
|
294
|
+
(the HyperCore fuses, market 55);
|
|
288
295
|
`base.py` re-exports the `core/fuse.py` base and validators,
|
|
289
296
|
`FuseAction` and `ZERO_ADDRESS` (from `types`). A fuse is a value: equality and
|
|
290
297
|
hash cover its class and every attribute it stores
|
|
291
298
|
- `crosschain/` — crosschain Plasma Vaults (executor on the hub chain, dispatcher at
|
|
292
299
|
the same address on each spoke chain), split like the contracts: shared `messages`
|
|
293
|
-
(`Command`, `MsgType`, envelopes), `
|
|
300
|
+
(`Command`, `MsgType`, envelopes), `logs` (`log_bytes` and the raw-log field
|
|
301
|
+
helpers the decoders share), `contracts` (wrapper bases), `transport`
|
|
294
302
|
(`CrosschainTransport` seam), `lane` (`CrosschainLane`: one executor/dispatcher pair
|
|
295
303
|
driven without knowing its transport; it carries its transport's fuse classes and
|
|
296
304
|
the keeper's attestation helpers), `discovery` (`discover_deployment` reads the
|
|
@@ -304,6 +312,8 @@ the lifecycle matrix follows that.
|
|
|
304
312
|
`ccip/`, each with its wire codecs, executor/dispatcher/factory wrappers, transport
|
|
305
313
|
and lane; `ccip/chainlink` reads Chainlink's Router, OnRamp, TokenAdminRegistry and
|
|
306
314
|
token pool (`ccip_token_lane`: is there a lane, does the token travel on it);
|
|
315
|
+
`ccip/quote` (`quote_ccip_native_fee`: the exact native fee of a send, from a
|
|
316
|
+
simulation with the payer's balance at zero);
|
|
307
317
|
`ccip/events` decodes the executor/dispatcher/factory events by topic (one
|
|
308
318
|
registry, mirrored from the v3 source; the v2 pair emits a subset of it);
|
|
309
319
|
`ccip/chainlink` also wraps the 2.0 OffRamp, verifier resolver and committee verifier
|
|
@@ -314,19 +324,27 @@ the lifecycle matrix follows that.
|
|
|
314
324
|
mirroring `contracts/fuses/crosschain/`; they build on `core/fuse.py`, so the
|
|
315
325
|
dependency direction is `core` <- `crosschain` and `fuses/` holds no crosschain code
|
|
316
326
|
(neither package imports the other). Names mirror the Solidity contracts and
|
|
317
|
-
libraries. Adding a transport: a new subpackage, its encoders in `crosschain/fuses
|
|
318
|
-
|
|
327
|
+
libraries. Adding a transport: a new subpackage, its encoders in `crosschain/fuses/`,
|
|
328
|
+
one `LANES` entry and its probe in `discovery.detect_transport` (CCIP answers
|
|
329
|
+
`transportKind()`, Stargate a non-zero `STARGATE_POOL()`). Layering: the encoders import the wire codecs and wrappers;
|
|
319
330
|
lanes, discovery, transports and the simulator import the encoders.
|
|
320
331
|
`ipor_fusion.crosschain` exports the whole surface (encoders, lanes, discovery,
|
|
321
332
|
transports, simulator). A package `__init__` runs before its modules and the
|
|
322
333
|
encoders import `ccip.contracts` / `stargate.layerzero`, so `.ccip` and `.stargate`
|
|
323
|
-
export only their codecs and wrappers
|
|
334
|
+
export only what sits below the encoders: their codecs and wrappers, and for
|
|
335
|
+
`.ccip` also the Chainlink readers (`ccip_token_lane`), the fee quote
|
|
336
|
+
(`quote_ccip_native_fee`) and the indexer client (`fetch_verifier_result`,
|
|
337
|
+
`manual_execution`). `tests/test_crosschain_layering.py` pins this:
|
|
324
338
|
the SDK's module-level import graph, with the implicit package-`__init__` edges, is
|
|
325
339
|
acyclic, and the crosschain package has no function-level or `TYPE_CHECKING`
|
|
326
340
|
imports; never work around a cycle with a lazy import, move the module instead.
|
|
327
|
-
- `readers/` — read side: lending_health, oracle_mapping
|
|
328
|
-
|
|
329
|
-
|
|
341
|
+
- `readers/` — read side: lending_health, oracle_mapping (with oracle_mapping_nodes
|
|
342
|
+
and oracle_mapping_reader), position_manager, aave_v3, aave_v4, compound_v3, morpho,
|
|
343
|
+
ramses_v2, uniswap_v3, unpriceable (middleware-priced tokens of granted substrates
|
|
344
|
+
the oracle cannot price), hypercore and hypercore_outcome (HyperCore reads and
|
|
345
|
+
transfer outcomes)
|
|
346
|
+
- `hypercore/` — the preview HyperCore names re-exported from `fuses/hypercore*.py`
|
|
347
|
+
and `readers/hypercore*.py`; no code of its own
|
|
330
348
|
- `cli/` — `main.py` root group; `changelog_cmd.py`, `config_cmd.py`, `market_cmd.py`,
|
|
331
349
|
`vault_cmd.py` command orchestration over `vault_data` / `vault_info`; `options.py`
|
|
332
350
|
the shared `--chain-id` / address param types and chain/provider resolution;
|
|
@@ -387,7 +405,8 @@ or examples.
|
|
|
387
405
|
- Fuses (`fuses/`) are stateless encoders: a method returns an immutable
|
|
388
406
|
`FuseAction(fuse_address, calldata)` and touches no chain. `Fuse` builds it
|
|
389
407
|
via `self._action_raw(solidity_signature, values)`; the shared `_validate_*`
|
|
390
|
-
helpers raise before encoding
|
|
408
|
+
helpers raise before encoding (the HyperCore encoders add their own integer-width
|
|
409
|
+
checks, `_validate_uint` / `_validate_positive_uint` in `fuses/hypercore.py`). `PlasmaVault.execute([actions])` runs the
|
|
391
410
|
batch atomically.
|
|
392
411
|
- Pin reads, never mutate a shared context: `ctx.at_block(n)` is a copy whose
|
|
393
412
|
reads (and `get_logs` upper bound) default to block ``n``; `Call.call(ctx, block=n)`
|
|
@@ -479,7 +498,9 @@ the rest share names). Read-only: `vault_info`, `vault_validate`,
|
|
|
479
498
|
`vault_list`, `vault_role_accounts`, `vault_oracle_mapping`, `market_morpho_blue`,
|
|
480
499
|
`market_meta_morpho`, `config_show`, `server_info`. Mutating: `vault_add`,
|
|
481
500
|
`vault_remove`, `config_set_provider`, `config_set_etherscan_key`. Every new
|
|
482
|
-
CLI command gets a matching MCP tool
|
|
501
|
+
CLI command gets a matching MCP tool with the same argument names; an optional
|
|
502
|
+
chain or block is `None` when omitted (`ChainArg`, `BlockArg`), never a `0`
|
|
503
|
+
sentinel. Put every fact in its structured home:
|
|
483
504
|
the return shape is the Pydantic return type (it becomes `outputSchema`),
|
|
484
505
|
per-argument semantics go in `Annotated[..., Field(description=...)]`, closed
|
|
485
506
|
value sets are `Literal` enums. The docstring keeps only what has no
|
|
@@ -990,6 +990,25 @@ and `log_index` was passed through as given (a hex string from a raw log); the e
|
|
|
990
990
|
These apply to every caller, type-checked or not. Changes described in full above
|
|
991
991
|
are listed with a link.
|
|
992
992
|
|
|
993
|
+
- **The CLI and the MCP tools take the same arguments.**
|
|
994
|
+
- **MCP inputs:** `chain_id` and `block_number` are optional and omitted means
|
|
995
|
+
auto-detected / latest. The `0` sentinel is gone, and `0` is now rejected with
|
|
996
|
+
an error naming the argument to omit (it is never read as chain or block 0);
|
|
997
|
+
a client that passed `0` must omit the argument instead. The CLI refuses
|
|
998
|
+
`--block-number 0` and `--chain-id 0` the same way. `server_info(changelog_since=)` is `server_info(since=)` as in
|
|
999
|
+
`fusion changelog --since`, and `vault_oracle_mapping` takes `max_depth` like
|
|
1000
|
+
`--max-depth`.
|
|
1001
|
+
- **Chain auto-detection:** both surfaces use the saved vault's chain, else the
|
|
1002
|
+
chain of the only configured provider. The CLI used only the saved vault.
|
|
1003
|
+
- **CLI options:** every `--block-number` rejects a negative block, and
|
|
1004
|
+
`fusion market morpho-blue --block` is `--block-number` (`--block` still
|
|
1005
|
+
works).
|
|
1006
|
+
- **One role-accounts document:** `fusion vault role-accounts --json` and
|
|
1007
|
+
`vault_role_accounts` publish `RoleAccountsJson`, built by
|
|
1008
|
+
`build_role_accounts_json` (exported).
|
|
1009
|
+
- **Parity test:** `tests/test_cli_mcp_parity.py` holds every command to a
|
|
1010
|
+
tool with the same arguments.
|
|
1011
|
+
|
|
993
1012
|
- **`vault validate` publishes one document.** In 3.x the CLI's `--json` output
|
|
994
1013
|
and the MCP `vault_validate` tool returned two different shapes; both now
|
|
995
1014
|
return `VaultValidationJson`, built by `build_vault_validation_json(validation,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ipor-fusion
|
|
3
|
-
Version: 4.0.0.
|
|
3
|
+
Version: 4.0.0.dev244
|
|
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
|
|
@@ -489,9 +489,12 @@ import these from their subpackage, and expect changes in a minor release.
|
|
|
489
489
|
| Crosschain fuses (market 54): `StargateCrosschainSupplyFuse`, `StargateCrosschainCommandFuse`, `CcipCrosschainSupplyFuse`, `CcipCrosschainCommandFuse`, `CrosschainClaimFuse`; transport-agnostic bases `CrosschainSupplyFuse` / `CrosschainCommandFuse` with `StargateSendParams` / `CcipSendParams`, plus `Command`, `CrosschainSubstrateLib`, `OptionsBuilder` | `ipor_fusion.crosschain` |
|
|
490
490
|
| HyperCore fuses (market 55): `HyperCoreDepositFuse`, `HyperCoreMarginFuse`, `HyperCoreSendFuse`, `HyperCoreOrderFuse`, `HyperCoreCancelFuse`, `HyperCoreBuilderFeeFuse`, `HyperCoreSubstrates` | `ipor_fusion.hypercore`, with `HyperCoreReader`, `read_hypercore_vault_state` and the event decoders |
|
|
491
491
|
|
|
492
|
-
The mainnet crosschain POC vault uses a keccak-derived market ID, not 54.
|
|
493
|
-
`crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")`
|
|
494
|
-
`
|
|
492
|
+
The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Pass
|
|
493
|
+
`market_id=crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` to `open_lane`
|
|
494
|
+
/ `discover_deployment` for that deployment. Both require it explicitly:
|
|
495
|
+
`discover_deployment` reads the market's executor grants; `open_lane` takes an executor
|
|
496
|
+
address and uses the market ID to find the vault's matching fuses unless they are
|
|
497
|
+
passed.
|
|
495
498
|
|
|
496
499
|
### Readers (`ipor_fusion.readers`)
|
|
497
500
|
|
|
@@ -542,15 +545,18 @@ takes the same object.
|
|
|
542
545
|
## Development
|
|
543
546
|
|
|
544
547
|
```bash
|
|
545
|
-
uv sync --all-extras
|
|
548
|
+
uv sync --all-extras --locked # Install dependencies (as CI)
|
|
546
549
|
uv run pytest tests/test_fuse_encoding.py -n auto -v # Unit tests (fast, offline)
|
|
547
|
-
uv run pytest -v -s # All tests
|
|
550
|
+
uv run pytest -v -s # All tests; live ones skip without their provider
|
|
548
551
|
uv run ruff format ./ # Format
|
|
549
552
|
uv run ruff check ./ # Lint
|
|
550
553
|
uv run pyright # Type check
|
|
551
554
|
```
|
|
552
555
|
|
|
553
|
-
|
|
556
|
+
Each integration test runs when its provider URL is in `.env` (eth_simulateV1 at a
|
|
557
|
+
pinned fork block, no local node) and skips otherwise; HyperEVM tests also need
|
|
558
|
+
`HYPEREVM_PROVIDER_URL`, and the source-backed rehearsals a contracts checkout (see
|
|
559
|
+
AGENTS.md):
|
|
554
560
|
|
|
555
561
|
```bash
|
|
556
562
|
cp .env.example .env
|
|
@@ -441,9 +441,12 @@ import these from their subpackage, and expect changes in a minor release.
|
|
|
441
441
|
| Crosschain fuses (market 54): `StargateCrosschainSupplyFuse`, `StargateCrosschainCommandFuse`, `CcipCrosschainSupplyFuse`, `CcipCrosschainCommandFuse`, `CrosschainClaimFuse`; transport-agnostic bases `CrosschainSupplyFuse` / `CrosschainCommandFuse` with `StargateSendParams` / `CcipSendParams`, plus `Command`, `CrosschainSubstrateLib`, `OptionsBuilder` | `ipor_fusion.crosschain` |
|
|
442
442
|
| HyperCore fuses (market 55): `HyperCoreDepositFuse`, `HyperCoreMarginFuse`, `HyperCoreSendFuse`, `HyperCoreOrderFuse`, `HyperCoreCancelFuse`, `HyperCoreBuilderFeeFuse`, `HyperCoreSubstrates` | `ipor_fusion.hypercore`, with `HyperCoreReader`, `read_hypercore_vault_state` and the event decoders |
|
|
443
443
|
|
|
444
|
-
The mainnet crosschain POC vault uses a keccak-derived market ID, not 54.
|
|
445
|
-
`crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")`
|
|
446
|
-
`
|
|
444
|
+
The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Pass
|
|
445
|
+
`market_id=crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1")` to `open_lane`
|
|
446
|
+
/ `discover_deployment` for that deployment. Both require it explicitly:
|
|
447
|
+
`discover_deployment` reads the market's executor grants; `open_lane` takes an executor
|
|
448
|
+
address and uses the market ID to find the vault's matching fuses unless they are
|
|
449
|
+
passed.
|
|
447
450
|
|
|
448
451
|
### Readers (`ipor_fusion.readers`)
|
|
449
452
|
|
|
@@ -494,15 +497,18 @@ takes the same object.
|
|
|
494
497
|
## Development
|
|
495
498
|
|
|
496
499
|
```bash
|
|
497
|
-
uv sync --all-extras
|
|
500
|
+
uv sync --all-extras --locked # Install dependencies (as CI)
|
|
498
501
|
uv run pytest tests/test_fuse_encoding.py -n auto -v # Unit tests (fast, offline)
|
|
499
|
-
uv run pytest -v -s # All tests
|
|
502
|
+
uv run pytest -v -s # All tests; live ones skip without their provider
|
|
500
503
|
uv run ruff format ./ # Format
|
|
501
504
|
uv run ruff check ./ # Lint
|
|
502
505
|
uv run pyright # Type check
|
|
503
506
|
```
|
|
504
507
|
|
|
505
|
-
|
|
508
|
+
Each integration test runs when its provider URL is in `.env` (eth_simulateV1 at a
|
|
509
|
+
pinned fork block, no local node) and skips otherwise; HyperEVM tests also need
|
|
510
|
+
`HYPEREVM_PROVIDER_URL`, and the source-backed rehearsals a contracts checkout (see
|
|
511
|
+
AGENTS.md):
|
|
506
512
|
|
|
507
513
|
```bash
|
|
508
514
|
cp .env.example .env
|
|
@@ -28,7 +28,9 @@ from ipor_fusion.core.access import (
|
|
|
28
28
|
AccessManager,
|
|
29
29
|
RoleAccount,
|
|
30
30
|
RoleAccountJson,
|
|
31
|
+
RoleAccountsJson,
|
|
31
32
|
RoleStatus,
|
|
33
|
+
build_role_accounts_json,
|
|
32
34
|
resolve_access_manager,
|
|
33
35
|
role_account_sort_key,
|
|
34
36
|
)
|
|
@@ -465,6 +467,8 @@ __all__ = [
|
|
|
465
467
|
"RoleStatus",
|
|
466
468
|
"resolve_access_manager",
|
|
467
469
|
"role_account_sort_key",
|
|
470
|
+
"RoleAccountsJson",
|
|
471
|
+
"build_role_accounts_json",
|
|
468
472
|
"RewardsManager",
|
|
469
473
|
"VestingData",
|
|
470
474
|
"ERC20",
|
|
@@ -88,3 +88,21 @@ def load_config() -> FusionConfig:
|
|
|
88
88
|
def save_config(config: FusionConfig) -> None:
|
|
89
89
|
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
90
90
|
CONFIG_FILE.write_text(config.model_dump_json(indent=2), encoding="utf-8")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def resolve_chain_id(
|
|
94
|
+
cfg: FusionConfig, vault_address: str, chain_id: ChainId | None
|
|
95
|
+
) -> ChainId | None:
|
|
96
|
+
"""The chain a command or tool runs on: the one given, else the saved
|
|
97
|
+
vault's, else the chain of the only configured provider; None when none
|
|
98
|
+
applies. The CLI and the MCP server share it so both resolve alike."""
|
|
99
|
+
if chain_id is not None:
|
|
100
|
+
return chain_id
|
|
101
|
+
if entry := next(
|
|
102
|
+
(v for v in cfg.vaults if v.address.lower() == vault_address.lower()),
|
|
103
|
+
None,
|
|
104
|
+
):
|
|
105
|
+
return entry.chain_id
|
|
106
|
+
if len(cfg.providers) == 1:
|
|
107
|
+
return ChainId(int(next(iter(cfg.providers))))
|
|
108
|
+
return None
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""`fusion market` command group — protocol-level market inspection.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
`morpho-blue <marketId>` fetches a Morpho Blue market's parameters and state
|
|
4
|
+
(loan/collateral/oracle/IRM/LLTV, totals, liquidity, APYs) plus the MetaMorpho
|
|
5
|
+
vaults supplying it (current allocation, supply caps, and PublicAllocator flow
|
|
6
|
+
caps + fees), combining on-chain reads via `MorphoReader` with the Morpho public
|
|
7
|
+
GraphQL API. `meta-morpho <vault>` inspects a MetaMorpho V1 or Vault V2 from the
|
|
8
|
+
API: roles, fees, adapters or allocations and caps.
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from __future__ import annotations
|
|
@@ -16,7 +17,7 @@ from eth_typing import ChecksumAddress
|
|
|
16
17
|
|
|
17
18
|
from ipor_fusion.chains import CHAIN_NAMES
|
|
18
19
|
from ipor_fusion.cli.config_store import load_config
|
|
19
|
-
from ipor_fusion.cli.options import ADDRESS, CHAIN, _resolve_provider
|
|
20
|
+
from ipor_fusion.cli.options import ADDRESS, BLOCK_NUMBER, CHAIN, _resolve_provider
|
|
20
21
|
from ipor_fusion.cli.vault_rendering import _format_amount, _print_table
|
|
21
22
|
from ipor_fusion.core.context import Web3Context
|
|
22
23
|
from ipor_fusion.market_info import (
|
|
@@ -81,8 +82,10 @@ def market() -> None:
|
|
|
81
82
|
help="Chain ID or name (ethereum, base, arbitrum, ...).",
|
|
82
83
|
)
|
|
83
84
|
@click.option(
|
|
85
|
+
"--block-number",
|
|
84
86
|
"--block",
|
|
85
|
-
|
|
87
|
+
"block_number",
|
|
88
|
+
type=BLOCK_NUMBER,
|
|
86
89
|
default=None,
|
|
87
90
|
help="Block number for on-chain reads (default: latest).",
|
|
88
91
|
)
|
|
@@ -109,7 +112,7 @@ def market() -> None:
|
|
|
109
112
|
def morpho_blue(
|
|
110
113
|
market_id: MorphoBlueMarketId,
|
|
111
114
|
chain_id: ChainId,
|
|
112
|
-
|
|
115
|
+
block_number: int | None,
|
|
113
116
|
no_api: bool,
|
|
114
117
|
vault_filter: str | None,
|
|
115
118
|
json_output: bool,
|
|
@@ -125,8 +128,8 @@ def morpho_blue(
|
|
|
125
128
|
cfg = load_config()
|
|
126
129
|
provider_url = _resolve_provider(cfg, chain_id)
|
|
127
130
|
ctx = Web3Context.from_url(provider_url)
|
|
128
|
-
if
|
|
129
|
-
ctx = ctx.at_block(
|
|
131
|
+
if block_number is not None:
|
|
132
|
+
ctx = ctx.at_block(block_number)
|
|
130
133
|
reader = MorphoReader(morpho_blue_address(chain_id))
|
|
131
134
|
mid = market_id
|
|
132
135
|
params, state = reader.read_existing_market(ctx, mid)
|
|
@@ -7,6 +7,7 @@ from web3 import Web3
|
|
|
7
7
|
from ipor_fusion.chains import CHAIN_NAME_TO_ID, ensure_supported_chain
|
|
8
8
|
from ipor_fusion.cli.config_store import (
|
|
9
9
|
FusionConfig,
|
|
10
|
+
resolve_chain_id,
|
|
10
11
|
)
|
|
11
12
|
from ipor_fusion.core.context import Web3Context
|
|
12
13
|
from ipor_fusion.errors import (
|
|
@@ -74,11 +75,11 @@ class ChainType(click.ParamType): # pyright: ignore[reportMissingTypeArgument]
|
|
|
74
75
|
self, value: object, param: click.Parameter | None, ctx: click.Context | None
|
|
75
76
|
) -> ChainId:
|
|
76
77
|
if isinstance(value, int):
|
|
77
|
-
return
|
|
78
|
+
return self._positive(value, param, ctx)
|
|
78
79
|
if not isinstance(value, str):
|
|
79
80
|
self.fail(f"expected string, got {type(value).__name__}", param, ctx)
|
|
80
81
|
try:
|
|
81
|
-
return
|
|
82
|
+
return self._positive(int(value), param, ctx)
|
|
82
83
|
except ValueError:
|
|
83
84
|
pass
|
|
84
85
|
lower = value.lower().strip()
|
|
@@ -91,22 +92,54 @@ class ChainType(click.ParamType): # pyright: ignore[reportMissingTypeArgument]
|
|
|
91
92
|
ctx,
|
|
92
93
|
)
|
|
93
94
|
|
|
95
|
+
def _positive(
|
|
96
|
+
self, value: int, param: click.Parameter | None, ctx: click.Context | None
|
|
97
|
+
) -> ChainId:
|
|
98
|
+
if value <= 0:
|
|
99
|
+
self.fail(
|
|
100
|
+
f"chain ID must be positive, got {value}; omit --chain-id to "
|
|
101
|
+
"auto-detect it",
|
|
102
|
+
param,
|
|
103
|
+
ctx,
|
|
104
|
+
)
|
|
105
|
+
return ChainId(value)
|
|
106
|
+
|
|
94
107
|
|
|
95
108
|
CHAIN = ChainType()
|
|
96
109
|
|
|
97
110
|
|
|
111
|
+
class BlockNumberType(click.IntRange):
|
|
112
|
+
"""A positive block number. 0 is refused rather than read as block 0:
|
|
113
|
+
some callers still pass it meaning "latest"."""
|
|
114
|
+
|
|
115
|
+
name = "block"
|
|
116
|
+
|
|
117
|
+
def __init__(self) -> None:
|
|
118
|
+
super().__init__(min=1)
|
|
119
|
+
|
|
120
|
+
def convert(
|
|
121
|
+
self, value: object, param: click.Parameter | None, ctx: click.Context | None
|
|
122
|
+
) -> int:
|
|
123
|
+
if value in (0, "0"):
|
|
124
|
+
self.fail(
|
|
125
|
+
"0 is not a block selector: omit --block-number for the latest block",
|
|
126
|
+
param,
|
|
127
|
+
ctx,
|
|
128
|
+
)
|
|
129
|
+
return super().convert(value, param, ctx)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
BLOCK_NUMBER = BlockNumberType()
|
|
133
|
+
|
|
134
|
+
|
|
98
135
|
def _resolve_chain_id(
|
|
99
136
|
cfg: FusionConfig, vault_address: PlasmaVaultAddress, chain_id: ChainId | None
|
|
100
137
|
) -> ChainId:
|
|
101
|
-
if chain_id is not None:
|
|
102
|
-
return
|
|
103
|
-
if entry := next(
|
|
104
|
-
(v for v in cfg.vaults if v.address.lower() == vault_address.lower()),
|
|
105
|
-
None,
|
|
106
|
-
):
|
|
107
|
-
return entry.chain_id
|
|
138
|
+
if (resolved := resolve_chain_id(cfg, vault_address, chain_id)) is not None:
|
|
139
|
+
return resolved
|
|
108
140
|
raise click.UsageError(
|
|
109
|
-
"
|
|
141
|
+
"Cannot tell the chain: the vault is not saved and there is not exactly one "
|
|
142
|
+
"configured provider — use --chain-id (e.g. --chain-id ethereum, --chain-id 8453)"
|
|
110
143
|
)
|
|
111
144
|
|
|
112
145
|
|
|
@@ -4,7 +4,6 @@ import json
|
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
|
-
from web3 import Web3
|
|
8
7
|
|
|
9
8
|
from ipor_fusion.chains import CHAIN_NAMES
|
|
10
9
|
from ipor_fusion.cli.config_store import (
|
|
@@ -13,7 +12,13 @@ from ipor_fusion.cli.config_store import (
|
|
|
13
12
|
load_config,
|
|
14
13
|
save_config,
|
|
15
14
|
)
|
|
16
|
-
from ipor_fusion.cli.options import
|
|
15
|
+
from ipor_fusion.cli.options import (
|
|
16
|
+
BLOCK_NUMBER,
|
|
17
|
+
CHAIN,
|
|
18
|
+
VAULT_ADDRESS,
|
|
19
|
+
_build_ctx,
|
|
20
|
+
_resolve_provider,
|
|
21
|
+
)
|
|
17
22
|
from ipor_fusion.cli.oracle_mapping_view import _print_oracle_mapping
|
|
18
23
|
from ipor_fusion.cli.vault_health import (
|
|
19
24
|
_print_validation,
|
|
@@ -27,8 +32,8 @@ from ipor_fusion.cli.vault_rendering import (
|
|
|
27
32
|
)
|
|
28
33
|
from ipor_fusion.config.roles import Roles
|
|
29
34
|
from ipor_fusion.core.access import (
|
|
35
|
+
build_role_accounts_json,
|
|
30
36
|
resolve_access_manager,
|
|
31
|
-
role_account_sort_key,
|
|
32
37
|
)
|
|
33
38
|
from ipor_fusion.core.context import Web3Context
|
|
34
39
|
from ipor_fusion.core.plasma_vault import PlasmaVault
|
|
@@ -181,7 +186,7 @@ vault.add_command(list_vaults, "ls")
|
|
|
181
186
|
)
|
|
182
187
|
@click.option(
|
|
183
188
|
"--block-number",
|
|
184
|
-
type=
|
|
189
|
+
type=BLOCK_NUMBER,
|
|
185
190
|
default=None,
|
|
186
191
|
help="Block number (default: latest).",
|
|
187
192
|
)
|
|
@@ -226,7 +231,7 @@ def info(
|
|
|
226
231
|
)
|
|
227
232
|
@click.option(
|
|
228
233
|
"--block-number",
|
|
229
|
-
type=
|
|
234
|
+
type=BLOCK_NUMBER,
|
|
230
235
|
default=None,
|
|
231
236
|
help="Block number (default: latest).",
|
|
232
237
|
)
|
|
@@ -287,7 +292,7 @@ def validate(
|
|
|
287
292
|
)
|
|
288
293
|
@click.option(
|
|
289
294
|
"--block-number",
|
|
290
|
-
type=
|
|
295
|
+
type=BLOCK_NUMBER,
|
|
291
296
|
default=None,
|
|
292
297
|
help="Block number (default: latest).",
|
|
293
298
|
)
|
|
@@ -326,21 +331,19 @@ def role_accounts(
|
|
|
326
331
|
raise click.ClickException(
|
|
327
332
|
f"RoleGranted log scan failed ({type(exc).__name__}: {exc})."
|
|
328
333
|
) from exc
|
|
329
|
-
|
|
330
|
-
|
|
334
|
+
document = build_role_accounts_json(
|
|
335
|
+
accounts,
|
|
336
|
+
vault=vault_address,
|
|
337
|
+
access_manager=manager.address,
|
|
338
|
+
chain_id=chain_id,
|
|
339
|
+
role=role_id,
|
|
340
|
+
)
|
|
331
341
|
if json_output:
|
|
332
|
-
|
|
333
|
-
"vault": Web3.to_checksum_address(vault_address),
|
|
334
|
-
"access_manager": manager.address,
|
|
335
|
-
"chain_id": chain_id,
|
|
336
|
-
"role_filter": Roles.get_name(role_id) if role_id is not None else None,
|
|
337
|
-
"accounts": rows,
|
|
338
|
-
}
|
|
339
|
-
click.echo(json.dumps(payload, indent=2))
|
|
342
|
+
click.echo(json.dumps(document, indent=2))
|
|
340
343
|
return
|
|
341
344
|
|
|
342
345
|
click.echo(f"Access Manager: {manager.address}")
|
|
343
|
-
_print_role_accounts_table(
|
|
346
|
+
_print_role_accounts_table(document["accounts"])
|
|
344
347
|
|
|
345
348
|
|
|
346
349
|
@vault.command("oracle-mapping")
|
|
@@ -350,7 +353,7 @@ def role_accounts(
|
|
|
350
353
|
)
|
|
351
354
|
@click.option(
|
|
352
355
|
"--block-number",
|
|
353
|
-
type=
|
|
356
|
+
type=BLOCK_NUMBER,
|
|
354
357
|
default=None,
|
|
355
358
|
help="Block number (default: latest).",
|
|
356
359
|
)
|
|
@@ -2,7 +2,9 @@ from ipor_fusion.core.access import (
|
|
|
2
2
|
AccessManager,
|
|
3
3
|
RoleAccount,
|
|
4
4
|
RoleAccountJson,
|
|
5
|
+
RoleAccountsJson,
|
|
5
6
|
RoleStatus,
|
|
7
|
+
build_role_accounts_json,
|
|
6
8
|
resolve_access_manager,
|
|
7
9
|
role_account_sort_key,
|
|
8
10
|
)
|
|
@@ -138,4 +140,6 @@ __all__ = [
|
|
|
138
140
|
"price_oracle_middleware",
|
|
139
141
|
"resolve_access_manager",
|
|
140
142
|
"role_account_sort_key",
|
|
143
|
+
"RoleAccountsJson",
|
|
144
|
+
"build_role_accounts_json",
|
|
141
145
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from collections.abc import Callable
|
|
1
|
+
from collections.abc import Callable, Sequence
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from typing import TypedDict
|
|
4
4
|
|
|
@@ -21,7 +21,13 @@ from ipor_fusion.errors import (
|
|
|
21
21
|
NotPlasmaVaultError,
|
|
22
22
|
OwnerNotFoundError,
|
|
23
23
|
)
|
|
24
|
-
from ipor_fusion.types import
|
|
24
|
+
from ipor_fusion.types import (
|
|
25
|
+
AccessManagerAddress,
|
|
26
|
+
ChainId,
|
|
27
|
+
Period,
|
|
28
|
+
PlasmaVaultAddress,
|
|
29
|
+
RoleId,
|
|
30
|
+
)
|
|
25
31
|
|
|
26
32
|
|
|
27
33
|
class RoleAccountJson(TypedDict):
|
|
@@ -71,6 +77,38 @@ def role_account_sort_key(role_account: RoleAccount) -> tuple[str, int]:
|
|
|
71
77
|
return (role_account.account.lower(), role_account.role_id)
|
|
72
78
|
|
|
73
79
|
|
|
80
|
+
class RoleAccountsJson(TypedDict):
|
|
81
|
+
"""The ``fusion vault role-accounts --json`` document and the
|
|
82
|
+
``vault_role_accounts`` MCP tool output (`build_role_accounts_json`)."""
|
|
83
|
+
|
|
84
|
+
vault: PlasmaVaultAddress
|
|
85
|
+
access_manager: AccessManagerAddress
|
|
86
|
+
chain_id: ChainId
|
|
87
|
+
role_filter: str | None
|
|
88
|
+
accounts: list[RoleAccountJson]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def build_role_accounts_json(
|
|
92
|
+
accounts: Sequence[RoleAccount],
|
|
93
|
+
*,
|
|
94
|
+
vault: PlasmaVaultAddress,
|
|
95
|
+
access_manager: AccessManagerAddress,
|
|
96
|
+
chain_id: ChainId,
|
|
97
|
+
role: RoleId | Roles | None = None,
|
|
98
|
+
) -> RoleAccountsJson:
|
|
99
|
+
"""Role holders as the one document both tools publish, in
|
|
100
|
+
`role_account_sort_key` order; ``role`` is the filter applied, if any."""
|
|
101
|
+
return {
|
|
102
|
+
"vault": vault,
|
|
103
|
+
"access_manager": access_manager,
|
|
104
|
+
"chain_id": chain_id,
|
|
105
|
+
"role_filter": Roles.get_name(role) if role is not None else None,
|
|
106
|
+
"accounts": [
|
|
107
|
+
ra.to_dict() for ra in sorted(accounts, key=role_account_sort_key)
|
|
108
|
+
],
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
74
112
|
def _role_status_decoder(values: tuple[bool, int]) -> RoleStatus:
|
|
75
113
|
is_member, execution_delay = values
|
|
76
114
|
return RoleStatus(is_member=is_member, execution_delay=Period(execution_delay))
|