ipor-fusion 4.0.0.dev49__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.dev49 → ipor_fusion-4.0.0.dev52}/MIGRATION_4.md +24 -9
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/PKG-INFO +1 -1
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/__init__.py +4 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_markets_view.py +6 -4
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/context.py +3 -3
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/dependency_graph.py +3 -1
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/explorer.py +14 -4
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/events.py +8 -1
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/external_state_events.py +3 -3
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/ramses_v2.py +3 -3
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/uniswap_v3.py +4 -4
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/hypercore.py +1 -1
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/lending_health.py +1 -1
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/substrates.py +19 -23
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/types.py +25 -1
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/validation.py +2 -2
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/vault_data.py +16 -15
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/vault_info.py +9 -4
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_helpers.py +4 -4
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_encoding.py +2 -2
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_explorer.py +11 -2
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_fuse_encoding.py +4 -4
- {ipor_fusion-4.0.0.dev49 → 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.dev49 → ipor_fusion-4.0.0.dev52}/tests/typed_client/consumer.py +5 -0
- ipor_fusion-4.0.0.dev49/tests/test_types_and_events.py +0 -63
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/AGENTS.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/README.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev49 → ipor_fusion-4.0.0.dev52}/uv.lock +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
vault/oracle/token addresses and transaction hashes a nominal type
|
|
5
5
|
(`typing.NewType`) across the public API, and checks the SDK, the examples and a
|
|
6
6
|
typed client with pyright's `reportArgumentType`. Other fields keep their types
|
|
7
|
-
(e.g.
|
|
7
|
+
(e.g. symbols, names and URLs stay `str`).
|
|
8
8
|
|
|
9
9
|
The wrappers do not change values: `ChainId(1)` is `1` at runtime. The
|
|
10
10
|
[behavior changes](#behavior-changes) below, however, apply to **every** caller,
|
|
@@ -15,7 +15,7 @@ type-checked or not. In particular, `fetch_market` and
|
|
|
15
15
|
returns already carry their type and go back in unwrapped.
|
|
16
16
|
|
|
17
17
|
All types are importable from `ipor_fusion` (and `ipor_fusion.types`);
|
|
18
|
-
`ChecksumAddress
|
|
18
|
+
`ChecksumAddress` and `HexAddress` come from `eth_typing`,
|
|
19
19
|
`BlockIdentifier` from `web3.types`, and `USDC_TOKEN_INDEX` / `hip3_action_asset` from
|
|
20
20
|
`ipor_fusion.fuses.hypercore`. The wrappers label values; they do not validate byte
|
|
21
21
|
widths or address spelling.
|
|
@@ -29,9 +29,9 @@ widths or address spelling.
|
|
|
29
29
|
| `RoleId` | a `Roles` member (accepted as is), or `RoleId(n)`; `Roles.resolve(name)` returns one |
|
|
30
30
|
| `Decimals`, `Period`, `Amount`, ... | `Decimals(8)`, `Period(0)`, `Amount(n)` (unchanged) |
|
|
31
31
|
| `ChecksumAddress` | `Web3.to_checksum_address(value)` |
|
|
32
|
-
| `HexAddress` | any `ChecksumAddress`, or `
|
|
32
|
+
| `HexAddress` | any `ChecksumAddress`, or `parse_hex_address(value)` (20 bytes or hex in any spelling → lowercase `0x` form) |
|
|
33
33
|
| `MorphoBlueMarketId` | `parse_morpho_market_id(value)`: 32 bytes or hex in any spelling, always `0x` + 64 lowercase digits |
|
|
34
|
-
| `TxHash` | `
|
|
34
|
+
| `TxHash` | `parse_tx_hash(value)` (32 bytes or hex in any spelling) |
|
|
35
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
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
37
|
| external state | `ProposalHash(b)` |
|
|
@@ -59,11 +59,18 @@ and the `dependency_graph` helpers (`compute_update_reach`, `compute_update_grou
|
|
|
59
59
|
`find_orphan_fuse_markets`, `find_markets_missing_erc20_dependency`).
|
|
60
60
|
A custom `VaultState` implementation also annotates `asset` as `ChecksumAddress` and
|
|
61
61
|
its `unpriceable_priced_tokens` items' `market_id` as `MarketId`
|
|
62
|
-
(`UnpriceableToken`)
|
|
63
|
-
`ChecksumAddress`,
|
|
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`;
|
|
64
70
|
`VaultValidation.chain_id` is `ChainId`; `LendingMarketHealth.market_id` and
|
|
65
|
-
`MiddlewarePricedToken.market_id` are `MarketId`; `Erc20Holding.address`
|
|
66
|
-
`SubstrateInfo.address`
|
|
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).
|
|
67
74
|
`LegacyPriceOracleMiddleware` takes `base_currency_decimals: Decimals`.
|
|
68
75
|
`get_logs_adaptive(chain_id=)` and `LogSource.get_logs` take `ChainId`;
|
|
69
76
|
`quote_ccip_native_fee(block=)` and `OracleMappingReader.asset_source_events` take a
|
|
@@ -112,7 +119,8 @@ takes a `ProposalHash`; `BalanceProposal.proposal_hash` and `NavMark.proposal_ha
|
|
|
112
119
|
are `ProposalHash`; `ExternalStateEvent.transaction_hash` is a `TxHash`.
|
|
113
120
|
|
|
114
121
|
**Errors and explorer.** `TransactionError(tx_hash=)` takes a `TxHash`,
|
|
115
|
-
`LogScanError(address=)` a `ChecksumAddress`; `get_deployment_tx` returns 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`).
|
|
116
124
|
|
|
117
125
|
**MCP response models** (Python constructors only; the JSON schemas are unchanged
|
|
118
126
|
except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
|
|
@@ -128,6 +136,13 @@ except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
|
|
|
128
136
|
|
|
129
137
|
- The MCP `vault_info` document reports the vault address checksummed (the CLI
|
|
130
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).
|
|
131
146
|
- `fusion market meta-morpho --json` and the MCP `market_meta_morpho` tool report
|
|
132
147
|
`null` instead of `""` for an absent curator, guardian or fee recipient; the MCP
|
|
133
148
|
schema's `curator` is nullable.
|
|
@@ -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.dev49 → 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.dev49 → 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.dev49 → 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")))
|
|
@@ -110,13 +110,13 @@ class VaultState(Protocol):
|
|
|
110
110
|
@property
|
|
111
111
|
def underlying_balance_on_vault(self) -> int: ...
|
|
112
112
|
@property
|
|
113
|
-
def price_oracle_addr(self) ->
|
|
113
|
+
def price_oracle_addr(self) -> ChecksumAddress: ...
|
|
114
114
|
@property
|
|
115
115
|
def balance_fuses(self) -> Sequence[BalanceFuse]: ...
|
|
116
116
|
@property
|
|
117
117
|
def market_substrates(self) -> Mapping[MarketId, Sequence[bytes]] | None: ...
|
|
118
118
|
@property
|
|
119
|
-
def fuse_markets(self) -> Mapping[
|
|
119
|
+
def fuse_markets(self) -> Mapping[ChecksumAddress, MarketId] | None: ...
|
|
120
120
|
@property
|
|
121
121
|
def dependency_graph(self) -> Mapping[MarketId, Sequence[MarketId]] | None: ...
|
|
122
122
|
@property
|
|
@@ -22,7 +22,7 @@ from eth_typing import HexAddress
|
|
|
22
22
|
from eth_utils import function_signature_to_4byte_selector
|
|
23
23
|
from web3 import Web3
|
|
24
24
|
from web3.exceptions import ContractLogicError, TimeExhausted, Web3RPCError
|
|
25
|
-
from web3.types import ChecksumAddress
|
|
25
|
+
from web3.types import ChecksumAddress
|
|
26
26
|
|
|
27
27
|
from ipor_fusion.cache import (
|
|
28
28
|
cache_key,
|
|
@@ -83,6 +83,7 @@ from ipor_fusion.types import (
|
|
|
83
83
|
ChainId,
|
|
84
84
|
MarketId,
|
|
85
85
|
MorphoBlueMarketId,
|
|
86
|
+
parse_hex_address,
|
|
86
87
|
parse_morpho_market_id,
|
|
87
88
|
)
|
|
88
89
|
|
|
@@ -124,8 +125,8 @@ class FeeData:
|
|
|
124
125
|
management_fee_vault_bps: int | None = None
|
|
125
126
|
management_fee_last_update: int | None = None
|
|
126
127
|
unrealized_management_fee: int | None = None
|
|
127
|
-
fee_manager:
|
|
128
|
-
ipor_dao_fee_recipient:
|
|
128
|
+
fee_manager: ChecksumAddress | None = None
|
|
129
|
+
ipor_dao_fee_recipient: ChecksumAddress | None = None
|
|
129
130
|
deposit_fee_wad: int | None = None
|
|
130
131
|
performance_fee_manager_bps: int | None = None
|
|
131
132
|
management_fee_manager_bps: int | None = None
|
|
@@ -167,14 +168,14 @@ class VaultData:
|
|
|
167
168
|
supply_cap: int
|
|
168
169
|
asset: ChecksumAddress
|
|
169
170
|
asset_symbol: str
|
|
170
|
-
access_manager:
|
|
171
|
-
price_oracle_addr:
|
|
172
|
-
rewards_manager:
|
|
173
|
-
withdraw_manager:
|
|
171
|
+
access_manager: ChecksumAddress
|
|
172
|
+
price_oracle_addr: ChecksumAddress
|
|
173
|
+
rewards_manager: ChecksumAddress | None
|
|
174
|
+
withdraw_manager: ChecksumAddress | None
|
|
174
175
|
asset_price_usd: float | None
|
|
175
|
-
fuses: list
|
|
176
|
-
balance_fuses: list
|
|
177
|
-
instant_fuses: list
|
|
176
|
+
fuses: list[ChecksumAddress]
|
|
177
|
+
balance_fuses: list[BalanceFuse]
|
|
178
|
+
instant_fuses: list[ChecksumAddress]
|
|
178
179
|
# Underlying asset balance held directly on the vault (raw). Part of
|
|
179
180
|
# totalAssets via ERC4626 base accounting; by design NOT tracked as an
|
|
180
181
|
# ERC20_VAULT_BALANCE substrate, so it is read directly in
|
|
@@ -206,7 +207,7 @@ class VaultData:
|
|
|
206
207
|
# breakdowns (Morpho loan/collateral, Aave reserve assets). Sourced from the
|
|
207
208
|
# vault's PriceOracleMiddleware. Missing keys mean the oracle has no source
|
|
208
209
|
# configured for that token.
|
|
209
|
-
token_prices_usd: dict[
|
|
210
|
+
token_prices_usd: dict[HexAddress, float] | None = None
|
|
210
211
|
# Middleware-priced tokens of granted substrates the PriceOracleMiddleware
|
|
211
212
|
# cannot price (see readers.unpriceable). None when the check did not run.
|
|
212
213
|
unpriceable_priced_tokens: list[MiddlewarePricedToken] | None = None
|
|
@@ -215,7 +216,7 @@ class VaultData:
|
|
|
215
216
|
# are absent from the dict — used to detect orphan markets (action fuses
|
|
216
217
|
# with no matching balance fuse, which silently drop positions from
|
|
217
218
|
# totalAssets).
|
|
218
|
-
fuse_markets: dict[
|
|
219
|
+
fuse_markets: dict[ChecksumAddress, MarketId] | None = None
|
|
219
220
|
# Substrates registered for each balance-fuse market. Allows the fuse
|
|
220
221
|
# tables to report substrate counts per fuse without duplicating reads.
|
|
221
222
|
market_substrates: dict[MarketId, list[bytes]] | None = None
|
|
@@ -665,7 +666,7 @@ def _fetch_breakdown_token_prices(
|
|
|
665
666
|
ctx: Web3Context,
|
|
666
667
|
oracle: PriceOracleMiddleware,
|
|
667
668
|
addresses: set[ChecksumAddress],
|
|
668
|
-
) -> dict[
|
|
669
|
+
) -> dict[HexAddress, float] | None:
|
|
669
670
|
"""Fetch USD prices for breakdown tokens in one batch via the vault's oracle.
|
|
670
671
|
|
|
671
672
|
Returns a dict keyed by lowercase token address. Tokens with no oracle
|
|
@@ -678,7 +679,7 @@ def _fetch_breakdown_token_prices(
|
|
|
678
679
|
[oracle.get_asset_price(addr) for addr in ordered]
|
|
679
680
|
)
|
|
680
681
|
result = {
|
|
681
|
-
addr
|
|
682
|
+
parse_hex_address(addr): price.readable()
|
|
682
683
|
for addr, price in zip(ordered, prices, strict=True)
|
|
683
684
|
if price is not None
|
|
684
685
|
}
|
|
@@ -1064,7 +1065,7 @@ def fetch_deployment_info(
|
|
|
1064
1065
|
return None, None, error
|
|
1065
1066
|
|
|
1066
1067
|
try:
|
|
1067
|
-
tx = ctx.web3.eth.get_transaction(
|
|
1068
|
+
tx = ctx.web3.eth.get_transaction(tx_hash)
|
|
1068
1069
|
block_number: int = tx["blockNumber"]
|
|
1069
1070
|
block_info = ctx.web3.eth.get_block(block_number)
|
|
1070
1071
|
timestamp: int = block_info["timestamp"]
|