ipor-fusion 4.2.0.dev11__tar.gz → 4.2.0.dev16__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.2.0.dev11 → ipor_fusion-4.2.0.dev16}/PKG-INFO +13 -2
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/README.md +12 -1
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/README.md +6 -0
- ipor_fusion-4.2.0.dev16/examples/advanced_compound_v3_institutional_usdc_ethereum.py +499 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/pyproject.toml +1 -1
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/__init__.py +6 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_markets_view.py +3 -3
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/__init__.py +9 -1
- ipor_fusion-4.2.0.dev16/src/ipor_fusion/readers/compound_v3.py +164 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/lending_health.py +132 -55
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/validation.py +8 -2
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_data.py +8 -4
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_positions.py +28 -1
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_lending_health.py +202 -1
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_vault_predates_getters.py +1 -1
- ipor_fusion-4.2.0.dev16/tests/test_examples_compound_v3_ethereum.py +73 -0
- ipor_fusion-4.2.0.dev16/tests/test_lending_health_compound_v3_onchain.py +87 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_public_api.py +6 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_reader_encoding.py +7 -1
- ipor_fusion-4.2.0.dev16/tests/test_simulate_examples_ethereum.py +24 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_validation.py +8 -0
- ipor_fusion-4.2.0.dev11/src/ipor_fusion/readers/compound_v3.py +0 -32
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.env.example +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.gemini/settings.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.gitignore +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/.python-version +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/AGENTS.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/CHANGELOG.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/CLAUDE.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/CODEOWNERS +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/LICENSE +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/MIGRATION_4.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/build.sh +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/hypercore_vault_official_deployment_hyperevm.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/pytest.ini +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/scripts/fusion_error_signatures.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/address_cmd.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/fuse_cmd.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/preflight_cmd.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/context_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fuse_decoding.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/simulation_result.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuse_whitelist.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/hypercore_pre_hooks.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/fusion_errors.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/hypercore/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/onchain_text.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/preflight.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/euler_v2.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/registry.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_addresses.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_event_args.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_fake_rpc.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_foundry.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_golden.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_guide.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_live.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_multicall.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_oracle_mapping_fake.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_simulate.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/_validation.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/addresses.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/conftest.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/constants.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/hypercore_official.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/simulate_v1_request.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/fetched.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/full.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/full.txt +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_about.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_addresses_helper.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cache.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_ccip_read_disabled.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_chains.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_address.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_helpers.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_market_golden.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_mcp_fuse_whitelist.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_mcp_parity.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_oracle_mapping_golden.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_preflight.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_docs_typing.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_examples_hypercore_hyperevm.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fuse_decoding.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fuse_whitelist.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fusion_errors.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_governance_wrappers.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_guide.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_pre_hooks.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_logs.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_address.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_preflight.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_onchain_text.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_preflight.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_registry.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_roles.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_compound_v3_institutional_usdc.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_examples_hyperevm.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_preflight_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_timelock_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulation_errors.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_simulation_observation.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_data_block.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_info_fetched_golden.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_reconciliation_onchain_base.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ipor-fusion
|
|
3
|
-
Version: 4.2.0.
|
|
3
|
+
Version: 4.2.0.dev16
|
|
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
|
|
@@ -394,6 +394,10 @@ ever signed or broadcast.
|
|
|
394
394
|
- [Advanced Euler V2 credit-market vault](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_euler_v2_credit_market_base.py) —
|
|
395
395
|
continue here for composing multiple functional fuses, typed Euler substrates and sub-accounts,
|
|
396
396
|
and an ordered collateral → borrow → repay → unwind lifecycle.
|
|
397
|
+
- [Compound V3 Institutional USDC borrow](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_compound_v3_institutional_usdc_ethereum.py) —
|
|
398
|
+
a WETH vault on market 57: collateral, borrow, repay and withdrawal with NAV at each step, the
|
|
399
|
+
USDC substrate as the borrow gate, and the ERC20 market valuing the borrowed USDC through a
|
|
400
|
+
dependency edge.
|
|
397
401
|
- [External-state margin leg](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/external_state_margin_leg_base.py) — a vault whose capital
|
|
398
402
|
sits off-chain: typed market-50 substrates, a dual-custodian propose/confirm marking the NAV,
|
|
399
403
|
the scheduled-withdrawal prep an alpha performs, and a mismatched confirmation being rejected.
|
|
@@ -413,6 +417,13 @@ uv run python examples/advanced_euler_v2_credit_market_base.py
|
|
|
413
417
|
uv run python examples/external_state_margin_leg_base.py
|
|
414
418
|
```
|
|
415
419
|
|
|
420
|
+
The Compound example needs an Ethereum archive RPC:
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
export ETHEREUM_PROVIDER_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
|
|
424
|
+
uv run python examples/advanced_compound_v3_institutional_usdc_ethereum.py
|
|
425
|
+
```
|
|
426
|
+
|
|
416
427
|
The crosschain example needs Arbitrum and HyperEVM archive RPCs and the executor and dispatcher
|
|
417
428
|
creation codes built with Foundry from the contracts release it pins; run without them, it prints
|
|
418
429
|
the `forge inspect` commands that build them:
|
|
@@ -564,7 +575,7 @@ Read-only aggregators over positions and health, no fuses involved:
|
|
|
564
575
|
|--------|---------|
|
|
565
576
|
| `MorphoReader`, `AaveV3Reader`, `CompoundV3Reader` | Market params, rates and vault positions per lending protocol |
|
|
566
577
|
| `UniswapV3Reader`, `RamsesV2Reader` | LP position details |
|
|
567
|
-
| `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) |
|
|
578
|
+
| `fetch_vault_lending_health` | Health factor per lending market a vault is in (Morpho, Aave V3/V4, Euler V2, Compound V3; any other market that can borrow is listed as unreadable) |
|
|
568
579
|
| `build_oracle_mapping` | How the vault prices every configured asset |
|
|
569
580
|
|
|
570
581
|
### Supported networks
|
|
@@ -345,6 +345,10 @@ ever signed or broadcast.
|
|
|
345
345
|
- [Advanced Euler V2 credit-market vault](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_euler_v2_credit_market_base.py) —
|
|
346
346
|
continue here for composing multiple functional fuses, typed Euler substrates and sub-accounts,
|
|
347
347
|
and an ordered collateral → borrow → repay → unwind lifecycle.
|
|
348
|
+
- [Compound V3 Institutional USDC borrow](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/advanced_compound_v3_institutional_usdc_ethereum.py) —
|
|
349
|
+
a WETH vault on market 57: collateral, borrow, repay and withdrawal with NAV at each step, the
|
|
350
|
+
USDC substrate as the borrow gate, and the ERC20 market valuing the borrowed USDC through a
|
|
351
|
+
dependency edge.
|
|
348
352
|
- [External-state margin leg](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/examples/external_state_margin_leg_base.py) — a vault whose capital
|
|
349
353
|
sits off-chain: typed market-50 substrates, a dual-custodian propose/confirm marking the NAV,
|
|
350
354
|
the scheduled-withdrawal prep an alpha performs, and a mismatched confirmation being rejected.
|
|
@@ -364,6 +368,13 @@ uv run python examples/advanced_euler_v2_credit_market_base.py
|
|
|
364
368
|
uv run python examples/external_state_margin_leg_base.py
|
|
365
369
|
```
|
|
366
370
|
|
|
371
|
+
The Compound example needs an Ethereum archive RPC:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
export ETHEREUM_PROVIDER_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
|
|
375
|
+
uv run python examples/advanced_compound_v3_institutional_usdc_ethereum.py
|
|
376
|
+
```
|
|
377
|
+
|
|
367
378
|
The crosschain example needs Arbitrum and HyperEVM archive RPCs and the executor and dispatcher
|
|
368
379
|
creation codes built with Foundry from the contracts release it pins; run without them, it prints
|
|
369
380
|
the `forge inspect` commands that build them:
|
|
@@ -515,7 +526,7 @@ Read-only aggregators over positions and health, no fuses involved:
|
|
|
515
526
|
|--------|---------|
|
|
516
527
|
| `MorphoReader`, `AaveV3Reader`, `CompoundV3Reader` | Market params, rates and vault positions per lending protocol |
|
|
517
528
|
| `UniswapV3Reader`, `RamsesV2Reader` | LP position details |
|
|
518
|
-
| `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) |
|
|
529
|
+
| `fetch_vault_lending_health` | Health factor per lending market a vault is in (Morpho, Aave V3/V4, Euler V2, Compound V3; any other market that can borrow is listed as unreadable) |
|
|
519
530
|
| `build_oracle_mapping` | How the vault prices every configured asset |
|
|
520
531
|
|
|
521
532
|
### Supported networks
|
|
@@ -23,6 +23,12 @@ transaction.
|
|
|
23
23
|
controller, borrow WETH, repay, then unwind (controller before collateral) and
|
|
24
24
|
withdraw the collateral, reading the outstanding debt from the eVault's
|
|
25
25
|
`debtOf` to confirm the position fully closed.
|
|
26
|
+
- [`advanced_compound_v3_institutional_usdc_ethereum.py`](advanced_compound_v3_institutional_usdc_ethereum.py) --
|
|
27
|
+
the same lifecycle on one Comet (market 57, Ethereum). Pairs the supply and borrow
|
|
28
|
+
fuses with the balance fuse, grants WETH as collateral and USDC as the borrow gate,
|
|
29
|
+
values the borrowed USDC through the ERC20 market and a dependency edge, then
|
|
30
|
+
supplies, borrows, repays part, lets interest accrue, repays in full and withdraws,
|
|
31
|
+
confirming NAV at each step.
|
|
26
32
|
- [`advanced_euler_v2_looping_arbitrum.py`](advanced_euler_v2_looping_arbitrum.py) --
|
|
27
33
|
builds a WBTC-denominated vault on Arbitrum and opens (but does not unwind) a
|
|
28
34
|
leveraged Euler V2 position in one Morpho flash-loan callback: supply WBTC,
|
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
"""Borrow USDC against WETH on Compound V3 Institutional USDC from a new Ethereum vault.
|
|
2
|
+
|
|
3
|
+
Continues from ``simple_aave_v3_supply_base.py``, which explains the vault
|
|
4
|
+
bootstrap at more length. This example shows the credit lifecycle on one Comet
|
|
5
|
+
(ciUSDCv3, market 57), end to end:
|
|
6
|
+
|
|
7
|
+
1. preview a vault deployment and clone it, underlying WETH;
|
|
8
|
+
2. bootstrap the vault's roles in the correct order;
|
|
9
|
+
3. pair the supply and borrow fuses with the market's balance fuse;
|
|
10
|
+
4. grant WETH as collateral and USDC as the borrow gate;
|
|
11
|
+
5. value the borrowed USDC through the ERC20 market and a dependency edge;
|
|
12
|
+
6. deposit WETH and supply it as collateral;
|
|
13
|
+
7. borrow USDC, confirming NAV holds because the balance fuse nets the debt;
|
|
14
|
+
8. repay part of the debt, let an hour of interest accrue, repay it in full;
|
|
15
|
+
9. withdraw the collateral and confirm the position closed.
|
|
16
|
+
|
|
17
|
+
Everything runs inside one ``eth_simulateV1`` batch against a pinned Ethereum
|
|
18
|
+
block -- nothing is signed or broadcast. To put a flow on-chain, hand the
|
|
19
|
+
``.calldata`` each builder produces to your own signer, each step signed by the
|
|
20
|
+
role authorized for it.
|
|
21
|
+
|
|
22
|
+
Reading the health of a real position does not fit in a batch, since the vault
|
|
23
|
+
here exists only inside the simulation. On a deployed vault use
|
|
24
|
+
``fetch_vault_data(ctx, vault).lending_health`` or ``fusion vault validate``.
|
|
25
|
+
|
|
26
|
+
Rewards are out of scope: the market's claim fuse (``CompoundV3ClaimFuse``) goes
|
|
27
|
+
through ``RewardsManager.claim_rewards``, and this Comet has no reward token at
|
|
28
|
+
the pinned block, so a claim reverts ``NotSupported(address)``.
|
|
29
|
+
|
|
30
|
+
Run it (the shell snippets assume a POSIX shell -- bash or zsh):
|
|
31
|
+
|
|
32
|
+
export ETHEREUM_PROVIDER_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
|
|
33
|
+
uv run python examples/advanced_compound_v3_institutional_usdc_ethereum.py
|
|
34
|
+
|
|
35
|
+
The provider must be an archive node that implements ``eth_simulateV1`` (Alchemy
|
|
36
|
+
and other geth/reth-based providers do).
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import logging
|
|
42
|
+
import os
|
|
43
|
+
import sys
|
|
44
|
+
from typing import TypedDict
|
|
45
|
+
|
|
46
|
+
from eth_typing import ChecksumAddress
|
|
47
|
+
from web3 import Web3
|
|
48
|
+
|
|
49
|
+
from ipor_fusion import (
|
|
50
|
+
ERC20,
|
|
51
|
+
AccessManager,
|
|
52
|
+
CompoundV3Reader,
|
|
53
|
+
FuseAction,
|
|
54
|
+
FusionFactory,
|
|
55
|
+
IporFusionMarkets,
|
|
56
|
+
PlasmaVault,
|
|
57
|
+
PriceOracleMiddlewareManager,
|
|
58
|
+
Roles,
|
|
59
|
+
SimulationResult,
|
|
60
|
+
VaultSimulator,
|
|
61
|
+
Web3Context,
|
|
62
|
+
erc20_balance_slot,
|
|
63
|
+
is_simulate_v1_supported,
|
|
64
|
+
parse_checksum_address,
|
|
65
|
+
parse_fuse_address,
|
|
66
|
+
)
|
|
67
|
+
from ipor_fusion.fuses import CompoundV3BorrowFuse, CompoundV3SupplyFuse
|
|
68
|
+
from ipor_fusion.types import MAX_UINT256, Amount, ChainId, Period
|
|
69
|
+
|
|
70
|
+
log = logging.getLogger("advanced_compound_v3_institutional_usdc_ethereum")
|
|
71
|
+
|
|
72
|
+
# ── Live IPOR / Ethereum infrastructure (do NOT change) ──────────────────────
|
|
73
|
+
# Every address below is a live, canonical deployment. Provenance for all IPOR
|
|
74
|
+
# Fusion addresses: https://github.com/IPOR-Labs/ipor-abi
|
|
75
|
+
# mainnet/mainnet-ethereum-fusion/addresses.json
|
|
76
|
+
|
|
77
|
+
ETHEREUM_CHAIN_ID = ChainId(1)
|
|
78
|
+
|
|
79
|
+
# IporFusionFactoryProxy on Ethereum.
|
|
80
|
+
ETHEREUM_FUSION_FACTORY = parse_checksum_address(
|
|
81
|
+
"0xcd05909C4A1F8E501e4ED554cEF4Ed5E48D9b852"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Canonical WETH and Circle-issued USDC on Ethereum (18 and 6 decimals).
|
|
85
|
+
ETHEREUM_WETH = parse_checksum_address("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
|
|
86
|
+
ETHEREUM_USDC = parse_checksum_address("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
|
|
87
|
+
|
|
88
|
+
# The ciUSDCv3 Comet (registry name CompoundV3CometInstitutionalUsdc), base token
|
|
89
|
+
# USDC. It is a separate Comet from cUSDCv3, so the fuses below are their own
|
|
90
|
+
# deployments, bound to this Comet and market 57 by their immutables.
|
|
91
|
+
COMET = parse_checksum_address("0x207158a267CBD2598BB3d611D8CBdEE2709F2F8C")
|
|
92
|
+
|
|
93
|
+
COMPOUND_MARKET = IporFusionMarkets.COMPOUND_V3_INSTITUTIONAL_USDC
|
|
94
|
+
ERC20_MARKET = IporFusionMarkets.ERC20_VAULT_BALANCE
|
|
95
|
+
|
|
96
|
+
# Registry name SupplyFuseCompoundV3InstitutionalUsdc: supplies and withdraws
|
|
97
|
+
# collateral and base token.
|
|
98
|
+
SUPPLY_FUSE = parse_fuse_address("0xe7CaDa548534973b9d6A4124bBC476b586d1b3e4")
|
|
99
|
+
# Registry name BorrowFuseCompoundV3InstitutionalUsdc: borrows and repays the base
|
|
100
|
+
# token. A Comet lends only its base token, so neither action names an asset.
|
|
101
|
+
BORROW_FUSE = parse_fuse_address("0xff1762A6579787c7eab2E4f55A502F7c36714eF6")
|
|
102
|
+
# Registry name BalanceFuseCompoundV3WithPriceOracleMiddlewareInstitutionalUsdc:
|
|
103
|
+
# values the position as collateral minus debt, at the vault's oracle prices.
|
|
104
|
+
BALANCE_FUSE = parse_fuse_address("0x5dF0CB1cE5159De1efb8038B5185E4612fAd9168")
|
|
105
|
+
# Registry name BalanceFuseErc20: values the borrowed USDC sitting idle on the vault.
|
|
106
|
+
ERC20_BALANCE_FUSE = parse_fuse_address("0x6cEBf3e3392D0860Ed174402884b941DCBB30654")
|
|
107
|
+
|
|
108
|
+
# ── Simulation parameters (safe to adjust) ───────────────────────────────────
|
|
109
|
+
#
|
|
110
|
+
# OWNER, ALPHA, DEPOSITOR and USDC_SOURCE are impersonated simulation actors: the
|
|
111
|
+
# simulator runs with validation disabled, so it can act as any address without
|
|
112
|
+
# its key. They are NOT accounts you control -- there is nothing to replace and
|
|
113
|
+
# nothing to broadcast here.
|
|
114
|
+
#
|
|
115
|
+
# OWNER, ALPHA and DEPOSITOR are kept distinct on purpose, because they are three
|
|
116
|
+
# distinct roles that must NOT share an address in production:
|
|
117
|
+
# OWNER -- governance/atomist; can reconfigure the whole vault. A cold key
|
|
118
|
+
# or multisig.
|
|
119
|
+
# ALPHA -- the online operator; drives execute(), least-privilege, it cannot
|
|
120
|
+
# reconfigure the vault. Collapsing it into OWNER means a compromised
|
|
121
|
+
# alpha key becomes full governance control.
|
|
122
|
+
# DEPOSITOR-- an end user; only ever holds and deposits funds.
|
|
123
|
+
|
|
124
|
+
OWNER = parse_checksum_address("0x533ac556E288625B267bD71B7928E0a8B46DcE82")
|
|
125
|
+
# Anvil account #0, a well-known test address.
|
|
126
|
+
ALPHA = parse_checksum_address("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
|
|
127
|
+
# Anvil account #1, the natural sibling to the alpha's Anvil account #0.
|
|
128
|
+
DEPOSITOR = parse_checksum_address("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
|
|
129
|
+
# Anvil account #2. Holds the USDC that covers the interest; see run_simulation.
|
|
130
|
+
USDC_SOURCE = parse_checksum_address("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC")
|
|
131
|
+
|
|
132
|
+
# Pinned Ethereum block, after the fuses above were deployed. Pinning makes the
|
|
133
|
+
# simulation deterministic and immune to mainnet state drift. It requires an
|
|
134
|
+
# archive node that can serve state at this height -- if your provider cannot (an
|
|
135
|
+
# error mentioning missing trie/state, or a pruned block), BUMP THIS to a recent
|
|
136
|
+
# Ethereum block your node can reach.
|
|
137
|
+
PINNED_BLOCK = 26_348_000
|
|
138
|
+
|
|
139
|
+
WETH_COLLATERAL = Amount(40 * 10**18) # 40 WETH
|
|
140
|
+
USDC_BORROW = Amount(20_000 * 10**6) # 20,000 USDC
|
|
141
|
+
USDC_PARTIAL_REPAY = Amount(5_000 * 10**6) # 5,000 USDC
|
|
142
|
+
USDC_INTEREST_COVER = Amount(10 * 10**6) # 10 USDC, far above an hour of interest
|
|
143
|
+
ONE_HOUR_SECONDS = 3600
|
|
144
|
+
# A clone deploys the whole vault stack, more than a node's default gas cap for a
|
|
145
|
+
# simulated call allows.
|
|
146
|
+
CLONE_GAS = 30_000_000
|
|
147
|
+
# Comet rounds a base balance through its principal and index, by a few base units.
|
|
148
|
+
USDC_DUST_TOLERANCE = Amount(10)
|
|
149
|
+
# The same rounding and the WAD flooring of the balance fuse, valued in WETH.
|
|
150
|
+
NAV_DUST_TOLERANCE = Amount(10**13) # 0.00001 WETH
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ── Inlined helpers (kept local so this file is self-contained) ──────────────
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _address_substrate(addr: str) -> bytes:
|
|
157
|
+
"""Pad an EVM address into a 32-byte substrate value (left-zero-padded)."""
|
|
158
|
+
return bytes.fromhex(addr.removeprefix("0x").lower().rjust(64, "0"))
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _check(condition: bool, message: str) -> None:
|
|
162
|
+
"""Fail loud unconditionally -- unlike ``assert``, survives ``python -O``."""
|
|
163
|
+
if not condition:
|
|
164
|
+
raise AssertionError(message)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _assert_all_success(result: SimulationResult) -> None:
|
|
168
|
+
"""Raise with a readable summary if any simulated call reverted."""
|
|
169
|
+
if result.all_success:
|
|
170
|
+
return
|
|
171
|
+
failed = [(c.label, c.error) for c in result.failed_calls]
|
|
172
|
+
raise AssertionError(f"simulation calls failed: {failed}")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _connected_web3() -> Web3:
|
|
176
|
+
"""Build a Web3 client from ETHEREUM_PROVIDER_URL, or exit with a clear message."""
|
|
177
|
+
url = os.environ.get("ETHEREUM_PROVIDER_URL")
|
|
178
|
+
if not url:
|
|
179
|
+
sys.exit("ETHEREUM_PROVIDER_URL is not set -- export your RPC URL first.")
|
|
180
|
+
web3 = Web3(Web3.HTTPProvider(url))
|
|
181
|
+
if not web3.is_connected():
|
|
182
|
+
sys.exit("cannot reach the RPC at ETHEREUM_PROVIDER_URL -- check the endpoint.")
|
|
183
|
+
if not is_simulate_v1_supported(web3):
|
|
184
|
+
sys.exit(
|
|
185
|
+
"the provider does not implement eth_simulateV1 -- use an archive node."
|
|
186
|
+
)
|
|
187
|
+
return web3
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# ── Pure builders (no chain access -- safe to import and unit-test offline) ───
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class CloneKwargs(TypedDict):
|
|
194
|
+
"""The keyword arguments of ``FusionFactory.clone``."""
|
|
195
|
+
|
|
196
|
+
asset_name: str
|
|
197
|
+
asset_symbol: str
|
|
198
|
+
underlying_token: ChecksumAddress
|
|
199
|
+
redemption_delay_seconds: Period
|
|
200
|
+
owner: ChecksumAddress
|
|
201
|
+
dao_fee_package_index: int
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def clone_args() -> CloneKwargs:
|
|
205
|
+
"""Arguments for FusionFactory.clone(...).
|
|
206
|
+
|
|
207
|
+
The preview and the in-batch create MUST use identical args: same args plus
|
|
208
|
+
the same factory state yields the same CREATE addresses in one simulation.
|
|
209
|
+
"""
|
|
210
|
+
return {
|
|
211
|
+
"asset_name": "IPOR ciUSDCv3 Borrow Vault (example)",
|
|
212
|
+
"asset_symbol": "ipCIBex",
|
|
213
|
+
"underlying_token": ETHEREUM_WETH,
|
|
214
|
+
"redemption_delay_seconds": Period(0),
|
|
215
|
+
"owner": OWNER,
|
|
216
|
+
"dao_fee_package_index": 0,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def compound_market_substrates() -> list[bytes]:
|
|
221
|
+
"""WETH is the collateral the supply and balance fuses accept. USDC is the gate
|
|
222
|
+
the borrow fuse checks on both borrow and repay: without it, a borrow reverts
|
|
223
|
+
``CompoundV3BorrowFuseUnsupportedAsset`` even though the fuse's target is fixed."""
|
|
224
|
+
return [_address_substrate(ETHEREUM_WETH), _address_substrate(ETHEREUM_USDC)]
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def erc20_market_substrates() -> list[bytes]:
|
|
228
|
+
"""The idle token the ERC20 market values: the borrowed USDC."""
|
|
229
|
+
return [_address_substrate(ETHEREUM_USDC)]
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def build_supply_collateral_action(amount: Amount) -> FuseAction:
|
|
233
|
+
return CompoundV3SupplyFuse(SUPPLY_FUSE).supply(asset=ETHEREUM_WETH, amount=amount)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def build_withdraw_collateral_action(amount: Amount) -> FuseAction:
|
|
237
|
+
return CompoundV3SupplyFuse(SUPPLY_FUSE).withdraw(
|
|
238
|
+
asset=ETHEREUM_WETH, amount=amount
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def build_borrow_action(amount: Amount) -> FuseAction:
|
|
243
|
+
return CompoundV3BorrowFuse(BORROW_FUSE).borrow(amount=amount)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def build_repay_action(amount: Amount) -> FuseAction:
|
|
247
|
+
"""A repay is capped at the debt: ``MAX_UINT256`` clears it, interest included,
|
|
248
|
+
without opening a base supply position with the rest of the idle USDC. The
|
|
249
|
+
vault must hold the USDC, or the token transfer reverts."""
|
|
250
|
+
return CompoundV3BorrowFuse(BORROW_FUSE).repay(amount=amount)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# ── Simulation (the whole build + strategy, no broadcast) ────────────────────
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def run_simulation(web3: Web3) -> SimulationResult:
|
|
257
|
+
"""Build the vault and run the credit lifecycle in one eth_simulateV1 batch."""
|
|
258
|
+
ctx = Web3Context(web3=web3, chain_id=ETHEREUM_CHAIN_ID, signer=OWNER).at_block(
|
|
259
|
+
PINNED_BLOCK
|
|
260
|
+
)
|
|
261
|
+
factory = FusionFactory(ETHEREUM_FUSION_FACTORY)
|
|
262
|
+
|
|
263
|
+
# Preview the deterministic addresses (eth_call, no gas, no state change).
|
|
264
|
+
preview = factory.clone(**clone_args()).call(ctx)
|
|
265
|
+
log.info("predicted vault=%s", preview.plasma_vault)
|
|
266
|
+
|
|
267
|
+
vault_address = preview.plasma_vault
|
|
268
|
+
plasma_vault = PlasmaVault(vault_address)
|
|
269
|
+
access_manager = AccessManager(preview.access_manager)
|
|
270
|
+
price_manager = PriceOracleMiddlewareManager(preview.price_manager)
|
|
271
|
+
comet = CompoundV3Reader(COMET)
|
|
272
|
+
weth = ERC20(ETHEREUM_WETH)
|
|
273
|
+
usdc = ERC20(ETHEREUM_USDC)
|
|
274
|
+
|
|
275
|
+
# Each simulator primitive stands in for a production action, read as
|
|
276
|
+
# "simulation -> production":
|
|
277
|
+
# - sim.add_call(call=X, from_=OWNER) -> X.send(ctx) by the owner/atomist
|
|
278
|
+
# (or X.calldata to a multisig / timelock).
|
|
279
|
+
# - sim.execute([action]) -> plasma_vault.execute([action]).send(ctx) by the alpha.
|
|
280
|
+
# - read = sim.observe(X), then read.value -> X.call(ctx), a plain read.
|
|
281
|
+
sim = VaultSimulator(
|
|
282
|
+
web3=web3, vault=vault_address, alpha=ALPHA, block=PINNED_BLOCK
|
|
283
|
+
)
|
|
284
|
+
plan: list[str] = []
|
|
285
|
+
|
|
286
|
+
# SIMULATION ONLY (storage overrides): these balances stand in for tokens the
|
|
287
|
+
# holders already have. Production has no equivalent -- a real depositor holds
|
|
288
|
+
# their WETH.
|
|
289
|
+
sim.with_erc20_balance(
|
|
290
|
+
ETHEREUM_WETH,
|
|
291
|
+
DEPOSITOR,
|
|
292
|
+
WETH_COLLATERAL,
|
|
293
|
+
slot=erc20_balance_slot(web3, ETHEREUM_WETH, block=PINNED_BLOCK),
|
|
294
|
+
)
|
|
295
|
+
sim.with_erc20_balance(
|
|
296
|
+
ETHEREUM_USDC,
|
|
297
|
+
USDC_SOURCE,
|
|
298
|
+
USDC_INTEREST_COVER,
|
|
299
|
+
slot=erc20_balance_slot(web3, ETHEREUM_USDC, block=PINNED_BLOCK),
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# The clone MUST be the first queued call -- any earlier call that bumps the
|
|
303
|
+
# factory index would change the addresses previewed above.
|
|
304
|
+
sim.add_call(
|
|
305
|
+
call=factory.clone(**clone_args()), from_=OWNER, label="clone", gas=CLONE_GAS
|
|
306
|
+
)
|
|
307
|
+
plan.append("deploy the vault stack (clone)")
|
|
308
|
+
|
|
309
|
+
# Role bootstrap order: a fresh clone grants OWNER only OWNER_ROLE. The admin
|
|
310
|
+
# chain is OWNER -> ATOMIST -> {ALPHA, FUSE_MANAGER, WHITELIST,
|
|
311
|
+
# UPDATE_MARKETS_BALANCES}, so OWNER must self-grant ATOMIST before it can
|
|
312
|
+
# grant the rest. Each grant uses Period(0) (no execution delay) to keep the
|
|
313
|
+
# simulation legible; production governance often puts a nonzero delay -- a
|
|
314
|
+
# timelock -- on the sensitive roles.
|
|
315
|
+
for role, holder in (
|
|
316
|
+
(Roles.ATOMIST_ROLE, OWNER),
|
|
317
|
+
(Roles.FUSE_MANAGER_ROLE, OWNER),
|
|
318
|
+
(Roles.ALPHA_ROLE, ALPHA),
|
|
319
|
+
(Roles.WHITELIST_ROLE, DEPOSITOR),
|
|
320
|
+
):
|
|
321
|
+
sim.add_call(
|
|
322
|
+
call=access_manager.grant_role(role, holder, Period(0)), from_=OWNER
|
|
323
|
+
)
|
|
324
|
+
plan.append("grant roles: ATOMIST, FUSE_MANAGER, ALPHA, WHITELIST")
|
|
325
|
+
|
|
326
|
+
# Wire market 57. There is no separate collateral fuse: Comet counts every
|
|
327
|
+
# supplied collateral towards the borrow limit on its own.
|
|
328
|
+
sim.add_call(call=plasma_vault.add_fuses([SUPPLY_FUSE, BORROW_FUSE]), from_=OWNER)
|
|
329
|
+
sim.add_call(
|
|
330
|
+
call=plasma_vault.add_balance_fuse(COMPOUND_MARKET, BALANCE_FUSE), from_=OWNER
|
|
331
|
+
)
|
|
332
|
+
sim.add_call(
|
|
333
|
+
call=plasma_vault.grant_market_substrates(
|
|
334
|
+
COMPOUND_MARKET, compound_market_substrates()
|
|
335
|
+
),
|
|
336
|
+
from_=OWNER,
|
|
337
|
+
)
|
|
338
|
+
# A borrow lowers this market's value and raises the vault's idle USDC in one
|
|
339
|
+
# step. The ERC20 market values that USDC, and the dependency edge refreshes it
|
|
340
|
+
# whenever the Compound market changes. Without the edge, NAV falls by the
|
|
341
|
+
# borrowed amount until someone refreshes the ERC20 market.
|
|
342
|
+
sim.add_call(
|
|
343
|
+
call=plasma_vault.add_balance_fuse(ERC20_MARKET, ERC20_BALANCE_FUSE),
|
|
344
|
+
from_=OWNER,
|
|
345
|
+
)
|
|
346
|
+
sim.add_call(
|
|
347
|
+
call=plasma_vault.grant_market_substrates(
|
|
348
|
+
ERC20_MARKET, erc20_market_substrates()
|
|
349
|
+
),
|
|
350
|
+
from_=OWNER,
|
|
351
|
+
)
|
|
352
|
+
sim.add_call(
|
|
353
|
+
call=plasma_vault.update_dependency_balance_graphs(
|
|
354
|
+
[COMPOUND_MARKET], [[ERC20_MARKET]]
|
|
355
|
+
),
|
|
356
|
+
from_=OWNER,
|
|
357
|
+
)
|
|
358
|
+
plan.append("wire market 57 and the ERC20 market with a dependency edge")
|
|
359
|
+
|
|
360
|
+
# Oracle coverage: the balance fuse prices both tokens through the vault's
|
|
361
|
+
# price manager, which delegates to the global middleware.
|
|
362
|
+
weth_price = sim.observe(price_manager.get_asset_price(ETHEREUM_WETH))
|
|
363
|
+
usdc_price = sim.observe(price_manager.get_asset_price(ETHEREUM_USDC))
|
|
364
|
+
plan.append("verify oracle coverage for WETH and USDC")
|
|
365
|
+
|
|
366
|
+
# The approve + deposit are exactly what a real depositor signs.
|
|
367
|
+
sim.add_call(call=weth.approve(vault_address, WETH_COLLATERAL), from_=DEPOSITOR)
|
|
368
|
+
sim.add_call(call=plasma_vault.deposit(WETH_COLLATERAL, DEPOSITOR), from_=DEPOSITOR)
|
|
369
|
+
plan.append("deposit 40 WETH")
|
|
370
|
+
|
|
371
|
+
sim.execute([build_supply_collateral_action(WETH_COLLATERAL)])
|
|
372
|
+
collateral = sim.observe(comet.collateral_balance_of(vault_address, ETHEREUM_WETH))
|
|
373
|
+
nav_after_supply = sim.observe(plasma_vault.total_assets())
|
|
374
|
+
plan.append("alpha supplies 40 WETH as collateral")
|
|
375
|
+
|
|
376
|
+
# Comet enforces the limit: a borrow above the collateral factor reverts
|
|
377
|
+
# NotCollateralized in the Comet, not in the fuse.
|
|
378
|
+
sim.execute([build_borrow_action(USDC_BORROW)])
|
|
379
|
+
idle_usdc_after_borrow = sim.observe(usdc.balance_of(vault_address))
|
|
380
|
+
debt_after_borrow = sim.observe(comet.borrow_balance_of(vault_address))
|
|
381
|
+
market_after_borrow = sim.observe(
|
|
382
|
+
plasma_vault.total_assets_in_market(COMPOUND_MARKET)
|
|
383
|
+
)
|
|
384
|
+
nav_after_borrow = sim.observe(plasma_vault.total_assets())
|
|
385
|
+
plan.append("alpha borrows 20,000 USDC")
|
|
386
|
+
|
|
387
|
+
sim.execute([build_repay_action(USDC_PARTIAL_REPAY)])
|
|
388
|
+
debt_after_partial = sim.observe(comet.borrow_balance_of(vault_address))
|
|
389
|
+
nav_after_partial = sim.observe(plasma_vault.total_assets())
|
|
390
|
+
plan.append("alpha repays 5,000 USDC")
|
|
391
|
+
|
|
392
|
+
# SIMULATION ONLY (time advancement): next_block shifts block.timestamp so
|
|
393
|
+
# interest accrues inside one batch. There is no production equivalent -- the
|
|
394
|
+
# debt simply grows between transactions.
|
|
395
|
+
sim.next_block(time_shift_seconds=ONE_HOUR_SECONDS)
|
|
396
|
+
debt_with_interest = sim.observe(comet.borrow_balance_of(vault_address))
|
|
397
|
+
# SIMULATION ONLY (the impersonated transfer): the debt now exceeds the USDC the
|
|
398
|
+
# vault borrowed. Production earns or swaps that difference in through a fuse.
|
|
399
|
+
# The repay and withdrawal below are NOT simulation-only.
|
|
400
|
+
sim.add_call(
|
|
401
|
+
call=usdc.transfer(vault_address, USDC_INTEREST_COVER), from_=USDC_SOURCE
|
|
402
|
+
)
|
|
403
|
+
plan.append("an hour of interest accrues; USDC arrives to cover it")
|
|
404
|
+
|
|
405
|
+
sim.execute([build_repay_action(Amount(MAX_UINT256))])
|
|
406
|
+
debt_after_full = sim.observe(comet.borrow_balance_of(vault_address))
|
|
407
|
+
base_supply_after_full = sim.observe(comet.balance_of(vault_address))
|
|
408
|
+
idle_usdc_after_full = sim.observe(usdc.balance_of(vault_address))
|
|
409
|
+
plan.append("alpha repays the debt in full")
|
|
410
|
+
|
|
411
|
+
# Comet blocks withdrawing collateral that still backs debt, so the order is
|
|
412
|
+
# repay first, then withdraw.
|
|
413
|
+
sim.execute([build_withdraw_collateral_action(WETH_COLLATERAL)])
|
|
414
|
+
collateral_after_exit = sim.observe(
|
|
415
|
+
comet.collateral_balance_of(vault_address, ETHEREUM_WETH)
|
|
416
|
+
)
|
|
417
|
+
idle_weth_after_exit = sim.observe(weth.balance_of(vault_address))
|
|
418
|
+
market_after_exit = sim.observe(
|
|
419
|
+
plasma_vault.total_assets_in_market(COMPOUND_MARKET)
|
|
420
|
+
)
|
|
421
|
+
plan.append("alpha withdraws the 40 WETH")
|
|
422
|
+
|
|
423
|
+
log.info("transaction plan:")
|
|
424
|
+
for i, step in enumerate(plan, start=1):
|
|
425
|
+
log.info(" %d. %s", i, step)
|
|
426
|
+
|
|
427
|
+
result = sim.run()
|
|
428
|
+
_assert_all_success(result)
|
|
429
|
+
|
|
430
|
+
# ── Simulation done. From here the recorded observations are read back and the
|
|
431
|
+
# outcome verified, failing loud on any mismatch. ──────────────────────────
|
|
432
|
+
_check(weth_price.value.amount > 0, "WETH has no oracle price")
|
|
433
|
+
_check(usdc_price.value.amount > 0, "USDC has no oracle price")
|
|
434
|
+
_check(collateral.value == WETH_COLLATERAL, "collateral did not reach the Comet")
|
|
435
|
+
|
|
436
|
+
# NAV is preserved across supply, borrow and repay: value only moves between the
|
|
437
|
+
# Compound and ERC20 markets. A balance fuse ignoring the debt would add the
|
|
438
|
+
# 20,000 USDC to NAV here; compare against the deposit, not a lower bound.
|
|
439
|
+
for step, nav in (
|
|
440
|
+
("supply", nav_after_supply),
|
|
441
|
+
("borrow", nav_after_borrow),
|
|
442
|
+
("partial repay", nav_after_partial),
|
|
443
|
+
):
|
|
444
|
+
_check(
|
|
445
|
+
abs(nav.value - WETH_COLLATERAL) <= NAV_DUST_TOLERANCE,
|
|
446
|
+
f"{step} moved NAV beyond dust: {nav.value}",
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
_check(
|
|
450
|
+
idle_usdc_after_borrow.value == USDC_BORROW,
|
|
451
|
+
"the borrowed USDC did not reach the vault",
|
|
452
|
+
)
|
|
453
|
+
_check(
|
|
454
|
+
0 <= debt_after_borrow.value - USDC_BORROW <= USDC_DUST_TOLERANCE,
|
|
455
|
+
f"unexpected debt after the borrow: {debt_after_borrow.value}",
|
|
456
|
+
)
|
|
457
|
+
_check(
|
|
458
|
+
market_after_borrow.value < WETH_COLLATERAL,
|
|
459
|
+
"the Compound market value did not net the debt",
|
|
460
|
+
)
|
|
461
|
+
remaining = USDC_BORROW - USDC_PARTIAL_REPAY
|
|
462
|
+
_check(
|
|
463
|
+
abs(debt_after_partial.value - remaining) <= USDC_DUST_TOLERANCE,
|
|
464
|
+
f"unexpected debt after the partial repay: {debt_after_partial.value}",
|
|
465
|
+
)
|
|
466
|
+
_check(
|
|
467
|
+
debt_with_interest.value > debt_after_partial.value,
|
|
468
|
+
"no interest accrued over the hour",
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
# The full repay paid exactly the debt, and the rest of the USDC stayed idle.
|
|
472
|
+
_check(debt_after_full.value == 0, "the debt did not clear")
|
|
473
|
+
_check(base_supply_after_full.value == 0, "the repay opened a base supply position")
|
|
474
|
+
expected_idle = remaining + USDC_INTEREST_COVER - debt_with_interest.value
|
|
475
|
+
_check(
|
|
476
|
+
abs(idle_usdc_after_full.value - expected_idle) <= USDC_DUST_TOLERANCE,
|
|
477
|
+
f"unexpected idle USDC after the full repay: {idle_usdc_after_full.value}",
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
_check(collateral_after_exit.value == 0, "collateral left on the Comet")
|
|
481
|
+
_check(idle_weth_after_exit.value == WETH_COLLATERAL, "the WETH did not come back")
|
|
482
|
+
_check(market_after_exit.value == 0, "the Compound market still has value")
|
|
483
|
+
|
|
484
|
+
log.info(
|
|
485
|
+
"OK -- borrowed %s USDC, paid %s of debt in full, gas_used=%s",
|
|
486
|
+
USDC_BORROW / 10**6,
|
|
487
|
+
debt_with_interest.value / 10**6,
|
|
488
|
+
result.gas_used,
|
|
489
|
+
)
|
|
490
|
+
return result
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def main() -> None:
|
|
494
|
+
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
495
|
+
run_simulation(_connected_web3())
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
if __name__ == "__main__":
|
|
499
|
+
main()
|
|
@@ -312,6 +312,9 @@ from ipor_fusion.readers import (
|
|
|
312
312
|
AaveV4OracleReader,
|
|
313
313
|
AaveV4SpokeReader,
|
|
314
314
|
AaveV4UserAccountData,
|
|
315
|
+
CometAccountValue,
|
|
316
|
+
CometAssetInfo,
|
|
317
|
+
CompoundV3FuseReader,
|
|
315
318
|
CompoundV3Reader,
|
|
316
319
|
LendingMarketHealth,
|
|
317
320
|
MiddlewarePricedToken,
|
|
@@ -788,6 +791,9 @@ __all__ = [
|
|
|
788
791
|
"AaveV4OracleReader",
|
|
789
792
|
"AaveV4SpokeReader",
|
|
790
793
|
"AaveV4UserAccountData",
|
|
794
|
+
"CometAccountValue",
|
|
795
|
+
"CometAssetInfo",
|
|
796
|
+
"CompoundV3FuseReader",
|
|
791
797
|
"CompoundV3Reader",
|
|
792
798
|
"HYPEREVM_CHAIN_ID",
|
|
793
799
|
"LendingMarketHealth",
|
{ipor_fusion-4.2.0.dev11 → ipor_fusion-4.2.0.dev16}/src/ipor_fusion/cli/vault_markets_view.py
RENAMED
|
@@ -116,8 +116,8 @@ def _index_lending_health(
|
|
|
116
116
|
"""Split lending health rows by protocol for fast lookup during rendering.
|
|
117
117
|
|
|
118
118
|
Morpho rows are keyed by morpho substrate id (one row per substrate).
|
|
119
|
-
Aave V3 rows are keyed by IPOR market id (account-aggregated,
|
|
120
|
-
market); Aave V4 rows by Spoke address (account-aggregated per Spoke) and
|
|
119
|
+
Aave V3 and Compound V3 rows are keyed by IPOR market id (account-aggregated,
|
|
120
|
+
one row per market); Aave V4 rows by Spoke address (account-aggregated per Spoke) and
|
|
121
121
|
Euler V2 rows by sub-account address, in the same account-level map.
|
|
122
122
|
"""
|
|
123
123
|
morpho: dict[str, LendingMarketHealth] = {}
|
|
@@ -127,7 +127,7 @@ def _index_lending_health(
|
|
|
127
127
|
for m in lh.markets:
|
|
128
128
|
if m.protocol == "morpho" and m.substrate_id:
|
|
129
129
|
morpho[str(m.substrate_id).lower().removeprefix("0x")] = m
|
|
130
|
-
elif m.protocol
|
|
130
|
+
elif m.protocol in ("aave_v3", "compound_v3"):
|
|
131
131
|
aave[m.market_id] = m
|
|
132
132
|
elif m.protocol in ("aave_v4", "euler_v2") and m.substrate_id:
|
|
133
133
|
aave[m.substrate_id] = m
|
|
@@ -11,7 +11,12 @@ from ipor_fusion.readers.aave_v4 import (
|
|
|
11
11
|
AaveV4SpokeReader,
|
|
12
12
|
AaveV4UserAccountData,
|
|
13
13
|
)
|
|
14
|
-
from ipor_fusion.readers.compound_v3 import
|
|
14
|
+
from ipor_fusion.readers.compound_v3 import (
|
|
15
|
+
CometAccountValue,
|
|
16
|
+
CometAssetInfo,
|
|
17
|
+
CompoundV3FuseReader,
|
|
18
|
+
CompoundV3Reader,
|
|
19
|
+
)
|
|
15
20
|
from ipor_fusion.readers.hypercore import (
|
|
16
21
|
HYPEREVM_CHAIN_ID,
|
|
17
22
|
)
|
|
@@ -79,6 +84,9 @@ __all__ = [
|
|
|
79
84
|
"AaveV4OracleReader",
|
|
80
85
|
"AaveV4SpokeReader",
|
|
81
86
|
"AaveV4UserAccountData",
|
|
87
|
+
"CometAccountValue",
|
|
88
|
+
"CometAssetInfo",
|
|
89
|
+
"CompoundV3FuseReader",
|
|
82
90
|
"CompoundV3Reader",
|
|
83
91
|
"HYPEREVM_CHAIN_ID",
|
|
84
92
|
"LendingMarketHealth",
|