ipor-fusion 4.0.0.dev231__tar.gz → 4.0.0.dev234__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.dev234}/AGENTS.md +7 -3
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/MIGRATION_4.md +23 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/PKG-INFO +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/__init__.py +8 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_cmd.py +5 -68
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/models.py +18 -236
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/server.py +5 -3
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/validation.py +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_info.py +109 -22
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/clean.json +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/clean.mcp.json +4 -8
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/deployment_error.json +1 -1
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +4 -8
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/fetched.mcp.json +6 -12
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/full.mcp.json +15 -34
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/rich.mcp.json +13 -30
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_vault_validate.py +8 -3
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_models.py +5 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_vault_validate.py +66 -30
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_public_api.py +11 -3
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/README.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/simulation_result.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/hypercore/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_event_args.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_fake_rpc.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_live.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_oracle_mapping_fake.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/simulate_v1_request.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/fetched.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/full.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/full.txt +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_helpers.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_market_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_oracle_mapping_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_docs_typing.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulation_errors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_simulation_observation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_data_block.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_info_fetched_golden.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_reconciliation_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev231 → ipor_fusion-4.0.0.dev234}/uv.lock +0 -0
|
@@ -487,9 +487,13 @@ structured home: a one-line summary, behavioral notes, cost warnings. Do not
|
|
|
487
487
|
repeat schema content as prose; it drifts.
|
|
488
488
|
|
|
489
489
|
`mcp/models.py` forbids extra keys and stays the source of the published JSON
|
|
490
|
-
schema.
|
|
491
|
-
|
|
492
|
-
|
|
490
|
+
schema. A command whose CLI `--json` and MCP tool return the same data publish one
|
|
491
|
+
document: an SDK builder returns its TypedDict (`build_vault_info_json`,
|
|
492
|
+
`build_vault_validation_json`, the `fusion market` builders) and the tool
|
|
493
|
+
validates it into the mirroring model, never a second hand-built shape. Adding a
|
|
494
|
+
field to a document means adding it to its TypedDict, to the matching model (with
|
|
495
|
+
a default for back-compat) and to the `_full_vault_info_dict` fixture for vault
|
|
496
|
+
info;
|
|
493
497
|
`TestTypedDocumentsMirrorModels` in `test_mcp_models.py` holds every TypedDict to
|
|
494
498
|
its model block by block, a union block (`deployment`, `position_breakdown`,
|
|
495
499
|
`source_detail`) shape by shape. Every block is a model; a block whose optional keys
|
|
@@ -990,6 +990,29 @@ 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
|
+
- **`vault validate` publishes one document.** In 3.x the CLI's `--json` output
|
|
994
|
+
and the MCP `vault_validate` tool returned two different shapes; both now
|
|
995
|
+
return `VaultValidationJson`, built by `build_vault_validation_json(validation,
|
|
996
|
+
chain)` (exported from `ipor_fusion` and `ipor_fusion.vault_info`):
|
|
997
|
+
- `findings` lists every check in order with its `check`, `severity` and
|
|
998
|
+
`message`. It replaces the CLI's `criticals` / `warnings` / `ok` string lists
|
|
999
|
+
and the MCP tool's `criticals` / `warnings` / `passed` lists; filter by
|
|
1000
|
+
`severity`.
|
|
1001
|
+
- `erc20` (`tokens`, `value_in_asset`, `market_cached_value`, `unpriced_tokens`)
|
|
1002
|
+
replaces the CLI's `erc20_balances`; `erc20.tokens` has the same entries.
|
|
1003
|
+
- `reconciliation`, `lending_health` and `asset` share their shapes with the
|
|
1004
|
+
`vault info` document. For the MCP tool this means `delta.percent` instead of
|
|
1005
|
+
`delta_percent`, amounts formatted with thousands separators and at most six
|
|
1006
|
+
decimals (`"14,912.465797"`; `raw` keeps the exact value) and the delta's
|
|
1007
|
+
`formatted` as its absolute value, as in `vault info`.
|
|
1008
|
+
- The MCP tool's output gains `block_timestamp`.
|
|
1009
|
+
- An ERC20 entry whose balance read failed keeps its `decimals` when they were
|
|
1010
|
+
read (in `vault info` too).
|
|
1011
|
+
- The MCP output is the builder's document key for key: an amount without a
|
|
1012
|
+
USD value and an ERC20 entry without a balance no longer gain `null` keys
|
|
1013
|
+
(`usd`, `price_usd`, ...) in either tool, `vault_info` included, and a zero
|
|
1014
|
+
USD delta without a price is `0.0`, not `0`.
|
|
1015
|
+
|
|
993
1016
|
- **A failed lending read is a warning, never a silent `ok`.** 3.x caught every
|
|
994
1017
|
exception in the lending-health readers and dropped the position, so a provider
|
|
995
1018
|
outage could leave no lending finding and an `ok` verdict. Now:
|
|
@@ -368,7 +368,9 @@ from ipor_fusion.vault_info import (
|
|
|
368
368
|
DeploymentErrorJson,
|
|
369
369
|
DeploymentJson,
|
|
370
370
|
Erc20BalanceJson,
|
|
371
|
+
Erc20HoldingsJson,
|
|
371
372
|
FeesJson,
|
|
373
|
+
FindingJson,
|
|
372
374
|
FuseJson,
|
|
373
375
|
HealthCheckJson,
|
|
374
376
|
HighWaterMarkJson,
|
|
@@ -392,8 +394,10 @@ from ipor_fusion.vault_info import (
|
|
|
392
394
|
VaultAssetJson,
|
|
393
395
|
VaultInfoJson,
|
|
394
396
|
VaultLinksJson,
|
|
397
|
+
VaultValidationJson,
|
|
395
398
|
WithdrawManagerJson,
|
|
396
399
|
build_vault_info_json,
|
|
400
|
+
build_vault_validation_json,
|
|
397
401
|
)
|
|
398
402
|
|
|
399
403
|
__version__ = package_version()
|
|
@@ -719,6 +723,10 @@ __all__ = [
|
|
|
719
723
|
"UsdAmountJson",
|
|
720
724
|
"VaultAssetJson",
|
|
721
725
|
"VaultInfoJson",
|
|
726
|
+
"VaultValidationJson",
|
|
727
|
+
"FindingJson",
|
|
728
|
+
"Erc20HoldingsJson",
|
|
729
|
+
"build_vault_validation_json",
|
|
722
730
|
"VaultLinksJson",
|
|
723
731
|
"WithdrawManagerJson",
|
|
724
732
|
"MorphoApiError",
|
|
@@ -2,11 +2,9 @@ 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
7
|
from web3 import Web3
|
|
9
|
-
from web3.types import Timestamp
|
|
10
8
|
|
|
11
9
|
from ipor_fusion.chains import CHAIN_NAMES
|
|
12
10
|
from ipor_fusion.cli.config_store import (
|
|
@@ -42,9 +40,8 @@ from ipor_fusion.errors import (
|
|
|
42
40
|
from ipor_fusion.readers.oracle_mapping import (
|
|
43
41
|
build_oracle_mapping,
|
|
44
42
|
)
|
|
45
|
-
from ipor_fusion.types import ChainId,
|
|
43
|
+
from ipor_fusion.types import ChainId, PlasmaVaultAddress
|
|
46
44
|
from ipor_fusion.validation import (
|
|
47
|
-
VaultValidation,
|
|
48
45
|
validate_vault,
|
|
49
46
|
)
|
|
50
47
|
from ipor_fusion.vault_data import (
|
|
@@ -52,43 +49,11 @@ from ipor_fusion.vault_data import (
|
|
|
52
49
|
fetch_vault_data,
|
|
53
50
|
)
|
|
54
51
|
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
52
|
_unix_to_iso,
|
|
53
|
+
build_vault_validation_json,
|
|
63
54
|
)
|
|
64
55
|
|
|
65
56
|
|
|
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
57
|
def _auto_save_vault(
|
|
93
58
|
cfg: FusionConfig,
|
|
94
59
|
vault_address: PlasmaVaultAddress,
|
|
@@ -296,7 +261,9 @@ def validate(
|
|
|
296
261
|
|
|
297
262
|
chain_label = CHAIN_NAMES.get(chain_id, str(chain_id))
|
|
298
263
|
if json_output:
|
|
299
|
-
click.echo(
|
|
264
|
+
click.echo(
|
|
265
|
+
json.dumps(build_vault_validation_json(validation, chain_label), indent=2)
|
|
266
|
+
)
|
|
300
267
|
else:
|
|
301
268
|
click.echo(f"Vault: {validation.vault}")
|
|
302
269
|
click.echo(f"Chain: {chain_label} (chain-id={chain_id})")
|
|
@@ -307,36 +274,6 @@ def validate(
|
|
|
307
274
|
sys.exit(1)
|
|
308
275
|
|
|
309
276
|
|
|
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
277
|
@vault.command("role-accounts")
|
|
341
278
|
@click.argument("vault_address", type=VAULT_ADDRESS)
|
|
342
279
|
@click.option(
|
|
@@ -33,8 +33,6 @@ Design notes:
|
|
|
33
33
|
|
|
34
34
|
from __future__ import annotations
|
|
35
35
|
|
|
36
|
-
from datetime import UTC, datetime
|
|
37
|
-
from decimal import Decimal
|
|
38
36
|
from typing import TYPE_CHECKING, Literal
|
|
39
37
|
|
|
40
38
|
from pydantic import (
|
|
@@ -62,19 +60,7 @@ from ipor_fusion.types import (
|
|
|
62
60
|
if TYPE_CHECKING:
|
|
63
61
|
from ipor_fusion.about import ChangelogEntry
|
|
64
62
|
from ipor_fusion.core.access import RoleAccount
|
|
65
|
-
from ipor_fusion.readers.lending_health import (
|
|
66
|
-
LendingMarketHealth as ReaderLendingMarketHealth,
|
|
67
|
-
)
|
|
68
|
-
from ipor_fusion.readers.lending_health import VaultLendingHealth
|
|
69
63
|
from ipor_fusion.readers.oracle_mapping import OracleMapping, OracleNode
|
|
70
|
-
from ipor_fusion.validation import (
|
|
71
|
-
Erc20Holding,
|
|
72
|
-
Erc20Holdings,
|
|
73
|
-
VaultValidation,
|
|
74
|
-
)
|
|
75
|
-
from ipor_fusion.validation import (
|
|
76
|
-
Reconciliation as ValidationReconciliation,
|
|
77
|
-
)
|
|
78
64
|
|
|
79
65
|
|
|
80
66
|
class _Base(BaseModel):
|
|
@@ -98,7 +84,7 @@ class _Sparse(_Base):
|
|
|
98
84
|
# ---------------------------------------------------------------------------
|
|
99
85
|
|
|
100
86
|
|
|
101
|
-
class Amount(
|
|
87
|
+
class Amount(_Sparse):
|
|
102
88
|
"""Token amount with both raw and formatted representations."""
|
|
103
89
|
|
|
104
90
|
raw: int | str = Field(description="Raw on-chain integer (or 'unlimited').")
|
|
@@ -382,7 +368,7 @@ class BalanceFuseEntry(_Base):
|
|
|
382
368
|
)
|
|
383
369
|
|
|
384
370
|
|
|
385
|
-
class ERC20Entry(
|
|
371
|
+
class ERC20Entry(_Sparse):
|
|
386
372
|
"""ERC-20 balance entry; field set varies by data availability."""
|
|
387
373
|
|
|
388
374
|
address: str
|
|
@@ -770,255 +756,51 @@ class OracleMappingResponse(_Base):
|
|
|
770
756
|
# ---------------------------------------------------------------------------
|
|
771
757
|
|
|
772
758
|
|
|
773
|
-
def _formatted(raw: int, decimals: int) -> str:
|
|
774
|
-
return format(Decimal(raw).scaleb(-decimals).normalize(), "f") if raw else "0"
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
def _amount(raw: int, decimals: int, usd: float | None = None) -> Amount:
|
|
778
|
-
"""An Amount in vault_info's convention, the sign kept in `formatted`."""
|
|
779
|
-
return Amount(raw=raw, formatted=_formatted(raw, decimals), usd=usd)
|
|
780
|
-
|
|
781
|
-
|
|
782
759
|
class FindingModel(_Base):
|
|
783
760
|
check: Check
|
|
784
761
|
severity: Severity
|
|
785
762
|
message: str
|
|
786
763
|
|
|
787
764
|
|
|
788
|
-
class ReconciliationModel(_Base):
|
|
789
|
-
"""totalAssets against balance fuses, idle underlying and the rewards
|
|
790
|
-
claim manager's balance."""
|
|
791
|
-
|
|
792
|
-
balance_fuses_total: Amount
|
|
793
|
-
underlying_on_vault: Amount
|
|
794
|
-
rewards_manager_balance: Amount = Field(
|
|
795
|
-
description="RewardsClaimManager.balanceOf(), which totalAssets includes; "
|
|
796
|
-
"0 without a manager or when it could not be read."
|
|
797
|
-
)
|
|
798
|
-
management_fee_netted: Amount = Field(
|
|
799
|
-
description="Unrealized management fee subtracted from sum: totalAssets "
|
|
800
|
-
"nets it on vaults built before ipor-fusion #475; 0 otherwise."
|
|
801
|
-
)
|
|
802
|
-
erc20_direct_total: Amount = Field(
|
|
803
|
-
description="Informational: already valued by the ERC20_VAULT_BALANCE "
|
|
804
|
-
"balance fuse, never added to sum."
|
|
805
|
-
)
|
|
806
|
-
sum: Amount
|
|
807
|
-
on_chain_total_assets: Amount
|
|
808
|
-
delta: Amount
|
|
809
|
-
delta_percent: float
|
|
810
|
-
pending_withdrawals: Amount
|
|
811
|
-
implied_market_total: Amount
|
|
812
|
-
market_storage_divergence: int = Field(
|
|
813
|
-
description="balance_fuses_total minus implied_market_total, raw: "
|
|
814
|
-
"per-market caches drifting from the stored global total."
|
|
815
|
-
)
|
|
816
|
-
|
|
817
|
-
@classmethod
|
|
818
|
-
def from_reconciliation(
|
|
819
|
-
cls, rec: ValidationReconciliation, decimals: int
|
|
820
|
-
) -> ReconciliationModel:
|
|
821
|
-
def amount(raw: int, usd: float | None = None) -> Amount:
|
|
822
|
-
return _amount(raw, decimals, usd)
|
|
823
|
-
|
|
824
|
-
return cls(
|
|
825
|
-
balance_fuses_total=amount(
|
|
826
|
-
rec.balance_fuses_total, rec.balance_fuses_total_usd
|
|
827
|
-
),
|
|
828
|
-
underlying_on_vault=amount(
|
|
829
|
-
rec.underlying_on_vault, rec.underlying_on_vault_usd
|
|
830
|
-
),
|
|
831
|
-
rewards_manager_balance=amount(
|
|
832
|
-
rec.rewards_manager_balance, rec.rewards_manager_balance_usd
|
|
833
|
-
),
|
|
834
|
-
management_fee_netted=amount(
|
|
835
|
-
rec.management_fee_netted, rec.management_fee_netted_usd
|
|
836
|
-
),
|
|
837
|
-
erc20_direct_total=amount(
|
|
838
|
-
rec.erc20_direct_total, rec.erc20_direct_total_usd
|
|
839
|
-
),
|
|
840
|
-
sum=amount(rec.sum, rec.sum_usd),
|
|
841
|
-
on_chain_total_assets=amount(
|
|
842
|
-
rec.on_chain_total_assets, rec.on_chain_total_assets_usd
|
|
843
|
-
),
|
|
844
|
-
delta=amount(rec.delta, rec.delta_usd),
|
|
845
|
-
delta_percent=rec.delta_percent,
|
|
846
|
-
pending_withdrawals=amount(
|
|
847
|
-
rec.pending_withdrawals, rec.pending_withdrawals_usd
|
|
848
|
-
),
|
|
849
|
-
implied_market_total=amount(rec.implied_market_total),
|
|
850
|
-
market_storage_divergence=rec.market_storage_divergence,
|
|
851
|
-
)
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
class LendingMarketModel(_Base):
|
|
855
|
-
protocol: str
|
|
856
|
-
market_id: MarketId
|
|
857
|
-
market_name: str
|
|
858
|
-
substrate_id: str | None = Field(
|
|
859
|
-
description="0x-prefixed Morpho market id or Aave V4 Spoke; null for "
|
|
860
|
-
"Aave V3, whose health is account-level."
|
|
861
|
-
)
|
|
862
|
-
current_ltv: float | None
|
|
863
|
-
max_ltv: float
|
|
864
|
-
health_factor: float | None
|
|
865
|
-
total_collateral_usd: float | None
|
|
866
|
-
total_debt_usd: float | None
|
|
867
|
-
ltv_usage_percent: float | None
|
|
868
|
-
|
|
869
|
-
@classmethod
|
|
870
|
-
def from_market(cls, m: ReaderLendingMarketHealth) -> LendingMarketModel:
|
|
871
|
-
substrate_id = m.substrate_id
|
|
872
|
-
if substrate_id is not None and not substrate_id.startswith("0x"):
|
|
873
|
-
substrate_id = f"0x{substrate_id}"
|
|
874
|
-
return cls(
|
|
875
|
-
protocol=m.protocol,
|
|
876
|
-
market_id=m.market_id,
|
|
877
|
-
market_name=m.market_name,
|
|
878
|
-
substrate_id=substrate_id,
|
|
879
|
-
current_ltv=m.current_ltv,
|
|
880
|
-
max_ltv=m.max_ltv,
|
|
881
|
-
health_factor=m.health_factor,
|
|
882
|
-
total_collateral_usd=m.total_collateral_usd,
|
|
883
|
-
total_debt_usd=m.total_debt_usd,
|
|
884
|
-
ltv_usage_percent=m.ltv_usage_percent,
|
|
885
|
-
)
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
class LendingHealthModel(_Base):
|
|
889
|
-
markets: list[LendingMarketModel]
|
|
890
|
-
unreadable: list[UnreadableLendingMarket] = Field(
|
|
891
|
-
description="Positions whose health could not be read: unknown, not healthy."
|
|
892
|
-
)
|
|
893
|
-
worst_ltv_usage_percent: float | None
|
|
894
|
-
|
|
895
|
-
@classmethod
|
|
896
|
-
def from_health(cls, health: VaultLendingHealth) -> LendingHealthModel:
|
|
897
|
-
return cls(
|
|
898
|
-
markets=[LendingMarketModel.from_market(m) for m in health.markets],
|
|
899
|
-
unreadable=[
|
|
900
|
-
UnreadableLendingMarket(
|
|
901
|
-
protocol=u.protocol,
|
|
902
|
-
market_id=u.market_id,
|
|
903
|
-
market_name=u.market_name,
|
|
904
|
-
substrate_id=u.substrate_id,
|
|
905
|
-
reason=u.reason,
|
|
906
|
-
)
|
|
907
|
-
for u in health.unreadable
|
|
908
|
-
],
|
|
909
|
-
worst_ltv_usage_percent=health.worst_ltv_usage,
|
|
910
|
-
)
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
class Erc20HoldingModel(_Base):
|
|
914
|
-
address: str
|
|
915
|
-
symbol: str
|
|
916
|
-
decimals: int | None
|
|
917
|
-
balance: Amount | None
|
|
918
|
-
price_usd: float | None
|
|
919
|
-
usd_value: float | None
|
|
920
|
-
note: str
|
|
921
|
-
|
|
922
|
-
@classmethod
|
|
923
|
-
def from_holding(cls, token: Erc20Holding) -> Erc20HoldingModel:
|
|
924
|
-
balance = (
|
|
925
|
-
_amount(token.balance, token.decimals)
|
|
926
|
-
if token.balance is not None and token.decimals is not None
|
|
927
|
-
else None
|
|
928
|
-
)
|
|
929
|
-
return cls(
|
|
930
|
-
address=token.address,
|
|
931
|
-
symbol=token.symbol,
|
|
932
|
-
decimals=token.decimals,
|
|
933
|
-
balance=balance,
|
|
934
|
-
price_usd=token.price_usd,
|
|
935
|
-
usd_value=token.usd_value,
|
|
936
|
-
note=token.note,
|
|
937
|
-
)
|
|
938
|
-
|
|
939
|
-
|
|
940
765
|
class Erc20HoldingsModel(_Base):
|
|
941
|
-
tokens
|
|
766
|
+
"""The ERC20_VAULT_BALANCE tokens read directly."""
|
|
767
|
+
|
|
768
|
+
tokens: list[ERC20Entry]
|
|
942
769
|
value_in_asset: Amount
|
|
943
770
|
market_cached_value: Amount = Field(
|
|
944
771
|
description="What the ERC20_VAULT_BALANCE market last stored for "
|
|
945
772
|
"these tokens, in the asset."
|
|
946
773
|
)
|
|
947
|
-
unpriced_tokens: list[str]
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
def from_holdings(cls, erc20: Erc20Holdings, decimals: int) -> Erc20HoldingsModel:
|
|
951
|
-
return cls(
|
|
952
|
-
tokens=[Erc20HoldingModel.from_holding(t) for t in erc20.tokens],
|
|
953
|
-
value_in_asset=_amount(erc20.value_in_asset, decimals, erc20.value_usd),
|
|
954
|
-
market_cached_value=_amount(erc20.market_cached_value, decimals),
|
|
955
|
-
unpriced_tokens=list(erc20.unpriced_tokens),
|
|
956
|
-
)
|
|
774
|
+
unpriced_tokens: list[str] = Field(
|
|
775
|
+
description="Held tokens that could not be valued, as 'address (symbol)'."
|
|
776
|
+
)
|
|
957
777
|
|
|
958
778
|
|
|
959
779
|
class VaultValidationResponse(_Base):
|
|
960
|
-
"""Health verdict of one Plasma Vault at one block
|
|
780
|
+
"""Health verdict of one Plasma Vault at one block; the same document as
|
|
781
|
+
``fusion vault validate --json``."""
|
|
961
782
|
|
|
962
783
|
vault: str
|
|
963
784
|
chain: str
|
|
964
785
|
chain_id: ChainId
|
|
965
786
|
block: int
|
|
787
|
+
block_timestamp: int
|
|
966
788
|
block_timestamp_utc: str
|
|
967
789
|
asset: AssetInfo
|
|
968
790
|
verdict: Verdict = Field(
|
|
969
791
|
description="Worst severity found: critical if any critical finding, "
|
|
970
792
|
"else warning if any warning, else ok."
|
|
971
793
|
)
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
passed: list[FindingModel] = Field(
|
|
975
|
-
description="Checks that ran and found nothing wrong."
|
|
794
|
+
findings: list[FindingModel] = Field(
|
|
795
|
+
description="Every check in the order it ran, ok ones included."
|
|
976
796
|
)
|
|
977
|
-
reconciliation:
|
|
978
|
-
|
|
797
|
+
reconciliation: Reconciliation
|
|
798
|
+
erc20: Erc20HoldingsModel
|
|
799
|
+
lending_health: LendingHealth | None = Field(
|
|
979
800
|
description="LTV and health factor per lending market (Morpho, Aave "
|
|
980
|
-
"V3/V4); null when the vault
|
|
801
|
+
"V3/V4) and the positions that could not be read; null when the vault "
|
|
802
|
+
"has no lending position."
|
|
981
803
|
)
|
|
982
|
-
erc20: Erc20HoldingsModel
|
|
983
|
-
|
|
984
|
-
@classmethod
|
|
985
|
-
def from_validation(cls, v: VaultValidation, chain: str) -> VaultValidationResponse:
|
|
986
|
-
def by(severity: Severity) -> list[FindingModel]:
|
|
987
|
-
return [
|
|
988
|
-
FindingModel(check=f.check, severity=f.severity, message=f.message)
|
|
989
|
-
for f in v.findings
|
|
990
|
-
if f.severity == severity
|
|
991
|
-
]
|
|
992
|
-
|
|
993
|
-
health = v.lending_health
|
|
994
|
-
return cls(
|
|
995
|
-
vault=v.vault,
|
|
996
|
-
chain=chain,
|
|
997
|
-
chain_id=v.chain_id,
|
|
998
|
-
block=v.block_number,
|
|
999
|
-
block_timestamp_utc=datetime.fromtimestamp(
|
|
1000
|
-
v.block_timestamp, tz=UTC
|
|
1001
|
-
).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
1002
|
-
asset=AssetInfo(
|
|
1003
|
-
address=v.asset,
|
|
1004
|
-
symbol=v.asset_symbol,
|
|
1005
|
-
decimals=v.asset_decimals,
|
|
1006
|
-
price_usd=v.asset_price_usd,
|
|
1007
|
-
),
|
|
1008
|
-
verdict=v.verdict,
|
|
1009
|
-
criticals=by("critical"),
|
|
1010
|
-
warnings=by("warning"),
|
|
1011
|
-
passed=by("ok"),
|
|
1012
|
-
reconciliation=ReconciliationModel.from_reconciliation(
|
|
1013
|
-
v.reconciliation, v.asset_decimals
|
|
1014
|
-
),
|
|
1015
|
-
lending_health=(
|
|
1016
|
-
LendingHealthModel.from_health(health)
|
|
1017
|
-
if health is not None and health.has_lending_positions
|
|
1018
|
-
else None
|
|
1019
|
-
),
|
|
1020
|
-
erc20=Erc20HoldingsModel.from_holdings(v.erc20, v.asset_decimals),
|
|
1021
|
-
)
|
|
1022
804
|
|
|
1023
805
|
|
|
1024
806
|
# ---------------------------------------------------------------------------
|
|
@@ -65,7 +65,7 @@ from ipor_fusion.vault_data import (
|
|
|
65
65
|
fetch_deployment_info,
|
|
66
66
|
fetch_vault_data,
|
|
67
67
|
)
|
|
68
|
-
from ipor_fusion.vault_info import build_vault_info_json
|
|
68
|
+
from ipor_fusion.vault_info import build_vault_info_json, build_vault_validation_json
|
|
69
69
|
|
|
70
70
|
# chain_id default: detect the chain from the saved vault or the single provider.
|
|
71
71
|
_AUTO_CHAIN = ChainId(0)
|
|
@@ -246,8 +246,10 @@ def vault_validate(
|
|
|
246
246
|
resolve_access_manager(ctx, checksum)
|
|
247
247
|
|
|
248
248
|
validation = validate_vault(ctx, checksum, block=effective_block, metadata=True)
|
|
249
|
-
return VaultValidationResponse.
|
|
250
|
-
|
|
249
|
+
return VaultValidationResponse.model_validate(
|
|
250
|
+
build_vault_validation_json(
|
|
251
|
+
validation, CHAIN_NAMES.get(chain_id, str(chain_id))
|
|
252
|
+
)
|
|
251
253
|
)
|
|
252
254
|
|
|
253
255
|
|
|
@@ -345,7 +345,7 @@ def _reconcile(state: VaultState) -> Reconciliation:
|
|
|
345
345
|
total = Amount(gross - fee)
|
|
346
346
|
total_usd = balance_fuses_usd + underlying_usd + rewards_usd - fee_usd
|
|
347
347
|
delta = Amount(total - on_chain)
|
|
348
|
-
delta_usd = abs(total_usd - (on_chain / 10**decimals * price)) if price else 0
|
|
348
|
+
delta_usd = abs(total_usd - (on_chain / 10**decimals * price)) if price else 0.0
|
|
349
349
|
delta_percent = abs(delta / on_chain * 100) if on_chain else 0.0
|
|
350
350
|
|
|
351
351
|
# totalAssets reads the global market total while balance fuses report
|