ipor-fusion 4.0.0.dev137__tar.gz → 4.0.0.dev139__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.dev137 → ipor_fusion-4.0.0.dev139}/MIGRATION_4.md +546 -434
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/PKG-INFO +24 -9
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/README.md +23 -8
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/skills/ipor-deploy-vault/SKILL.md +17 -12
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/guide/architecture.md +6 -3
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/guide/glossary.md +3 -3
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/guide/invariants.md +1 -1
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/guide/quickstart.md +16 -11
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_guide.py +3 -1
- ipor_fusion-4.0.0.dev139/tests/test_docs_typing.py +72 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/AGENTS.md +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_helpers.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_public_api.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev137 → ipor_fusion-4.0.0.dev139}/uv.lock +0 -0
|
@@ -1,336 +1,66 @@
|
|
|
1
1
|
# Migrating to ipor-fusion 4.0
|
|
2
2
|
|
|
3
|
-
4.0
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
`
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`
|
|
64
|
-
|
|
65
|
-
**Vault reads and validation.** The vault read is one pinned snapshot, and the
|
|
66
|
-
chain is always the context's:
|
|
67
|
-
|
|
68
|
-
- `fetch_vault_data(ctx, vault, *, block=None, lending=True, metadata=True)` takes
|
|
69
|
-
the vault address (not a `PlasmaVault`) and no `chain_id`. It resolves one block
|
|
70
|
-
(``block``, else a numeric `ctx.default_block`, else the latest) and runs every
|
|
71
|
-
read, log scans included, on `ctx.at_block(block)`; `ctx` is not modified. The old
|
|
72
|
-
``chain_id=0`` switch is ``lending=False``; ``metadata=False`` skips the
|
|
73
|
-
display metadata: `token_symbols` and `token_decimals` stay empty and the ERC20
|
|
74
|
-
holdings' symbols are `"?"`. The underlying asset's symbol and decimals and the
|
|
75
|
-
decimals the valuation needs are still read. With ``metadata=True`` the holdings
|
|
76
|
-
are named by the same cached, batched read as `token_symbols`. `VaultData` gains `vault`, `chain_id`, `market_balances`, `erc20`,
|
|
77
|
-
`token_symbols` and `token_decimals`; `WithdrawManagerData` gains
|
|
78
|
-
`shares_to_release_assets` and `pending_request_assets`.
|
|
79
|
-
- `validate_vault(ctx, vault, *, block=None, metadata=False)` fetches (by default
|
|
80
|
-
without display metadata) and validates; it no longer takes ``resolve_symbol``.
|
|
81
|
-
``metadata=True`` names the ERC20 holdings in the result and its findings. `validate_vault_state(state)` is
|
|
82
|
-
pure: a custom `VaultState` also provides `vault`, `chain_id`, `market_balances`,
|
|
83
|
-
`erc20` and the queue's `shares_to_release_assets`. `read_erc20_holdings` is gone:
|
|
84
|
-
the holdings are `VaultData.erc20`.
|
|
85
|
-
- `build_vault_info_json(data, *, resolve_name=None)` renders from the snapshot and
|
|
86
|
-
reads nothing; the chain and its label come from `data.chain_id`.
|
|
87
|
-
- `fetch_deployment_info(ctx, vault, *, api_key=None)` and
|
|
88
|
-
`fetch_vault_lending_health(ctx, vault, *, balance_fuse_market_ids,
|
|
89
|
-
market_substrates, aave_pools=None)` take the chain from `ctx`.
|
|
90
|
-
- `Web3Context(web3, chain_id, *, ...)` and `Web3Context.from_url(url, *, ...)` take
|
|
91
|
-
their options by keyword. New: `Web3Context.at_block(block)` (a pinned copy) and
|
|
92
|
-
`Call.call(ctx, *, block=None)` (see [Context-free wrappers](#context-free-wrappers)). `Web3Context.get_logs(to_block=None)` ends at
|
|
93
|
-
the context's default block and `Web3Context.get_block(block=None)` returns it,
|
|
94
|
-
both instead of ``"latest"``.
|
|
95
|
-
- `Web3Context.default_block` is read-only: `ctx.default_block = n` raises
|
|
96
|
-
`AttributeError`. Pin a copy instead, `ctx = ctx.at_block(n)`; the old setter
|
|
97
|
-
changed the block under every thread sharing the context.
|
|
98
|
-
|
|
99
|
-
Snapshot fields carry their units: `VaultData.total_assets`,
|
|
100
|
-
`underlying_balance_on_vault` and `market_balances` values are `Amount`;
|
|
101
|
-
`total_supply` and `supply_cap` are `Shares`; `share_decimals` / `asset_decimals` are `Decimals`;
|
|
102
|
-
`block_timestamp` and `deployment_timestamp` are `Timestamp`. `FeeData` fee fields
|
|
103
|
-
are `Fee`, `management_fee_last_update` a `Timestamp`, `unrealized_management_fee` an
|
|
104
|
-
`Amount`; `WithdrawManagerData.withdraw_window` is a `Period`, its fees `Fee`,
|
|
105
|
-
`shares_to_release` `Shares`, `last_release_funds_timestamp` a `Timestamp` and the
|
|
106
|
-
converted amounts `Amount`. A custom `VaultState` annotates the same.
|
|
107
|
-
`VaultValidation.block_timestamp` is a `Timestamp` and `asset_decimals` `Decimals`;
|
|
108
|
-
`Erc20Holding.decimals` / `balance` are `Decimals | None` / `Amount | None` and
|
|
109
|
-
`Erc20Holdings.value_in_asset` / `market_cached_value` `Amount`;
|
|
110
|
-
`VaultData.token_decimals` values and `resolve_token_decimals` are `Decimals | None`.
|
|
111
|
-
`PlasmaVault.total_supply()`, `balance_of()` and `get_total_supply_cap()` return
|
|
112
|
-
`Shares` (vault shares), and `transfer` / `approve` / `transfer_from` /
|
|
113
|
-
`set_total_supply_cap` take `Shares`; `ERC20` keeps `Amount`. The supply cap is
|
|
114
|
-
now shown in share decimals labelled `shares` (``fusion vault info``) and its JSON
|
|
115
|
-
``formatted`` uses `share_decimals`; it was formatted with the asset's decimals.
|
|
116
|
-
|
|
117
|
-
`Web3Context.from_url(..., max_concurrent_requests=8)` bounds the RPC requests in
|
|
118
|
-
flight across every thread using the context (``None`` lifts the bound); the vault
|
|
119
|
-
reads fan out on nested thread pools, and the bound is what a rate-limited provider
|
|
120
|
-
sees. An `int` bounds one context. To bound several contexts on one provider (a
|
|
121
|
-
service that builds a context per request or per scan), create one
|
|
122
|
-
`InFlightLimit(n)` per provider and pass it as `max_concurrent_requests=`: every
|
|
123
|
-
context built with it shares the same `n` slots. A context built from your own
|
|
124
|
-
`Web3` object is not bounded unless you add the limit to it:
|
|
125
|
-
`limit.install(web3)` (innermost, as `from_url` does) or
|
|
126
|
-
`web3.middleware_onion.inject(limit.middleware, layer=0)`.
|
|
127
|
-
|
|
128
|
-
`ensure_supported_chain` takes `ChainId`. `fetch_unpriceable_priced_tokens(ctx, *,
|
|
129
|
-
price_oracle, underlying, balance_fuses, market_substrates, morpho_positions)` takes
|
|
130
|
-
everything after `ctx` by keyword, the oracle and underlying as `ChecksumAddress`.
|
|
131
|
-
`resolve_token_symbol`, `resolve_token_decimals`,
|
|
132
|
-
`get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
|
|
133
|
-
fits) and `api_key` by keyword only (`get_contract_name(chain_id, address, *,
|
|
134
|
-
api_key=None, cache=None)`, `get_deployment_tx(chain_id, address, *, api_key=None)`),
|
|
135
|
-
and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
|
|
136
|
-
typed for `str` still fits). Market-keyed data is keyed by `MarketId`:
|
|
137
|
-
`VaultData.market_substrates`, `dependency_graph`, `morpho_positions`,
|
|
138
|
-
`aave_positions`, `market_balances`; the `VaultState` protocol, `fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens`
|
|
139
|
-
and the `dependency_graph` helpers (`compute_update_reach`, `compute_update_groups`,
|
|
140
|
-
`find_orphan_fuse_markets`, `find_markets_missing_erc20_dependency`).
|
|
141
|
-
`find_orphan_fuse_markets` returns `dict[MarketId, list[FuseAddress]]`, so its
|
|
142
|
-
fuses go into `PlasmaVault.remove_fuses` as they are.
|
|
143
|
-
|
|
144
|
-
`VaultData.price_oracle_addr` is renamed `price_oracle`, like its sibling
|
|
145
|
-
address fields (a custom `VaultState` renames it too).
|
|
146
|
-
`VaultData.asset`, `price_oracle`, `rewards_manager`, `withdraw_manager`,
|
|
147
|
-
`FeeData.fee_manager` / `ipor_dao_fee_recipient` and `VaultValidation.vault` /
|
|
148
|
-
`asset` are `ChecksumAddress` (`VaultValidation.vault` a `PlasmaVaultAddress`);
|
|
149
|
-
`VaultData.access_manager` is an `AccessManagerAddress`, `fuses` / `instant_fuses`
|
|
150
|
-
and the `fuse_markets` keys are `FuseAddress`, `balance_fuses` is
|
|
151
|
-
`list[BalanceFuse]`, and `token_prices_usd` is keyed by lowercase `HexAddress`. A
|
|
152
|
-
custom `VaultState` implementation annotates `asset` and `price_oracle` as
|
|
153
|
-
`ChecksumAddress`, `fuse_markets` keys as `FuseAddress`, and its
|
|
154
|
-
`unpriceable_priced_tokens` items' `market_id` as `MarketId` (`UnpriceableToken`).
|
|
155
|
-
The Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient` are
|
|
156
|
-
`ChecksumAddress`. `VaultValidation.chain_id` is `ChainId`;
|
|
157
|
-
`LendingMarketHealth.market_id` and `MiddlewarePricedToken.market_id` are
|
|
158
|
-
`MarketId`; `Erc20Holding.address` is `HexAddress`, and `SubstrateInfo.address` is
|
|
159
|
-
`HexAddress | None` (`None`, no longer `""`, when a substrate carries no address).
|
|
160
|
-
`LegacyPriceOracleMiddleware` takes `base_currency_decimals: Decimals`, and
|
|
161
|
-
`PriceOracleMiddleware.base_currency_decimals()` returns `Call[Decimals]`.
|
|
162
|
-
`get_logs_adaptive(chain_id=)` and `LogSource.get_logs` take `ChainId`;
|
|
163
|
-
`quote_ccip_native_fee(block=)` and `OracleMappingReader.asset_source_events` take a
|
|
164
|
-
`BlockIdentifier`.
|
|
165
|
-
|
|
166
|
-
**Address roles.** `PlasmaVault(address)`,
|
|
167
|
-
`validate_vault`, `fetch_deployment_info`, the HyperCore, lending-health and
|
|
168
|
-
oracle-mapping readers, `read_external_state`, `VaultSimulator(vault=)`,
|
|
169
|
-
`CrosschainSimulator`, crosschain discovery, the hub-manager inputs
|
|
170
|
-
(`ExecutorInitParams.manager`, `create_executor(manager=)`, `executors_of(manager=)`),
|
|
171
|
-
the async command helpers `Command.request_shares` / `redeem_from_request`,
|
|
172
|
-
`pending_request_shares` and `WithdrawManager.update_plasma_vault_address` take a
|
|
173
|
-
`PlasmaVaultAddress`; `.address`, `FusionInstance.plasma_vault` and the executor's
|
|
174
|
-
`manager()` return one. Crosschain deposit and redeem destinations stay
|
|
175
|
-
`ChecksumAddress`: `Command.deposit` / `redeem`, `is_allowed_vault`,
|
|
176
|
-
`is_tracked_vault` and the remote-vault substrate accept any allowlisted ERC4626.
|
|
177
|
-
Every fuse constructor, `FuseAction.fuse`, `BalanceFuse.fuse`,
|
|
178
|
-
`PlasmaVault.add_fuses` / `remove_fuses` / `add_balance_fuse` / `remove_balance_fuse`
|
|
179
|
-
/ `balance_fuse_value`, `RewardsManager.add_reward_fuses` / `is_reward_fuse_supported`,
|
|
180
|
-
`LaneFuses`, the HyperCore balance-fuse argument and the
|
|
181
|
-
`PlasmaVault.configure_instant_withdrawal_fuses` configs
|
|
182
|
-
(`Sequence[tuple[FuseAddress, Sequence[bytes]]]`) take a `FuseAddress`, and
|
|
183
|
-
`get_fuses()`, `get_instant_withdrawal_fuses()`, `get_rewards_fuses()` and
|
|
184
|
-
`VaultData.fuses` / `instant_fuses` / `fuse_markets` keys return it.
|
|
185
|
-
`AccessManager(address)` takes an `AccessManagerAddress`;
|
|
186
|
-
`get_access_manager_address()`, `VaultData.access_manager` and
|
|
187
|
-
`FusionInstance.access_manager` return one. `resolve_access_manager` still takes any
|
|
188
|
-
`ChecksumAddress`: it is what checks that the address is a vault.
|
|
189
|
-
|
|
190
|
-
The vault parameter is named `vault` throughout: `validate_vault`,
|
|
191
|
-
`fetch_deployment_info`, `fetch_vault_lending_health`, `build_oracle_mapping`,
|
|
192
|
-
`read_external_state`, `ExternalStateExecutor.for_vault`, `read_hypercore_nav`,
|
|
193
|
-
`read_hypercore_vault_state`, `read_hypercore_transfer_snapshot`,
|
|
194
|
-
`read_hypercore_evm_exit_ceiling` and `resolve_access_manager` renamed
|
|
195
|
-
``vault_address`` to ``vault``; only a call that passes it by keyword changes.
|
|
196
|
-
|
|
197
|
-
**Simulation and factory records.** `VaultSimulator(web3, *, vault=None, alpha=None,
|
|
198
|
-
block=...)` takes every argument after `web3` by keyword, and `vault` and `alpha` are
|
|
199
|
-
optional: a simulator that only observes, deploys or sends `execute_call(call,
|
|
200
|
-
from_=...)` needs neither, so drop a zero-address placeholder. `execute()` without
|
|
201
|
-
`vault` and `alpha` raises `ValueError`, as does `execute_call` without `from_` or
|
|
202
|
-
`alpha`. `FusionInstance.from_dict(data)` rebuilds a stored instance (the
|
|
203
|
-
`dataclasses.asdict` form) with every field parsed; use it instead of
|
|
204
|
-
`FusionInstance(**data)`, which passes raw strings into the role-typed fields.
|
|
205
|
-
|
|
206
|
-
**Transactions, LayerZero and constants.** `Web3Context.build_transaction` and
|
|
207
|
-
`Call.build_transaction` return web3's `TxParams` (the same keys; `data` is `0x` hex,
|
|
208
|
-
the fee fields are `Wei`). LayerZero endpoint ids are `LayerZeroEid`:
|
|
209
|
-
`StargateChain.eid`, `Packet.src_eid` / `dst_eid`, `EnforcedOptionParam.eid`,
|
|
210
|
-
`executor_eid()` and `eid_of()` return it and `chain_id_of`, `peers`, `lz_receive_calldata(src_eid=)`
|
|
211
|
-
and the Stargate fuse's `eid=` take it (`LayerZeroEid(30101)`). CCIP chain
|
|
212
|
-
selectors are `CcipChainSelector`: `CcipChain.chain_selector`,
|
|
213
|
-
`CcipRouteConfig.chain_selector`, `CcipTokenLane.chain_selector`, the `MessageV1`,
|
|
214
|
-
`CcipMessageSent` and `Any2EVMMessage` selectors and `CcipRouter.get_off_ramps()`
|
|
215
|
-
carry it, and `chain_id_of_selector`, `CcipRouter.is_chain_supported` /
|
|
216
|
-
`get_on_ramp`, `CcipTokenPool.is_supported_chain` and `ccip_token_lane` take it
|
|
217
|
-
(`CcipChainSelector(4949039107694359620)`).
|
|
218
|
-
`encode_oft_compose_msg(*, nonce=, src_eid=, amount_ld=, compose_msg=)` takes
|
|
219
|
-
keyword arguments only. `ZERO_ADDRESS` is a `ChecksumAddress` (same value).
|
|
220
|
-
|
|
221
|
-
**Chain registry and removed aliases.** `CHAIN_NAMES`, `CHAIN_NAME_TO_ID`,
|
|
222
|
-
`SUPPORTED_CHAIN_IDS`, `GET_LOGS_RANGE_HINTS`, `BLOCK_EXPLORER_URLS`,
|
|
223
|
-
`MORPHO_BLUE_ADDRESSES`, `AAVE_V3_POOL`, `PUBLIC_ALLOCATOR_ADDRESSES` and
|
|
224
|
-
`HYPERCORE_PENDING_READERS` are keyed by `ChainId` (`HYPEREVM_CHAIN_ID` is one) and
|
|
225
|
-
`explorer.SUPPORTED_CHAINS` is a `frozenset[ChainId]`, so
|
|
226
|
-
`ensure_supported_chain(CHAIN_NAME_TO_ID["ethereum"])` type-checks;
|
|
227
|
-
`PUBLIC_ALLOCATOR_ADDRESSES` values are `ChecksumAddress`. Import `CHAIN_NAMES`,
|
|
228
|
-
`CHAIN_NAME_TO_ID`, `SUPPORTED_CHAIN_IDS`, `GET_LOGS_RANGE_HINTS`,
|
|
229
|
-
`PUBLIC_ALLOCATOR_ADDRESSES`, `HYPERCORE_PENDING_READERS`, `HYPEREVM_CHAIN_ID` and
|
|
230
|
-
`HYPERCORE_MARKET` from `ipor_fusion` (the HyperCore ones also from
|
|
231
|
-
`ipor_fusion.readers`). `BLOCK_EXPLORER_URLS`, `MORPHO_BLUE_ADDRESSES`,
|
|
232
|
-
`AAVE_V3_POOL` and `explorer.SUPPORTED_CHAINS` are internal: use
|
|
233
|
-
`morpho_blue_address(chain_id)` for Morpho Blue. `IporFusionMarkets.RWA`
|
|
234
|
-
is gone: market 50 is `IporFusionMarkets.EXTERNAL_STATE`. `MORPHO_BLUE_ADDRESS` is
|
|
235
|
-
gone: use `morpho_blue_address(chain_id)`.
|
|
236
|
-
|
|
237
|
-
**Markets and substrates.** Every `IporFusionMarkets` member is a `MarketId`.
|
|
238
|
-
`decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
|
|
239
|
-
|
|
240
|
-
**Access.** `AccessManager.has_role`, `grant_role`, `revoke_role`,
|
|
241
|
-
`read_accounts_with_role` take `RoleId | Roles`; `RoleAccount.role_id` is `RoleId`.
|
|
242
|
-
|
|
243
|
-
**Morpho.** `MorphoBlueMarketId` now always carries `0x`; build it with
|
|
244
|
-
`parse_morpho_market_id` instead of `MorphoBlueMarketId("<64 hex>")`. `fetch_market`,
|
|
245
|
-
`build_morpho_blue_market_json` take it; `MorphoApiMarket.market_id`,
|
|
246
|
-
`VaultV1MarketAllocation.market_id`, `VaultV2Cap.market_id`,
|
|
247
|
-
`MorphoPositionBreakdown.market_id` (as read from substrates) and the keys of
|
|
248
|
-
`VaultV1Info.public_allocator_flow_caps` are canonical. `fetch_market`, `fetch_vault`,
|
|
249
|
-
`morpho_blue_address`, `build_*_json` take `ChainId`; `fetch_vault` takes a
|
|
250
|
-
`ChecksumAddress`. `build_morpho_blue_market_json(*, market_id, chain_id, params,
|
|
251
|
-
state, rates, api_market=None, api_error=None, public_allocator=None)` takes every
|
|
252
|
-
argument by keyword, `public_allocator` as `ChecksumAddress | None`.
|
|
253
|
-
Every address on the `morpho_api` records is `ChecksumAddress`;
|
|
254
|
-
optional ones (`curator`, `guardian`, `fee_recipient`, `performance_fee_recipient`,
|
|
255
|
-
`management_fee_recipient`, `inner_vault`, `VaultFlowCap.admin`, the `VaultV2Cap`
|
|
256
|
-
market params) are `ChecksumAddress | None`.
|
|
257
|
-
|
|
258
|
-
**Crosschain.** The executor, dispatcher and factory wrappers, the lanes and
|
|
259
|
-
`StargateCrosschainCommandFuse.enter(request_id=)` take the crosschain ids above, and
|
|
260
|
-
their getters return them (`active_command`, `active_proposal_id`, `propose_balance`
|
|
261
|
-
on CCIP, `active_return` → `OperationId`, `asset_id`, `tracked_position_set_hash`,
|
|
262
|
-
`pending_request_by_chain`, `CrosschainLane.proposal_id_from_logs` /
|
|
263
|
-
`proposal_id_from_receipt`). Records carry them too: `Command`, `ActiveCommand`,
|
|
264
|
-
`ExecutorInitParams`, `BalanceObservation`, `LaneObservation`, `Observation`,
|
|
265
|
-
`CcipObservation`, `OutboundMessage`, `CcipMessageSent`, `Any2EVMMessage`,
|
|
266
|
-
`VerifierResult`, `Packet.guid`, `TaxiMessage`, `StargateChain`; and the helpers
|
|
267
|
-
`fetch_verifier_result(message_id=)`, `lz_receive_calldata(guid=)` and
|
|
268
|
-
`lz_compose_calldata(guid=)` take a `MessageId`. Decoded event values
|
|
269
|
-
(`ccip_events` → `CcipEvent.values`) stay a generic mapping: convert a value
|
|
270
|
-
before passing it on.
|
|
271
|
-
|
|
272
|
-
**HyperCore (preview).** The fuses, `HyperCoreSubstrates`, `HyperCoreReader`,
|
|
273
|
-
`read_hypercore_transfer_snapshot` and the helpers `system_address`, `precompile_index_of`
|
|
274
|
-
(3.x `read_index_of`, renamed: it reads nothing),
|
|
275
|
-
`perp_dex_of`, `is_native_perp_asset`, `is_hip3_asset` take the HyperCore ids;
|
|
276
|
-
`HyperCorePerpAssetInfo`, `HyperCorePerpMarket`, `HyperCoreSpotLeg`,
|
|
277
|
-
`HyperCorePendingState` and `HyperCoreTransferSnapshot` carry them.
|
|
278
|
-
`HyperCorePendingReader(vault, reader)` takes the vault's `PlasmaVaultAddress`, not a
|
|
279
|
-
`PlasmaVault` wrapper.
|
|
280
|
-
|
|
281
|
-
**External state.** `ExternalStateExecutor.proposal_hash` returns and `confirm_balance`
|
|
282
|
-
takes a `ProposalHash`; `BalanceProposal.proposal_hash` is a `ProposalHash`;
|
|
283
|
-
`ExternalStateEvent.transaction_hash` is a `TxHash`.
|
|
284
|
-
`ExternalStateVaultState.last_checked_custodian_timestamp`,
|
|
285
|
-
`BalanceFuseSnapshot.last_custodian_update_timestamp`, `BalanceProposal.proposed_at`,
|
|
286
|
-
`last_updated()` and `get_oldest_update_timestamp()` are web3's `Timestamp`, and
|
|
287
|
-
`ExternalStateExecutor.proposal_hash(proposed_at=)` takes one.
|
|
288
|
-
`ExternalStateExecutor.mark_nav` and `NavMark` are gone: send `propose_balance` and
|
|
289
|
-
`confirm_balance` from separate custodians, then
|
|
290
|
-
`PlasmaVault.update_markets_balances([IporFusionMarkets.EXTERNAL_STATE])`.
|
|
291
|
-
|
|
292
|
-
**Errors and explorer.** `TransactionError(tx_hash=)` takes a `TxHash`,
|
|
293
|
-
`LogScanError(address=)` a `ChecksumAddress`; `get_revert_reason` takes a `TxHash`;
|
|
294
|
-
`get_deployment_tx` returns a `TxHash`
|
|
295
|
-
(a malformed hash from Etherscan is reported as the error `malformed-tx-hash`).
|
|
296
|
-
|
|
297
|
-
**MCP response models** (Python constructors only; the JSON schemas are unchanged
|
|
298
|
-
except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
|
|
299
|
-
`MarketId`. Calling an MCP tool function directly from Python takes `ChainId`.
|
|
300
|
-
|
|
301
|
-
**Fuse base.** `Fuse`, `StakeFuse` and the shared validators and substrate packers
|
|
302
|
-
moved to `ipor_fusion.core.fuse`. Import `Fuse` and `StakeFuse` from `ipor_fusion`
|
|
303
|
-
or `ipor_fusion.fuses`. `ipor_fusion.fuses.base` still re-exports them in 4.0, but
|
|
304
|
-
it is a submodule of a public subpackage and therefore internal (see the README,
|
|
305
|
-
*Public API and stability*); so are the underscore validators and packers. Fuse equality and
|
|
306
|
-
hash now cover every attribute a fuse stores, not only its class and address (see
|
|
307
|
-
[Behavior changes](#behavior-changes)).
|
|
308
|
-
|
|
309
|
-
**Crosschain fuse encoders moved.** The crosschain market encoders moved from
|
|
310
|
-
`ipor_fusion.fuses.crosschain` into the crosschain package, and
|
|
311
|
-
`ipor_fusion.fuses` no longer re-exports them. There is no shim: the old paths
|
|
312
|
-
raise `ImportError`. The top-level names (`from ipor_fusion import ...`) are
|
|
313
|
-
unchanged.
|
|
314
|
-
|
|
315
|
-
| 3.x import | 4.0 import |
|
|
316
|
-
|---|---|
|
|
317
|
-
| `ipor_fusion.fuses.crosschain` | `ipor_fusion.crosschain.fuses` (or `ipor_fusion.crosschain`) |
|
|
318
|
-
| `ipor_fusion.fuses.crosschain.base`, `.ccip`, `.stargate` | `ipor_fusion.crosschain.fuses` (its submodules `base`, `ccip` and `stargate` exist but are internal) |
|
|
319
|
-
| `from ipor_fusion.fuses import CcipCrosschainSupplyFuse` (and the other crosschain encoders, `SendParams`, `CrosschainSubstrate*`, `crosschain_market_id`) | `from ipor_fusion.crosschain import ...` or `from ipor_fusion import ...` |
|
|
320
|
-
|
|
321
|
-
`ipor_fusion.crosschain` now exports the whole crosschain surface: besides the
|
|
322
|
-
codecs and wrappers, the encoders, `CrosschainLane`, `CcipLane`, `StargateLane`,
|
|
323
|
-
the transports and chains, `discover_deployment`, `open_lane(s)`, `LANES` and
|
|
324
|
-
`CrosschainSimulator`, which 3.x exported only from `ipor_fusion`.
|
|
325
|
-
`ipor_fusion.crosschain.ccip` and `.stargate` keep exporting only their codecs
|
|
326
|
-
and wrappers.
|
|
327
|
-
|
|
328
|
-
**Moved, still importable.** `FuseAction` lives in `ipor_fusion.core.fuse_action` and
|
|
329
|
-
`ZERO_ADDRESS` in `ipor_fusion.types`; import both from `ipor_fusion` (`FuseAction`
|
|
330
|
-
also from `ipor_fusion.fuses`, `ZERO_ADDRESS` from the public module
|
|
331
|
-
`ipor_fusion.types`). The internal `ipor_fusion.fuses.base` still re-exports them. The CLI's internal
|
|
332
|
-
`ipor_fusion.cli.vault_cmd` was split into `options`, `vault_info_view`,
|
|
333
|
-
`vault_markets_view` and `oracle_mapping_view` (private names, not API).
|
|
3
|
+
4.0 changes how every program calls the SDK, not only how type-checked code
|
|
4
|
+
spells its arguments. Start with the checklist below; the sections after it hold
|
|
5
|
+
the details, and each change is described once, in its section.
|
|
6
|
+
|
|
7
|
+
## Start here
|
|
8
|
+
|
|
9
|
+
> **A 3.x program fails when a line runs, not when it is imported.**
|
|
10
|
+
> `PlasmaVault(ctx, address)`, `ERC20(ctx, token)` or any other `Wrapper(ctx, ...)`
|
|
11
|
+
> raises `TypeError` only when that line executes, and so does `call.call()` without
|
|
12
|
+
> a context. A service that wraps its reads in a broad `except Exception` degrades
|
|
13
|
+
> silently (empty values, skipped vaults) instead of failing. Before you deploy:
|
|
14
|
+
>
|
|
15
|
+
> - search for wrappers built with a context (`(ctx,`, `(self.ctx,`, `(self._ctx,`)
|
|
16
|
+
> and for `.call()`, `.send()`, `.build_transaction()` and `.estimate_gas()` with no
|
|
17
|
+
> argument;
|
|
18
|
+
> - look for caches or containers of wrappers typed `Any` (or untyped): a type
|
|
19
|
+
> checker cannot see the calls made through them;
|
|
20
|
+
> - run the reads against a provider, not only the test suite.
|
|
21
|
+
|
|
22
|
+
Work through these in order; each is what breaks first at run time after the one
|
|
23
|
+
before it is fixed.
|
|
24
|
+
|
|
25
|
+
1. **Wrappers are built from an address; execution takes the context.**
|
|
26
|
+
`PlasmaVault(vault)`, `ERC20(token)`, `AccessManager(manager)`; then
|
|
27
|
+
`.call(ctx)`, `.send(ctx)`, `.build_transaction(ctx)`, `.estimate_gas(ctx)`. See
|
|
28
|
+
[Context-free wrappers](#context-free-wrappers).
|
|
29
|
+
2. **Methods that read the chain to answer are `read_*(ctx, ...)`**:
|
|
30
|
+
`vault.get_balance_fuses()` is `vault.read_balance_fuses(ctx)`,
|
|
31
|
+
`access_manager.owner()` is `read_owner(ctx)`. See
|
|
32
|
+
[Renamed methods](#renamed-methods).
|
|
33
|
+
3. **Pin a block with `ctx = ctx.at_block(n)`.** Assigning `ctx.default_block`
|
|
34
|
+
raises `AttributeError`. See [Vault reads and validation](#vault-reads-and-validation).
|
|
35
|
+
4. **The vault read is one snapshot.** `fetch_vault_data(ctx, vault, *, block=None,
|
|
36
|
+
lending=True, metadata=True)` takes the vault address and the chain from `ctx`;
|
|
37
|
+
`validate_vault_state(state)` and `build_vault_info_json(data, *,
|
|
38
|
+
resolve_name=None)` are pure. See [Vault reads and validation](#vault-reads-and-validation).
|
|
39
|
+
5. **Moved and removed names.** The crosschain encoders left
|
|
40
|
+
`ipor_fusion.fuses.crosschain` (no shim); `IporFusionMarkets.RWA`,
|
|
41
|
+
`MORPHO_BLUE_ADDRESS`, the 3.x cache functions, `read_erc20_holdings`,
|
|
42
|
+
`ExternalStateExecutor.mark_nav` and `NavMark` are gone; several functions take
|
|
43
|
+
their options by keyword or renamed a parameter. See
|
|
44
|
+
[Moved, removed and reshaped](#moved-removed-and-reshaped).
|
|
45
|
+
6. **Errors.** Every SDK exception derives from `IporFusionError`; some raises changed
|
|
46
|
+
class (empty call results, crosschain discovery, simulation, Morpho API). See
|
|
47
|
+
[Errors](#errors).
|
|
48
|
+
7. **Cache and request limit.** `Web3Context.from_url` bounds requests in flight at
|
|
49
|
+
8 (`max_concurrent_requests`; `InFlightLimit` to share a bound, `None` to lift
|
|
50
|
+
it), and the contract cache is injected per context (`NullCache()` keeps
|
|
51
|
+
nothing). See [Contract cache and request limit](#contract-cache-and-request-limit).
|
|
52
|
+
8. **Typed callers** (pyright, mypy, Pylance): ids, addresses and amounts are
|
|
53
|
+
nominal types. Wrap literals and raw values, `ChainId(8453)`, `Period(0)`,
|
|
54
|
+
`Amount(n)`, `parse_fuse_address(...)`; values the SDK returns already carry
|
|
55
|
+
their type. See [Nominal types](#nominal-types).
|
|
56
|
+
|
|
57
|
+
Then read [Behavior changes](#behavior-changes): they apply to every caller,
|
|
58
|
+
type-checked or not.
|
|
59
|
+
|
|
60
|
+
Addresses a user types in (CLI arguments, config files, forms): normalize first,
|
|
61
|
+
then parse strictly, `parse_plasma_vault_address(Web3.to_checksum_address(raw.strip()))`.
|
|
62
|
+
The `parse_*` address parsers reject hex without `0x` and surrounding whitespace;
|
|
63
|
+
`Web3.to_checksum_address` accepts hex without `0x` but not whitespace.
|
|
334
64
|
|
|
335
65
|
## Context-free wrappers
|
|
336
66
|
|
|
@@ -346,7 +76,8 @@ placeholder wrapper.
|
|
|
346
76
|
`LegacyPriceOracleMiddleware(oracle, Decimals(8))`, the crosschain executor,
|
|
347
77
|
dispatcher and factory wrappers, the Chainlink CCIP wrappers, the readers
|
|
348
78
|
(`MorphoReader(morpho)`, `AaveV3Reader(pool)`, ...). `HyperCoreReader()` takes no
|
|
349
|
-
argument
|
|
79
|
+
argument, and `HyperCorePendingReader(vault, reader)` takes the vault's
|
|
80
|
+
`PlasmaVaultAddress`, not a `PlasmaVault` wrapper.
|
|
350
81
|
- Execute a `Call` on a context: `.call(ctx, *, block=None)` (reads default to the
|
|
351
82
|
context's, possibly pinned, block), `.send(ctx)`, `.build_transaction(ctx, *,
|
|
352
83
|
block="latest")`, `.estimate_gas(ctx, *, block="latest")`. `ctx` may be passed by
|
|
@@ -401,6 +132,16 @@ placeholder wrapper.
|
|
|
401
132
|
| `call.send()` | `call.send(ctx)` |
|
|
402
133
|
| `call.build_transaction(block=b)`, `call.estimate_gas(block=b)` | `call.build_transaction(ctx, block=b)`, `call.estimate_gas(ctx, block=b)` |
|
|
403
134
|
| `call.ctx` | gone: keep the context yourself |
|
|
135
|
+
| `Multicall3(ctx, address)` | `Multicall3(ctx, address=address)` |
|
|
136
|
+
| `executor.pending_proposals(account)` | `executor.pending_proposals(account, chain_id=ctx.chain_id)` |
|
|
137
|
+
| `executor.parse_balance_proposed(log)` / `find_balance_proposed(logs)` | `...(log, chain_id=...)` / `...(logs, chain_id=...)` |
|
|
138
|
+
| `CcipLane(executor=, dispatcher=, spoke_chain_id=, ...)` | `CcipLane(hub_ctx=, spoke_ctx=, executor=, dispatcher=, ...)` (same for `StargateLane`) |
|
|
139
|
+
| `transport.executor(ctx, address)` / `transport.dispatcher(ctx, address)` | `transport.executor(address)` / `transport.dispatcher(address)` |
|
|
140
|
+
|
|
141
|
+
### Renamed methods
|
|
142
|
+
|
|
143
|
+
| 3.x | 4.0 |
|
|
144
|
+
|---|---|
|
|
404
145
|
| `PlasmaVault.get_balance_fuses()` | `PlasmaVault.read_balance_fuses(ctx)` |
|
|
405
146
|
| `PlasmaVault.withdraw_manager_address()` | `PlasmaVault.read_withdraw_manager_address(ctx)` |
|
|
406
147
|
| `PlasmaVault.price_oracle_address()` | `PlasmaVault.read_price_oracle_address(ctx)` |
|
|
@@ -417,13 +158,8 @@ placeholder wrapper.
|
|
|
417
158
|
| `AaveV3Reader.position_breakdown(asset, user)` | `AaveV3Reader.read_position_breakdown(ctx, asset, user)` |
|
|
418
159
|
| `AaveV3FuseReader.pool()` | `AaveV3FuseReader.read_pool(ctx)` |
|
|
419
160
|
| `PlasmaVault.read(target, data)` / `read_as(target, data, ...)` | `PlasmaVault.universal_read(target, data)` / `universal_read_decoded(target, data, ...)` (they build a Call; `read_*` names only methods that execute) |
|
|
420
|
-
| `read_index_of(asset)` | `precompile_index_of(asset)` |
|
|
161
|
+
| `read_index_of(asset)` | `precompile_index_of(asset)` (it reads nothing) |
|
|
421
162
|
| `ctx.default_block = n` | `ctx = ctx.at_block(n)` |
|
|
422
|
-
| `Multicall3(ctx, address)` | `Multicall3(ctx, address=address)` |
|
|
423
|
-
| `executor.pending_proposals(account)` | `executor.pending_proposals(account, chain_id=ctx.chain_id)` |
|
|
424
|
-
| `executor.parse_balance_proposed(log)` / `find_balance_proposed(logs)` | `...(log, chain_id=...)` / `...(logs, chain_id=...)` |
|
|
425
|
-
| `CcipLane(executor=, dispatcher=, spoke_chain_id=, ...)` | `CcipLane(hub_ctx=, spoke_ctx=, executor=, dispatcher=, ...)` (same for `StargateLane`) |
|
|
426
|
-
| `transport.executor(ctx, address)` / `transport.dispatcher(ctx, address)` | `transport.executor(address)` / `transport.dispatcher(address)` |
|
|
427
163
|
|
|
428
164
|
A service that caches wrappers per chain can now share one wrapper across chains and
|
|
429
165
|
snapshots; what it must not share is a `Call` result decoded with a chain-bound
|
|
@@ -431,105 +167,136 @@ argument (`chain_id=`) for another chain. An external signer still needs the rea
|
|
|
431
167
|
target address (`call.to`) and builds the chain-qualified transaction itself, or
|
|
432
168
|
gets it from `call.build_transaction(ctx)`.
|
|
433
169
|
|
|
434
|
-
##
|
|
170
|
+
## Vault reads and validation
|
|
435
171
|
|
|
436
|
-
|
|
437
|
-
`ContractCache`, injected instead of always written to `~/.cache`:
|
|
172
|
+
The vault read is one pinned snapshot, and the chain is always the context's:
|
|
438
173
|
|
|
439
|
-
- `
|
|
440
|
-
(
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
`
|
|
445
|
-
`
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
`
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
`
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
`
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
`
|
|
174
|
+
- `fetch_vault_data(ctx, vault, *, block=None, lending=True, metadata=True)` takes
|
|
175
|
+
the vault address (not a `PlasmaVault`) and no `chain_id`. It resolves one block
|
|
176
|
+
(`block`, else a numeric `ctx.default_block`, else the latest) and runs every
|
|
177
|
+
read, log scans included, on `ctx.at_block(block)`; `ctx` is not modified. In 3.x
|
|
178
|
+
`block_number` only selected the block reported in `VaultData`, and the reads ran
|
|
179
|
+
wherever `ctx` pointed. The old `chain_id=0` switch is `lending=False`;
|
|
180
|
+
`metadata=False` skips the display metadata: `token_symbols` and `token_decimals`
|
|
181
|
+
stay empty and the ERC20 holdings' symbols are `"?"`. The underlying asset's
|
|
182
|
+
symbol and decimals and the decimals the valuation needs are still read. With
|
|
183
|
+
`metadata=True` the holdings are named by the same cached, batched read as
|
|
184
|
+
`token_symbols`. `VaultData` gains `vault`, `chain_id`, `market_balances`,
|
|
185
|
+
`erc20`, `token_symbols` and `token_decimals`; `WithdrawManagerData` gains
|
|
186
|
+
`shares_to_release_assets` and `pending_request_assets`.
|
|
187
|
+
- `validate_vault(ctx, vault, *, block=None, metadata=False)` fetches (by default
|
|
188
|
+
without display metadata, so its ERC20 holdings are named `"?"`) and validates; it
|
|
189
|
+
no longer takes `resolve_symbol`. `metadata=True` names the ERC20 holdings in the
|
|
190
|
+
result and its findings. `validate_vault_state(state)` is pure: a custom
|
|
191
|
+
`VaultState` also provides `vault`, `chain_id`, `market_balances`, `erc20` and the
|
|
192
|
+
queue's `shares_to_release_assets`. `read_erc20_holdings` is gone: the holdings are
|
|
193
|
+
`VaultData.erc20`.
|
|
194
|
+
- `build_vault_info_json(data, *, resolve_name=None)` renders from the snapshot and
|
|
195
|
+
reads nothing; the chain and its label come from `data.chain_id`. Without
|
|
196
|
+
`resolve_name` it names no contract (see
|
|
197
|
+
[Contract cache and request limit](#contract-cache-and-request-limit)).
|
|
198
|
+
- `fetch_deployment_info(ctx, vault, *, api_key=None)` and
|
|
199
|
+
`fetch_vault_lending_health(ctx, vault, *, balance_fuse_market_ids,
|
|
200
|
+
market_substrates, aave_pools=None)` take the chain from `ctx`.
|
|
201
|
+
- `Web3Context.at_block(block)` returns a pinned copy (same connection, signer, log
|
|
202
|
+
source, cache and scan bounds). `Web3Context.default_block` is read-only:
|
|
203
|
+
`ctx.default_block = n` raises `AttributeError`; the old setter changed the block
|
|
204
|
+
under every thread sharing the context. `Web3Context.get_logs(to_block=None)` ends
|
|
205
|
+
at the context's default block and `Web3Context.get_block(block=None)` returns it,
|
|
206
|
+
both instead of `"latest"`, so a pinned context never sees later logs or blocks.
|
|
207
|
+
- `VaultData.price_oracle_addr` is renamed `price_oracle`, like its sibling address
|
|
208
|
+
fields (a custom `VaultState` renames it too).
|
|
465
209
|
|
|
466
|
-
|
|
467
|
-
`symbol`, `decimals`, `deployment_block`, `deployment_timestamp`), string values. On
|
|
468
|
-
load, a 3.x contract name (`<chain_id>:<address>`) is kept as that chain's `name`;
|
|
469
|
-
3.x token entries (`symbol:<address>`, `decimals:<address>`) name no chain and are
|
|
470
|
-
discarded, so symbols and decimals are read again once per chain. `deployment_cache.json`
|
|
471
|
-
is no longer read: a vault's deployment block is resolved again (it needs an
|
|
472
|
-
Etherscan key) and the old file can be deleted.
|
|
210
|
+
## Moved, removed and reshaped
|
|
473
211
|
|
|
474
|
-
**
|
|
212
|
+
**Public API.** 4.0 declares it: `ipor_fusion.__all__`, which now also exports the
|
|
213
|
+
names earlier reachable only by module path (`FusionFactory`, `FusionInstance`,
|
|
214
|
+
`fetch_market`, `resolve_token_symbol`, the crosschain codecs, ...). Import from
|
|
215
|
+
`ipor_fusion`: a module path outside the README's list (*Public API and stability*)
|
|
216
|
+
still works but is internal. Every project type a public signature or field uses is
|
|
217
|
+
exported too, among them `AaveV3PositionBreakdown`, `AaveV3ReserveTokens`,
|
|
218
|
+
`AccountRequest`, `StateOverrideProvider`, `TransferDirection` and the validation
|
|
219
|
+
vocabulary `Severity`, `Verdict` and `Check`. Also new: `InFlightLimit` (see
|
|
220
|
+
[Contract cache and request limit](#contract-cache-and-request-limit)) and
|
|
221
|
+
`log_bytes`, the hex-or-bytes log field helper the crosschain decoders use.
|
|
475
222
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
- A write merges with the file as it is on disk and replaces it atomically (a
|
|
482
|
-
temporary file in the same directory, then `os.replace`), so a crash or two CLI/MCP
|
|
483
|
-
processes writing at once never leave a torn file; one of two concurrent writers may
|
|
484
|
-
lose its newest entry, which is read again later.
|
|
485
|
-
- A cache never fails a read: a corrupt or unreadable file reads as empty (logged
|
|
486
|
-
warning from `ipor_fusion.cache`) and is rewritten by the next write; an unwritable
|
|
487
|
-
directory (read-only file system) keeps the entries in memory for the process; a
|
|
488
|
-
custom cache that raises is treated as a miss, and so is a stored number that does
|
|
489
|
-
not parse.
|
|
490
|
-
- `fetch_deployment_info` returns `rpc-fetch-failed` only when the creation
|
|
491
|
-
transaction or its block cannot be read; a cache failure is no longer reported as
|
|
492
|
-
one (the deployment block is returned).
|
|
223
|
+
**Crosschain fuse encoders moved.** The crosschain market encoders moved from
|
|
224
|
+
`ipor_fusion.fuses.crosschain` into the crosschain package, and
|
|
225
|
+
`ipor_fusion.fuses` no longer re-exports them. There is no shim: the old paths
|
|
226
|
+
raise `ImportError`. The top-level names (`from ipor_fusion import ...`) are
|
|
227
|
+
unchanged.
|
|
493
228
|
|
|
494
|
-
|
|
229
|
+
| 3.x import | 4.0 import |
|
|
230
|
+
|---|---|
|
|
231
|
+
| `ipor_fusion.fuses.crosschain` | `ipor_fusion.crosschain.fuses` (or `ipor_fusion.crosschain`) |
|
|
232
|
+
| `ipor_fusion.fuses.crosschain.base`, `.ccip`, `.stargate` | `ipor_fusion.crosschain.fuses` (its submodules `base`, `ccip` and `stargate` exist but are internal) |
|
|
233
|
+
| `from ipor_fusion.fuses import CcipCrosschainSupplyFuse` (and the other crosschain encoders, `SendParams`, `CrosschainSubstrate*`, `crosschain_market_id`) | `from ipor_fusion.crosschain import ...` or `from ipor_fusion import ...` |
|
|
495
234
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
`
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
-
|
|
532
|
-
|
|
235
|
+
`ipor_fusion.crosschain` now exports the whole crosschain surface: besides the
|
|
236
|
+
codecs and wrappers, the encoders, `CrosschainLane`, `CcipLane`, `StargateLane`,
|
|
237
|
+
the transports and chains, `discover_deployment`, `open_lane(s)`, `LANES` and
|
|
238
|
+
`CrosschainSimulator`, which 3.x exported only from `ipor_fusion`.
|
|
239
|
+
`ipor_fusion.crosschain.ccip` and `.stargate` keep exporting only their codecs
|
|
240
|
+
and wrappers.
|
|
241
|
+
|
|
242
|
+
**Fuse base.** `Fuse`, `StakeFuse` and the shared validators and substrate packers
|
|
243
|
+
moved to `ipor_fusion.core.fuse`. Import `Fuse` and `StakeFuse` from `ipor_fusion`
|
|
244
|
+
or `ipor_fusion.fuses`. `ipor_fusion.fuses.base` still re-exports them in 4.0, but
|
|
245
|
+
it is a submodule of a public subpackage and therefore internal (see the README,
|
|
246
|
+
*Public API and stability*); so are the underscore validators and packers.
|
|
247
|
+
`FuseAction` lives in `ipor_fusion.core.fuse_action` and `ZERO_ADDRESS` in
|
|
248
|
+
`ipor_fusion.types`; import both from `ipor_fusion` (`FuseAction` also from
|
|
249
|
+
`ipor_fusion.fuses`, `ZERO_ADDRESS` from the public module `ipor_fusion.types`).
|
|
250
|
+
The internal `ipor_fusion.fuses.base` still re-exports them. The CLI's internal
|
|
251
|
+
`ipor_fusion.cli.vault_cmd` was split into `options`, `vault_info_view`,
|
|
252
|
+
`vault_markets_view` and `oracle_mapping_view` (private names, not API).
|
|
253
|
+
|
|
254
|
+
**Removed.**
|
|
255
|
+
|
|
256
|
+
- `IporFusionMarkets.RWA`: market 50 is `IporFusionMarkets.EXTERNAL_STATE`.
|
|
257
|
+
- `MORPHO_BLUE_ADDRESS`: use `morpho_blue_address(chain_id)`. `BLOCK_EXPLORER_URLS`,
|
|
258
|
+
`MORPHO_BLUE_ADDRESSES`, `AAVE_V3_POOL` and `explorer.SUPPORTED_CHAINS` are
|
|
259
|
+
internal. Import `CHAIN_NAMES`, `CHAIN_NAME_TO_ID`, `SUPPORTED_CHAIN_IDS`,
|
|
260
|
+
`GET_LOGS_RANGE_HINTS`, `PUBLIC_ALLOCATOR_ADDRESSES`, `HYPERCORE_PENDING_READERS`,
|
|
261
|
+
`HYPEREVM_CHAIN_ID` and `HYPERCORE_MARKET` from `ipor_fusion` (the HyperCore ones
|
|
262
|
+
also from `ipor_fusion.readers`).
|
|
263
|
+
- `read_erc20_holdings`: the holdings are `VaultData.erc20`.
|
|
264
|
+
- `ExternalStateExecutor.mark_nav` and `NavMark`: send `propose_balance` and
|
|
265
|
+
`confirm_balance` from separate custodians, then
|
|
266
|
+
`PlasmaVault.update_markets_balances([IporFusionMarkets.EXTERNAL_STATE])`.
|
|
267
|
+
- The 3.x cache functions and constants (`load_contract_cache`, ...): see
|
|
268
|
+
[Contract cache and request limit](#contract-cache-and-request-limit).
|
|
269
|
+
- `ContractWrapper.encoder()` and `Call.ctx`: see
|
|
270
|
+
[Context-free wrappers](#context-free-wrappers).
|
|
271
|
+
|
|
272
|
+
**Options by keyword and renamed parameters.** A positional call to one of these
|
|
273
|
+
fails at run time:
|
|
274
|
+
|
|
275
|
+
- `Web3Context(web3, chain_id, *, ...)` and `Web3Context.from_url(url, *, ...)` take
|
|
276
|
+
their options by keyword.
|
|
277
|
+
- `VaultSimulator(web3, *, vault=None, alpha=None, block=...)` takes every argument
|
|
278
|
+
after `web3` by keyword, and `vault` and `alpha` are optional: a simulator that only
|
|
279
|
+
observes, deploys or sends `execute_call(call, from_=...)` needs neither, so drop a
|
|
280
|
+
zero-address placeholder. `execute()` without `vault` and `alpha` raises
|
|
281
|
+
`ValueError`, as does `execute_call` without `from_` or `alpha`.
|
|
282
|
+
- `fetch_unpriceable_priced_tokens(ctx, *, price_oracle, underlying, balance_fuses,
|
|
283
|
+
market_substrates, morpho_positions)` takes everything after `ctx` by keyword.
|
|
284
|
+
- `get_contract_name(chain_id, address, *, api_key=None, cache=None)` and
|
|
285
|
+
`get_deployment_tx(chain_id, address, *, api_key=None)` take `api_key` by keyword.
|
|
286
|
+
- `build_morpho_blue_market_json(*, market_id, chain_id, params, state, rates,
|
|
287
|
+
api_market=None, api_error=None, public_allocator=None)` takes every argument by
|
|
288
|
+
keyword.
|
|
289
|
+
- `encode_oft_compose_msg(*, nonce=, src_eid=, amount_ld=, compose_msg=)` takes
|
|
290
|
+
keyword arguments only.
|
|
291
|
+
- The vault parameter is named `vault` throughout: `validate_vault`,
|
|
292
|
+
`fetch_deployment_info`, `fetch_vault_lending_health`, `build_oracle_mapping`,
|
|
293
|
+
`read_external_state`, `ExternalStateExecutor.for_vault`, `read_hypercore_nav`,
|
|
294
|
+
`read_hypercore_vault_state`, `read_hypercore_transfer_snapshot`,
|
|
295
|
+
`read_hypercore_evm_exit_ceiling` and `resolve_access_manager` renamed
|
|
296
|
+
`vault_address` to `vault`; only a call that passes it by keyword changes.
|
|
297
|
+
- `FusionInstance.from_dict(data)` rebuilds a stored instance (the
|
|
298
|
+
`dataclasses.asdict` form) with every field parsed; use it instead of
|
|
299
|
+
`FusionInstance(**data)`, which passes raw strings into the role-typed fields.
|
|
533
300
|
|
|
534
301
|
## Errors
|
|
535
302
|
|
|
@@ -559,7 +326,7 @@ is the wrong one.
|
|
|
559
326
|
- `AccessManager.read_owner(ctx)` raises the new `OwnerNotFoundError` (a
|
|
560
327
|
`LookupError`) when no account holds `OWNER_ROLE`, instead of an `IndexError`.
|
|
561
328
|
- `EmptyCallResultError` is no longer an eth_abi `InsufficientDataBytes` (nor a
|
|
562
|
-
`DecodingError`). Code that caught either around `.call()` to tolerate a missing
|
|
329
|
+
`DecodingError`). Code that caught either around `.call(ctx)` to tolerate a missing
|
|
563
330
|
contract or getter must catch `EmptyCallResultError`; return data that is present
|
|
564
331
|
but too short still raises eth_abi's error.
|
|
565
332
|
- Crosschain discovery (`discover_deployment`, `discover_lane_fuses`,
|
|
@@ -576,9 +343,20 @@ is the wrong one.
|
|
|
576
343
|
- `VaultSimulator.run` (provider error from `eth_simulateV1`, or a result count that
|
|
577
344
|
does not match the queued calls) and `CrosschainSimulator.relay` (not settled within
|
|
578
345
|
`max_rounds`) raise the new `SimulationRunError`, still a `RuntimeError`.
|
|
579
|
-
- The Morpho API client raises `MorphoApiError` for every
|
|
580
|
-
|
|
581
|
-
|
|
346
|
+
- The Morpho API client raises `MorphoApiError` for every answer it cannot use and
|
|
347
|
+
every transport failure, never `KeyError`, `TypeError` or `ValueError`:
|
|
348
|
+
- a required market id or address (market and V1 allocation ids, V1 flow-cap ids,
|
|
349
|
+
vault/asset/owner/token/oracle/IRM/allocator addresses) that is absent or
|
|
350
|
+
invalid; an optional address (curator, guardian, fee recipients, ...) that is
|
|
351
|
+
absent or `""` reads as `None`, and raises only when present and invalid;
|
|
352
|
+
- any other answer it cannot read (a missing key such as a `loanAsset` without
|
|
353
|
+
`address`, a value of the wrong type, a body that is not a JSON object);
|
|
354
|
+
- any `OSError` or `http.client.HTTPException` while fetching (it caught only
|
|
355
|
+
`URLError` and `TimeoutError` before).
|
|
356
|
+
|
|
357
|
+
When matching a market's own allocation and flow cap, an id that does not parse
|
|
358
|
+
matches nothing, and a V1 flow cap without an id is skipped, as before.
|
|
359
|
+
`fusion market meta-morpho --market` rejects a malformed id.
|
|
582
360
|
- The MCP `market_meta_morpho` tool lets `MorphoApiError` propagate instead of
|
|
583
361
|
re-raising it as `ValueError`. MCP clients see the same tool error message; only a
|
|
584
362
|
direct Python call of the tool function sees the new type.
|
|
@@ -614,3 +392,337 @@ is the wrong one.
|
|
|
614
392
|
- `quote_ccip_native_fee` raises `SimulationError` (label `"quote"`, with
|
|
615
393
|
`revert_reason`) instead of `ValueError` when the call reverts before the CCIP fee
|
|
616
394
|
check. A call that succeeds without sending a message is still a `ValueError`.
|
|
395
|
+
- `cache_key(chain_id, address, field)` raises `ValueError` for an address that is
|
|
396
|
+
not 20 bytes of hex, so `get_contract_name` does too.
|
|
397
|
+
|
|
398
|
+
The rule covers what the SDK itself raises. Errors from web3, the provider or the
|
|
399
|
+
HTTP transport while `Call.call(ctx)`, `.send(ctx)` or a `Web3Context` method runs
|
|
400
|
+
are not wrapped in an `IporFusionError`; they propagate as web3 raises them (a log
|
|
401
|
+
scan that cannot complete raises `LogScanError` instead of returning partial
|
|
402
|
+
logs).
|
|
403
|
+
|
|
404
|
+
## Contract cache and request limit
|
|
405
|
+
|
|
406
|
+
**Request limit.** `Web3Context.from_url(..., max_concurrent_requests=8)` bounds the
|
|
407
|
+
RPC requests in flight across every thread using the context (`None` lifts the
|
|
408
|
+
bound); the vault reads fan out on nested thread pools, and the bound is what a
|
|
409
|
+
rate-limited provider sees. An `int` bounds one context. To bound several contexts on
|
|
410
|
+
one provider (a service that builds a context per request or per scan), create one
|
|
411
|
+
`InFlightLimit(n)` per provider and pass it as `max_concurrent_requests=`: every
|
|
412
|
+
context built with it shares the same `n` slots. A context built from your own
|
|
413
|
+
`Web3` object is not bounded unless you add the limit to it: `limit.install(web3)`
|
|
414
|
+
(innermost, as `from_url` does) or
|
|
415
|
+
`web3.middleware_onion.inject(limit.middleware, layer=0)`.
|
|
416
|
+
|
|
417
|
+
**Contract cache.** Contract names, token symbols and decimals and deployment blocks
|
|
418
|
+
are cached in a `ContractCache`, injected instead of always written to `~/.cache`:
|
|
419
|
+
|
|
420
|
+
- `Web3Context(..., cache=...)` and `Web3Context.from_url(..., cache=...)` take one
|
|
421
|
+
(keyword-only); `ctx.cache` returns it, and `ctx.at_block(...)` keeps it. The
|
|
422
|
+
default (`None`) is `default_contract_cache()`, a `FileContractCache` at
|
|
423
|
+
`$XDG_CACHE_HOME/ipor-fusion/contract_cache.json` (`~/.cache/...` when unset),
|
|
424
|
+
resolved when used rather than at import. `NullCache()` keeps nothing;
|
|
425
|
+
`FileContractCache(path)` moves the file; any object with `get(keys)` and
|
|
426
|
+
`put(entries)` over `CacheKey(chain_id, address, field)` → `str` fits the
|
|
427
|
+
protocol (for example a shared store in a service).
|
|
428
|
+
- `resolve_token_symbol`, `resolve_token_decimals`, the snapshot's token metadata
|
|
429
|
+
(`fetch_vault_data(metadata=True)`) and `fetch_deployment_info` use `ctx.cache`.
|
|
430
|
+
`get_contract_name(chain_id, address, *, api_key=None, cache=None)` takes the cache
|
|
431
|
+
explicitly (default: the file cache); pass `cache=ctx.cache` to share the
|
|
432
|
+
context's policy. The CLI's text and JSON output name contracts through the
|
|
433
|
+
context's cache.
|
|
434
|
+
- `build_vault_info_json(data)` without `resolve_name` names no contract (every
|
|
435
|
+
`contract` is `"?"`) instead of reading names from the on-disk cache; pass
|
|
436
|
+
`resolve_name=partial(get_contract_name, chain_id, api_key=..., cache=ctx.cache)`
|
|
437
|
+
for the CLI's document.
|
|
438
|
+
- `ipor_fusion.cache` is a public module now and exports `ContractCache`,
|
|
439
|
+
`FileContractCache`, `NullCache`, `CacheKey`, `CacheField`, `cache_key` and
|
|
440
|
+
`default_contract_cache` (also from `ipor_fusion`). Its 3.x functions and constants
|
|
441
|
+
are gone: `load_contract_cache`, `save_contract_cache`, `update_contract_cache`,
|
|
442
|
+
`load_deployment_cache`, `update_deployment_cache`, `CACHE_DIR`, `CACHE_FILE` and
|
|
443
|
+
`DEPLOYMENT_CACHE_FILE`.
|
|
444
|
+
|
|
445
|
+
**File format.** One file, keys `<chain_id>:<lowercase address>:<field>` (`name`,
|
|
446
|
+
`symbol`, `decimals`, `deployment_block`, `deployment_timestamp`), string values. On
|
|
447
|
+
load, a 3.x contract name (`<chain_id>:<address>`) is kept as that chain's `name`;
|
|
448
|
+
3.x token entries (`symbol:<address>`, `decimals:<address>`) name no chain and are
|
|
449
|
+
discarded, so symbols and decimals are read again once per chain. `deployment_cache.json`
|
|
450
|
+
is no longer read: a vault's deployment block is resolved again (it needs an
|
|
451
|
+
Etherscan key) and the old file can be deleted.
|
|
452
|
+
|
|
453
|
+
**Cache behavior.**
|
|
454
|
+
|
|
455
|
+
- The same address on two chains has two entries: a token's symbol or decimals read
|
|
456
|
+
on one chain is never served for another.
|
|
457
|
+
- Absence is not cached: `resolve_token_symbol` still returns `"no contract"` for an
|
|
458
|
+
address without code, but no longer stores it, so a token deployed later (or seen at
|
|
459
|
+
a later block) resolves.
|
|
460
|
+
- A write merges with the file as it is on disk and replaces it atomically (a
|
|
461
|
+
temporary file in the same directory, then `os.replace`), so a crash or two CLI/MCP
|
|
462
|
+
processes writing at once never leave a torn file; one of two concurrent writers may
|
|
463
|
+
lose its newest entry, which is read again later.
|
|
464
|
+
- A cache never fails a read: a corrupt or unreadable file reads as empty (logged
|
|
465
|
+
warning from `ipor_fusion.cache`) and is rewritten by the next write; an unwritable
|
|
466
|
+
directory (read-only file system) keeps the entries in memory for the process; a
|
|
467
|
+
custom cache that raises is treated as a miss, and so is a stored number that does
|
|
468
|
+
not parse.
|
|
469
|
+
- `fetch_deployment_info` returns `rpc-fetch-failed` only when the creation
|
|
470
|
+
transaction or its block cannot be read; a cache failure is no longer reported as
|
|
471
|
+
one (the deployment block is returned).
|
|
472
|
+
|
|
473
|
+
## Nominal types
|
|
474
|
+
|
|
475
|
+
4.0 gives chain, market and role ids, the crosschain, HyperCore and Morpho ids,
|
|
476
|
+
vault/oracle/token addresses, amounts and transaction hashes a nominal type
|
|
477
|
+
(`typing.NewType`) across the public API, and checks the SDK, the examples and a
|
|
478
|
+
typed client with pyright's `reportArgumentType`. Other fields keep their types
|
|
479
|
+
(e.g. symbols, names and URLs stay `str`). The wrappers do not change values:
|
|
480
|
+
`ChainId(1)` is `1` at runtime, so untyped code needs none of this section. Values
|
|
481
|
+
the SDK returns already carry their type and go back in unwrapped.
|
|
482
|
+
|
|
483
|
+
The SDK's own types are importable from `ipor_fusion` (and the public module
|
|
484
|
+
`ipor_fusion.types`), as are `USDC_TOKEN_INDEX` and `hip3_action_asset`. Third-party
|
|
485
|
+
types are not re-exported: `ChecksumAddress` and `HexAddress` come from `eth_typing`,
|
|
486
|
+
and `BlockIdentifier`, `Timestamp` and `TxParams` from `web3.types`. The wrappers
|
|
487
|
+
label values; they do not validate byte widths or address spelling.
|
|
488
|
+
|
|
489
|
+
### Building the types
|
|
490
|
+
|
|
491
|
+
| Type | From a literal or raw value |
|
|
492
|
+
|---|---|
|
|
493
|
+
| `ChainId` | `ChainId(1)` |
|
|
494
|
+
| `MarketId` | an `IporFusionMarkets` member, or `MarketId(n)` |
|
|
495
|
+
| `RoleId` | a `Roles` member (accepted as is), or `RoleId(n)`; `Roles.resolve(name)` returns one |
|
|
496
|
+
| `Decimals`, `Period`, `Amount`, `Shares`, ... | `Decimals(8)`, `Period(0)`, `Amount(n)`, `Shares(n)` |
|
|
497
|
+
| `ChecksumAddress` | `Web3.to_checksum_address(value)` |
|
|
498
|
+
| `HexAddress` | any `ChecksumAddress`, or `parse_hex_address(value)` (20 bytes or `0x`/`0X`-prefixed hex in any case → lowercase `0x` form) |
|
|
499
|
+
| `MorphoBlueMarketId` | `parse_morpho_market_id(value)`: 32 bytes or hex in any spelling, always `0x` + 64 lowercase digits |
|
|
500
|
+
| `TxHash` | `parse_tx_hash(value)` (32 bytes or `0x`/`0X`-prefixed hex in any case) |
|
|
501
|
+
| `PlasmaVaultAddress`, `FuseAddress`, `AccessManagerAddress` | from the SDK (`PlasmaVault.address`, `get_fuses()`, `get_access_manager_address()`, `FusionInstance`, discovery), or `parse_plasma_vault_address` / `parse_fuse_address` / `parse_access_manager_address(value)` for a raw address; they label the role and check nothing on chain |
|
|
502
|
+
| crosschain ids | `CommandId(b)`, `MessageId(b)`, `AssetId(b)`, `OperationId(b)`, `TransferId(b)`, `ReturnRequestId(b)`, `PositionSetHash(b)` (bytes32); `ProposalId(n)`, `DeploymentRequestId(n)`, `StargateAssetId(n)`, `LayerZeroEid(n)`, `CcipChainSelector(n)` (int) |
|
|
503
|
+
| HyperCore ids | `HyperCoreTokenIndex(n)` (or `USDC_TOKEN_INDEX`), `HyperCoreAsset(n)` (or `hip3_action_asset(dex, index)`), `HyperCoreOrderId(n)`, `HyperCoreClientOrderId(n)`, `MarginTableId(n)`, `HyperCoreActionId(n)`, `PayloadHash(b)` |
|
|
504
|
+
| external state | `ProposalHash(b)` |
|
|
505
|
+
| `BlockIdentifier` | a block number, `"latest"` / `"pending"` / ..., or a block hash |
|
|
506
|
+
|
|
507
|
+
The address and hash parsers (`parse_hex_address`, `parse_tx_hash`,
|
|
508
|
+
`parse_plasma_vault_address`, `parse_fuse_address`, `parse_access_manager_address`)
|
|
509
|
+
validate strictly: a string needs exactly one `0x`/`0X` prefix and the exact number of
|
|
510
|
+
hex digits, with no surrounding whitespace; anything else raises `ValueError`.
|
|
511
|
+
Normalize user input first (see [Start here](#start-here)).
|
|
512
|
+
`parse_morpho_market_id` still accepts hex without `0x`.
|
|
513
|
+
|
|
514
|
+
`MessageId` covers both a CCIP `messageId` and a LayerZero `guid`. `AssetId` is the
|
|
515
|
+
crosschain executor's `bytes32` asset id; a Stargate pool's `uint16` asset id is
|
|
516
|
+
`StargateAssetId`. `DeploymentRequestId` is a factory's `uint256` dispatcher request;
|
|
517
|
+
a `bytes32` return (recall) request is `ReturnRequestId`.
|
|
518
|
+
|
|
519
|
+
### Typed signatures, by area
|
|
520
|
+
|
|
521
|
+
**Units.** `VaultData.total_assets`, `underlying_balance_on_vault` and
|
|
522
|
+
`market_balances` values are `Amount`; `total_supply` and `supply_cap` are `Shares`;
|
|
523
|
+
`share_decimals` / `asset_decimals` are `Decimals`; `block_timestamp` and
|
|
524
|
+
`deployment_timestamp` are `Timestamp`. `FeeData` fee fields are `Fee`,
|
|
525
|
+
`management_fee_last_update` a `Timestamp`, `unrealized_management_fee` an `Amount`;
|
|
526
|
+
`WithdrawManagerData.withdraw_window` is a `Period`, its fees `Fee`,
|
|
527
|
+
`shares_to_release` `Shares`, `last_release_funds_timestamp` a `Timestamp` and the
|
|
528
|
+
converted amounts `Amount`. A custom `VaultState` annotates the same.
|
|
529
|
+
`VaultValidation.block_timestamp` is a `Timestamp` and `asset_decimals` `Decimals`;
|
|
530
|
+
`Erc20Holding.decimals` / `balance` are `Decimals | None` / `Amount | None` and
|
|
531
|
+
`Erc20Holdings.value_in_asset` / `market_cached_value` `Amount`;
|
|
532
|
+
`VaultData.token_decimals` values and `resolve_token_decimals` are `Decimals | None`.
|
|
533
|
+
`PlasmaVault.total_supply()`, `balance_of()` and `get_total_supply_cap()` return
|
|
534
|
+
`Shares` (vault shares), and `transfer` / `approve` / `transfer_from` /
|
|
535
|
+
`set_total_supply_cap` take `Shares`; `ERC20` keeps `Amount`.
|
|
536
|
+
|
|
537
|
+
**Vault data and helpers.** `ensure_supported_chain` takes `ChainId`.
|
|
538
|
+
`fetch_unpriceable_priced_tokens` takes the oracle and underlying as
|
|
539
|
+
`ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
|
|
540
|
+
`get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
|
|
541
|
+
fits), and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
|
|
542
|
+
typed for `str` still fits). Market-keyed data is keyed by `MarketId`:
|
|
543
|
+
`VaultData.market_substrates`, `dependency_graph`, `morpho_positions`,
|
|
544
|
+
`aave_positions`, `market_balances`; the `VaultState` protocol,
|
|
545
|
+
`fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens` and the
|
|
546
|
+
`dependency_graph` helpers (`compute_update_reach`, `compute_update_groups`,
|
|
547
|
+
`find_orphan_fuse_markets`, `find_markets_missing_erc20_dependency`).
|
|
548
|
+
`find_orphan_fuse_markets` returns `dict[MarketId, list[FuseAddress]]`, so its fuses
|
|
549
|
+
go into `PlasmaVault.remove_fuses` as they are.
|
|
550
|
+
|
|
551
|
+
`VaultData.asset`, `price_oracle`, `rewards_manager`, `withdraw_manager`,
|
|
552
|
+
`FeeData.fee_manager` / `ipor_dao_fee_recipient` and `VaultValidation.vault` /
|
|
553
|
+
`asset` are `ChecksumAddress` (`VaultValidation.vault` a `PlasmaVaultAddress`);
|
|
554
|
+
`VaultData.access_manager` is an `AccessManagerAddress`, `fuses` / `instant_fuses`
|
|
555
|
+
and the `fuse_markets` keys are `FuseAddress`, `balance_fuses` is
|
|
556
|
+
`list[BalanceFuse]`, and `token_prices_usd` is keyed by lowercase `HexAddress`. A
|
|
557
|
+
custom `VaultState` implementation annotates `asset` and `price_oracle` as
|
|
558
|
+
`ChecksumAddress`, `fuse_markets` keys as `FuseAddress`, and its
|
|
559
|
+
`unpriceable_priced_tokens` items' `market_id` as `MarketId` (`UnpriceableToken`).
|
|
560
|
+
The Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient` are
|
|
561
|
+
`ChecksumAddress`. `VaultValidation.chain_id` is `ChainId`;
|
|
562
|
+
`LendingMarketHealth.market_id` and `MiddlewarePricedToken.market_id` are
|
|
563
|
+
`MarketId`; `Erc20Holding.address` is `HexAddress`, and `SubstrateInfo.address` is
|
|
564
|
+
`HexAddress | None`. `LegacyPriceOracleMiddleware` takes `base_currency_decimals:
|
|
565
|
+
Decimals`, and `PriceOracleMiddleware.base_currency_decimals()` returns
|
|
566
|
+
`Call[Decimals]`. `get_logs_adaptive(chain_id=)` and `LogSource.get_logs` take
|
|
567
|
+
`ChainId`; `quote_ccip_native_fee(block=)` and
|
|
568
|
+
`OracleMappingReader.asset_source_events` take a `BlockIdentifier`.
|
|
569
|
+
|
|
570
|
+
**Address roles.** `PlasmaVault(address)`,
|
|
571
|
+
`validate_vault`, `fetch_deployment_info`, the HyperCore, lending-health and
|
|
572
|
+
oracle-mapping readers, `read_external_state`, `VaultSimulator(vault=)`,
|
|
573
|
+
`CrosschainSimulator`, crosschain discovery, the hub-manager inputs
|
|
574
|
+
(`ExecutorInitParams.manager`, `create_executor(manager=)`, `executors_of(manager=)`),
|
|
575
|
+
the async command helpers `Command.request_shares` / `redeem_from_request`,
|
|
576
|
+
`pending_request_shares` and `WithdrawManager.update_plasma_vault_address` take a
|
|
577
|
+
`PlasmaVaultAddress`; `.address`, `FusionInstance.plasma_vault` and the executor's
|
|
578
|
+
`manager()` return one. Crosschain deposit and redeem destinations stay
|
|
579
|
+
`ChecksumAddress`: `Command.deposit` / `redeem`, `is_allowed_vault`,
|
|
580
|
+
`is_tracked_vault` and the remote-vault substrate accept any allowlisted ERC4626.
|
|
581
|
+
Every fuse constructor, `FuseAction.fuse`, `BalanceFuse.fuse`,
|
|
582
|
+
`PlasmaVault.add_fuses` / `remove_fuses` / `add_balance_fuse` / `remove_balance_fuse`
|
|
583
|
+
/ `balance_fuse_value`, `RewardsManager.add_reward_fuses` / `is_reward_fuse_supported`,
|
|
584
|
+
`LaneFuses`, the HyperCore balance-fuse argument and the
|
|
585
|
+
`PlasmaVault.configure_instant_withdrawal_fuses` configs
|
|
586
|
+
(`Sequence[tuple[FuseAddress, Sequence[bytes]]]`) take a `FuseAddress`, and
|
|
587
|
+
`get_fuses()`, `get_instant_withdrawal_fuses()`, `get_rewards_fuses()` and
|
|
588
|
+
`VaultData.fuses` / `instant_fuses` / `fuse_markets` keys return it.
|
|
589
|
+
`AccessManager(address)` takes an `AccessManagerAddress`;
|
|
590
|
+
`get_access_manager_address()`, `VaultData.access_manager` and
|
|
591
|
+
`FusionInstance.access_manager` return one. `resolve_access_manager` still takes any
|
|
592
|
+
`ChecksumAddress`: it is what checks that the address is a vault.
|
|
593
|
+
|
|
594
|
+
**Chain registry.** `CHAIN_NAMES`, `CHAIN_NAME_TO_ID`, `SUPPORTED_CHAIN_IDS`,
|
|
595
|
+
`GET_LOGS_RANGE_HINTS`, `BLOCK_EXPLORER_URLS`, `MORPHO_BLUE_ADDRESSES`,
|
|
596
|
+
`AAVE_V3_POOL`, `PUBLIC_ALLOCATOR_ADDRESSES` and `HYPERCORE_PENDING_READERS` are keyed
|
|
597
|
+
by `ChainId` (`HYPEREVM_CHAIN_ID` is one) and `explorer.SUPPORTED_CHAINS` is a
|
|
598
|
+
`frozenset[ChainId]`, so `ensure_supported_chain(CHAIN_NAME_TO_ID["ethereum"])`
|
|
599
|
+
type-checks; `PUBLIC_ALLOCATOR_ADDRESSES` values are `ChecksumAddress`.
|
|
600
|
+
|
|
601
|
+
**Markets, substrates and access.** Every `IporFusionMarkets` member is a `MarketId`.
|
|
602
|
+
`decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
|
|
603
|
+
`AccessManager.has_role`, `grant_role`, `revoke_role` and `read_accounts_with_role`
|
|
604
|
+
take `RoleId | Roles`; `RoleAccount.role_id` is `RoleId`.
|
|
605
|
+
|
|
606
|
+
**Morpho.** `MorphoBlueMarketId` always carries `0x` (see
|
|
607
|
+
[Behavior changes](#behavior-changes)); build it with `parse_morpho_market_id`
|
|
608
|
+
instead of `MorphoBlueMarketId("<64 hex>")`. `fetch_market` and
|
|
609
|
+
`build_morpho_blue_market_json` take it; `MorphoApiMarket.market_id`,
|
|
610
|
+
`VaultV1MarketAllocation.market_id`, `VaultV2Cap.market_id`,
|
|
611
|
+
`MorphoPositionBreakdown.market_id` (as read from substrates) and the keys of
|
|
612
|
+
`VaultV1Info.public_allocator_flow_caps` are canonical. `fetch_market`, `fetch_vault`,
|
|
613
|
+
`morpho_blue_address` and the `build_*_json` helpers take `ChainId`; `fetch_vault`
|
|
614
|
+
takes a `ChecksumAddress`, and `build_morpho_blue_market_json` takes
|
|
615
|
+
`public_allocator` as `ChecksumAddress | None`. Every address on the `morpho_api`
|
|
616
|
+
records is `ChecksumAddress`; optional ones (`curator`, `guardian`, `fee_recipient`,
|
|
617
|
+
`performance_fee_recipient`, `management_fee_recipient`, `inner_vault`,
|
|
618
|
+
`VaultFlowCap.admin`, the `VaultV2Cap` market params) are `ChecksumAddress | None`.
|
|
619
|
+
|
|
620
|
+
**Crosschain.** The executor, dispatcher and factory wrappers, the lanes and
|
|
621
|
+
`StargateCrosschainCommandFuse.enter(request_id=)` take the crosschain ids above, and
|
|
622
|
+
their getters return them (`active_command`, `active_proposal_id`, `propose_balance`
|
|
623
|
+
on CCIP, `active_return` → `OperationId`, `asset_id`, `tracked_position_set_hash`,
|
|
624
|
+
`pending_request_by_chain`, `CrosschainLane.proposal_id_from_logs` /
|
|
625
|
+
`proposal_id_from_receipt`). Records carry them too: `Command`, `ActiveCommand`,
|
|
626
|
+
`ExecutorInitParams`, `BalanceObservation`, `LaneObservation`, `Observation`,
|
|
627
|
+
`CcipObservation`, `OutboundMessage`, `CcipMessageSent`, `Any2EVMMessage`,
|
|
628
|
+
`VerifierResult`, `Packet.guid`, `TaxiMessage`, `StargateChain`; and the helpers
|
|
629
|
+
`fetch_verifier_result(message_id=)`, `lz_receive_calldata(guid=)` and
|
|
630
|
+
`lz_compose_calldata(guid=)` take a `MessageId`. Decoded event values
|
|
631
|
+
(`ccip_events` → `CcipEvent.values`) stay a generic mapping: convert a value
|
|
632
|
+
before passing it on.
|
|
633
|
+
|
|
634
|
+
**Transactions, LayerZero and CCIP.** `Web3Context.build_transaction` and
|
|
635
|
+
`Call.build_transaction` return web3's `TxParams` (the same keys; `data` is `0x` hex,
|
|
636
|
+
the fee fields are `Wei`). LayerZero endpoint ids are `LayerZeroEid`:
|
|
637
|
+
`StargateChain.eid`, `Packet.src_eid` / `dst_eid`, `EnforcedOptionParam.eid`,
|
|
638
|
+
`executor_eid()` and `eid_of()` return it and `chain_id_of`, `peers`,
|
|
639
|
+
`lz_receive_calldata(src_eid=)`, `encode_oft_compose_msg(src_eid=)` and the Stargate
|
|
640
|
+
fuse's `eid=` take it (`LayerZeroEid(30101)`). CCIP chain selectors are
|
|
641
|
+
`CcipChainSelector`: `CcipChain.chain_selector`, `CcipRouteConfig.chain_selector`,
|
|
642
|
+
`CcipTokenLane.chain_selector`, the `MessageV1`, `CcipMessageSent` and
|
|
643
|
+
`Any2EVMMessage` selectors and `CcipRouter.get_off_ramps()` carry it, and
|
|
644
|
+
`chain_id_of_selector`, `CcipRouter.is_chain_supported` / `get_on_ramp`,
|
|
645
|
+
`CcipTokenPool.is_supported_chain` and `ccip_token_lane` take it
|
|
646
|
+
(`CcipChainSelector(4949039107694359620)`). `ZERO_ADDRESS` is a `ChecksumAddress`
|
|
647
|
+
(same value).
|
|
648
|
+
|
|
649
|
+
**HyperCore (preview).** The fuses, `HyperCoreSubstrates`, `HyperCoreReader`,
|
|
650
|
+
`read_hypercore_transfer_snapshot` and the helpers `system_address`,
|
|
651
|
+
`precompile_index_of`, `perp_dex_of`, `is_native_perp_asset`, `is_hip3_asset` take the
|
|
652
|
+
HyperCore ids; `HyperCorePerpAssetInfo`, `HyperCorePerpMarket`, `HyperCoreSpotLeg`,
|
|
653
|
+
`HyperCorePendingState` and `HyperCoreTransferSnapshot` carry them.
|
|
654
|
+
|
|
655
|
+
**External state.** `ExternalStateExecutor.proposal_hash` returns and `confirm_balance`
|
|
656
|
+
takes a `ProposalHash`; `BalanceProposal.proposal_hash` is a `ProposalHash`;
|
|
657
|
+
`ExternalStateEvent.transaction_hash` is a `TxHash`.
|
|
658
|
+
`ExternalStateVaultState.last_checked_custodian_timestamp`,
|
|
659
|
+
`BalanceFuseSnapshot.last_custodian_update_timestamp`, `BalanceProposal.proposed_at`,
|
|
660
|
+
`last_updated()` and `get_oldest_update_timestamp()` are web3's `Timestamp`, and
|
|
661
|
+
`ExternalStateExecutor.proposal_hash(proposed_at=)` takes one.
|
|
662
|
+
|
|
663
|
+
**Errors and explorer.** `TransactionError(tx_hash=)` takes a `TxHash`,
|
|
664
|
+
`LogScanError(address=)` a `ChecksumAddress`; `get_revert_reason` takes a `TxHash`;
|
|
665
|
+
`get_deployment_tx` returns a `TxHash` (a malformed hash from Etherscan is reported
|
|
666
|
+
as the error `malformed-tx-hash`).
|
|
667
|
+
|
|
668
|
+
**MCP response models** (Python constructors only; the JSON schemas are unchanged
|
|
669
|
+
except as listed under [Behavior changes](#behavior-changes)): `chain_id` fields are
|
|
670
|
+
`ChainId`, `role_id` `RoleId`, `market_id` `MarketId`. Calling an MCP tool function
|
|
671
|
+
directly from Python takes `ChainId`.
|
|
672
|
+
|
|
673
|
+
## Behavior changes
|
|
674
|
+
|
|
675
|
+
These apply to every caller, type-checked or not. Changes described in full above
|
|
676
|
+
are listed with a link.
|
|
677
|
+
|
|
678
|
+
- **Vault reads are pinned to one block**: every read of `fetch_vault_data`, log
|
|
679
|
+
scans included, runs at the resolved block; see
|
|
680
|
+
[Vault reads and validation](#vault-reads-and-validation).
|
|
681
|
+
- **`Web3Context.get_logs` and `get_block` default to the context's block**, not
|
|
682
|
+
`"latest"`; see [Vault reads and validation](#vault-reads-and-validation).
|
|
683
|
+
- **`metadata=False`** leaves `token_symbols` / `token_decimals` empty and names the
|
|
684
|
+
ERC20 holdings `"?"`, and `validate_vault` defaults to it; see
|
|
685
|
+
[Vault reads and validation](#vault-reads-and-validation).
|
|
686
|
+
- **`Web3Context.from_url` bounds requests in flight at 8** by default; see
|
|
687
|
+
[Contract cache and request limit](#contract-cache-and-request-limit).
|
|
688
|
+
- **The contract cache** keys every entry by chain and lowercase address, no longer
|
|
689
|
+
caches an absence, and is injected per context; see
|
|
690
|
+
[Contract cache and request limit](#contract-cache-and-request-limit).
|
|
691
|
+
- **The supply cap is formatted in share decimals.** `fusion vault info` shows it
|
|
692
|
+
labelled `shares`, and the JSON `supply_cap.formatted` uses `share_decimals`; both
|
|
693
|
+
used the asset's decimals before.
|
|
694
|
+
- **`"no contract"` no longer appears in vault output.** The snapshot reads token
|
|
695
|
+
symbols in one batch, which does not tell a token without code from one whose
|
|
696
|
+
symbol cannot be read: the text of `fusion vault info` / `validate` and the ERC20
|
|
697
|
+
holdings show it as `"?"`, and the `vault info` JSON (CLI and MCP) leaves its
|
|
698
|
+
symbol out or `null`. `resolve_token_symbol` itself still returns `"no contract"`.
|
|
699
|
+
- **Morpho market ids always carry `0x`.** `fetch_market` and
|
|
700
|
+
`build_morpho_blue_market_json` no longer add the prefix themselves: pass
|
|
701
|
+
`parse_morpho_market_id(raw_id)`, never a bare 64-hex string.
|
|
702
|
+
- The Morpho API client checksums every address it returns (market, vault,
|
|
703
|
+
allocation, adapter and cap records, and the `fusion market` / MCP market documents
|
|
704
|
+
built from them), so string comparisons against the API's own spelling change. Its
|
|
705
|
+
error handling is under [Errors](#errors).
|
|
706
|
+
- `fusion market meta-morpho --json` and the MCP `market_meta_morpho` tool report
|
|
707
|
+
`null` instead of `""` for an absent curator, guardian or fee recipient; the MCP
|
|
708
|
+
schema's `curator` is nullable.
|
|
709
|
+
- Two fuses are equal (and hash alike) only when they have the same class and the
|
|
710
|
+
same stored state. `StakeFuse` subclasses (`GearboxStakeFuse`,
|
|
711
|
+
`FluidInstadappStakingFuse`) with the same fuse address but different
|
|
712
|
+
`staking_address` were equal and collapsed in a set or dict key; they are now
|
|
713
|
+
distinct. A custom `Fuse` subclass is compared on its instance attributes, which
|
|
714
|
+
must be hashable; drop an `__eq__` / `__hash__` override that only added them.
|
|
715
|
+
- `SubstrateInfo.address` is `None` instead of `""` for a substrate without an
|
|
716
|
+
address. Truthiness checks are unaffected and the SDK's JSON documents already
|
|
717
|
+
omit it, but `dataclasses.asdict(info)` now gives `"address": None` and
|
|
718
|
+
`info.address == ""` is false.
|
|
719
|
+
- The MCP `vault_info` document reports the vault address checksummed (the CLI
|
|
720
|
+
already did).
|
|
721
|
+
- The Uniswap V3 / Ramses V2 position events (`extract_new_position_events`,
|
|
722
|
+
`extract_close_position_events`) return their addresses checksummed (they were the
|
|
723
|
+
lowercase ABI-decoded strings).
|
|
724
|
+
- `LendingMarketHealth.substrate_id` for a Morpho market is `0x`-prefixed (the JSON
|
|
725
|
+
documents already were).
|
|
726
|
+
- `TransactionError.tx_hash` is `0x`-prefixed.
|
|
727
|
+
- Saved config entries are checksummed when loaded; the MCP `vault_add` tool rejects
|
|
728
|
+
a value that is not an address.
|