ipor-fusion 4.1.0.dev7__tar.gz → 4.1.0.dev12__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.1.0.dev7 → ipor_fusion-4.1.0.dev12}/PKG-INFO +2 -2
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/README.md +1 -1
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +11 -7
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/pyproject.toml +1 -1
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/vault_markets_view.py +5 -2
- ipor_fusion-4.1.0.dev12/src/ipor_fusion/readers/euler_v2.py +111 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/lending_health.py +192 -11
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/vault_data.py +1 -1
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_helpers.py +15 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_lending_health.py +243 -3
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_crosschain_hyperevm.py +7 -6
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.env.example +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.gemini/settings.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.gitignore +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/.python-version +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/AGENTS.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/CHANGELOG.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/CLAUDE.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/CODEOWNERS +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/LICENSE +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/MIGRATION_4.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/build.sh +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/examples/README.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/examples/hypercore_vault_official_deployment_hyperevm.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/pytest.ini +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/scripts/fusion_error_signatures.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/address_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/preflight_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/context_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/fuse_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/simulation_result.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/fusion_errors.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/hypercore/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/preflight.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/registry.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_event_args.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_fake_rpc.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_foundry.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_golden.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_guide.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_live.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_multicall.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_oracle_mapping_fake.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_simulate.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/_validation.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/addresses.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/conftest.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/constants.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/hypercore_official.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_blue.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/simulate_v1_request.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/fetched.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/full.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/full.txt +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_about.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cache.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_chains.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_address.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_market_golden.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_mcp_parity.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_oracle_mapping_golden.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_preflight.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_docs_typing.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_examples_hypercore_hyperevm.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_fuse_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_fusion_errors.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_governance_wrappers.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_guide.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_logs.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_mcp_address.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_mcp_preflight.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_preflight.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_public_api.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_registry.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_roles.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_examples_hyperevm.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_preflight_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_timelock_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulation_errors.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_simulation_observation.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_validation.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_vault_data_block.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_vault_info_fetched_golden.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_vault_reconciliation_onchain_base.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ipor-fusion
|
|
3
|
-
Version: 4.1.0.
|
|
3
|
+
Version: 4.1.0.dev12
|
|
4
4
|
Summary: The IPOR Fusion SDK for Python
|
|
5
5
|
Project-URL: homepage, https://ipor.io
|
|
6
6
|
Project-URL: repository, https://github.com/IPOR-Labs/ipor-fusion.py
|
|
@@ -560,7 +560,7 @@ Read-only aggregators over positions and health, no fuses involved:
|
|
|
560
560
|
|--------|---------|
|
|
561
561
|
| `MorphoReader`, `AaveV3Reader`, `CompoundV3Reader` | Market params, rates and vault positions per lending protocol |
|
|
562
562
|
| `UniswapV3Reader`, `RamsesV2Reader` | LP position details |
|
|
563
|
-
| `fetch_vault_lending_health` | Health factor per lending market a vault is in |
|
|
563
|
+
| `fetch_vault_lending_health` | Health factor per lending market a vault is in (Morpho, Aave V3/V4, Euler V2; any other market that can borrow is listed as unreadable) |
|
|
564
564
|
| `build_oracle_mapping` | How the vault prices every configured asset |
|
|
565
565
|
|
|
566
566
|
### Supported networks
|
|
@@ -511,7 +511,7 @@ Read-only aggregators over positions and health, no fuses involved:
|
|
|
511
511
|
|--------|---------|
|
|
512
512
|
| `MorphoReader`, `AaveV3Reader`, `CompoundV3Reader` | Market params, rates and vault positions per lending protocol |
|
|
513
513
|
| `UniswapV3Reader`, `RamsesV2Reader` | LP position details |
|
|
514
|
-
| `fetch_vault_lending_health` | Health factor per lending market a vault is in |
|
|
514
|
+
| `fetch_vault_lending_health` | Health factor per lending market a vault is in (Morpho, Aave V3/V4, Euler V2; any other market that can borrow is listed as unreadable) |
|
|
515
515
|
| `build_oracle_mapping` | How the vault prices every configured asset |
|
|
516
516
|
|
|
517
517
|
### Supported networks
|
|
@@ -59,7 +59,7 @@ One live fact this simulation does not reproduce: Chainlink's executors submit
|
|
|
59
59
|
into HyperEVM's 3 M-gas small blocks. A delivery that does not fit one needs a
|
|
60
60
|
manual execution through the permissionless ``OffRamp.execute``
|
|
61
61
|
(``ipor_fusion.crosschain.manual_execution``); the one-time dispatcher
|
|
62
|
-
deployment ticket, which carries
|
|
62
|
+
deployment ticket, which carries 7 M of gas, always does. The relay here
|
|
63
63
|
delivers every message directly.
|
|
64
64
|
|
|
65
65
|
The creation codes are the release build of ``CcipCrosschainExecutor`` and
|
|
@@ -329,13 +329,17 @@ USDC_ASSET_ID = AssetId(bytes(Web3.keccak(text="USDC")))
|
|
|
329
329
|
# which is also the dispatcher address on the spoke.
|
|
330
330
|
EXECUTOR_SALT = bytes(Web3.keccak(text="ipor-fusion.py crosschain CCIP example"))
|
|
331
331
|
|
|
332
|
-
# The factory route policy. Commands carry 2.
|
|
333
|
-
#
|
|
334
|
-
#
|
|
335
|
-
#
|
|
336
|
-
|
|
332
|
+
# The factory route policy. Commands carry 2.4 M of gas: Chainlink's executors
|
|
333
|
+
# submit a delivery with about the message gas + 365 k, so 2.4 M still fits
|
|
334
|
+
# HyperEVM's 3 M small block and executes on its own (the CCIP README's 2.7 M
|
|
335
|
+
# is a recommendation the code does not enforce, and would not fit). At 2.4 M
|
|
336
|
+
# one UPDATE_VAULTS carries at most 22 vaults. Token deliveries 1 M; the
|
|
337
|
+
# dispatcher deployment ticket 7 M (the deployment itself uses about 5.6 M).
|
|
338
|
+
# The fee caps are per message, in the source chain's native token: 0.01 ETH
|
|
339
|
+
# on Arbitrum, 1 HYPE on HyperEVM.
|
|
340
|
+
MESSAGE_GAS_LIMIT = 2_400_000
|
|
337
341
|
TOKEN_GAS_LIMIT = 1_000_000
|
|
338
|
-
DISPATCHER_DEPLOYMENT_GAS_LIMIT =
|
|
342
|
+
DISPATCHER_DEPLOYMENT_GAS_LIMIT = 7_000_000
|
|
339
343
|
ARBITRUM_ROUTE_MAX_FEE = 10**16
|
|
340
344
|
HYPEREVM_ROUTE_MAX_FEE = 10**18
|
|
341
345
|
|
{ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/cli/vault_markets_view.py
RENAMED
|
@@ -94,6 +94,8 @@ def _print_lending_health(data: VaultData) -> None: # noqa: C901
|
|
|
94
94
|
click.echo(f" {format_market_label(m.market_id)}:")
|
|
95
95
|
if m.protocol == "aave_v4":
|
|
96
96
|
click.echo(f" spoke {m.substrate_id}:")
|
|
97
|
+
elif m.protocol == "euler_v2":
|
|
98
|
+
click.echo(f" sub-account {m.substrate_id}:")
|
|
97
99
|
_print_health_lines(m, indent=" ")
|
|
98
100
|
_print_unreadable_health(lh)
|
|
99
101
|
|
|
@@ -115,7 +117,8 @@ def _index_lending_health(
|
|
|
115
117
|
|
|
116
118
|
Morpho rows are keyed by morpho substrate id (one row per substrate).
|
|
117
119
|
Aave V3 rows are keyed by IPOR market id (account-aggregated, one row per
|
|
118
|
-
market); Aave V4 rows by Spoke address (account-aggregated per Spoke)
|
|
120
|
+
market); Aave V4 rows by Spoke address (account-aggregated per Spoke) and
|
|
121
|
+
Euler V2 rows by sub-account address, in the same account-level map.
|
|
119
122
|
"""
|
|
120
123
|
morpho: dict[str, LendingMarketHealth] = {}
|
|
121
124
|
aave: dict[MarketId | str, LendingMarketHealth] = {}
|
|
@@ -126,7 +129,7 @@ def _index_lending_health(
|
|
|
126
129
|
morpho[str(m.substrate_id).lower().removeprefix("0x")] = m
|
|
127
130
|
elif m.protocol == "aave_v3":
|
|
128
131
|
aave[m.market_id] = m
|
|
129
|
-
elif m.protocol
|
|
132
|
+
elif m.protocol in ("aave_v4", "euler_v2") and m.substrate_id:
|
|
130
133
|
aave[m.substrate_id] = m
|
|
131
134
|
return morpho, aave
|
|
132
135
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import NewType
|
|
5
|
+
|
|
6
|
+
from eth_typing import ChecksumAddress
|
|
7
|
+
from web3 import Web3
|
|
8
|
+
|
|
9
|
+
from ipor_fusion.core.contract import Call, ContractWrapper
|
|
10
|
+
from ipor_fusion.types import Amount
|
|
11
|
+
|
|
12
|
+
# A value in an Euler vault's unit of account, in that unit's decimals.
|
|
13
|
+
UnitOfAccountValue = NewType("UnitOfAccountValue", int)
|
|
14
|
+
# An Euler LTV in basis points (10_000 = 100%).
|
|
15
|
+
LtvBps = NewType("LtvBps", int)
|
|
16
|
+
# The byte XORed into the Plasma Vault address to name an EVC sub-account.
|
|
17
|
+
EulerSubAccountId = NewType("EulerSubAccountId", int)
|
|
18
|
+
|
|
19
|
+
# Euler's ISO 4217 code for USD (840) used as a unit-of-account address.
|
|
20
|
+
EULER_USD_UNIT_OF_ACCOUNT = Web3.to_checksum_address(
|
|
21
|
+
"0x0000000000000000000000000000000000000348"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True, slots=True)
|
|
26
|
+
class EulerV2Substrate:
|
|
27
|
+
"""A decoded Euler V2 market substrate (`EulerFuseLib.EulerSubstrate`)."""
|
|
28
|
+
|
|
29
|
+
euler_vault: ChecksumAddress
|
|
30
|
+
is_collateral: bool
|
|
31
|
+
can_borrow: bool
|
|
32
|
+
sub_account_id: EulerSubAccountId
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def from_bytes(cls, substrate: bytes) -> EulerV2Substrate:
|
|
36
|
+
"""Inverse of `EulerFuseLib.substrateToBytes32`:
|
|
37
|
+
`eulerVault<<96 | isCollateral<<88 | canBorrow<<80 | subAccounts<<72`."""
|
|
38
|
+
word = int.from_bytes(substrate, "big")
|
|
39
|
+
return cls(
|
|
40
|
+
euler_vault=Web3.to_checksum_address(f"0x{word >> 96:040x}"),
|
|
41
|
+
is_collateral=bool(word >> 88 & 0x01),
|
|
42
|
+
can_borrow=bool(word >> 80 & 0x01),
|
|
43
|
+
sub_account_id=EulerSubAccountId(word >> 72 & 0xFF),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def sub_account(self, plasma_vault: ChecksumAddress) -> ChecksumAddress:
|
|
47
|
+
"""`EulerFuseLib.generateSubAccountAddress`: the vault address XOR the id."""
|
|
48
|
+
return Web3.to_checksum_address(
|
|
49
|
+
f"0x{int(plasma_vault, 16) ^ self.sub_account_id:040x}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(slots=True)
|
|
54
|
+
class EulerV2AccountLiquidity:
|
|
55
|
+
"""`accountLiquidityFull(account, liquidation=true)` of the account's controller.
|
|
56
|
+
|
|
57
|
+
Each collateral value is already scaled by its liquidation LTV, so it is
|
|
58
|
+
the most debt that collateral backs, not its market value.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
collaterals: list[ChecksumAddress]
|
|
62
|
+
risk_adjusted_collateral_values: list[UnitOfAccountValue]
|
|
63
|
+
liability_value: UnitOfAccountValue
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _account_liquidity_decoder(
|
|
67
|
+
value: tuple[list[str], list[int], int],
|
|
68
|
+
) -> EulerV2AccountLiquidity:
|
|
69
|
+
collaterals, values, liability = value
|
|
70
|
+
return EulerV2AccountLiquidity(
|
|
71
|
+
collaterals=[Web3.to_checksum_address(c) for c in collaterals],
|
|
72
|
+
risk_adjusted_collateral_values=[UnitOfAccountValue(v) for v in values],
|
|
73
|
+
liability_value=UnitOfAccountValue(liability),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class EulerV2VaultReader(ContractWrapper):
|
|
78
|
+
"""Reader for an Euler V2 (EVK) vault."""
|
|
79
|
+
|
|
80
|
+
def debt_of(self, account: ChecksumAddress) -> Call[Amount]:
|
|
81
|
+
return self._view(
|
|
82
|
+
"debtOf(address)", account, output_types=["uint256"], decoder=Amount
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def account_liquidity_full(
|
|
86
|
+
self, account: ChecksumAddress
|
|
87
|
+
) -> Call[EulerV2AccountLiquidity]:
|
|
88
|
+
"""Reverts unless this vault is the account's controller."""
|
|
89
|
+
return self._view(
|
|
90
|
+
"accountLiquidityFull(address,bool)",
|
|
91
|
+
account,
|
|
92
|
+
True,
|
|
93
|
+
output_types=["address[]", "uint256[]", "uint256"],
|
|
94
|
+
decoder=_account_liquidity_decoder,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def ltv_liquidation(self, collateral: ChecksumAddress) -> Call[LtvBps]:
|
|
98
|
+
"""Liquidation LTV of ``collateral``; 0 when not accepted."""
|
|
99
|
+
return self._view(
|
|
100
|
+
"LTVLiquidation(address)",
|
|
101
|
+
collateral,
|
|
102
|
+
output_types=["uint16"],
|
|
103
|
+
decoder=LtvBps,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def unit_of_account(self) -> Call[ChecksumAddress]:
|
|
107
|
+
return self._view(
|
|
108
|
+
"unitOfAccount()",
|
|
109
|
+
output_types=["address"],
|
|
110
|
+
decoder=Web3.to_checksum_address,
|
|
111
|
+
)
|
{ipor_fusion-4.1.0.dev7 → ipor_fusion-4.1.0.dev12}/src/ipor_fusion/readers/lending_health.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Lending health computation for Morpho Blue, Aave V3
|
|
1
|
+
"""Lending health computation for Morpho Blue, Aave V3, Aave V4 and Euler V2 markets.
|
|
2
2
|
|
|
3
3
|
Provides a unified view of LTV, liquidation thresholds, and health factors
|
|
4
4
|
for lending positions held by Plasma Vaults.
|
|
@@ -30,6 +30,11 @@ from ipor_fusion.errors import EmptyCallResultError
|
|
|
30
30
|
from ipor_fusion.market_ids import IporFusionMarkets
|
|
31
31
|
from ipor_fusion.readers.aave_v3 import AaveV3Reader
|
|
32
32
|
from ipor_fusion.readers.aave_v4 import AaveV4OracleReader, AaveV4SpokeReader
|
|
33
|
+
from ipor_fusion.readers.euler_v2 import (
|
|
34
|
+
EULER_USD_UNIT_OF_ACCOUNT,
|
|
35
|
+
EulerV2Substrate,
|
|
36
|
+
EulerV2VaultReader,
|
|
37
|
+
)
|
|
33
38
|
from ipor_fusion.readers.morpho import (
|
|
34
39
|
MorphoReader,
|
|
35
40
|
morpho_blue_address,
|
|
@@ -61,6 +66,7 @@ _READ_ERRORS = (
|
|
|
61
66
|
|
|
62
67
|
WAD = 10**18
|
|
63
68
|
RAY = 10**27
|
|
69
|
+
BPS = 10_000
|
|
64
70
|
ORACLE_PRICE_SCALE = 10**36
|
|
65
71
|
|
|
66
72
|
# Aave V3 Core Pool per chain — the Pool of market AAVE_V3 only. Aave V3 Prime
|
|
@@ -109,6 +115,27 @@ AAVE_V4_MARKET_IDS = frozenset(
|
|
|
109
115
|
}
|
|
110
116
|
)
|
|
111
117
|
|
|
118
|
+
# Market IDs served by the Euler V2 fuses. Health is account-level per
|
|
119
|
+
# sub-account, read from the controller: the one borrowable vault it owes.
|
|
120
|
+
EULER_V2_MARKET_IDS = frozenset(
|
|
121
|
+
{
|
|
122
|
+
IporFusionMarkets.EULER_V2,
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Markets whose fuses can borrow but whose health has no reader yet. Their
|
|
127
|
+
# positions are listed as unreadable rather than left out: an unknown
|
|
128
|
+
# liquidation risk is not a healthy one. Dolomite counts only when a
|
|
129
|
+
# substrate grants borrowing.
|
|
130
|
+
UNSUPPORTED_BORROW_MARKET_IDS = frozenset(
|
|
131
|
+
{
|
|
132
|
+
IporFusionMarkets.MOONWELL,
|
|
133
|
+
IporFusionMarkets.SILO_V2,
|
|
134
|
+
IporFusionMarkets.EBISU,
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
_UNSUPPORTED_REASON = "no lending health reader for this market"
|
|
138
|
+
|
|
112
139
|
|
|
113
140
|
@dataclass(slots=True)
|
|
114
141
|
class LendingMarketHealth:
|
|
@@ -118,6 +145,7 @@ class LendingMarketHealth:
|
|
|
118
145
|
- Morpho: the morpho market id (`0x` + 64 lowercase hex) (one row per substrate).
|
|
119
146
|
- Aave V3: None — Aave health is account-level, aggregated across reserves.
|
|
120
147
|
- Aave V4: the Spoke address — account-level per Spoke, across its reserves.
|
|
148
|
+
- Euler V2: the sub-account address — account-level, across its collaterals.
|
|
121
149
|
"""
|
|
122
150
|
|
|
123
151
|
protocol: str
|
|
@@ -409,6 +437,127 @@ def _compute_aave_v4_market_health(
|
|
|
409
437
|
)
|
|
410
438
|
|
|
411
439
|
|
|
440
|
+
@dataclass(frozen=True, slots=True)
|
|
441
|
+
class _EulerV2Account:
|
|
442
|
+
"""An EVC sub-account of the vault and the Euler vaults it may borrow from."""
|
|
443
|
+
|
|
444
|
+
sub_account: ChecksumAddress
|
|
445
|
+
borrow_vaults: tuple[ChecksumAddress, ...]
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _compute_euler_v2_account_health(
|
|
449
|
+
ctx: Web3Context,
|
|
450
|
+
account: _EulerV2Account,
|
|
451
|
+
ipor_market_id: MarketId,
|
|
452
|
+
market_name: str,
|
|
453
|
+
) -> LendingMarketHealth | UnreadableLendingMarket | None:
|
|
454
|
+
"""Compute LTV health for one Euler V2 sub-account; None when it owes nothing.
|
|
455
|
+
|
|
456
|
+
Euler values each collateral at its liquidation LTV, so the market value
|
|
457
|
+
is recovered by dividing that LTV back out.
|
|
458
|
+
"""
|
|
459
|
+
sub_account = account.sub_account
|
|
460
|
+
multicall = Multicall3(ctx)
|
|
461
|
+
try:
|
|
462
|
+
debts = multicall.aggregate(
|
|
463
|
+
[EulerV2VaultReader(v).debt_of(sub_account) for v in account.borrow_vaults]
|
|
464
|
+
)
|
|
465
|
+
except _READ_ERRORS as exc:
|
|
466
|
+
return UnreadableLendingMarket(
|
|
467
|
+
protocol="euler_v2",
|
|
468
|
+
market_id=ipor_market_id,
|
|
469
|
+
market_name=market_name,
|
|
470
|
+
reason=_reason("debt", exc),
|
|
471
|
+
substrate_id=sub_account,
|
|
472
|
+
)
|
|
473
|
+
# A sub-account has at most one controller: the only vault it can owe.
|
|
474
|
+
controller = next(
|
|
475
|
+
(v for v, d in zip(account.borrow_vaults, debts, strict=True) if d > 0),
|
|
476
|
+
None,
|
|
477
|
+
)
|
|
478
|
+
if controller is None:
|
|
479
|
+
return None
|
|
480
|
+
|
|
481
|
+
reader = EulerV2VaultReader(controller)
|
|
482
|
+
try:
|
|
483
|
+
batch = multicall.batch()
|
|
484
|
+
liquidity_read = batch.add(reader.account_liquidity_full(sub_account))
|
|
485
|
+
unit_read = batch.add(reader.unit_of_account())
|
|
486
|
+
batch.run()
|
|
487
|
+
liquidity = liquidity_read.value
|
|
488
|
+
liquidation_ltvs = multicall.aggregate(
|
|
489
|
+
[reader.ltv_liquidation(c) for c in liquidity.collaterals]
|
|
490
|
+
)
|
|
491
|
+
except _READ_ERRORS as exc:
|
|
492
|
+
return UnreadableLendingMarket(
|
|
493
|
+
protocol="euler_v2",
|
|
494
|
+
market_id=ipor_market_id,
|
|
495
|
+
market_name=market_name,
|
|
496
|
+
reason=_reason("account liquidity", exc),
|
|
497
|
+
substrate_id=sub_account,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
adjusted = sum(liquidity.risk_adjusted_collateral_values)
|
|
501
|
+
collateral = sum(
|
|
502
|
+
value * BPS // ltv
|
|
503
|
+
for value, ltv in zip(
|
|
504
|
+
liquidity.risk_adjusted_collateral_values, liquidation_ltvs, strict=True
|
|
505
|
+
)
|
|
506
|
+
if ltv > 0
|
|
507
|
+
)
|
|
508
|
+
liability = liquidity.liability_value
|
|
509
|
+
current_ltv = liability / collateral if collateral > 0 else None
|
|
510
|
+
max_ltv = adjusted / collateral if collateral > 0 else 0.0
|
|
511
|
+
health_factor = adjusted / liability if liability > 0 else None
|
|
512
|
+
ltv_usage = liability / adjusted * 100 if adjusted > 0 else None
|
|
513
|
+
# The USD unit of account prices in 18 decimals; other units are not USD.
|
|
514
|
+
in_usd = unit_read.value == EULER_USD_UNIT_OF_ACCOUNT
|
|
515
|
+
|
|
516
|
+
return LendingMarketHealth(
|
|
517
|
+
protocol="euler_v2",
|
|
518
|
+
market_id=ipor_market_id,
|
|
519
|
+
market_name=market_name,
|
|
520
|
+
current_ltv=round(current_ltv, 6) if current_ltv is not None else None,
|
|
521
|
+
max_ltv=round(max_ltv, 6),
|
|
522
|
+
health_factor=round(health_factor, 4) if health_factor is not None else None,
|
|
523
|
+
total_collateral_usd=round(collateral / WAD, 2) if in_usd else None,
|
|
524
|
+
total_debt_usd=round(liability / WAD, 2) if in_usd else None,
|
|
525
|
+
ltv_usage_percent=round(ltv_usage, 2) if ltv_usage is not None else None,
|
|
526
|
+
substrate_id=sub_account,
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def _euler_v2_borrow_accounts(
|
|
531
|
+
substrates: list[bytes], vault: PlasmaVaultAddress
|
|
532
|
+
) -> list[_EulerV2Account]:
|
|
533
|
+
"""The vault's sub-accounts that may borrow, from its Euler V2 substrates."""
|
|
534
|
+
borrow_vaults: dict[ChecksumAddress, list[ChecksumAddress]] = {}
|
|
535
|
+
for raw in substrates:
|
|
536
|
+
substrate = EulerV2Substrate.from_bytes(raw)
|
|
537
|
+
if substrate.can_borrow:
|
|
538
|
+
borrow_vaults.setdefault(substrate.sub_account(vault), []).append(
|
|
539
|
+
substrate.euler_vault
|
|
540
|
+
)
|
|
541
|
+
return [
|
|
542
|
+
_EulerV2Account(sub_account, tuple(vaults))
|
|
543
|
+
for sub_account, vaults in borrow_vaults.items()
|
|
544
|
+
]
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def _dolomite_can_borrow(substrate: bytes) -> bool:
|
|
548
|
+
"""DolomiteFuseLib: asset<<96 | subAccountId<<88 | canBorrow<<80."""
|
|
549
|
+
return bool(int.from_bytes(substrate, "big") >> 80 & 0x01)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def _can_borrow(market_id: MarketId, substrates: list[bytes]) -> bool:
|
|
553
|
+
"""Whether a market without a health reader may hold a borrow position."""
|
|
554
|
+
if market_id in UNSUPPORTED_BORROW_MARKET_IDS:
|
|
555
|
+
return True
|
|
556
|
+
if market_id == IporFusionMarkets.DOLOMITE:
|
|
557
|
+
return any(_dolomite_can_borrow(sub) for sub in substrates)
|
|
558
|
+
return False
|
|
559
|
+
|
|
560
|
+
|
|
412
561
|
def _aave_v4_spokes(substrates: list[bytes]) -> list[ChecksumAddress]:
|
|
413
562
|
"""Unique Spokes named by Aave V4 reserve substrates, in grant order."""
|
|
414
563
|
spokes: dict[ChecksumAddress, None] = {}
|
|
@@ -424,25 +573,32 @@ class _LendingMarkets:
|
|
|
424
573
|
morpho: list[tuple[MarketId, str, MorphoBlueMarketId]]
|
|
425
574
|
aave_v3: list[tuple[MarketId, str, ChecksumAddress]]
|
|
426
575
|
aave_v4: list[tuple[MarketId, str, ChecksumAddress]]
|
|
576
|
+
euler_v2: list[tuple[MarketId, str, _EulerV2Account]]
|
|
427
577
|
unreadable: list[UnreadableLendingMarket]
|
|
428
578
|
|
|
429
579
|
|
|
430
580
|
def _collect_lending_markets(
|
|
581
|
+
vault: PlasmaVaultAddress,
|
|
431
582
|
balance_fuse_market_ids: list[MarketId],
|
|
432
583
|
market_substrates: dict[MarketId, list[bytes]],
|
|
433
584
|
aave_pools: dict[MarketId, ChecksumAddress],
|
|
434
585
|
) -> _LendingMarkets:
|
|
435
586
|
"""Pick the positions to read: Morpho substrates, Aave V3 markets with a Pool,
|
|
436
|
-
|
|
437
|
-
|
|
587
|
+
the Spokes of Aave V4 markets and the borrowing Euler V2 sub-accounts. A
|
|
588
|
+
market that may borrow but has no reader is listed as unreadable."""
|
|
589
|
+
markets = _LendingMarkets(
|
|
590
|
+
morpho=[], aave_v3=[], aave_v4=[], euler_v2=[], unreadable=[]
|
|
591
|
+
)
|
|
438
592
|
|
|
439
593
|
for mid in balance_fuse_market_ids:
|
|
440
594
|
name = market_name(mid)
|
|
595
|
+
substrates = market_substrates.get(mid, [])
|
|
441
596
|
if mid in MORPHO_MARKET_IDS:
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
597
|
+
markets.morpho.extend(
|
|
598
|
+
(mid, name, parse_morpho_market_id(sub))
|
|
599
|
+
for sub in substrates
|
|
600
|
+
if len(sub) == 32
|
|
601
|
+
)
|
|
446
602
|
elif mid in AAVE_V3_MARKET_IDS:
|
|
447
603
|
if aave_pool := aave_pools.get(mid):
|
|
448
604
|
markets.aave_v3.append((mid, name, aave_pool))
|
|
@@ -456,8 +612,23 @@ def _collect_lending_markets(
|
|
|
456
612
|
)
|
|
457
613
|
)
|
|
458
614
|
elif mid in AAVE_V4_MARKET_IDS:
|
|
459
|
-
|
|
460
|
-
|
|
615
|
+
markets.aave_v4.extend(
|
|
616
|
+
(mid, name, spoke) for spoke in _aave_v4_spokes(substrates)
|
|
617
|
+
)
|
|
618
|
+
elif mid in EULER_V2_MARKET_IDS:
|
|
619
|
+
markets.euler_v2.extend(
|
|
620
|
+
(mid, name, account)
|
|
621
|
+
for account in _euler_v2_borrow_accounts(substrates, vault)
|
|
622
|
+
)
|
|
623
|
+
elif _can_borrow(mid, substrates):
|
|
624
|
+
markets.unreadable.append(
|
|
625
|
+
UnreadableLendingMarket(
|
|
626
|
+
protocol=name.lower(),
|
|
627
|
+
market_id=mid,
|
|
628
|
+
market_name=name,
|
|
629
|
+
reason=_UNSUPPORTED_REASON,
|
|
630
|
+
)
|
|
631
|
+
)
|
|
461
632
|
|
|
462
633
|
return markets
|
|
463
634
|
|
|
@@ -477,7 +648,8 @@ def fetch_vault_lending_health(
|
|
|
477
648
|
vault: The Plasma Vault address.
|
|
478
649
|
balance_fuse_market_ids: List of market IDs from balance fuses.
|
|
479
650
|
market_substrates: Map of market_id -> list of raw substrate bytes.
|
|
480
|
-
Morpho market ids
|
|
651
|
+
Morpho market ids, Aave V4 Spokes and Euler V2 sub-accounts are
|
|
652
|
+
read from it.
|
|
481
653
|
aave_pools: Map of Aave V3 market_id -> the Pool its balance fuse
|
|
482
654
|
reads (`AaveV3FuseReader.pool`). An Aave V3 market without an
|
|
483
655
|
entry is listed in ``unreadable``. Defaults to the chain's Core pool for AAVE_V3 only:
|
|
@@ -489,7 +661,7 @@ def fetch_vault_lending_health(
|
|
|
489
661
|
aave_pools = {IporFusionMarkets.AAVE_V3: core_pool} if core_pool else {}
|
|
490
662
|
|
|
491
663
|
markets = _collect_lending_markets(
|
|
492
|
-
balance_fuse_market_ids, market_substrates, aave_pools
|
|
664
|
+
vault, balance_fuse_market_ids, market_substrates, aave_pools
|
|
493
665
|
)
|
|
494
666
|
|
|
495
667
|
results: list[LendingMarketHealth] = []
|
|
@@ -538,8 +710,17 @@ def fetch_vault_lending_health(
|
|
|
538
710
|
)
|
|
539
711
|
)
|
|
540
712
|
|
|
713
|
+
for ipor_mid, name, account in markets.euler_v2:
|
|
714
|
+
futures.append(
|
|
715
|
+
pool.submit(
|
|
716
|
+
_compute_euler_v2_account_health, ctx, account, ipor_mid, name
|
|
717
|
+
)
|
|
718
|
+
)
|
|
719
|
+
|
|
541
720
|
for fut in futures:
|
|
542
721
|
result = fut.result()
|
|
722
|
+
if result is None:
|
|
723
|
+
continue
|
|
543
724
|
if isinstance(result, UnreadableLendingMarket):
|
|
544
725
|
unreadable.append(result)
|
|
545
726
|
else:
|
|
@@ -554,7 +554,7 @@ def _fetch_lending_reads(
|
|
|
554
554
|
vault_addr: PlasmaVaultAddress,
|
|
555
555
|
markets: _MarketReads,
|
|
556
556
|
) -> _LendingReads:
|
|
557
|
-
"""Lending health
|
|
557
|
+
"""Lending health plus the Morpho and Aave V3 per-position breakdowns.
|
|
558
558
|
|
|
559
559
|
Health runs as one pool task (it fans out on its own executor); the
|
|
560
560
|
breakdowns fan out on `pool` from the calling thread, so no pool task ever
|
|
@@ -1494,6 +1494,21 @@ class TestPrintLendingHealth:
|
|
|
1494
1494
|
assert "AAVE_V3 (1):" in out
|
|
1495
1495
|
assert "Health Factor:" in out
|
|
1496
1496
|
|
|
1497
|
+
def test_euler_health_renders_under_its_sub_account(self, capsys):
|
|
1498
|
+
sub_account = "0x" + "3e" * 20
|
|
1499
|
+
m = _make_lending_market(
|
|
1500
|
+
protocol="euler_v2",
|
|
1501
|
+
market_id=MarketId(11),
|
|
1502
|
+
substrate_id=sub_account,
|
|
1503
|
+
health_factor=1.17,
|
|
1504
|
+
)
|
|
1505
|
+
data = _make_data(lending_health=VaultLendingHealth(markets=[m]))
|
|
1506
|
+
_print_lending_health(data)
|
|
1507
|
+
out = capsys.readouterr().out
|
|
1508
|
+
assert "EULER_V2 (11):" in out
|
|
1509
|
+
assert f"sub-account {sub_account}:" in out
|
|
1510
|
+
assert "Health Factor:" in out
|
|
1511
|
+
|
|
1497
1512
|
def test_orphan_health_critical_status_colored(self, capsys):
|
|
1498
1513
|
m = _make_lending_market(health_factor=1.03)
|
|
1499
1514
|
m.protocol = "aave_v3"
|