ipor-fusion 4.0.0.dev231__tar.gz → 4.0.0.dev240__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.dev231 → ipor_fusion-4.0.0.dev240}/AGENTS.md +11 -5
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/MIGRATION_4.md +42 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/PKG-INFO +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/__init__.py +12 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/config_cmd.py +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/config_store.py +18 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/market_cmd.py +7 -5
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/options.py +43 -10
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/vault_cmd.py +26 -86
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/__init__.py +4 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/access.py +40 -2
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/mcp/models.py +20 -271
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/mcp/server.py +109 -70
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/validation.py +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/vault_info.py +109 -22
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/clean.json +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/clean.mcp.json +4 -8
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/deployment_error.json +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +4 -8
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/fetched.mcp.json +6 -12
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/full.mcp.json +15 -34
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/rich.mcp.json +13 -30
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_helpers.py +8 -2
- ipor_fusion-4.0.0.dev240/tests/test_cli_mcp_parity.py +157 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_oracle_mapping.py +6 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_role_accounts.py +6 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_vault_validate.py +8 -3
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_mcp_models.py +26 -12
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_mcp_server.py +10 -3
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_mcp_vault_validate.py +74 -32
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_public_api.py +35 -23
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/README.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/simulation_result.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/hypercore/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_event_args.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_fake_rpc.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_live.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_oracle_mapping_fake.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_blue.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/simulate_v1_request.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/fetched.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/full.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/full.txt +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_market_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_oracle_mapping_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_docs_typing.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulation_errors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_simulation_observation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_vault_data_block.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_vault_info_fetched_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_vault_reconciliation_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev240}/uv.lock +0 -0
|
@@ -218,7 +218,7 @@ the lifecycle matrix follows that.
|
|
|
218
218
|
| `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
219
|
| substrate decoders | `substrates.py` registry mirrors each market's `contracts/fuses/<protocol>/*SubstrateLib.sol` or `*FuseLib.sol` |
|
|
220
220
|
| `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`) |
|
|
221
|
+
| 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
222
|
|
|
223
223
|
## Layout (`src/ipor_fusion/`)
|
|
224
224
|
|
|
@@ -479,7 +479,9 @@ the rest share names). Read-only: `vault_info`, `vault_validate`,
|
|
|
479
479
|
`vault_list`, `vault_role_accounts`, `vault_oracle_mapping`, `market_morpho_blue`,
|
|
480
480
|
`market_meta_morpho`, `config_show`, `server_info`. Mutating: `vault_add`,
|
|
481
481
|
`vault_remove`, `config_set_provider`, `config_set_etherscan_key`. Every new
|
|
482
|
-
CLI command gets a matching MCP tool
|
|
482
|
+
CLI command gets a matching MCP tool with the same argument names; an optional
|
|
483
|
+
chain or block is `None` when omitted (`ChainArg`, `BlockArg`), never a `0`
|
|
484
|
+
sentinel. Put every fact in its structured home:
|
|
483
485
|
the return shape is the Pydantic return type (it becomes `outputSchema`),
|
|
484
486
|
per-argument semantics go in `Annotated[..., Field(description=...)]`, closed
|
|
485
487
|
value sets are `Literal` enums. The docstring keeps only what has no
|
|
@@ -487,9 +489,13 @@ structured home: a one-line summary, behavioral notes, cost warnings. Do not
|
|
|
487
489
|
repeat schema content as prose; it drifts.
|
|
488
490
|
|
|
489
491
|
`mcp/models.py` forbids extra keys and stays the source of the published JSON
|
|
490
|
-
schema.
|
|
491
|
-
|
|
492
|
-
|
|
492
|
+
schema. A command whose CLI `--json` and MCP tool return the same data publish one
|
|
493
|
+
document: an SDK builder returns its TypedDict (`build_vault_info_json`,
|
|
494
|
+
`build_vault_validation_json`, the `fusion market` builders) and the tool
|
|
495
|
+
validates it into the mirroring model, never a second hand-built shape. Adding a
|
|
496
|
+
field to a document means adding it to its TypedDict, to the matching model (with
|
|
497
|
+
a default for back-compat) and to the `_full_vault_info_dict` fixture for vault
|
|
498
|
+
info;
|
|
493
499
|
`TestTypedDocumentsMirrorModels` in `test_mcp_models.py` holds every TypedDict to
|
|
494
500
|
its model block by block, a union block (`deployment`, `position_breakdown`,
|
|
495
501
|
`source_detail`) shape by shape. Every block is a model; a block whose optional keys
|
|
@@ -990,6 +990,48 @@ 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
|
+
|
|
1012
|
+
- **`vault validate` publishes one document.** In 3.x the CLI's `--json` output
|
|
1013
|
+
and the MCP `vault_validate` tool returned two different shapes; both now
|
|
1014
|
+
return `VaultValidationJson`, built by `build_vault_validation_json(validation,
|
|
1015
|
+
chain)` (exported from `ipor_fusion` and `ipor_fusion.vault_info`):
|
|
1016
|
+
- `findings` lists every check in order with its `check`, `severity` and
|
|
1017
|
+
`message`. It replaces the CLI's `criticals` / `warnings` / `ok` string lists
|
|
1018
|
+
and the MCP tool's `criticals` / `warnings` / `passed` lists; filter by
|
|
1019
|
+
`severity`.
|
|
1020
|
+
- `erc20` (`tokens`, `value_in_asset`, `market_cached_value`, `unpriced_tokens`)
|
|
1021
|
+
replaces the CLI's `erc20_balances`; `erc20.tokens` has the same entries.
|
|
1022
|
+
- `reconciliation`, `lending_health` and `asset` share their shapes with the
|
|
1023
|
+
`vault info` document. For the MCP tool this means `delta.percent` instead of
|
|
1024
|
+
`delta_percent`, amounts formatted with thousands separators and at most six
|
|
1025
|
+
decimals (`"14,912.465797"`; `raw` keeps the exact value) and the delta's
|
|
1026
|
+
`formatted` as its absolute value, as in `vault info`.
|
|
1027
|
+
- The MCP tool's output gains `block_timestamp`.
|
|
1028
|
+
- An ERC20 entry whose balance read failed keeps its `decimals` when they were
|
|
1029
|
+
read (in `vault info` too).
|
|
1030
|
+
- The MCP output is the builder's document key for key: an amount without a
|
|
1031
|
+
USD value and an ERC20 entry without a balance no longer gain `null` keys
|
|
1032
|
+
(`usd`, `price_usd`, ...) in either tool, `vault_info` included, and a zero
|
|
1033
|
+
USD delta without a price is `0.0`, not `0`.
|
|
1034
|
+
|
|
993
1035
|
- **A failed lending read is a warning, never a silent `ok`.** 3.x caught every
|
|
994
1036
|
exception in the lending-health readers and dropped the position, so a provider
|
|
995
1037
|
outage could leave no lending finding and an `ok` verdict. Now:
|
|
@@ -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
|
)
|
|
@@ -368,7 +370,9 @@ from ipor_fusion.vault_info import (
|
|
|
368
370
|
DeploymentErrorJson,
|
|
369
371
|
DeploymentJson,
|
|
370
372
|
Erc20BalanceJson,
|
|
373
|
+
Erc20HoldingsJson,
|
|
371
374
|
FeesJson,
|
|
375
|
+
FindingJson,
|
|
372
376
|
FuseJson,
|
|
373
377
|
HealthCheckJson,
|
|
374
378
|
HighWaterMarkJson,
|
|
@@ -392,8 +396,10 @@ from ipor_fusion.vault_info import (
|
|
|
392
396
|
VaultAssetJson,
|
|
393
397
|
VaultInfoJson,
|
|
394
398
|
VaultLinksJson,
|
|
399
|
+
VaultValidationJson,
|
|
395
400
|
WithdrawManagerJson,
|
|
396
401
|
build_vault_info_json,
|
|
402
|
+
build_vault_validation_json,
|
|
397
403
|
)
|
|
398
404
|
|
|
399
405
|
__version__ = package_version()
|
|
@@ -461,6 +467,8 @@ __all__ = [
|
|
|
461
467
|
"RoleStatus",
|
|
462
468
|
"resolve_access_manager",
|
|
463
469
|
"role_account_sort_key",
|
|
470
|
+
"RoleAccountsJson",
|
|
471
|
+
"build_role_accounts_json",
|
|
464
472
|
"RewardsManager",
|
|
465
473
|
"VestingData",
|
|
466
474
|
"ERC20",
|
|
@@ -719,6 +727,10 @@ __all__ = [
|
|
|
719
727
|
"UsdAmountJson",
|
|
720
728
|
"VaultAssetJson",
|
|
721
729
|
"VaultInfoJson",
|
|
730
|
+
"VaultValidationJson",
|
|
731
|
+
"FindingJson",
|
|
732
|
+
"Erc20HoldingsJson",
|
|
733
|
+
"build_vault_validation_json",
|
|
722
734
|
"VaultLinksJson",
|
|
723
735
|
"WithdrawManagerJson",
|
|
724
736
|
"MorphoApiError",
|
|
@@ -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
|
|
@@ -16,7 +16,7 @@ from eth_typing import ChecksumAddress
|
|
|
16
16
|
|
|
17
17
|
from ipor_fusion.chains import CHAIN_NAMES
|
|
18
18
|
from ipor_fusion.cli.config_store import load_config
|
|
19
|
-
from ipor_fusion.cli.options import ADDRESS, CHAIN, _resolve_provider
|
|
19
|
+
from ipor_fusion.cli.options import ADDRESS, BLOCK_NUMBER, CHAIN, _resolve_provider
|
|
20
20
|
from ipor_fusion.cli.vault_rendering import _format_amount, _print_table
|
|
21
21
|
from ipor_fusion.core.context import Web3Context
|
|
22
22
|
from ipor_fusion.market_info import (
|
|
@@ -81,8 +81,10 @@ def market() -> None:
|
|
|
81
81
|
help="Chain ID or name (ethereum, base, arbitrum, ...).",
|
|
82
82
|
)
|
|
83
83
|
@click.option(
|
|
84
|
+
"--block-number",
|
|
84
85
|
"--block",
|
|
85
|
-
|
|
86
|
+
"block_number",
|
|
87
|
+
type=BLOCK_NUMBER,
|
|
86
88
|
default=None,
|
|
87
89
|
help="Block number for on-chain reads (default: latest).",
|
|
88
90
|
)
|
|
@@ -109,7 +111,7 @@ def market() -> None:
|
|
|
109
111
|
def morpho_blue(
|
|
110
112
|
market_id: MorphoBlueMarketId,
|
|
111
113
|
chain_id: ChainId,
|
|
112
|
-
|
|
114
|
+
block_number: int | None,
|
|
113
115
|
no_api: bool,
|
|
114
116
|
vault_filter: str | None,
|
|
115
117
|
json_output: bool,
|
|
@@ -125,8 +127,8 @@ def morpho_blue(
|
|
|
125
127
|
cfg = load_config()
|
|
126
128
|
provider_url = _resolve_provider(cfg, chain_id)
|
|
127
129
|
ctx = Web3Context.from_url(provider_url)
|
|
128
|
-
if
|
|
129
|
-
ctx = ctx.at_block(
|
|
130
|
+
if block_number is not None:
|
|
131
|
+
ctx = ctx.at_block(block_number)
|
|
130
132
|
reader = MorphoReader(morpho_blue_address(chain_id))
|
|
131
133
|
mid = market_id
|
|
132
134
|
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
|
|
|
@@ -2,11 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import sys
|
|
5
|
-
from typing import TypedDict
|
|
6
5
|
|
|
7
6
|
import click
|
|
8
|
-
from web3 import Web3
|
|
9
|
-
from web3.types import Timestamp
|
|
10
7
|
|
|
11
8
|
from ipor_fusion.chains import CHAIN_NAMES
|
|
12
9
|
from ipor_fusion.cli.config_store import (
|
|
@@ -15,7 +12,13 @@ from ipor_fusion.cli.config_store import (
|
|
|
15
12
|
load_config,
|
|
16
13
|
save_config,
|
|
17
14
|
)
|
|
18
|
-
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
|
+
)
|
|
19
22
|
from ipor_fusion.cli.oracle_mapping_view import _print_oracle_mapping
|
|
20
23
|
from ipor_fusion.cli.vault_health import (
|
|
21
24
|
_print_validation,
|
|
@@ -29,8 +32,8 @@ from ipor_fusion.cli.vault_rendering import (
|
|
|
29
32
|
)
|
|
30
33
|
from ipor_fusion.config.roles import Roles
|
|
31
34
|
from ipor_fusion.core.access import (
|
|
35
|
+
build_role_accounts_json,
|
|
32
36
|
resolve_access_manager,
|
|
33
|
-
role_account_sort_key,
|
|
34
37
|
)
|
|
35
38
|
from ipor_fusion.core.context import Web3Context
|
|
36
39
|
from ipor_fusion.core.plasma_vault import PlasmaVault
|
|
@@ -42,9 +45,8 @@ from ipor_fusion.errors import (
|
|
|
42
45
|
from ipor_fusion.readers.oracle_mapping import (
|
|
43
46
|
build_oracle_mapping,
|
|
44
47
|
)
|
|
45
|
-
from ipor_fusion.types import ChainId,
|
|
48
|
+
from ipor_fusion.types import ChainId, PlasmaVaultAddress
|
|
46
49
|
from ipor_fusion.validation import (
|
|
47
|
-
VaultValidation,
|
|
48
50
|
validate_vault,
|
|
49
51
|
)
|
|
50
52
|
from ipor_fusion.vault_data import (
|
|
@@ -52,43 +54,11 @@ from ipor_fusion.vault_data import (
|
|
|
52
54
|
fetch_vault_data,
|
|
53
55
|
)
|
|
54
56
|
from ipor_fusion.vault_info import (
|
|
55
|
-
Erc20BalanceJson,
|
|
56
|
-
LendingHealthJson,
|
|
57
|
-
ReconciliationJson,
|
|
58
|
-
VaultAssetJson,
|
|
59
|
-
_erc20_balances_json,
|
|
60
|
-
_lending_health_json,
|
|
61
|
-
_reconciliation_json,
|
|
62
57
|
_unix_to_iso,
|
|
58
|
+
build_vault_validation_json,
|
|
63
59
|
)
|
|
64
60
|
|
|
65
61
|
|
|
66
|
-
class _FindingJson(TypedDict):
|
|
67
|
-
check: Check
|
|
68
|
-
severity: Severity
|
|
69
|
-
message: str
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class _ValidationJson(TypedDict):
|
|
73
|
-
"""The ``fusion vault validate --json`` document."""
|
|
74
|
-
|
|
75
|
-
vault: PlasmaVaultAddress
|
|
76
|
-
chain: str
|
|
77
|
-
chain_id: ChainId
|
|
78
|
-
block: int
|
|
79
|
-
block_timestamp: Timestamp
|
|
80
|
-
block_timestamp_utc: str
|
|
81
|
-
asset: VaultAssetJson
|
|
82
|
-
verdict: Verdict
|
|
83
|
-
criticals: list[str]
|
|
84
|
-
warnings: list[str]
|
|
85
|
-
ok: list[str]
|
|
86
|
-
findings: list[_FindingJson]
|
|
87
|
-
reconciliation: ReconciliationJson
|
|
88
|
-
erc20_balances: list[Erc20BalanceJson]
|
|
89
|
-
lending_health: LendingHealthJson | None
|
|
90
|
-
|
|
91
|
-
|
|
92
62
|
def _auto_save_vault(
|
|
93
63
|
cfg: FusionConfig,
|
|
94
64
|
vault_address: PlasmaVaultAddress,
|
|
@@ -216,7 +186,7 @@ vault.add_command(list_vaults, "ls")
|
|
|
216
186
|
)
|
|
217
187
|
@click.option(
|
|
218
188
|
"--block-number",
|
|
219
|
-
type=
|
|
189
|
+
type=BLOCK_NUMBER,
|
|
220
190
|
default=None,
|
|
221
191
|
help="Block number (default: latest).",
|
|
222
192
|
)
|
|
@@ -261,7 +231,7 @@ def info(
|
|
|
261
231
|
)
|
|
262
232
|
@click.option(
|
|
263
233
|
"--block-number",
|
|
264
|
-
type=
|
|
234
|
+
type=BLOCK_NUMBER,
|
|
265
235
|
default=None,
|
|
266
236
|
help="Block number (default: latest).",
|
|
267
237
|
)
|
|
@@ -296,7 +266,9 @@ def validate(
|
|
|
296
266
|
|
|
297
267
|
chain_label = CHAIN_NAMES.get(chain_id, str(chain_id))
|
|
298
268
|
if json_output:
|
|
299
|
-
click.echo(
|
|
269
|
+
click.echo(
|
|
270
|
+
json.dumps(build_vault_validation_json(validation, chain_label), indent=2)
|
|
271
|
+
)
|
|
300
272
|
else:
|
|
301
273
|
click.echo(f"Vault: {validation.vault}")
|
|
302
274
|
click.echo(f"Chain: {chain_label} (chain-id={chain_id})")
|
|
@@ -307,36 +279,6 @@ def validate(
|
|
|
307
279
|
sys.exit(1)
|
|
308
280
|
|
|
309
281
|
|
|
310
|
-
def _validation_json(validation: VaultValidation, chain_label: str) -> _ValidationJson:
|
|
311
|
-
return {
|
|
312
|
-
"vault": validation.vault,
|
|
313
|
-
"chain": chain_label,
|
|
314
|
-
"chain_id": validation.chain_id,
|
|
315
|
-
"block": validation.block_number,
|
|
316
|
-
"block_timestamp": validation.block_timestamp,
|
|
317
|
-
"block_timestamp_utc": _unix_to_iso(validation.block_timestamp),
|
|
318
|
-
"asset": {
|
|
319
|
-
"address": validation.asset,
|
|
320
|
-
"symbol": validation.asset_symbol,
|
|
321
|
-
"decimals": validation.asset_decimals,
|
|
322
|
-
"price_usd": validation.asset_price_usd,
|
|
323
|
-
},
|
|
324
|
-
"verdict": validation.verdict,
|
|
325
|
-
"criticals": list(validation.criticals),
|
|
326
|
-
"warnings": list(validation.warnings),
|
|
327
|
-
"ok": list(validation.ok),
|
|
328
|
-
"findings": [
|
|
329
|
-
{"check": f.check, "severity": f.severity, "message": f.message}
|
|
330
|
-
for f in validation.findings
|
|
331
|
-
],
|
|
332
|
-
"reconciliation": _reconciliation_json(
|
|
333
|
-
validation.reconciliation, validation.asset_decimals
|
|
334
|
-
),
|
|
335
|
-
"erc20_balances": _erc20_balances_json(validation),
|
|
336
|
-
"lending_health": _lending_health_json(validation.lending_health),
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
282
|
@vault.command("role-accounts")
|
|
341
283
|
@click.argument("vault_address", type=VAULT_ADDRESS)
|
|
342
284
|
@click.option(
|
|
@@ -350,7 +292,7 @@ def _validation_json(validation: VaultValidation, chain_label: str) -> _Validati
|
|
|
350
292
|
)
|
|
351
293
|
@click.option(
|
|
352
294
|
"--block-number",
|
|
353
|
-
type=
|
|
295
|
+
type=BLOCK_NUMBER,
|
|
354
296
|
default=None,
|
|
355
297
|
help="Block number (default: latest).",
|
|
356
298
|
)
|
|
@@ -389,21 +331,19 @@ def role_accounts(
|
|
|
389
331
|
raise click.ClickException(
|
|
390
332
|
f"RoleGranted log scan failed ({type(exc).__name__}: {exc})."
|
|
391
333
|
) from exc
|
|
392
|
-
|
|
393
|
-
|
|
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
|
+
)
|
|
394
341
|
if json_output:
|
|
395
|
-
|
|
396
|
-
"vault": Web3.to_checksum_address(vault_address),
|
|
397
|
-
"access_manager": manager.address,
|
|
398
|
-
"chain_id": chain_id,
|
|
399
|
-
"role_filter": Roles.get_name(role_id) if role_id is not None else None,
|
|
400
|
-
"accounts": rows,
|
|
401
|
-
}
|
|
402
|
-
click.echo(json.dumps(payload, indent=2))
|
|
342
|
+
click.echo(json.dumps(document, indent=2))
|
|
403
343
|
return
|
|
404
344
|
|
|
405
345
|
click.echo(f"Access Manager: {manager.address}")
|
|
406
|
-
_print_role_accounts_table(
|
|
346
|
+
_print_role_accounts_table(document["accounts"])
|
|
407
347
|
|
|
408
348
|
|
|
409
349
|
@vault.command("oracle-mapping")
|
|
@@ -413,7 +353,7 @@ def role_accounts(
|
|
|
413
353
|
)
|
|
414
354
|
@click.option(
|
|
415
355
|
"--block-number",
|
|
416
|
-
type=
|
|
356
|
+
type=BLOCK_NUMBER,
|
|
417
357
|
default=None,
|
|
418
358
|
help="Block number (default: latest).",
|
|
419
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))
|