ipor-fusion 4.1.0.dev50__tar.gz → 4.1.0.dev54__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.dev50 → ipor_fusion-4.1.0.dev54}/PKG-INFO +1 -1
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/pyproject.toml +1 -1
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/__init__.py +2 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/vault_health.py +6 -1
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/mcp/models.py +10 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/mcp/server.py +3 -1
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/__init__.py +2 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/oracle_mapping.py +4 -1
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/oracle_mapping_nodes.py +41 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/oracle_mapping_reader.py +8 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/validation.py +7 -2
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/vault_erc20.py +11 -1
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_oracle_mapping_fake.py +4 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/oracle_mapping_golden/mapping.json +16 -1
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +16 -1
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_oracle_mapping_golden.py +11 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_oracle_mapping.py +48 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_public_api.py +2 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_validation.py +33 -1
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.env.example +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.gemini/settings.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.gitignore +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/.python-version +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/AGENTS.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/CHANGELOG.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/CLAUDE.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/CODEOWNERS +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/LICENSE +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/MIGRATION_4.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/README.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/build.sh +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/README.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/hypercore_vault_official_deployment_hyperevm.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/pytest.ini +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/scripts/fusion_error_signatures.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/address_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/preflight_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/context_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/fuse_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/simulation_result.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/hypercore_pre_hooks.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/fusion_errors.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/hypercore/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/preflight.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/euler_v2.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/registry.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_addresses.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_event_args.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_fake_rpc.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_foundry.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_golden.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_guide.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_live.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_multicall.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_simulate.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/_validation.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/addresses.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/conftest.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/constants.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/hypercore_official.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_blue.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/simulate_v1_request.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/fetched.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/full.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/full.txt +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_about.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_addresses_helper.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cache.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_chains.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_address.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_helpers.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_market_golden.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_mcp_parity.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_preflight.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_docs_typing.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_examples_hypercore_hyperevm.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_fuse_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_fusion_errors.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_governance_wrappers.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_guide.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_hypercore_pre_hooks.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_logs.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_mcp_address.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_mcp_preflight.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_preflight.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_registry.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_roles.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_examples_hyperevm.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_preflight_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_timelock_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulation_errors.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_simulation_observation.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_vault_data_block.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_vault_info_fetched_golden.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_vault_reconciliation_onchain_base.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/uv.lock +0 -0
|
@@ -325,6 +325,7 @@ from ipor_fusion.readers.oracle_mapping_nodes import (
|
|
|
325
325
|
Erc4626DetailJson,
|
|
326
326
|
MiddlewareFallbackDetailJson,
|
|
327
327
|
MorphoFeedDetailJson,
|
|
328
|
+
MorphoOracleFeedJson,
|
|
328
329
|
OracleAssetJson,
|
|
329
330
|
OracleMappingJson,
|
|
330
331
|
OracleNodeJson,
|
|
@@ -760,6 +761,7 @@ __all__ = [
|
|
|
760
761
|
"Erc4626DetailJson",
|
|
761
762
|
"MiddlewareFallbackDetailJson",
|
|
762
763
|
"MorphoFeedDetailJson",
|
|
764
|
+
"MorphoOracleFeedJson",
|
|
763
765
|
"OracleAssetJson",
|
|
764
766
|
"OracleMappingJson",
|
|
765
767
|
"OracleNodeJson",
|
|
@@ -12,6 +12,7 @@ from ipor_fusion.validation import (
|
|
|
12
12
|
VaultValidation,
|
|
13
13
|
)
|
|
14
14
|
from ipor_fusion.vault_data import VaultData
|
|
15
|
+
from ipor_fusion.vault_erc20 import ERC20_CACHE_TOLERANCE
|
|
15
16
|
from ipor_fusion.vault_info import health_check_lists
|
|
16
17
|
|
|
17
18
|
|
|
@@ -146,7 +147,11 @@ def _print_erc20_cache_divergence(
|
|
|
146
147
|
f"ERC20 direct={direct_fmt} — likely stale cache",
|
|
147
148
|
fg="yellow",
|
|
148
149
|
)
|
|
149
|
-
elif
|
|
150
|
+
elif (
|
|
151
|
+
cached > 0
|
|
152
|
+
and direct > 0
|
|
153
|
+
and abs(cached - direct) / direct > ERC20_CACHE_TOLERANCE
|
|
154
|
+
):
|
|
150
155
|
click.secho(
|
|
151
156
|
f" ERC20_VAULT_BALANCE cached={cached_fmt}, "
|
|
152
157
|
f"ERC20 direct={direct_fmt} — significant divergence",
|
|
@@ -564,11 +564,21 @@ class Erc4626Detail(_Base):
|
|
|
564
564
|
rate: str | None
|
|
565
565
|
|
|
566
566
|
|
|
567
|
+
class MorphoOracleFeed(AggregatorFeedDetail):
|
|
568
|
+
"""A feed behind a Morpho oracle; role names the getter it came from."""
|
|
569
|
+
|
|
570
|
+
role: str
|
|
571
|
+
|
|
572
|
+
|
|
567
573
|
class MorphoFeedDetail(_Base):
|
|
568
574
|
morpho_oracle: str
|
|
569
575
|
collateral_token: str | None
|
|
570
576
|
loan_token: str
|
|
571
577
|
morpho_price: str | None
|
|
578
|
+
oracle_feeds: list[MorphoOracleFeed] | None = Field(
|
|
579
|
+
description="Feeds the oracle uses; null when it does not expose them, "
|
|
580
|
+
"so their freshness cannot be read."
|
|
581
|
+
)
|
|
572
582
|
|
|
573
583
|
|
|
574
584
|
class MiddlewareFallbackDetail(_Base):
|
|
@@ -468,7 +468,9 @@ def vault_oracle_mapping(
|
|
|
468
468
|
description, a foreign getter answering; still resolved, but verify the
|
|
469
469
|
address yourself if identity matters), ERC4626PriceFeed (share→asset
|
|
470
470
|
rate, recurses on the underlying), CollateralTokenOnMorphoMarketPriceFeed
|
|
471
|
-
(recurses on the loan token
|
|
471
|
+
(recurses on the loan token; source_detail.oracle_feeds lists the feeds
|
|
472
|
+
behind the Morpho oracle, null when it does not expose them),
|
|
473
|
+
middleware_fallback (no per-vault source;
|
|
472
474
|
priced by the oracle's underlying global middleware, followed as a
|
|
473
475
|
dependency when discoverable), custom_unknown (partial). Classification is
|
|
474
476
|
heuristic interface probing: it grades on-chain evidence and cannot prove
|
|
@@ -45,6 +45,7 @@ from ipor_fusion.readers.oracle_mapping_nodes import (
|
|
|
45
45
|
Erc4626DetailJson,
|
|
46
46
|
MiddlewareFallbackDetailJson,
|
|
47
47
|
MorphoFeedDetailJson,
|
|
48
|
+
MorphoOracleFeedJson,
|
|
48
49
|
OracleAssetJson,
|
|
49
50
|
OracleMappingJson,
|
|
50
51
|
OracleNodeJson,
|
|
@@ -94,6 +95,7 @@ __all__ = [
|
|
|
94
95
|
"Erc4626DetailJson",
|
|
95
96
|
"MiddlewareFallbackDetailJson",
|
|
96
97
|
"MorphoFeedDetailJson",
|
|
98
|
+
"MorphoOracleFeedJson",
|
|
97
99
|
"OracleAssetJson",
|
|
98
100
|
"OracleMappingJson",
|
|
99
101
|
"OracleNodeJson",
|
{ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/oracle_mapping.py
RENAMED
|
@@ -9,7 +9,8 @@ collateral feeds). Unknown feeds are reported as partial, never dropped.
|
|
|
9
9
|
A zero source is not a dead end: the oracle may be a manager variant that
|
|
10
10
|
delegates such assets to an underlying global middleware — that delegation is
|
|
11
11
|
followed and reported as ``middleware_fallback``. Every aggregator-compatible
|
|
12
|
-
read (Chainlink-tier leaves, dual-cross-reference component feeds
|
|
12
|
+
read (Chainlink-tier leaves, dual-cross-reference component feeds, feeds
|
|
13
|
+
behind a Morpho oracle) carries
|
|
13
14
|
``description()`` and the full ``latestRoundData()`` round in
|
|
14
15
|
``source_detail`` — raw values plus ISO-8601 UTC twins of the timestamps,
|
|
15
16
|
no staleness judgment; ``description`` is null when the feed does not
|
|
@@ -83,6 +84,7 @@ from ipor_fusion.readers.oracle_mapping_nodes import (
|
|
|
83
84
|
_apply_dependency_status,
|
|
84
85
|
_collect_unresolved,
|
|
85
86
|
_mapping_status,
|
|
87
|
+
_morpho_oracle_feeds,
|
|
86
88
|
_partial,
|
|
87
89
|
_price_block,
|
|
88
90
|
_resolve_chainlink,
|
|
@@ -181,6 +183,7 @@ def _resolve_morpho(
|
|
|
181
183
|
"collateral_token": collateral,
|
|
182
184
|
"loan_token": loan,
|
|
183
185
|
"morpho_price": str(price) if price is not None else None,
|
|
186
|
+
"oracle_feeds": _morpho_oracle_feeds(reader, morpho_oracle),
|
|
184
187
|
}
|
|
185
188
|
dep = _resolve(reader, loan, depth + 1, visited, max_depth)
|
|
186
189
|
node.dependencies = [dep]
|
{ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/oracle_mapping_nodes.py
RENAMED
|
@@ -30,6 +30,12 @@ TYPE_CHAINLINK = "ChainlinkAggregator"
|
|
|
30
30
|
TYPE_CHAINLINK_STYLE = "chainlink_style"
|
|
31
31
|
TYPE_ERC4626 = "ERC4626PriceFeed"
|
|
32
32
|
TYPE_MORPHO = "CollateralTokenOnMorphoMarketPriceFeed"
|
|
33
|
+
MORPHO_ORACLE_FEED_ROLES = (
|
|
34
|
+
"BASE_FEED_1",
|
|
35
|
+
"BASE_FEED_2",
|
|
36
|
+
"QUOTE_FEED_1",
|
|
37
|
+
"QUOTE_FEED_2",
|
|
38
|
+
)
|
|
33
39
|
TYPE_DUAL_XREF = "DualCrossReferencePriceFeed"
|
|
34
40
|
TYPE_MIDDLEWARE_FALLBACK = "middleware_fallback"
|
|
35
41
|
TYPE_UNKNOWN = "custom_unknown"
|
|
@@ -83,11 +89,21 @@ class Erc4626DetailJson(TypedDict):
|
|
|
83
89
|
rate: str | None
|
|
84
90
|
|
|
85
91
|
|
|
92
|
+
class MorphoOracleFeedJson(AggregatorFeedDetailJson):
|
|
93
|
+
"""A feed behind a Morpho oracle; ``role`` names the getter it came from."""
|
|
94
|
+
|
|
95
|
+
role: str
|
|
96
|
+
|
|
97
|
+
|
|
86
98
|
class MorphoFeedDetailJson(TypedDict):
|
|
99
|
+
"""``oracle_feeds`` lists the feeds the oracle uses; null when the oracle
|
|
100
|
+
does not expose them, so their freshness cannot be read."""
|
|
101
|
+
|
|
87
102
|
morpho_oracle: ChecksumAddress
|
|
88
103
|
collateral_token: ChecksumAddress | None
|
|
89
104
|
loan_token: ChecksumAddress
|
|
90
105
|
morpho_price: str | None
|
|
106
|
+
oracle_feeds: list[MorphoOracleFeedJson] | None
|
|
91
107
|
|
|
92
108
|
|
|
93
109
|
class MiddlewareFallbackDetailJson(TypedDict):
|
|
@@ -413,6 +429,31 @@ def _resolve_chainlink(
|
|
|
413
429
|
return node
|
|
414
430
|
|
|
415
431
|
|
|
432
|
+
def _morpho_oracle_feeds(
|
|
433
|
+
reader: OracleMappingReader, morpho_oracle: ChecksumAddress
|
|
434
|
+
) -> list[MorphoOracleFeedJson] | None:
|
|
435
|
+
"""The feeds behind a Morpho oracle, skipping its unused (zero) slots."""
|
|
436
|
+
feeds: list[MorphoOracleFeedJson] = []
|
|
437
|
+
for role in MORPHO_ORACLE_FEED_ROLES:
|
|
438
|
+
address = reader.morpho_oracle_feed(morpho_oracle, role)
|
|
439
|
+
if address is None:
|
|
440
|
+
return None
|
|
441
|
+
if address.lower() == ZERO_ADDRESS:
|
|
442
|
+
continue
|
|
443
|
+
feeds.append(
|
|
444
|
+
{
|
|
445
|
+
"address": address,
|
|
446
|
+
**_aggregator_detail(
|
|
447
|
+
reader.feed_description(address),
|
|
448
|
+
reader.feed_latest_round_data(address),
|
|
449
|
+
reader.feed_decimals(address),
|
|
450
|
+
),
|
|
451
|
+
"role": role,
|
|
452
|
+
}
|
|
453
|
+
)
|
|
454
|
+
return feeds
|
|
455
|
+
|
|
456
|
+
|
|
416
457
|
def _resolve_dual_xref(
|
|
417
458
|
reader: OracleMappingReader,
|
|
418
459
|
node: OracleNode,
|
{ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/readers/oracle_mapping_reader.py
RENAMED
|
@@ -59,6 +59,9 @@ class _MorphoOracle(ContractWrapper):
|
|
|
59
59
|
def price(self) -> Call[int]:
|
|
60
60
|
return self._view("price()", output_types=["uint256"], decoder=int)
|
|
61
61
|
|
|
62
|
+
def feed(self, role: str) -> Call[ChecksumAddress]:
|
|
63
|
+
return self._view(f"{role}()", output_types=["address"], decoder=_addr)
|
|
64
|
+
|
|
62
65
|
|
|
63
66
|
class _DualXrefFeed(ContractWrapper):
|
|
64
67
|
def asset_x(self) -> Call[ChecksumAddress]:
|
|
@@ -222,6 +225,11 @@ class OracleMappingReader:
|
|
|
222
225
|
def morpho_oracle_price(self, morpho_oracle: ChecksumAddress) -> int | None:
|
|
223
226
|
return self._safe(_MorphoOracle(morpho_oracle).price())
|
|
224
227
|
|
|
228
|
+
def morpho_oracle_feed(
|
|
229
|
+
self, morpho_oracle: ChecksumAddress, role: str
|
|
230
|
+
) -> ChecksumAddress | None:
|
|
231
|
+
return self._safe(_MorphoOracle(morpho_oracle).feed(role))
|
|
232
|
+
|
|
225
233
|
def feed_asset_x(self, source: ChecksumAddress) -> ChecksumAddress | None:
|
|
226
234
|
return self._safe(_DualXrefFeed(source).asset_x())
|
|
227
235
|
|
|
@@ -43,6 +43,10 @@ from ipor_fusion.vault_data import (
|
|
|
43
43
|
GET_TOTAL_SUPPLY_CAP,
|
|
44
44
|
fetch_vault_data,
|
|
45
45
|
)
|
|
46
|
+
from ipor_fusion.vault_erc20 import (
|
|
47
|
+
ERC20_CACHE_TOLERANCE,
|
|
48
|
+
cache_lags_value,
|
|
49
|
+
)
|
|
46
50
|
from ipor_fusion.vault_erc20 import Erc20Holding as Erc20Holding
|
|
47
51
|
from ipor_fusion.vault_erc20 import Erc20Holdings as Erc20Holdings
|
|
48
52
|
|
|
@@ -704,7 +708,8 @@ def _erc20_findings(state: VaultState, erc20: Erc20Holdings) -> list[Finding]:
|
|
|
704
708
|
non_underlying = erc20.value_in_asset - underlying
|
|
705
709
|
cached = erc20.market_cached_value
|
|
706
710
|
if non_underlying > 0 and (
|
|
707
|
-
cached == 0
|
|
711
|
+
cached == 0
|
|
712
|
+
or abs(non_underlying - cached) / non_underlying > ERC20_CACHE_TOLERANCE
|
|
708
713
|
):
|
|
709
714
|
findings.append(
|
|
710
715
|
Finding(
|
|
@@ -747,7 +752,7 @@ def _uncovered_erc20_lines(state: VaultState, erc20: Erc20Holdings) -> list[str]
|
|
|
747
752
|
)
|
|
748
753
|
if address not in covered:
|
|
749
754
|
lines.append(f"{row} — not in any balance fuse substrate")
|
|
750
|
-
elif
|
|
755
|
+
elif cached_usd > 0 and cache_lags_value(token.usd_value or 0, cached_usd):
|
|
751
756
|
lines.append(
|
|
752
757
|
f"{row} — {usd.strip()} not reflected in totalAssets (stale cache)"
|
|
753
758
|
)
|
|
@@ -19,6 +19,16 @@ from ipor_fusion.market_ids import IporFusionMarkets
|
|
|
19
19
|
from ipor_fusion.substrates import decode_substrate
|
|
20
20
|
from ipor_fusion.types import Amount, Decimals, MarketId, PlasmaVaultAddress, Price
|
|
21
21
|
|
|
22
|
+
# The cached value is written at an earlier price and the live one is rounded,
|
|
23
|
+
# so the two never match exactly; a gap within this share of the live value is
|
|
24
|
+
# not staleness.
|
|
25
|
+
ERC20_CACHE_TOLERANCE = 0.1
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def cache_lags_value(usd_value: float, cached_usd: float) -> bool:
|
|
29
|
+
"""Whether a live USD value exceeds the market's cached value beyond tolerance."""
|
|
30
|
+
return usd_value - cached_usd > ERC20_CACHE_TOLERANCE * usd_value
|
|
31
|
+
|
|
22
32
|
|
|
23
33
|
@dataclass(frozen=True)
|
|
24
34
|
class Erc20Holding:
|
|
@@ -216,7 +226,7 @@ def _holding(
|
|
|
216
226
|
elif (
|
|
217
227
|
read.price_usd
|
|
218
228
|
and cached_usd is not None
|
|
219
|
-
and amount * read.price_usd
|
|
229
|
+
and cache_lags_value(amount * read.price_usd, cached_usd)
|
|
220
230
|
):
|
|
221
231
|
note = (
|
|
222
232
|
f"${amount * read.price_usd:,.2f} not reflected in totalAssets "
|
|
@@ -52,6 +52,7 @@ class FakeReader:
|
|
|
52
52
|
self.feeds: dict[str, Feed] = {}
|
|
53
53
|
self.vaults: dict[str, Erc4626] = {}
|
|
54
54
|
self.morpho_prices: dict[str, int] = {}
|
|
55
|
+
self.morpho_feeds: dict[str, dict[str, str]] = {}
|
|
55
56
|
# oracle variant (middleware-fallback resolution); "_mw" because the
|
|
56
57
|
# exact method name would be shadowed by the attribute
|
|
57
58
|
self.underlying_mw: str | None = None
|
|
@@ -125,6 +126,9 @@ class FakeReader:
|
|
|
125
126
|
def morpho_oracle_price(self, o: str) -> int | None:
|
|
126
127
|
return self.morpho_prices.get(o)
|
|
127
128
|
|
|
129
|
+
def morpho_oracle_feed(self, o: str, role: str) -> str | None:
|
|
130
|
+
return self.morpho_feeds.get(o, {}).get(role)
|
|
131
|
+
|
|
128
132
|
def feed_asset_x(self, s: str) -> str | None:
|
|
129
133
|
return self._f(s).get("asset_x")
|
|
130
134
|
|
|
@@ -300,7 +300,22 @@
|
|
|
300
300
|
"morpho_oracle": "0x0000000000000000000000000000000000000031",
|
|
301
301
|
"collateral_token": "0x0000000000000000000000000000000000000006",
|
|
302
302
|
"loan_token": "0x0000000000000000000000000000000000000001",
|
|
303
|
-
"morpho_price": "970000000000000000000000"
|
|
303
|
+
"morpho_price": "970000000000000000000000",
|
|
304
|
+
"oracle_feeds": [
|
|
305
|
+
{
|
|
306
|
+
"address": "0x0000000000000000000000000000000000000032",
|
|
307
|
+
"description": "COLL / LOAN",
|
|
308
|
+
"round_id": "9",
|
|
309
|
+
"answer": "970000000000000000",
|
|
310
|
+
"decimals": 18,
|
|
311
|
+
"started_at": 1700000000,
|
|
312
|
+
"started_at_utc": "2023-11-14T22:13:20Z",
|
|
313
|
+
"updated_at": 1700000050,
|
|
314
|
+
"updated_at_utc": "2023-11-14T22:14:10Z",
|
|
315
|
+
"answered_in_round": "9",
|
|
316
|
+
"role": "BASE_FEED_1"
|
|
317
|
+
}
|
|
318
|
+
]
|
|
304
319
|
},
|
|
305
320
|
"dependencies": [
|
|
306
321
|
{
|
|
@@ -300,7 +300,22 @@
|
|
|
300
300
|
"morpho_oracle": "0x0000000000000000000000000000000000000031",
|
|
301
301
|
"collateral_token": "0x0000000000000000000000000000000000000006",
|
|
302
302
|
"loan_token": "0x0000000000000000000000000000000000000001",
|
|
303
|
-
"morpho_price": "970000000000000000000000"
|
|
303
|
+
"morpho_price": "970000000000000000000000",
|
|
304
|
+
"oracle_feeds": [
|
|
305
|
+
{
|
|
306
|
+
"address": "0x0000000000000000000000000000000000000032",
|
|
307
|
+
"description": "COLL / LOAN",
|
|
308
|
+
"round_id": "9",
|
|
309
|
+
"answer": "970000000000000000",
|
|
310
|
+
"decimals": 18,
|
|
311
|
+
"started_at": 1700000000,
|
|
312
|
+
"started_at_utc": "2023-11-14T22:13:20Z",
|
|
313
|
+
"updated_at": 1700000050,
|
|
314
|
+
"updated_at_utc": "2023-11-14T22:14:10Z",
|
|
315
|
+
"answered_in_round": "9",
|
|
316
|
+
"role": "BASE_FEED_1"
|
|
317
|
+
}
|
|
318
|
+
]
|
|
304
319
|
},
|
|
305
320
|
"dependencies": [
|
|
306
321
|
{
|
|
@@ -154,6 +154,17 @@ def _reader() -> FakeReader:
|
|
|
154
154
|
"loan": USDC,
|
|
155
155
|
}
|
|
156
156
|
r.morpho_prices[addr(0x31)] = 970_000 * 10**18
|
|
157
|
+
r.morpho_feeds[addr(0x31)] = {
|
|
158
|
+
"BASE_FEED_1": addr(0x32),
|
|
159
|
+
"BASE_FEED_2": addr(0),
|
|
160
|
+
"QUOTE_FEED_1": addr(0),
|
|
161
|
+
"QUOTE_FEED_2": addr(0),
|
|
162
|
+
}
|
|
163
|
+
r.feeds[addr(0x32)] = {
|
|
164
|
+
"round": (9, 97 * 10**16, 1_700_000_000, 1_700_000_050, 9),
|
|
165
|
+
"feed_decimals": 18,
|
|
166
|
+
"description": "COLL / LOAN",
|
|
167
|
+
}
|
|
157
168
|
|
|
158
169
|
r.symbols[WAT] = "WAT"
|
|
159
170
|
r.decimals[WAT] = 18
|
|
@@ -270,6 +270,53 @@ class TestResolve:
|
|
|
270
270
|
assert [d.asset for d in node.dependencies] == [loan]
|
|
271
271
|
assert node.path[:2] == ["COLL", "Morpho collateral/loan oracle"]
|
|
272
272
|
|
|
273
|
+
def test_morpho_oracle_feeds_report_their_rounds_and_skip_unused_slots(self):
|
|
274
|
+
r = FakeReader()
|
|
275
|
+
coll, loan = addr(1), addr(2)
|
|
276
|
+
feed, m_oracle, base_feed = addr(0x11), addr(0x31), addr(0x41)
|
|
277
|
+
r.sources[coll] = feed
|
|
278
|
+
r.prices[coll] = Price(asset=coll, amount=10**8, decimals=8)
|
|
279
|
+
r.feeds[feed] = {"morpho_oracle": m_oracle, "collateral": coll, "loan": loan}
|
|
280
|
+
r.feeds[base_feed] = {
|
|
281
|
+
"round": (7, 10**18, 1_700_000_000, 1_700_000_100, 7),
|
|
282
|
+
"feed_decimals": 18,
|
|
283
|
+
"description": "COLL / LOAN",
|
|
284
|
+
}
|
|
285
|
+
zero = addr(0)
|
|
286
|
+
r.morpho_feeds[m_oracle] = {
|
|
287
|
+
"BASE_FEED_1": base_feed,
|
|
288
|
+
"BASE_FEED_2": zero,
|
|
289
|
+
"QUOTE_FEED_1": zero,
|
|
290
|
+
"QUOTE_FEED_2": zero,
|
|
291
|
+
}
|
|
292
|
+
chainlink_asset(r, loan, addr(0x12), symbol="LOAN")
|
|
293
|
+
|
|
294
|
+
detail = om.resolve_asset(r, coll, max_depth=6).source_detail
|
|
295
|
+
|
|
296
|
+
assert detail is not None and "oracle_feeds" in detail
|
|
297
|
+
feeds = detail["oracle_feeds"]
|
|
298
|
+
assert feeds is not None
|
|
299
|
+
assert [(f["role"], f["address"]) for f in feeds] == [
|
|
300
|
+
("BASE_FEED_1", base_feed)
|
|
301
|
+
]
|
|
302
|
+
assert feeds[0]["description"] == "COLL / LOAN"
|
|
303
|
+
assert feeds[0]["updated_at"] == 1_700_000_100
|
|
304
|
+
|
|
305
|
+
def test_morpho_oracle_without_the_feed_getters_reports_none(self):
|
|
306
|
+
r = FakeReader()
|
|
307
|
+
coll, loan = addr(1), addr(2)
|
|
308
|
+
feed, m_oracle = addr(0x11), addr(0x31)
|
|
309
|
+
r.sources[coll] = feed
|
|
310
|
+
r.prices[coll] = Price(asset=coll, amount=10**8, decimals=8)
|
|
311
|
+
r.feeds[feed] = {"morpho_oracle": m_oracle, "collateral": coll, "loan": loan}
|
|
312
|
+
chainlink_asset(r, loan, addr(0x12), symbol="LOAN")
|
|
313
|
+
|
|
314
|
+
node = om.resolve_asset(r, coll, max_depth=6)
|
|
315
|
+
|
|
316
|
+
assert node.status == "resolved"
|
|
317
|
+
assert node.source_detail is not None and "oracle_feeds" in node.source_detail
|
|
318
|
+
assert node.source_detail["oracle_feeds"] is None
|
|
319
|
+
|
|
273
320
|
def test_dual_xref_wins_over_chainlink(self):
|
|
274
321
|
r = FakeReader()
|
|
275
322
|
wsteth = addr(1)
|
|
@@ -1165,6 +1212,7 @@ class TestOracleMappingReader:
|
|
|
1165
1212
|
assert reader.feed_asset_x_asset_y_feed(feed) == target
|
|
1166
1213
|
assert reader.feed_asset_y_usd_feed(feed) == target
|
|
1167
1214
|
assert reader.feed_aggregator(feed) == target
|
|
1215
|
+
assert reader.morpho_oracle_feed(addr(0x31), "BASE_FEED_1") == target
|
|
1168
1216
|
|
|
1169
1217
|
ctx.call.return_value = encode(["uint256"], [1_030_000])
|
|
1170
1218
|
assert reader.morpho_oracle_price(addr(0x31)) == 1_030_000
|
|
@@ -228,6 +228,7 @@ _ALL_SNAPSHOT: dict[str, frozenset[str]] = {
|
|
|
228
228
|
"MorphoClaimFuse",
|
|
229
229
|
"MorphoCollateralFuse",
|
|
230
230
|
"MorphoFeedDetailJson",
|
|
231
|
+
"MorphoOracleFeedJson",
|
|
231
232
|
"MorphoFlashLoanFuse",
|
|
232
233
|
"MorphoFlowCapConfigJson",
|
|
233
234
|
"MorphoMarket",
|
|
@@ -952,6 +953,7 @@ _ALL_SNAPSHOT: dict[str, frozenset[str]] = {
|
|
|
952
953
|
"MiddlewarePricedToken",
|
|
953
954
|
"UnpriceableCheck",
|
|
954
955
|
"MorphoFeedDetailJson",
|
|
956
|
+
"MorphoOracleFeedJson",
|
|
955
957
|
"MorphoMarket",
|
|
956
958
|
"MorphoMarketParams",
|
|
957
959
|
"MorphoMarketRates",
|
|
@@ -40,7 +40,13 @@ from ipor_fusion.market_ids import IporFusionMarkets
|
|
|
40
40
|
from ipor_fusion.types import ChainId, MarketId
|
|
41
41
|
from ipor_fusion.validation import GET_MARKET_SUBSTRATES, Reconciliation
|
|
42
42
|
from ipor_fusion.vault_data import VaultData, _MarketReads, _VaultReads
|
|
43
|
-
from ipor_fusion.vault_erc20 import
|
|
43
|
+
from ipor_fusion.vault_erc20 import (
|
|
44
|
+
_Asset,
|
|
45
|
+
_erc20_holdings,
|
|
46
|
+
_Erc20Market,
|
|
47
|
+
_read_erc20_market,
|
|
48
|
+
_TokenRead,
|
|
49
|
+
)
|
|
44
50
|
|
|
45
51
|
|
|
46
52
|
def _addr(suffix: str) -> str:
|
|
@@ -57,6 +63,11 @@ ORPHAN_FUSE = _addr("f5")
|
|
|
57
63
|
AAVE = IporFusionMarkets.AAVE_V3
|
|
58
64
|
MORPHO = IporFusionMarkets.MORPHO
|
|
59
65
|
ERC20_MARKET = IporFusionMarkets.ERC20_VAULT_BALANCE
|
|
66
|
+
# A token whose value matches the market's cached value but for float rounding.
|
|
67
|
+
ASSET_PRICE = 2594.826591677957
|
|
68
|
+
ROUNDED_BALANCE = 3663278943628990
|
|
69
|
+
ROUNDED_PRICE = 2269.8825
|
|
70
|
+
ROUNDED_CACHE = 3204535051949216
|
|
60
71
|
WAD = 10**18
|
|
61
72
|
|
|
62
73
|
|
|
@@ -276,6 +287,16 @@ class TestRouting:
|
|
|
276
287
|
assert heading == "ERC20 holdings not covered by balance fuses:"
|
|
277
288
|
assert [row.split()[0] for row in rows] == ["AAA", "BBB"]
|
|
278
289
|
|
|
290
|
+
def test_rounding_gap_to_the_cache_is_not_uncovered(self):
|
|
291
|
+
state = _erc20_state(asset_price_usd=ASSET_PRICE)
|
|
292
|
+
erc20 = Erc20Holdings(
|
|
293
|
+
tokens=(holding(TOKEN, balance=ROUNDED_BALANCE, price_usd=ROUNDED_PRICE),),
|
|
294
|
+
value_in_asset=ROUNDED_CACHE,
|
|
295
|
+
market_cached_value=ROUNDED_CACHE,
|
|
296
|
+
)
|
|
297
|
+
validation = validate(state, 100 * WAD, erc20)
|
|
298
|
+
assert "uncovered_erc20" not in [check for check, _ in _checks(validation)]
|
|
299
|
+
|
|
279
300
|
def test_stale_erc20_cache_and_missing_price_feed(self):
|
|
280
301
|
erc20 = Erc20Holdings(
|
|
281
302
|
value_in_asset=50 * WAD,
|
|
@@ -598,6 +619,17 @@ class TestReadErc20Holdings:
|
|
|
598
619
|
assert erc20.market_cached_value == 2 * WAD
|
|
599
620
|
assert erc20.unpriced_tokens == (f"{OTHER.lower()} (?)",)
|
|
600
621
|
|
|
622
|
+
def test_rounding_gap_to_the_cache_keeps_the_token_in_the_market(self):
|
|
623
|
+
erc20 = _erc20_holdings(
|
|
624
|
+
_Asset(ASSET, 18, ASSET_PRICE), # type: ignore[arg-type]
|
|
625
|
+
_Erc20Market( # type: ignore[arg-type]
|
|
626
|
+
ROUNDED_CACHE,
|
|
627
|
+
(_TokenRead(TOKEN, 18, ROUNDED_BALANCE, ROUNDED_PRICE),), # type: ignore[arg-type]
|
|
628
|
+
),
|
|
629
|
+
{},
|
|
630
|
+
)
|
|
631
|
+
assert erc20.tokens[0].note == "in ERC20_VAULT_BALANCE"
|
|
632
|
+
|
|
601
633
|
def test_symbols_name_the_holdings(self):
|
|
602
634
|
symbols = {a.lower(): a.lower()[-2:] for a in (ASSET, TOKEN, OTHER)}
|
|
603
635
|
erc20 = self._read(_erc20_state(), symbols)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/advanced_euler_v2_credit_market_base.py
RENAMED
|
File without changes
|
{ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/advanced_euler_v2_looping_arbitrum.py
RENAMED
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/examples/external_state_margin_leg_base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/oracle_mapping_view.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.1.0.dev50 → ipor_fusion-4.1.0.dev54}/src/ipor_fusion/cli/vault_markets_view.py
RENAMED
|
File without changes
|