ipor-fusion 4.0.0.dev45__tar.gz → 4.0.0.dev52__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.dev52/MIGRATION_4.md +166 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/PKG-INFO +5 -1
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/README.md +4 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/pyproject.toml +2 -1
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/__init__.py +4 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_markets_view.py +6 -4
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/context.py +3 -3
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/dependency_graph.py +3 -1
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/explorer.py +14 -4
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/events.py +8 -1
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/external_state_events.py +3 -3
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/ramses_v2.py +3 -3
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/uniswap_v3.py +4 -4
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/hypercore.py +1 -1
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/lending_health.py +1 -1
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/substrates.py +19 -23
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/types.py +25 -1
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/validation.py +2 -2
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/vault_data.py +16 -15
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/vault_info.py +9 -4
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_helpers.py +4 -4
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_encoding.py +2 -2
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_explorer.py +11 -2
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_fuse_encoding.py +4 -4
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_substrates.py +12 -12
- ipor_fusion-4.0.0.dev52/tests/test_types_and_events.py +122 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/typed_client/consumer.py +5 -0
- ipor_fusion-4.0.0.dev45/tests/test_types_and_events.py +0 -63
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/AGENTS.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/uv.lock +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Migrating to ipor-fusion 4.0
|
|
2
|
+
|
|
3
|
+
4.0 gives chain, market and role ids, the crosschain, HyperCore and Morpho ids,
|
|
4
|
+
vault/oracle/token addresses and transaction hashes a nominal type
|
|
5
|
+
(`typing.NewType`) across the public API, and checks the SDK, the examples and a
|
|
6
|
+
typed client with pyright's `reportArgumentType`. Other fields keep their types
|
|
7
|
+
(e.g. symbols, names and URLs stay `str`).
|
|
8
|
+
|
|
9
|
+
The wrappers do not change values: `ChainId(1)` is `1` at runtime. The
|
|
10
|
+
[behavior changes](#behavior-changes) below, however, apply to **every** caller,
|
|
11
|
+
type-checked or not. In particular, `fetch_market` and
|
|
12
|
+
`build_morpho_blue_market_json` no longer add a `0x` prefix themselves: pass
|
|
13
|
+
`parse_morpho_market_id(raw_id)`, never a bare 64-hex string. Type-checked code
|
|
14
|
+
(pyright, mypy, Pylance) also wraps literals and raw values as below. Values the SDK
|
|
15
|
+
returns already carry their type and go back in unwrapped.
|
|
16
|
+
|
|
17
|
+
All types are importable from `ipor_fusion` (and `ipor_fusion.types`);
|
|
18
|
+
`ChecksumAddress` and `HexAddress` come from `eth_typing`,
|
|
19
|
+
`BlockIdentifier` from `web3.types`, and `USDC_TOKEN_INDEX` / `hip3_action_asset` from
|
|
20
|
+
`ipor_fusion.fuses.hypercore`. The wrappers label values; they do not validate byte
|
|
21
|
+
widths or address spelling.
|
|
22
|
+
|
|
23
|
+
## Building the types
|
|
24
|
+
|
|
25
|
+
| Type | From a literal or raw value |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `ChainId` | `ChainId(1)` |
|
|
28
|
+
| `MarketId` | an `IporFusionMarkets` member, or `MarketId(n)` |
|
|
29
|
+
| `RoleId` | a `Roles` member (accepted as is), or `RoleId(n)`; `Roles.resolve(name)` returns one |
|
|
30
|
+
| `Decimals`, `Period`, `Amount`, ... | `Decimals(8)`, `Period(0)`, `Amount(n)` (unchanged) |
|
|
31
|
+
| `ChecksumAddress` | `Web3.to_checksum_address(value)` |
|
|
32
|
+
| `HexAddress` | any `ChecksumAddress`, or `parse_hex_address(value)` (20 bytes or hex in any spelling → lowercase `0x` form) |
|
|
33
|
+
| `MorphoBlueMarketId` | `parse_morpho_market_id(value)`: 32 bytes or hex in any spelling, always `0x` + 64 lowercase digits |
|
|
34
|
+
| `TxHash` | `parse_tx_hash(value)` (32 bytes or hex in any spelling) |
|
|
35
|
+
| crosschain ids | `CommandId(b)`, `MessageId(b)`, `AssetId(b)`, `OperationId(b)`, `TransferId(b)`, `ReturnRequestId(b)`, `PositionSetHash(b)` (bytes32); `ProposalId(n)`, `DeploymentRequestId(n)`, `StargateAssetId(n)` (int) |
|
|
36
|
+
| 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)` |
|
|
37
|
+
| external state | `ProposalHash(b)` |
|
|
38
|
+
| `BlockIdentifier` | a block number, `"latest"` / `"pending"` / ..., or a block hash |
|
|
39
|
+
|
|
40
|
+
`MessageId` covers both a CCIP `messageId` and a LayerZero `guid`. `AssetId` is the
|
|
41
|
+
crosschain executor's `bytes32` asset id; a Stargate pool's `uint16` asset id is
|
|
42
|
+
`StargateAssetId`. `DeploymentRequestId` is a factory's `uint256` dispatcher request;
|
|
43
|
+
a `bytes32` return (recall) request is `ReturnRequestId`.
|
|
44
|
+
|
|
45
|
+
## What changed, by area
|
|
46
|
+
|
|
47
|
+
**Vault reads and validation.** `fetch_vault_data(chain_id=)`, `fetch_deployment_info`,
|
|
48
|
+
`build_vault_info_json`, `ensure_supported_chain`, `fetch_vault_lending_health` take
|
|
49
|
+
`ChainId`; vault and oracle addresses
|
|
50
|
+
(`validate_vault`, `fetch_deployment_info`, `build_vault_info_json`,
|
|
51
|
+
`fetch_unpriceable_priced_tokens`) take `ChecksumAddress`; `resolve_token_symbol`,
|
|
52
|
+
`resolve_token_decimals`, `get_contract_name`, `get_deployment_tx` take `HexAddress`
|
|
53
|
+
(a checksum address fits). The `resolve_name` / `resolve_symbol` callbacks receive a
|
|
54
|
+
`HexAddress` (a callback typed for `str` still fits). Market-keyed data is keyed by
|
|
55
|
+
`MarketId`: `VaultData.market_substrates`, `dependency_graph`, `fuse_markets`,
|
|
56
|
+
`morpho_positions`, `aave_positions`; the `VaultState` protocol, `validate_vault_state`
|
|
57
|
+
(`market_balances`), `fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens`
|
|
58
|
+
and the `dependency_graph` helpers (`compute_update_reach`, `compute_update_groups`,
|
|
59
|
+
`find_orphan_fuse_markets`, `find_markets_missing_erc20_dependency`).
|
|
60
|
+
A custom `VaultState` implementation also annotates `asset` as `ChecksumAddress` and
|
|
61
|
+
its `unpriceable_priced_tokens` items' `market_id` as `MarketId`
|
|
62
|
+
(`UnpriceableToken`), and `price_oracle_addr` / `fuse_markets` keys as
|
|
63
|
+
`ChecksumAddress`. `VaultData.asset`, `access_manager`, `price_oracle_addr`,
|
|
64
|
+
`rewards_manager`, `withdraw_manager`, `fuses` and `instant_fuses` (and the
|
|
65
|
+
`fuse_markets` keys), `FeeData.fee_manager` / `ipor_dao_fee_recipient`, and
|
|
66
|
+
`VaultValidation.vault` / `asset` are `ChecksumAddress`; `VaultData.balance_fuses`
|
|
67
|
+
is `list[BalanceFuse]` and `token_prices_usd` is keyed by `HexAddress` (lowercase);
|
|
68
|
+
the Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient` are
|
|
69
|
+
`ChecksumAddress`;
|
|
70
|
+
`VaultValidation.chain_id` is `ChainId`; `LendingMarketHealth.market_id` and
|
|
71
|
+
`MiddlewarePricedToken.market_id` are `MarketId`; `Erc20Holding.address` is
|
|
72
|
+
`HexAddress`, and `SubstrateInfo.address` is `HexAddress | None` (`None`, no longer
|
|
73
|
+
`""`, when a substrate carries no address).
|
|
74
|
+
`LegacyPriceOracleMiddleware` takes `base_currency_decimals: Decimals`.
|
|
75
|
+
`get_logs_adaptive(chain_id=)` and `LogSource.get_logs` take `ChainId`;
|
|
76
|
+
`quote_ccip_native_fee(block=)` and `OracleMappingReader.asset_source_events` take a
|
|
77
|
+
`BlockIdentifier`.
|
|
78
|
+
|
|
79
|
+
**Markets and substrates.** Every `IporFusionMarkets` member is a `MarketId`.
|
|
80
|
+
`decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
|
|
81
|
+
|
|
82
|
+
**Access.** `AccessManager.has_role`, `grant_role`, `revoke_role`,
|
|
83
|
+
`get_accounts_with_role` take `RoleId | Roles`; `RoleAccount.role_id` is `RoleId`.
|
|
84
|
+
|
|
85
|
+
**Morpho.** `MorphoBlueMarketId` now always carries `0x`; build it with
|
|
86
|
+
`parse_morpho_market_id` instead of `MorphoBlueMarketId("<64 hex>")`. `fetch_market`,
|
|
87
|
+
`build_morpho_blue_market_json` take it; `MorphoApiMarket.market_id`,
|
|
88
|
+
`VaultV1MarketAllocation.market_id`, `VaultV2Cap.market_id`,
|
|
89
|
+
`MorphoPositionBreakdown.market_id` (as read from substrates) and the keys of
|
|
90
|
+
`VaultV1Info.public_allocator_flow_caps` are canonical. `fetch_market`, `fetch_vault`,
|
|
91
|
+
`morpho_blue_address`, `build_*_json` take `ChainId`; `fetch_vault` takes a
|
|
92
|
+
`ChecksumAddress`. Every address on the `morpho_api` records is `ChecksumAddress`;
|
|
93
|
+
optional ones (`curator`, `guardian`, `fee_recipient`, `performance_fee_recipient`,
|
|
94
|
+
`management_fee_recipient`, `inner_vault`, `VaultFlowCap.admin`, the `VaultV2Cap`
|
|
95
|
+
market params) are `ChecksumAddress | None`.
|
|
96
|
+
|
|
97
|
+
**Crosschain.** The executor, dispatcher and factory wrappers, the lanes and
|
|
98
|
+
`StargateCrosschainCommandFuse.enter(request_id=)` take the crosschain ids above, and
|
|
99
|
+
their getters return them (`active_command`, `active_proposal_id`, `propose_balance`
|
|
100
|
+
on CCIP, `active_return` → `OperationId`, `asset_id`, `tracked_position_set_hash`,
|
|
101
|
+
`pending_request_by_chain`, `CrosschainLane.proposal_id_from_logs` /
|
|
102
|
+
`proposal_id_from_receipt`). Records carry them too: `Command`, `ActiveCommand`,
|
|
103
|
+
`ExecutorInitParams`, `BalanceObservation`, `LaneObservation`, `Observation`,
|
|
104
|
+
`CcipObservation`, `OutboundMessage`, `CcipMessageSent`, `Any2EVMMessage`,
|
|
105
|
+
`VerifierResult`, `Packet.guid`, `TaxiMessage`, `StargateChain`; and the helpers
|
|
106
|
+
`fetch_verifier_result(message_id=)`, `lz_receive_calldata(guid=)` and
|
|
107
|
+
`lz_compose_calldata(guid=)` take a `MessageId`. Decoded event values
|
|
108
|
+
(`ccip_events` → `CcipEvent.values`) stay a generic mapping: convert a value
|
|
109
|
+
before passing it on.
|
|
110
|
+
|
|
111
|
+
**HyperCore (preview).** The fuses, `HyperCoreSubstrates`, `HyperCoreReader`,
|
|
112
|
+
`read_hypercore_transfer_snapshot` and the helpers `system_address`, `read_index_of`,
|
|
113
|
+
`perp_dex_of`, `is_native_perp_asset`, `is_hip3_asset` take the HyperCore ids;
|
|
114
|
+
`HyperCorePerpAssetInfo`, `HyperCorePerpMarket`, `HyperCoreSpotLeg`,
|
|
115
|
+
`HyperCorePendingState` and `HyperCoreTransferSnapshot` carry them.
|
|
116
|
+
|
|
117
|
+
**External state.** `ExternalStateExecutor.proposal_hash` returns and `confirm_balance`
|
|
118
|
+
takes a `ProposalHash`; `BalanceProposal.proposal_hash` and `NavMark.proposal_hash`
|
|
119
|
+
are `ProposalHash`; `ExternalStateEvent.transaction_hash` is a `TxHash`.
|
|
120
|
+
|
|
121
|
+
**Errors and explorer.** `TransactionError(tx_hash=)` takes a `TxHash`,
|
|
122
|
+
`LogScanError(address=)` a `ChecksumAddress`; `get_deployment_tx` returns a `TxHash`
|
|
123
|
+
(a malformed hash from Etherscan is reported as the error `malformed-tx-hash`).
|
|
124
|
+
|
|
125
|
+
**MCP response models** (Python constructors only; the JSON schemas are unchanged
|
|
126
|
+
except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
|
|
127
|
+
`MarketId`. Calling an MCP tool function directly from Python takes `ChainId`.
|
|
128
|
+
|
|
129
|
+
**Moved, still importable.** `FuseAction` lives in `ipor_fusion.core.fuse_action` and
|
|
130
|
+
`ZERO_ADDRESS` in `ipor_fusion.types`; both are re-exported from
|
|
131
|
+
`ipor_fusion.fuses.base` and `FuseAction` from `ipor_fusion`. The CLI's internal
|
|
132
|
+
`ipor_fusion.cli.vault_cmd` was split into `options`, `vault_info_view`,
|
|
133
|
+
`vault_markets_view` and `oracle_mapping_view` (private names, not API).
|
|
134
|
+
|
|
135
|
+
## Behavior changes
|
|
136
|
+
|
|
137
|
+
- The MCP `vault_info` document reports the vault address checksummed (the CLI
|
|
138
|
+
already did).
|
|
139
|
+
- `SubstrateInfo.address` is `None` instead of `""` for a substrate without an
|
|
140
|
+
address. Truthiness checks are unaffected and the SDK's JSON documents already
|
|
141
|
+
omit it, but `dataclasses.asdict(info)` now gives `"address": None` and
|
|
142
|
+
`info.address == ""` is false.
|
|
143
|
+
- The Uniswap V3 / Ramses V2 position events (`extract_new_position_events`,
|
|
144
|
+
`extract_close_position_events`) return their addresses checksummed (they were the
|
|
145
|
+
lowercase ABI-decoded strings).
|
|
146
|
+
- `fusion market meta-morpho --json` and the MCP `market_meta_morpho` tool report
|
|
147
|
+
`null` instead of `""` for an absent curator, guardian or fee recipient; the MCP
|
|
148
|
+
schema's `curator` is nullable.
|
|
149
|
+
- The Morpho API client checksums every address it returns (market, vault,
|
|
150
|
+
allocation, adapter and cap records, and the `fusion market` / MCP market documents
|
|
151
|
+
built from them), so string comparisons against the API's own spelling change.
|
|
152
|
+
- It raises `MorphoApiError` for a market id or address it parses (market and V1
|
|
153
|
+
allocation ids, V1 flow-cap ids, vault/asset/owner/token/oracle/IRM/allocator
|
|
154
|
+
addresses) when the value is invalid, or absent where the field is optional in the
|
|
155
|
+
answer. A key the parser requires can instead raise `KeyError`, e.g. a `loanAsset`
|
|
156
|
+
without `address`. When matching a market's own allocation and flow cap, an id that
|
|
157
|
+
does not parse matches nothing, and a V1 flow cap without an id is skipped, as
|
|
158
|
+
before. `--market` rejects a malformed id.
|
|
159
|
+
- `LendingMarketHealth.substrate_id` for a Morpho market is `0x`-prefixed (the JSON
|
|
160
|
+
documents already were).
|
|
161
|
+
- `TransactionError.tx_hash` is `0x`-prefixed.
|
|
162
|
+
- Saved config entries are checksummed when loaded; the MCP `vault_add` tool rejects
|
|
163
|
+
a value that is not an address.
|
|
164
|
+
- The on-disk caches (`~/.cache/ipor-fusion/`) key addresses lowercased; older entries
|
|
165
|
+
are rekeyed on load, so a contract name cached for one spelling now also resolves
|
|
166
|
+
for the other.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ipor-fusion
|
|
3
|
-
Version: 4.0.0.
|
|
3
|
+
Version: 4.0.0.dev52
|
|
4
4
|
Summary: The IPOR Fusion SDK for Python
|
|
5
5
|
Project-URL: homepage, https://ipor.io
|
|
6
6
|
Project-URL: repository, https://github.com/IPOR-Labs/ipor-fusion.py
|
|
@@ -115,6 +115,10 @@ alt="Release">
|
|
|
115
115
|
</tr>
|
|
116
116
|
</table>
|
|
117
117
|
|
|
118
|
+
> **Upgrading from 3.x?** 4.0 types the public API's ids and addresses and changes
|
|
119
|
+
> a few behaviors (Morpho market ids, nullable optional addresses); see
|
|
120
|
+
> [MIGRATION_4.md](MIGRATION_4.md).
|
|
121
|
+
|
|
118
122
|
## Quickstart
|
|
119
123
|
|
|
120
124
|
### Install
|
|
@@ -69,6 +69,10 @@ alt="Release">
|
|
|
69
69
|
</tr>
|
|
70
70
|
</table>
|
|
71
71
|
|
|
72
|
+
> **Upgrading from 3.x?** 4.0 types the public API's ids and addresses and changes
|
|
73
|
+
> a few behaviors (Morpho market ids, nullable optional addresses); see
|
|
74
|
+
> [MIGRATION_4.md](MIGRATION_4.md).
|
|
75
|
+
|
|
72
76
|
## Quickstart
|
|
73
77
|
|
|
74
78
|
### Install
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ipor-fusion"
|
|
3
|
-
version = "4.0.0.
|
|
3
|
+
version = "4.0.0.dev52"
|
|
4
4
|
description = "The IPOR Fusion SDK for Python"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Adam Mydlarz", email = "amydlarz@gmail.com" },
|
|
@@ -165,3 +165,4 @@ packages = ["src/ipor_fusion"]
|
|
|
165
165
|
# semantic-release before `build_command`, so each wheel carries its own entry.
|
|
166
166
|
[tool.hatch.build.targets.wheel.force-include]
|
|
167
167
|
"CHANGELOG.md" = "ipor_fusion/CHANGELOG.md"
|
|
168
|
+
"MIGRATION_4.md" = "ipor_fusion/MIGRATION_4.md"
|
|
@@ -345,7 +345,9 @@ from ipor_fusion.types import (
|
|
|
345
345
|
TokenId,
|
|
346
346
|
TransferId,
|
|
347
347
|
TxHash,
|
|
348
|
+
parse_hex_address,
|
|
348
349
|
parse_morpho_market_id,
|
|
350
|
+
parse_tx_hash,
|
|
349
351
|
)
|
|
350
352
|
from ipor_fusion.validation import (
|
|
351
353
|
Erc20Holding,
|
|
@@ -624,7 +626,9 @@ __all__ = [
|
|
|
624
626
|
"PayloadHash",
|
|
625
627
|
"ProposalHash",
|
|
626
628
|
"TxHash",
|
|
629
|
+
"parse_hex_address",
|
|
627
630
|
"parse_morpho_market_id",
|
|
631
|
+
"parse_tx_hash",
|
|
628
632
|
"AssetId",
|
|
629
633
|
"CommandId",
|
|
630
634
|
"DeploymentRequestId",
|
{ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_markets_view.py
RENAMED
|
@@ -20,7 +20,7 @@ from ipor_fusion.substrates import (
|
|
|
20
20
|
decode_substrate,
|
|
21
21
|
format_market_label,
|
|
22
22
|
)
|
|
23
|
-
from ipor_fusion.types import ChainId, MarketId
|
|
23
|
+
from ipor_fusion.types import ChainId, MarketId, parse_hex_address
|
|
24
24
|
from ipor_fusion.vault_data import (
|
|
25
25
|
GET_MARKET_SUBSTRATES,
|
|
26
26
|
VaultData,
|
|
@@ -213,7 +213,7 @@ def _format_token_amount(
|
|
|
213
213
|
ctx: Web3Context,
|
|
214
214
|
raw: int,
|
|
215
215
|
token_address: HexAddress,
|
|
216
|
-
prices_usd: dict[
|
|
216
|
+
prices_usd: dict[HexAddress, float] | None = None,
|
|
217
217
|
) -> str:
|
|
218
218
|
"""Format a raw on-chain amount using cached ERC-20 symbol + decimals.
|
|
219
219
|
|
|
@@ -226,7 +226,9 @@ def _format_token_amount(
|
|
|
226
226
|
return f"{raw} raw ({symbol})"
|
|
227
227
|
base = f"{_format_amount(raw, decimals)} {symbol}"
|
|
228
228
|
if prices_usd:
|
|
229
|
-
usd_suffix = _format_usd(
|
|
229
|
+
usd_suffix = _format_usd(
|
|
230
|
+
raw, decimals, prices_usd.get(parse_hex_address(token_address))
|
|
231
|
+
)
|
|
230
232
|
return f"{base}{usd_suffix}"
|
|
231
233
|
return base
|
|
232
234
|
|
|
@@ -259,7 +261,7 @@ def _print_dependency_graph(data: VaultData) -> None:
|
|
|
259
261
|
def _print_fuse_section(
|
|
260
262
|
title: str,
|
|
261
263
|
fuses: Sequence[ChecksumAddress],
|
|
262
|
-
fuse_markets: dict[
|
|
264
|
+
fuse_markets: dict[ChecksumAddress, MarketId] | None,
|
|
263
265
|
market_substrates: dict[MarketId, list[bytes]] | None,
|
|
264
266
|
chain_id: ChainId,
|
|
265
267
|
api_key: str | None,
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
from eth_account import Account
|
|
6
|
-
from eth_typing import ChecksumAddress
|
|
6
|
+
from eth_typing import ChecksumAddress
|
|
7
7
|
from hexbytes import HexBytes
|
|
8
8
|
from requests import ConnectionError as RequestsConnectionError
|
|
9
9
|
from requests import HTTPError, Timeout
|
|
@@ -25,7 +25,7 @@ from ipor_fusion.core.logs import (
|
|
|
25
25
|
get_logs_adaptive,
|
|
26
26
|
)
|
|
27
27
|
from ipor_fusion.errors import LogSourceUnavailable, TransactionError, get_revert_reason
|
|
28
|
-
from ipor_fusion.types import ChainId,
|
|
28
|
+
from ipor_fusion.types import ChainId, parse_tx_hash
|
|
29
29
|
|
|
30
30
|
# web3 retries every allowlisted method on any HTTP error, a 400 included;
|
|
31
31
|
# a too-wide eth_getLogs answered with HTTP 400/413 or a query timeout would be
|
|
@@ -229,7 +229,7 @@ class Web3Context:
|
|
|
229
229
|
reason = get_revert_reason(self.web3, tx_hash, receipt)
|
|
230
230
|
raise TransactionError(
|
|
231
231
|
"Transaction failed",
|
|
232
|
-
tx_hash=
|
|
232
|
+
tx_hash=parse_tx_hash(bytes(tx_hash)),
|
|
233
233
|
revert_reason=reason,
|
|
234
234
|
)
|
|
235
235
|
return receipt
|
|
@@ -4,6 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from collections.abc import Mapping, Sequence, Set
|
|
6
6
|
|
|
7
|
+
from eth_typing import ChecksumAddress
|
|
8
|
+
|
|
7
9
|
from ipor_fusion.market_ids import IporFusionMarkets
|
|
8
10
|
from ipor_fusion.types import MarketId
|
|
9
11
|
|
|
@@ -38,7 +40,7 @@ _ERC20_DEP_NOT_REQUIRED: frozenset[MarketId] = frozenset(
|
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
def find_orphan_fuse_markets(
|
|
41
|
-
fuse_markets: Mapping[
|
|
43
|
+
fuse_markets: Mapping[ChecksumAddress, MarketId],
|
|
42
44
|
balance_fuse_market_ids: Set[MarketId],
|
|
43
45
|
) -> dict[MarketId, list[str]]:
|
|
44
46
|
"""Return markets claimed by action fuses but lacking a balance fuse.
|
|
@@ -10,10 +10,10 @@ import time
|
|
|
10
10
|
from urllib.parse import urlencode
|
|
11
11
|
from urllib.request import urlopen
|
|
12
12
|
|
|
13
|
-
from eth_typing import HexAddress
|
|
13
|
+
from eth_typing import HexAddress
|
|
14
14
|
|
|
15
15
|
from ipor_fusion.cache import cache_key, load_contract_cache, update_contract_cache
|
|
16
|
-
from ipor_fusion.types import ChainId, TxHash
|
|
16
|
+
from ipor_fusion.types import ChainId, TxHash, parse_tx_hash
|
|
17
17
|
|
|
18
18
|
ETHERSCAN_V2_URL = "https://api.etherscan.io/v2/api"
|
|
19
19
|
|
|
@@ -67,6 +67,17 @@ def get_deployment_tx(
|
|
|
67
67
|
return _fetch_contract_creation_tx(chain_id, address, api_key)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
def _creation_tx_hash(raw: object) -> tuple[TxHash | None, str | None]:
|
|
71
|
+
if not raw:
|
|
72
|
+
return None, None
|
|
73
|
+
if not isinstance(raw, str):
|
|
74
|
+
return None, "malformed-tx-hash"
|
|
75
|
+
try:
|
|
76
|
+
return parse_tx_hash(raw), None
|
|
77
|
+
except ValueError:
|
|
78
|
+
return None, "malformed-tx-hash"
|
|
79
|
+
|
|
80
|
+
|
|
70
81
|
def _fetch_contract_creation_tx(
|
|
71
82
|
chain_id: ChainId, address: HexAddress, api_key: str | None = None
|
|
72
83
|
) -> tuple[TxHash | None, str | None]:
|
|
@@ -91,8 +102,7 @@ def _fetch_contract_creation_tx(
|
|
|
91
102
|
with urlopen(url, timeout=10) as resp: # noqa: S310
|
|
92
103
|
data = json.loads(resp.read().decode())
|
|
93
104
|
if data.get("status") == "1" and data.get("result"):
|
|
94
|
-
|
|
95
|
-
return (TxHash(HexStr(tx_hash)) if tx_hash else None), None
|
|
105
|
+
return _creation_tx_hash(data["result"][0].get("txHash"))
|
|
96
106
|
result_text = str(data.get("result", "")).lower()
|
|
97
107
|
if "free api access is not supported" in result_text:
|
|
98
108
|
return None, "etherscan-paid-tier-required"
|
|
@@ -14,5 +14,12 @@ def extract_events(
|
|
|
14
14
|
for log in receipt["logs"]:
|
|
15
15
|
if log["topics"][0] == sig_hash:
|
|
16
16
|
decoded = decode(abi_types, log["data"])
|
|
17
|
-
events.append(
|
|
17
|
+
events.append(
|
|
18
|
+
dataclass_type(
|
|
19
|
+
*(
|
|
20
|
+
Web3.to_checksum_address(value) if abi == "address" else value
|
|
21
|
+
for abi, value in zip(abi_types, decoded, strict=True)
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
)
|
|
18
25
|
return events
|
{ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/external_state_events.py
RENAMED
|
@@ -28,11 +28,11 @@ from typing import Any
|
|
|
28
28
|
|
|
29
29
|
from eth_abi import decode
|
|
30
30
|
from eth_abi.exceptions import DecodingError
|
|
31
|
-
from eth_typing import ChecksumAddress
|
|
31
|
+
from eth_typing import ChecksumAddress
|
|
32
32
|
from web3 import Web3
|
|
33
33
|
from web3.types import TxReceipt
|
|
34
34
|
|
|
35
|
-
from ipor_fusion.types import TxHash
|
|
35
|
+
from ipor_fusion.types import TxHash, parse_tx_hash
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
@dataclass(frozen=True, slots=True)
|
|
@@ -196,7 +196,7 @@ def external_state_event_name(topic: bytes | str) -> str | None:
|
|
|
196
196
|
def _hex(value: Any) -> TxHash | None:
|
|
197
197
|
if value is None:
|
|
198
198
|
return None
|
|
199
|
-
return
|
|
199
|
+
return parse_tx_hash(_as_bytes(value, "transactionHash"))
|
|
200
200
|
|
|
201
201
|
|
|
202
202
|
def decode_external_state_event(log: Mapping[str, Any]) -> ExternalStateEvent | None:
|
|
@@ -136,13 +136,13 @@ class RamsesClaimFuse(Fuse):
|
|
|
136
136
|
|
|
137
137
|
@dataclass(slots=True)
|
|
138
138
|
class RamsesNewPositionEvent:
|
|
139
|
-
version:
|
|
139
|
+
version: ChecksumAddress
|
|
140
140
|
token_id: TokenId
|
|
141
141
|
liquidity: Liquidity
|
|
142
142
|
amount0: Amount
|
|
143
143
|
amount1: Amount
|
|
144
|
-
sender:
|
|
145
|
-
recipient:
|
|
144
|
+
sender: ChecksumAddress
|
|
145
|
+
recipient: ChecksumAddress
|
|
146
146
|
fee: Fee
|
|
147
147
|
tick_lower: Tick
|
|
148
148
|
tick_upper: Tick
|
|
@@ -140,13 +140,13 @@ class UniswapV3CollectFuse(Fuse):
|
|
|
140
140
|
|
|
141
141
|
@dataclass(slots=True)
|
|
142
142
|
class UniswapV3NewPositionEvent:
|
|
143
|
-
version:
|
|
143
|
+
version: ChecksumAddress
|
|
144
144
|
token_id: TokenId
|
|
145
145
|
liquidity: Liquidity
|
|
146
146
|
amount0: Amount
|
|
147
147
|
amount1: Amount
|
|
148
|
-
sender:
|
|
149
|
-
recipient:
|
|
148
|
+
sender: ChecksumAddress
|
|
149
|
+
recipient: ChecksumAddress
|
|
150
150
|
fee: Fee
|
|
151
151
|
tick_lower: Tick
|
|
152
152
|
tick_upper: Tick
|
|
@@ -154,7 +154,7 @@ class UniswapV3NewPositionEvent:
|
|
|
154
154
|
|
|
155
155
|
@dataclass(slots=True)
|
|
156
156
|
class UniswapV3ClosePositionEvent:
|
|
157
|
-
version:
|
|
157
|
+
version: ChecksumAddress
|
|
158
158
|
token_id: TokenId
|
|
159
159
|
|
|
160
160
|
|
|
@@ -499,7 +499,7 @@ def _market_layout(
|
|
|
499
499
|
bitmap = 0
|
|
500
500
|
for raw in substrates:
|
|
501
501
|
info = decode_substrate(raw, HYPERCORE_MARKET)
|
|
502
|
-
if info.type_label == "SPOT_TOKEN" and not info.is_error:
|
|
502
|
+
if info.type_label == "SPOT_TOKEN" and not info.is_error and info.address:
|
|
503
503
|
index = HyperCoreTokenIndex(int(info.extra["token_index"]))
|
|
504
504
|
if index in seen:
|
|
505
505
|
continue
|
{ipor_fusion-4.0.0.dev45 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/lending_health.py
RENAMED
|
@@ -353,7 +353,7 @@ def _aave_v4_spokes(substrates: list[bytes]) -> list[ChecksumAddress]:
|
|
|
353
353
|
spokes: dict[ChecksumAddress, None] = {}
|
|
354
354
|
for sub in substrates:
|
|
355
355
|
info = decode_substrate(sub, market_id=IporFusionMarkets.AAVE_V4)
|
|
356
|
-
if info.type_label == "AAVE_V4_RESERVE":
|
|
356
|
+
if info.type_label == "AAVE_V4_RESERVE" and info.address:
|
|
357
357
|
spokes[Web3.to_checksum_address(info.address)] = None
|
|
358
358
|
return list(spokes)
|
|
359
359
|
|
|
@@ -15,7 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
from collections.abc import Callable
|
|
16
16
|
from dataclasses import dataclass, field
|
|
17
17
|
|
|
18
|
-
from eth_typing import HexAddress
|
|
18
|
+
from eth_typing import HexAddress
|
|
19
19
|
from eth_utils import keccak
|
|
20
20
|
|
|
21
21
|
from ipor_fusion.fuses.hypercore import (
|
|
@@ -25,12 +25,12 @@ from ipor_fusion.fuses.hypercore import (
|
|
|
25
25
|
read_index_of,
|
|
26
26
|
)
|
|
27
27
|
from ipor_fusion.market_ids import IporFusionMarkets
|
|
28
|
-
from ipor_fusion.types import HyperCoreAsset, MarketId
|
|
28
|
+
from ipor_fusion.types import HyperCoreAsset, MarketId, parse_hex_address
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@dataclass
|
|
32
32
|
class SubstrateInfo:
|
|
33
|
-
address: HexAddress =
|
|
33
|
+
address: HexAddress | None = None
|
|
34
34
|
raw_hex: str = ""
|
|
35
35
|
type_label: str = ""
|
|
36
36
|
is_error: bool = False
|
|
@@ -47,14 +47,10 @@ class SubstrateInfo:
|
|
|
47
47
|
# Slippage variant: [2 type chars][62 value chars]
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def _hex_address(hex_digits: str) -> HexAddress:
|
|
51
|
-
return HexAddress(HexStr(f"0x{hex_digits}"))
|
|
52
|
-
|
|
53
|
-
|
|
54
50
|
def _decode_type_lshift160(hex_str: str, types: dict[int, str]) -> SubstrateInfo:
|
|
55
51
|
"""Decode type<<160 | address (Ebisu, Midas, Balancer, Velodrome)."""
|
|
56
52
|
type_byte = int(hex_str[22:24], 16)
|
|
57
|
-
addr =
|
|
53
|
+
addr = parse_hex_address(hex_str[24:])
|
|
58
54
|
label = types.get(type_byte, f"type={type_byte}")
|
|
59
55
|
return SubstrateInfo(address=addr, type_label=label)
|
|
60
56
|
|
|
@@ -67,13 +63,13 @@ def _decode_type_lshift248(hex_str: str, types: dict[int, str]) -> SubstrateInfo
|
|
|
67
63
|
return SubstrateInfo(
|
|
68
64
|
raw_hex=f"0x{hex_str}", type_label=label, extra={"slippage": str(value)}
|
|
69
65
|
)
|
|
70
|
-
addr =
|
|
66
|
+
addr = parse_hex_address(hex_str[24:])
|
|
71
67
|
return SubstrateInfo(address=addr, type_label=label)
|
|
72
68
|
|
|
73
69
|
|
|
74
70
|
def _decode_plain_address(hex_str: str) -> SubstrateInfo:
|
|
75
71
|
"""Decode zero-padded address: 12 zero bytes + 20 address bytes."""
|
|
76
|
-
return SubstrateInfo(address=
|
|
72
|
+
return SubstrateInfo(address=parse_hex_address(hex_str[24:]))
|
|
77
73
|
|
|
78
74
|
|
|
79
75
|
def _decode_morpho(hex_str: str) -> SubstrateInfo:
|
|
@@ -88,14 +84,14 @@ def _decode_uniswap_v4(hex_str: str) -> SubstrateInfo:
|
|
|
88
84
|
substrate-as-asset (12 zero bytes + address)."""
|
|
89
85
|
if hex_str[:24] == "0" * 24:
|
|
90
86
|
return SubstrateInfo(
|
|
91
|
-
address=
|
|
87
|
+
address=parse_hex_address(hex_str[24:]), type_label="uniswap_v4_token"
|
|
92
88
|
)
|
|
93
89
|
return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label="uniswap_v4_pool_id")
|
|
94
90
|
|
|
95
91
|
|
|
96
92
|
def _decode_enso(hex_str: str) -> SubstrateInfo:
|
|
97
93
|
"""Decode address<<96 | selector<<64 (Enso)."""
|
|
98
|
-
addr =
|
|
94
|
+
addr = parse_hex_address(hex_str[0:40])
|
|
99
95
|
selector = f"0x{hex_str[40:48]}"
|
|
100
96
|
return SubstrateInfo(
|
|
101
97
|
address=addr, type_label="ENSO_TARGET", extra={"selector": selector}
|
|
@@ -104,7 +100,7 @@ def _decode_enso(hex_str: str) -> SubstrateInfo:
|
|
|
104
100
|
|
|
105
101
|
def _decode_dolomite(hex_str: str) -> SubstrateInfo:
|
|
106
102
|
"""Decode asset<<96 | subAccountId<<88 | canBorrow<<80 (Dolomite)."""
|
|
107
|
-
addr =
|
|
103
|
+
addr = parse_hex_address(hex_str[0:40])
|
|
108
104
|
sub_account_id = int(hex_str[40:42], 16)
|
|
109
105
|
can_borrow = (int(hex_str[42:44], 16) & 0x01) == 1
|
|
110
106
|
return SubstrateInfo(
|
|
@@ -131,7 +127,7 @@ def _decode_aave_v4(hex_str: str) -> SubstrateInfo:
|
|
|
131
127
|
return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label=label)
|
|
132
128
|
flags = int(hex_str[50:52], 16)
|
|
133
129
|
return SubstrateInfo(
|
|
134
|
-
address=
|
|
130
|
+
address=parse_hex_address(hex_str[2:42]),
|
|
135
131
|
type_label="AAVE_V4_RESERVE",
|
|
136
132
|
extra={
|
|
137
133
|
"reserve_id": str(int(hex_str[42:50], 16)),
|
|
@@ -149,7 +145,7 @@ def _decode_euler_v2(hex_str: str) -> SubstrateInfo:
|
|
|
149
145
|
plain-address path silently produces a malformed address (last 20 bytes are
|
|
150
146
|
flag bytes + zero padding).
|
|
151
147
|
"""
|
|
152
|
-
addr =
|
|
148
|
+
addr = parse_hex_address(hex_str[0:40])
|
|
153
149
|
is_collateral = (int(hex_str[40:42], 16) & 0x01) == 1
|
|
154
150
|
can_borrow = (int(hex_str[42:44], 16) & 0x01) == 1
|
|
155
151
|
sub_account = f"0x{hex_str[44:46]}"
|
|
@@ -178,13 +174,13 @@ def _decode_async_action(hex_str: str) -> SubstrateInfo:
|
|
|
178
174
|
type_byte = int(hex_str[0:2], 16)
|
|
179
175
|
if type_byte == 0:
|
|
180
176
|
return SubstrateInfo(
|
|
181
|
-
address=
|
|
177
|
+
address=parse_hex_address(hex_str[2:42]),
|
|
182
178
|
type_label="ALLOWED_AMOUNT_TO_OUTSIDE",
|
|
183
179
|
extra={"amount": str(int(hex_str[42:64], 16))},
|
|
184
180
|
)
|
|
185
181
|
if type_byte == 1:
|
|
186
182
|
return SubstrateInfo(
|
|
187
|
-
address=
|
|
183
|
+
address=parse_hex_address(hex_str[16:56]),
|
|
188
184
|
type_label="ALLOWED_TARGETS",
|
|
189
185
|
extra={"selector": f"0x{hex_str[56:64]}"},
|
|
190
186
|
)
|
|
@@ -229,10 +225,10 @@ def _decode_external_state(hex_str: str) -> SubstrateInfo:
|
|
|
229
225
|
if type_byte == 0:
|
|
230
226
|
return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label="UNDEFINED")
|
|
231
227
|
if label := _EXTERNAL_STATE_ADDRESS_TYPES.get(type_byte):
|
|
232
|
-
return SubstrateInfo(address=
|
|
228
|
+
return SubstrateInfo(address=parse_hex_address(hex_str[24:]), type_label=label)
|
|
233
229
|
if type_byte == 2:
|
|
234
230
|
return SubstrateInfo(
|
|
235
|
-
address=
|
|
231
|
+
address=parse_hex_address(hex_str[24:]),
|
|
236
232
|
type_label="TARGET",
|
|
237
233
|
extra={"selector": f"0x{hex_str[16:24]}"},
|
|
238
234
|
)
|
|
@@ -270,7 +266,7 @@ def _decode_crosschain(hex_str: str) -> SubstrateInfo:
|
|
|
270
266
|
is_error=True,
|
|
271
267
|
extra={"chain_id": str(chain_id)},
|
|
272
268
|
)
|
|
273
|
-
info = SubstrateInfo(address=
|
|
269
|
+
info = SubstrateInfo(address=parse_hex_address(hex_str[24:]), type_label=label)
|
|
274
270
|
if type_byte == 2:
|
|
275
271
|
info.extra = {"chain_id": str(chain_id)}
|
|
276
272
|
return info
|
|
@@ -330,7 +326,7 @@ def _decode_hypercore(hex_str: str) -> SubstrateInfo:
|
|
|
330
326
|
return _hypercore_invalid(hex_str, label)
|
|
331
327
|
if type_byte == 1:
|
|
332
328
|
return SubstrateInfo(
|
|
333
|
-
address=
|
|
329
|
+
address=parse_hex_address(hex_str[24:]),
|
|
334
330
|
type_label=label,
|
|
335
331
|
extra={"token_index": str(int(hex_str[8:24], 16))},
|
|
336
332
|
)
|
|
@@ -351,10 +347,10 @@ def _decode_hypercore(hex_str: str) -> SubstrateInfo:
|
|
|
351
347
|
extra["read_index"] = str(read_index_of(asset))
|
|
352
348
|
return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label=label, extra=extra)
|
|
353
349
|
if type_byte == 3:
|
|
354
|
-
return SubstrateInfo(address=
|
|
350
|
+
return SubstrateInfo(address=parse_hex_address(hex_str[24:]), type_label=label)
|
|
355
351
|
if type_byte == 4:
|
|
356
352
|
return SubstrateInfo(
|
|
357
|
-
address=
|
|
353
|
+
address=parse_hex_address(hex_str[24:]),
|
|
358
354
|
type_label=label,
|
|
359
355
|
extra={"max_fee_rate_decibps": str(int(hex_str[8:24], 16))},
|
|
360
356
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import ClassVar, Literal, NewType, TypeAlias
|
|
3
3
|
|
|
4
|
-
from eth_typing import ChecksumAddress, HexStr
|
|
4
|
+
from eth_typing import ChecksumAddress, HexAddress, HexStr
|
|
5
5
|
|
|
6
6
|
Amount = NewType("Amount", int)
|
|
7
7
|
Shares = NewType("Shares", int)
|
|
@@ -131,3 +131,27 @@ def parse_morpho_market_id(value: str | bytes) -> MorphoBlueMarketId:
|
|
|
131
131
|
if len(digits) != 64 or any(c not in "0123456789abcdef" for c in digits):
|
|
132
132
|
raise ValueError(f"invalid Morpho market ID (expected 32-byte hex): {value}")
|
|
133
133
|
return MorphoBlueMarketId("0x" + digits)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _hex_digits(value: str | bytes, size: int, what: str) -> str:
|
|
137
|
+
if isinstance(value, bytes):
|
|
138
|
+
if len(value) != size:
|
|
139
|
+
raise ValueError(f"invalid {what} (expected {size} bytes): {value!r}")
|
|
140
|
+
return value.hex()
|
|
141
|
+
digits = value.strip().removeprefix("0x").removeprefix("0X").lower()
|
|
142
|
+
if len(digits) != 2 * size or any(c not in "0123456789abcdef" for c in digits):
|
|
143
|
+
raise ValueError(f"invalid {what} (expected {size}-byte hex): {value}")
|
|
144
|
+
return digits
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def parse_hex_address(value: str | bytes) -> HexAddress:
|
|
148
|
+
"""An address as ``0x`` and 40 lowercase hex digits, from 20 raw bytes or hex
|
|
149
|
+
in any spelling; raises ``ValueError`` on anything else. For a checksummed
|
|
150
|
+
address use ``Web3.to_checksum_address``."""
|
|
151
|
+
return HexAddress(HexStr("0x" + _hex_digits(value, 20, "address")))
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def parse_tx_hash(value: str | bytes) -> TxHash:
|
|
155
|
+
"""A transaction hash as ``0x`` and 64 lowercase hex digits, from 32 raw bytes
|
|
156
|
+
or hex in any spelling; raises ``ValueError`` on anything else."""
|
|
157
|
+
return TxHash(HexStr("0x" + _hex_digits(value, 32, "transaction hash")))
|