ipor-fusion 4.0.0.dev274__tar.gz → 4.0.0.dev282__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/AGENTS.md +8 -3
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/PKG-INFO +10 -2
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/README.md +9 -1
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/skills/ipor-deploy-vault/SKILL.md +5 -3
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/__init__.py +34 -0
- ipor_fusion-4.0.0.dev282/src/ipor_fusion/cli/address_cmd.py +108 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/main.py +2 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/errors.py +9 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/universal.py +38 -7
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/guide/quickstart.md +5 -3
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/mcp/models.py +41 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/mcp/server.py +55 -2
- ipor_fusion-4.0.0.dev282/src/ipor_fusion/registry.py +269 -0
- ipor_fusion-4.0.0.dev282/tests/test_cli_address.py +81 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_fuse_encoding.py +72 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_guide.py +2 -18
- ipor_fusion-4.0.0.dev282/tests/test_mcp_address.py +73 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_mcp_models.py +11 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_public_api.py +36 -0
- ipor_fusion-4.0.0.dev282/tests/test_registry.py +227 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/MIGRATION_4.md +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/context_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/simulation_result.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/hypercore/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_event_args.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_fake_rpc.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_golden.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_live.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_oracle_mapping_fake.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_blue.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/simulate_v1_request.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/fetched.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/full.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/full.txt +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_helpers.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_market_golden.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_mcp_parity.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_oracle_mapping_golden.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_docs_typing.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_governance_wrappers.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulation_errors.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_simulation_observation.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_vault_data_block.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_vault_info_fetched_golden.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_vault_reconciliation_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev274 → ipor_fusion-4.0.0.dev282}/uv.lock +0 -0
|
@@ -198,7 +198,7 @@ the lifecycle matrix follows that.
|
|
|
198
198
|
subpackages do not list them). Their exceptions stay in `errors` and the top
|
|
199
199
|
level. `ipor_fusion.guide` keeps its own namespace. The
|
|
200
200
|
public modules (`about`, `cache`, `chains`, `dependency_graph`, `errors`, `explorer`,
|
|
201
|
-
`field_docs`, `market_ids`, `market_info`, `morpho_api`, `substrates`, `types`,
|
|
201
|
+
`field_docs`, `market_ids`, `market_info`, `morpho_api`, `registry`, `substrates`, `types`,
|
|
202
202
|
`validation`, `vault_data`, `vault_info`) stay importable for the names they
|
|
203
203
|
define and the moved names README lists; `cli`, `mcp`, every other module and
|
|
204
204
|
every submodule of a public subpackage (`core.contract`, `fuses.base`, ...) are
|
|
@@ -221,7 +221,7 @@ the lifecycle matrix follows that.
|
|
|
221
221
|
| `IporFusionMarkets`, `Roles` | `market_ids.py`, `config/roles.py` mirror `IporFusionMarkets.sol`, `Roles.sol` in `ipor-fusion/contracts/libraries/`; drift-gated by `tests/test_solidity_mirrors.py` — bump its pinned ref in the same change that syncs the mirrors An id sourced from a contracts feature branch ahead of the pinned ref goes in the test's `_AHEAD_OF_UPSTREAM` allowlist and is removed when the ref catches up |
|
|
222
222
|
| substrate decoders | `substrates.py` registry mirrors each market's `contracts/fuses/<protocol>/*SubstrateLib.sol` or `*FuseLib.sol` |
|
|
223
223
|
| `vault_info` / `fusion market` JSON shapes | the builders and their TypedDicts (`VaultInfoJson`, `MorphoBlueMarketJson`, `MetaMorphoV1Json` / `MetaMorphoV2Json`) in `vault_info.py` / `market_info.py`, models in `mcp/models.py` (`extra="forbid"`, the published schema), the `VaultInfoJson`-typed `_full_vault_info_dict` fixture in `test_mcp_models.py` |
|
|
224
|
-
| CLI command set | every CLI command has a matching tool in `mcp/server.py` with the same arguments (`changelog` maps to `server_info`; `--json` and display filters are CLI-only); `tests/test_cli_mcp_parity.py` enforces it, and
|
|
224
|
+
| CLI command set | every CLI command has a matching tool in `mcp/server.py` with the same arguments (`changelog` maps to `server_info`; `--json` and display filters are CLI-only); `tests/test_cli_mcp_parity.py` enforces it, and the RPC-backed commands resolve chain and provider through `cli/config_store.resolve_chain_id` (`fusion address` reads no RPC: an omitted chain means every registry chain) |
|
|
225
225
|
|
|
226
226
|
## Layout (`src/ipor_fusion/`)
|
|
227
227
|
|
|
@@ -258,6 +258,9 @@ the lifecycle matrix follows that.
|
|
|
258
258
|
`ctx.cache` (`Web3Context(cache=...)`, default `default_contract_cache()` under
|
|
259
259
|
`$XDG_CACHE_HOME/ipor-fusion/`) or `get_contract_name(cache=...)`. Never cache an
|
|
260
260
|
absence (no code, no answer): it can change at a later block.
|
|
261
|
+
`registry.py` the ipor-abi address registry (`fetch_address_registry` →
|
|
262
|
+
`AddressRegistry`, one raw-GitHub file per chain in `REGISTRY_CHAIN_IDS`, cached ten
|
|
263
|
+
minutes in the process, for good at a commit ref) and the `fusion address` documents;
|
|
261
264
|
`morpho_api.py` the Morpho public GraphQL API client (`fetch_market`, `fetch_vault`,
|
|
262
265
|
`PUBLIC_ALLOCATOR_ADDRESSES`; it narrows the GraphQL body from `object`);
|
|
263
266
|
`market_info.py` — `build_morpho_blue_market_json`, `build_meta_morpho_json`, the
|
|
@@ -481,6 +484,7 @@ byte, e.g. Ebisu ZAPPER/REGISTRY).
|
|
|
481
484
|
|
|
482
485
|
## CLI and MCP
|
|
483
486
|
|
|
487
|
+
`fusion address` (lookup, names, list; the ipor-abi registry through `registry.py`),
|
|
484
488
|
`fusion config` (set-provider, set-etherscan-key, show), `fusion market`
|
|
485
489
|
(morpho-blue, meta-morpho), `fusion vault` (add, remove, list, info, validate,
|
|
486
490
|
role-accounts, oracle-mapping). `vault info` runs independent fetch pipelines
|
|
@@ -498,7 +502,8 @@ inject a cache.
|
|
|
498
502
|
Every CLI command has an MCP tool (`fusion changelog` maps to `server_info`;
|
|
499
503
|
the rest share names). Read-only: `vault_info`, `vault_validate`,
|
|
500
504
|
`vault_list`, `vault_role_accounts`, `vault_oracle_mapping`, `market_morpho_blue`,
|
|
501
|
-
`market_meta_morpho`, `
|
|
505
|
+
`market_meta_morpho`, `address_lookup`, `address_names`, `address_list`,
|
|
506
|
+
`config_show`, `server_info`. Mutating: `vault_add`,
|
|
502
507
|
`vault_remove`, `config_set_provider`, `config_set_etherscan_key`. Every new
|
|
503
508
|
CLI command gets a matching MCP tool with the same argument names; an optional
|
|
504
509
|
chain or block is `None` when omitted (`ChainArg`, `BlockArg`), never a `0`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ipor-fusion
|
|
3
|
-
Version: 4.0.0.
|
|
3
|
+
Version: 4.0.0.dev282
|
|
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
|
|
@@ -168,7 +168,8 @@ receipt = vault.execute([action]).send(ctx)
|
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
Fuse, factory and manager addresses per chain are published in
|
|
171
|
-
[ipor-abi](https://github.com/IPOR-Labs/ipor-abi) (`mainnet/mainnet-<chain>-fusion/addresses.json`)
|
|
171
|
+
[ipor-abi](https://github.com/IPOR-Labs/ipor-abi) (`mainnet/mainnet-<chain>-fusion/addresses.json`);
|
|
172
|
+
`fetch_address_registry(chain_id).address("SupplyFuseAaveV3")` reads them by name (`ipor_fusion.registry`).
|
|
172
173
|
A fuse must also be registered on the vault; `vault.get_fuses().call(ctx)` lists the registered ones.
|
|
173
174
|
|
|
174
175
|
Amounts are raw on-chain integers (`Amount`, `Shares` in `ipor_fusion.types`); the SDK scales by decimals
|
|
@@ -225,6 +226,10 @@ fusion vault list
|
|
|
225
226
|
# Inspect a Morpho Blue market or MetaMorpho vault
|
|
226
227
|
fusion market morpho-blue 0xMARKET_ID --chain ethereum
|
|
227
228
|
fusion market meta-morpho 0xVAULT_ADDRESS --chain ethereum
|
|
229
|
+
|
|
230
|
+
# Find IPOR Fusion contracts in the ipor-abi registry
|
|
231
|
+
fusion address lookup SupplyFuseAaveV3 --chain base
|
|
232
|
+
fusion address list --chain base
|
|
228
233
|
```
|
|
229
234
|
|
|
230
235
|
## MCP Server
|
|
@@ -291,6 +296,9 @@ Available tools:
|
|
|
291
296
|
| `vault_remove` | Remove a vault from the local config |
|
|
292
297
|
| `market_morpho_blue` | Morpho Blue market parameters and state |
|
|
293
298
|
| `market_meta_morpho` | MetaMorpho V1 or Morpho Vault V2 allocations and caps (Morpho API) |
|
|
299
|
+
| `address_lookup` | IPOR Fusion contracts by name substring or address (ipor-abi registry) |
|
|
300
|
+
| `address_names` | Contract names in the registry, with the chains that have them |
|
|
301
|
+
| `address_list` | One chain's full `{name: address}` registry |
|
|
294
302
|
|
|
295
303
|
Configure providers and vaults via `fusion config` or the MCP config tools first.
|
|
296
304
|
|
|
@@ -119,7 +119,8 @@ receipt = vault.execute([action]).send(ctx)
|
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
Fuse, factory and manager addresses per chain are published in
|
|
122
|
-
[ipor-abi](https://github.com/IPOR-Labs/ipor-abi) (`mainnet/mainnet-<chain>-fusion/addresses.json`)
|
|
122
|
+
[ipor-abi](https://github.com/IPOR-Labs/ipor-abi) (`mainnet/mainnet-<chain>-fusion/addresses.json`);
|
|
123
|
+
`fetch_address_registry(chain_id).address("SupplyFuseAaveV3")` reads them by name (`ipor_fusion.registry`).
|
|
123
124
|
A fuse must also be registered on the vault; `vault.get_fuses().call(ctx)` lists the registered ones.
|
|
124
125
|
|
|
125
126
|
Amounts are raw on-chain integers (`Amount`, `Shares` in `ipor_fusion.types`); the SDK scales by decimals
|
|
@@ -176,6 +177,10 @@ fusion vault list
|
|
|
176
177
|
# Inspect a Morpho Blue market or MetaMorpho vault
|
|
177
178
|
fusion market morpho-blue 0xMARKET_ID --chain ethereum
|
|
178
179
|
fusion market meta-morpho 0xVAULT_ADDRESS --chain ethereum
|
|
180
|
+
|
|
181
|
+
# Find IPOR Fusion contracts in the ipor-abi registry
|
|
182
|
+
fusion address lookup SupplyFuseAaveV3 --chain base
|
|
183
|
+
fusion address list --chain base
|
|
179
184
|
```
|
|
180
185
|
|
|
181
186
|
## MCP Server
|
|
@@ -242,6 +247,9 @@ Available tools:
|
|
|
242
247
|
| `vault_remove` | Remove a vault from the local config |
|
|
243
248
|
| `market_morpho_blue` | Morpho Blue market parameters and state |
|
|
244
249
|
| `market_meta_morpho` | MetaMorpho V1 or Morpho Vault V2 allocations and caps (Morpho API) |
|
|
250
|
+
| `address_lookup` | IPOR Fusion contracts by name substring or address (ipor-abi registry) |
|
|
251
|
+
| `address_names` | Contract names in the registry, with the chains that have them |
|
|
252
|
+
| `address_list` | One chain's full `{name: address}` registry |
|
|
245
253
|
|
|
246
254
|
Configure providers and vaults via `fusion config` or the MCP config tools first.
|
|
247
255
|
|
|
@@ -231,9 +231,11 @@ slot was read on chain and matches the registry's `IporFusionFactoryImpl`;
|
|
|
231
231
|
The factory implementations are upgraded in place, so the implementation
|
|
232
232
|
column changes with releases while the proxy stays.
|
|
233
233
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
234
|
+
Resolve fuse addresses by registry name and chain from `ipor-abi`
|
|
235
|
+
(`SupplyFuseAaveV3`, `BalanceFuseAaveV3`, ...):
|
|
236
|
+
`fetch_address_registry(ChainId(8453)).address("SupplyFuseAaveV3")` in the SDK,
|
|
237
|
+
`fusion address lookup SupplyFuseAaveV3 --chain base`, or the MCP tool
|
|
238
|
+
`address_lookup` (the hosted server's `fusion_address_lookup`). The SDK class
|
|
237
239
|
for a registry name swaps the parts: `SupplyFuseAaveV3` is `AaveV3SupplyFuse`.
|
|
238
240
|
|
|
239
241
|
## Pointers
|
|
@@ -107,6 +107,8 @@ from ipor_fusion.dependency_graph import (
|
|
|
107
107
|
)
|
|
108
108
|
from ipor_fusion.errors import (
|
|
109
109
|
CUSTOM_ERRORS,
|
|
110
|
+
AddressNotInRegistryError,
|
|
111
|
+
AddressRegistryError,
|
|
110
112
|
BalanceSlotNotFoundError,
|
|
111
113
|
ContractNotFoundError,
|
|
112
114
|
CrosschainDiscoveryError,
|
|
@@ -301,6 +303,22 @@ from ipor_fusion.readers.oracle_mapping_nodes import (
|
|
|
301
303
|
OraclePriceJson,
|
|
302
304
|
SourceDetailJson,
|
|
303
305
|
)
|
|
306
|
+
from ipor_fusion.registry import (
|
|
307
|
+
REGISTRY_CHAIN_IDS,
|
|
308
|
+
REGISTRY_REPOSITORY,
|
|
309
|
+
AddressListJson,
|
|
310
|
+
AddressLookupJson,
|
|
311
|
+
AddressMatchJson,
|
|
312
|
+
AddressNameJson,
|
|
313
|
+
AddressNamesJson,
|
|
314
|
+
AddressRegistry,
|
|
315
|
+
build_address_list_json,
|
|
316
|
+
build_address_lookup_json,
|
|
317
|
+
build_address_names_json,
|
|
318
|
+
fetch_address_registries,
|
|
319
|
+
fetch_address_registry,
|
|
320
|
+
registry_url,
|
|
321
|
+
)
|
|
304
322
|
from ipor_fusion.substrates import (
|
|
305
323
|
SubstrateInfo,
|
|
306
324
|
decode_substrate,
|
|
@@ -450,6 +468,22 @@ __all__ = [
|
|
|
450
468
|
"repository_url",
|
|
451
469
|
"Web3Context",
|
|
452
470
|
"InFlightLimit",
|
|
471
|
+
"AddressNotInRegistryError",
|
|
472
|
+
"AddressRegistryError",
|
|
473
|
+
"REGISTRY_CHAIN_IDS",
|
|
474
|
+
"REGISTRY_REPOSITORY",
|
|
475
|
+
"AddressListJson",
|
|
476
|
+
"AddressLookupJson",
|
|
477
|
+
"AddressMatchJson",
|
|
478
|
+
"AddressNameJson",
|
|
479
|
+
"AddressNamesJson",
|
|
480
|
+
"AddressRegistry",
|
|
481
|
+
"build_address_list_json",
|
|
482
|
+
"build_address_lookup_json",
|
|
483
|
+
"build_address_names_json",
|
|
484
|
+
"fetch_address_registries",
|
|
485
|
+
"fetch_address_registry",
|
|
486
|
+
"registry_url",
|
|
453
487
|
"ContextManager",
|
|
454
488
|
"Call",
|
|
455
489
|
"get_logs_adaptive",
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""`fusion address` — IPOR Fusion deployment addresses from the ipor-abi registry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json as json_lib
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ipor_fusion.cli.options import CHAIN
|
|
10
|
+
from ipor_fusion.cli.vault_rendering import _print_table
|
|
11
|
+
from ipor_fusion.errors import AddressRegistryError, UnsupportedChainError
|
|
12
|
+
from ipor_fusion.registry import (
|
|
13
|
+
AddressRegistry,
|
|
14
|
+
build_address_list_json,
|
|
15
|
+
build_address_lookup_json,
|
|
16
|
+
build_address_names_json,
|
|
17
|
+
fetch_address_registries,
|
|
18
|
+
fetch_address_registry,
|
|
19
|
+
)
|
|
20
|
+
from ipor_fusion.types import ChainId
|
|
21
|
+
|
|
22
|
+
_JSON = click.option(
|
|
23
|
+
"--json",
|
|
24
|
+
"json_output",
|
|
25
|
+
is_flag=True,
|
|
26
|
+
default=False,
|
|
27
|
+
help="Emit machine-readable JSON instead of a table.",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _registries(chain_id: ChainId | None) -> list[AddressRegistry]:
|
|
32
|
+
try:
|
|
33
|
+
return fetch_address_registries(None if chain_id is None else [chain_id])
|
|
34
|
+
except (AddressRegistryError, UnsupportedChainError) as exc:
|
|
35
|
+
raise click.UsageError(str(exc)) from exc
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@click.group()
|
|
39
|
+
def address() -> None:
|
|
40
|
+
"""Look up IPOR Fusion contract addresses (ipor-abi registry)."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@address.command("lookup")
|
|
44
|
+
@click.argument("query")
|
|
45
|
+
@click.option(
|
|
46
|
+
"--chain",
|
|
47
|
+
"chain_id",
|
|
48
|
+
type=CHAIN,
|
|
49
|
+
default=None,
|
|
50
|
+
help="Search one chain (ID or name); default: every chain with a registry.",
|
|
51
|
+
)
|
|
52
|
+
@_JSON
|
|
53
|
+
def lookup(query: str, chain_id: ChainId | None, json_output: bool) -> None:
|
|
54
|
+
"""Find contracts by name substring, or the names of an address."""
|
|
55
|
+
document = build_address_lookup_json(query, _registries(chain_id))
|
|
56
|
+
if json_output:
|
|
57
|
+
click.echo(json_lib.dumps(document, indent=2))
|
|
58
|
+
return
|
|
59
|
+
_print_table(
|
|
60
|
+
("Chain", "Name", "Address"),
|
|
61
|
+
[(m["chain"], m["name"], m["address"]) for m in document["matches"]],
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@address.command("names")
|
|
66
|
+
@click.option(
|
|
67
|
+
"--chain",
|
|
68
|
+
"chain_id",
|
|
69
|
+
type=CHAIN,
|
|
70
|
+
default=None,
|
|
71
|
+
help="List one chain (ID or name); default: every chain with a registry.",
|
|
72
|
+
)
|
|
73
|
+
@_JSON
|
|
74
|
+
def names(chain_id: ChainId | None, json_output: bool) -> None:
|
|
75
|
+
"""List the contract names in the registry, with the chains that have them."""
|
|
76
|
+
document = build_address_names_json(_registries(chain_id))
|
|
77
|
+
if json_output:
|
|
78
|
+
click.echo(json_lib.dumps(document, indent=2))
|
|
79
|
+
return
|
|
80
|
+
_print_table(
|
|
81
|
+
("Name", "Chains"),
|
|
82
|
+
[
|
|
83
|
+
(entry["name"], ", ".join(str(c) for c in entry["chain_ids"]))
|
|
84
|
+
for entry in document["names"]
|
|
85
|
+
],
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@address.command("list")
|
|
90
|
+
@click.option(
|
|
91
|
+
"--chain",
|
|
92
|
+
"chain_id",
|
|
93
|
+
type=CHAIN,
|
|
94
|
+
required=True,
|
|
95
|
+
help="Chain ID or name (ethereum, base, arbitrum, ...).",
|
|
96
|
+
)
|
|
97
|
+
@_JSON
|
|
98
|
+
def list_addresses(chain_id: ChainId, json_output: bool) -> None:
|
|
99
|
+
"""Every registered contract of one chain."""
|
|
100
|
+
try:
|
|
101
|
+
registry = fetch_address_registry(chain_id)
|
|
102
|
+
except (AddressRegistryError, UnsupportedChainError) as exc:
|
|
103
|
+
raise click.UsageError(str(exc)) from exc
|
|
104
|
+
document = build_address_list_json(registry)
|
|
105
|
+
if json_output:
|
|
106
|
+
click.echo(json_lib.dumps(document, indent=2))
|
|
107
|
+
return
|
|
108
|
+
_print_table(("Name", "Address"), list(document["addresses"].items()))
|
|
@@ -5,6 +5,7 @@ import sys
|
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
7
|
|
|
8
|
+
from ipor_fusion.cli.address_cmd import address
|
|
8
9
|
from ipor_fusion.cli.changelog_cmd import changelog
|
|
9
10
|
from ipor_fusion.cli.config_cmd import config
|
|
10
11
|
from ipor_fusion.cli.market_cmd import market
|
|
@@ -29,6 +30,7 @@ def cli(ctx: click.Context, verbose: bool, quiet: bool, no_color: bool) -> None:
|
|
|
29
30
|
ctx.obj["no_color"] = False
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
cli.add_command(address)
|
|
32
34
|
cli.add_command(changelog)
|
|
33
35
|
cli.add_command(config)
|
|
34
36
|
cli.add_command(market)
|
|
@@ -293,6 +293,15 @@ class MorphoApiError(IporFusionError, RuntimeError):
|
|
|
293
293
|
addresses or market ids). Also a RuntimeError, its base before 4.0."""
|
|
294
294
|
|
|
295
295
|
|
|
296
|
+
class AddressRegistryError(IporFusionError, RuntimeError):
|
|
297
|
+
"""The ipor-abi address registry is unreachable or its file does not parse
|
|
298
|
+
as a ``{name: address}`` map."""
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class AddressNotInRegistryError(IporFusionError, LookupError):
|
|
302
|
+
"""A chain's address registry has no contract by the requested name."""
|
|
303
|
+
|
|
304
|
+
|
|
296
305
|
class CrosschainDiscoveryError(IporFusionError):
|
|
297
306
|
"""A crosschain vault's on-chain configuration is not one discovery can
|
|
298
307
|
use: a malformed or unexpected market grant, an executor managed by another
|
|
@@ -14,14 +14,18 @@ from ipor_fusion.types import Amount, FuseAddress
|
|
|
14
14
|
class UniversalTokenSwapperAbi(Enum):
|
|
15
15
|
"""ABI revision of the deployed UniversalTokenSwapperFuse contract.
|
|
16
16
|
|
|
17
|
-
Deployed fuses are immutable, so
|
|
18
|
-
older deployments (e.g. Base) accept only the LEGACY signature,
|
|
19
|
-
ones (
|
|
20
|
-
|
|
17
|
+
Deployed fuses are immutable, so the revisions coexist across chains:
|
|
18
|
+
older deployments (e.g. Base) accept only the LEGACY signature, the
|
|
19
|
+
HyperEVM ones (markets 12 and 1202) only the MIN_AMOUNT_OUT one, and the
|
|
20
|
+
Arbitrum fuses of the 2026-10-21 contracts release (markets 12 and 1202)
|
|
21
|
+
only TOKENS_TO_RETURN, which adds the tokens the swap calls may leave in
|
|
22
|
+
the executor. Pick the member matching the fuse address this instance is
|
|
23
|
+
bound to.
|
|
21
24
|
"""
|
|
22
25
|
|
|
23
26
|
LEGACY = auto()
|
|
24
27
|
MIN_AMOUNT_OUT = auto()
|
|
28
|
+
TOKENS_TO_RETURN = auto()
|
|
25
29
|
|
|
26
30
|
|
|
27
31
|
class UniversalTokenSwapperSubstrates:
|
|
@@ -73,7 +77,11 @@ class UniversalTokenSwapperFuse(Fuse):
|
|
|
73
77
|
targets: list[ChecksumAddress],
|
|
74
78
|
data: list[bytes],
|
|
75
79
|
min_amount_out: Amount | None = None,
|
|
80
|
+
tokens_to_return: list[ChecksumAddress] | None = None,
|
|
76
81
|
) -> FuseAction:
|
|
82
|
+
"""``tokens_to_return`` (TOKENS_TO_RETURN only): intermediate or extra
|
|
83
|
+
output tokens the swap calls may leave in the executor, each a granted
|
|
84
|
+
Token substrate; the executor returns them to the vault."""
|
|
77
85
|
self._validate_address(token_in, "token_in")
|
|
78
86
|
self._validate_address(token_out, "token_out")
|
|
79
87
|
self._validate_amount(amount_in, "amount_in")
|
|
@@ -81,6 +89,13 @@ class UniversalTokenSwapperFuse(Fuse):
|
|
|
81
89
|
raise ValueError(
|
|
82
90
|
f"targets and data must have the same length, got {len(targets)} and {len(data)}"
|
|
83
91
|
)
|
|
92
|
+
if (
|
|
93
|
+
tokens_to_return is not None
|
|
94
|
+
and self._abi is not UniversalTokenSwapperAbi.TOKENS_TO_RETURN
|
|
95
|
+
):
|
|
96
|
+
raise ValueError(
|
|
97
|
+
"tokens_to_return requires abi=UniversalTokenSwapperAbi.TOKENS_TO_RETURN"
|
|
98
|
+
)
|
|
84
99
|
if self._abi is UniversalTokenSwapperAbi.LEGACY:
|
|
85
100
|
if min_amount_out is not None:
|
|
86
101
|
raise ValueError(
|
|
@@ -92,11 +107,27 @@ class UniversalTokenSwapperFuse(Fuse):
|
|
|
92
107
|
)
|
|
93
108
|
if min_amount_out is None:
|
|
94
109
|
raise ValueError(
|
|
95
|
-
"min_amount_out is required with abi=UniversalTokenSwapperAbi.
|
|
110
|
+
f"min_amount_out is required with abi=UniversalTokenSwapperAbi.{self._abi.name}"
|
|
96
111
|
" (pass 0 to rely on the vault-side slippage cap only)"
|
|
97
112
|
)
|
|
98
113
|
self._validate_non_negative(min_amount_out, "min_amount_out")
|
|
114
|
+
if self._abi is UniversalTokenSwapperAbi.MIN_AMOUNT_OUT:
|
|
115
|
+
return self._action_raw(
|
|
116
|
+
"enter((address,address,uint256,uint256,(address[],bytes[])))",
|
|
117
|
+
[[token_in, token_out, amount_in, min_amount_out, [targets, data]]],
|
|
118
|
+
)
|
|
119
|
+
returned = list(tokens_to_return or [])
|
|
120
|
+
for index, token in enumerate(returned):
|
|
121
|
+
self._validate_address(token, f"tokens_to_return[{index}]")
|
|
99
122
|
return self._action_raw(
|
|
100
|
-
"enter((address,address,uint256,uint256,(address[],bytes[])))",
|
|
101
|
-
[
|
|
123
|
+
"enter((address,address,uint256,uint256,(address[],bytes[],address[])))",
|
|
124
|
+
[
|
|
125
|
+
[
|
|
126
|
+
token_in,
|
|
127
|
+
token_out,
|
|
128
|
+
amount_in,
|
|
129
|
+
min_amount_out,
|
|
130
|
+
[targets, data, returned],
|
|
131
|
+
]
|
|
132
|
+
],
|
|
102
133
|
)
|
|
@@ -146,7 +146,9 @@ slot was read on chain and matches the registry's `IporFusionFactoryImpl`;
|
|
|
146
146
|
The factory implementations are upgraded in place, so the implementation
|
|
147
147
|
column changes with releases while the proxy stays.
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
Resolve fuse addresses by registry name and chain from `ipor-abi`
|
|
150
|
+
(`SupplyFuseAaveV3`, `BalanceFuseAaveV3`, ...):
|
|
151
|
+
`fetch_address_registry(ChainId(8453)).address("SupplyFuseAaveV3")` in the SDK,
|
|
152
|
+
`fusion address lookup SupplyFuseAaveV3 --chain base`, or the MCP tool
|
|
153
|
+
`address_lookup` (the hosted server's `fusion_address_lookup`). The SDK class
|
|
152
154
|
for a registry name swaps the parts: `SupplyFuseAaveV3` is `AaveV3SupplyFuse`.
|
|
@@ -1199,3 +1199,44 @@ class ServerInfoResponse(_Base):
|
|
|
1199
1199
|
"crosschain and HyperCore). Everything the package ships outside these "
|
|
1200
1200
|
"is production.",
|
|
1201
1201
|
)
|
|
1202
|
+
|
|
1203
|
+
|
|
1204
|
+
class AddressMatch(_Base):
|
|
1205
|
+
chain_id: ChainId
|
|
1206
|
+
chain: str
|
|
1207
|
+
name: str = Field(description="Contract name in the ipor-abi registry.")
|
|
1208
|
+
address: str
|
|
1209
|
+
|
|
1210
|
+
|
|
1211
|
+
class AddressLookupResponse(_Base):
|
|
1212
|
+
"""Registry entries matching a name substring or an address."""
|
|
1213
|
+
|
|
1214
|
+
query: str
|
|
1215
|
+
query_type: Literal["address", "name"] = Field(
|
|
1216
|
+
description="'address' for 0x and 40 hex digits (exact match), else 'name' "
|
|
1217
|
+
"(case-insensitive substring)."
|
|
1218
|
+
)
|
|
1219
|
+
match_count: int
|
|
1220
|
+
matches: list[AddressMatch]
|
|
1221
|
+
|
|
1222
|
+
|
|
1223
|
+
class AddressName(_Base):
|
|
1224
|
+
name: str
|
|
1225
|
+
chain_ids: list[ChainId] = Field(description="Chains registering this name.")
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
class AddressNamesResponse(_Base):
|
|
1229
|
+
"""Every contract name in the registries read."""
|
|
1230
|
+
|
|
1231
|
+
chain_ids: list[ChainId] = Field(description="Chains whose registry was read.")
|
|
1232
|
+
names: list[AddressName]
|
|
1233
|
+
|
|
1234
|
+
|
|
1235
|
+
class AddressListResponse(_Base):
|
|
1236
|
+
"""One chain's full ``{name: address}`` registry."""
|
|
1237
|
+
|
|
1238
|
+
chain_id: ChainId
|
|
1239
|
+
chain: str
|
|
1240
|
+
ref: str = Field(description="ipor-abi git ref the map was read at.")
|
|
1241
|
+
count: int
|
|
1242
|
+
addresses: dict[str, str]
|
|
@@ -38,6 +38,9 @@ from ipor_fusion.market_info import (
|
|
|
38
38
|
from ipor_fusion.mcp.guide import register_guide
|
|
39
39
|
from ipor_fusion.mcp.models import (
|
|
40
40
|
ActionResult,
|
|
41
|
+
AddressListResponse,
|
|
42
|
+
AddressLookupResponse,
|
|
43
|
+
AddressNamesResponse,
|
|
41
44
|
ChangelogEntryModel,
|
|
42
45
|
ConfigShowResponse,
|
|
43
46
|
MetaMorphoVaultResponse,
|
|
@@ -57,6 +60,13 @@ from ipor_fusion.morpho_api import (
|
|
|
57
60
|
)
|
|
58
61
|
from ipor_fusion.readers.morpho import MorphoReader, morpho_blue_address
|
|
59
62
|
from ipor_fusion.readers.oracle_mapping import build_oracle_mapping
|
|
63
|
+
from ipor_fusion.registry import (
|
|
64
|
+
build_address_list_json,
|
|
65
|
+
build_address_lookup_json,
|
|
66
|
+
build_address_names_json,
|
|
67
|
+
fetch_address_registries,
|
|
68
|
+
fetch_address_registry,
|
|
69
|
+
)
|
|
60
70
|
from ipor_fusion.types import (
|
|
61
71
|
ChainId,
|
|
62
72
|
PlasmaVaultAddress,
|
|
@@ -127,7 +137,9 @@ def _build_ctx(
|
|
|
127
137
|
return ctx, block_number
|
|
128
138
|
|
|
129
139
|
|
|
130
|
-
def _no_zero_sentinel(
|
|
140
|
+
def _no_zero_sentinel(
|
|
141
|
+
name: str, omitted: str | None = None
|
|
142
|
+
) -> Callable[[object], object]:
|
|
131
143
|
"""Refuse 0 with an explicit message: clients built against the 3.x tools
|
|
132
144
|
still send it meaning auto / latest, and reading it as block or chain 0
|
|
133
145
|
would answer with genesis-block data instead of an error."""
|
|
@@ -136,7 +148,14 @@ def _no_zero_sentinel(name: str) -> Callable[[object], object]:
|
|
|
136
148
|
if value == 0 and not isinstance(value, bool):
|
|
137
149
|
raise ValueError(
|
|
138
150
|
f"{name}=0 is not a sentinel any more: omit {name} for "
|
|
139
|
-
+ (
|
|
151
|
+
+ (
|
|
152
|
+
omitted
|
|
153
|
+
or (
|
|
154
|
+
"the latest block"
|
|
155
|
+
if name == "block_number"
|
|
156
|
+
else "auto-detection"
|
|
157
|
+
)
|
|
158
|
+
)
|
|
140
159
|
)
|
|
141
160
|
return value
|
|
142
161
|
|
|
@@ -158,6 +177,11 @@ ChainArg = Annotated[
|
|
|
158
177
|
|
|
159
178
|
# The market tools need the chain: no auto-detection, so no omission either.
|
|
160
179
|
RequiredChainArg = Annotated[ChainId, Field(description="Chain ID.", gt=0)]
|
|
180
|
+
RegistryChainArg = Annotated[
|
|
181
|
+
ChainId | None,
|
|
182
|
+
Field(description="Chain ID; omitted: every chain with a deployment file.", gt=0),
|
|
183
|
+
BeforeValidator(_no_zero_sentinel("chain_id", "every chain")),
|
|
184
|
+
]
|
|
161
185
|
BlockArg = Annotated[
|
|
162
186
|
int | None,
|
|
163
187
|
Field(description="Block number (positive); omitted: latest.", ge=1),
|
|
@@ -681,6 +705,35 @@ def market_meta_morpho(
|
|
|
681
705
|
return MetaMorphoVaultResponse.model_validate(payload)
|
|
682
706
|
|
|
683
707
|
|
|
708
|
+
@mcp.tool()
|
|
709
|
+
def address_lookup(
|
|
710
|
+
query: str, chain_id: RegistryChainArg = None
|
|
711
|
+
) -> AddressLookupResponse:
|
|
712
|
+
"""Find IPOR Fusion contracts in the ipor-abi registry by name substring, or
|
|
713
|
+
the names of an address. Reads one file per chain (cached ten minutes)."""
|
|
714
|
+
registries = fetch_address_registries(None if chain_id is None else [chain_id])
|
|
715
|
+
return AddressLookupResponse.model_validate(
|
|
716
|
+
build_address_lookup_json(query, registries)
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
@mcp.tool()
|
|
721
|
+
def address_names(chain_id: RegistryChainArg = None) -> AddressNamesResponse:
|
|
722
|
+
"""List the contract names in the ipor-abi registry, with the chains that
|
|
723
|
+
have each; use them with address_lookup."""
|
|
724
|
+
registries = fetch_address_registries(None if chain_id is None else [chain_id])
|
|
725
|
+
return AddressNamesResponse.model_validate(build_address_names_json(registries))
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
@mcp.tool()
|
|
729
|
+
def address_list(chain_id: RequiredChainArg) -> AddressListResponse:
|
|
730
|
+
"""One chain's full {name: address} registry: hundreds of entries;
|
|
731
|
+
address_lookup is cheaper for one contract."""
|
|
732
|
+
return AddressListResponse.model_validate(
|
|
733
|
+
build_address_list_json(fetch_address_registry(chain_id))
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
|
|
684
737
|
def main() -> None:
|
|
685
738
|
mcp.run(transport="stdio")
|
|
686
739
|
|