ipor-fusion 4.0.0.dev74__tar.gz → 4.0.0.dev83__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.dev74 → ipor_fusion-4.0.0.dev83}/MIGRATION_4.md +54 -2
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/PKG-INFO +1 -1
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/__init__.py +4 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/chains.py +28 -28
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/access.py +9 -4
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/contract.py +2 -2
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/multicall.py +1 -1
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/simulation.py +7 -3
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/indexer.py +4 -2
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/discovery.py +31 -11
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/simulation.py +6 -4
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/errors.py +43 -12
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/explorer.py +3 -1
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/external_state_events.py +4 -2
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/market_ids.py +0 -7
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/morpho_api.py +116 -72
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/hypercore.py +3 -2
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/lending_health.py +5 -10
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/morpho.py +10 -10
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/substrates.py +2 -3
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/vault_info.py +9 -9
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/vault_managers.py +5 -3
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_multicall.py +2 -1
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_helpers.py +13 -5
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_lending_health.py +3 -4
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_market_morpho_blue.py +90 -2
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_contract_call.py +23 -5
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_lane.py +40 -7
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_transport.py +5 -4
- ipor_fusion-4.0.0.dev83/tests/test_error_hierarchy.py +110 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_public_api.py +2 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_solidity_mirrors.py +0 -2
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_substrates.py +1 -4
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_vault_simulator_blocks.py +12 -2
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/AGENTS.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/README.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/uv.lock +0 -0
|
@@ -133,6 +133,16 @@ and the Stargate fuse's `eid=` take it (`LayerZeroEid(30101)`).
|
|
|
133
133
|
`encode_oft_compose_msg(*, nonce=, src_eid=, amount_ld=, compose_msg=)` takes
|
|
134
134
|
keyword arguments only. `ZERO_ADDRESS` is a `ChecksumAddress` (same value).
|
|
135
135
|
|
|
136
|
+
**Chain registry and removed aliases.** `CHAIN_NAMES`, `CHAIN_NAME_TO_ID`,
|
|
137
|
+
`SUPPORTED_CHAIN_IDS`, `GET_LOGS_RANGE_HINTS`, `BLOCK_EXPLORER_URLS`,
|
|
138
|
+
`MORPHO_BLUE_ADDRESSES`, `AAVE_V3_POOL`, `PUBLIC_ALLOCATOR_ADDRESSES` and
|
|
139
|
+
`HYPERCORE_PENDING_READERS` are keyed by `ChainId` (`HYPEREVM_CHAIN_ID` is one) and
|
|
140
|
+
`explorer.SUPPORTED_CHAINS` is a `frozenset[ChainId]`, so
|
|
141
|
+
`ensure_supported_chain(CHAIN_NAME_TO_ID["ethereum"])` type-checks;
|
|
142
|
+
`PUBLIC_ALLOCATOR_ADDRESSES` values are `ChecksumAddress`. `IporFusionMarkets.RWA`
|
|
143
|
+
is gone: market 50 is `IporFusionMarkets.EXTERNAL_STATE`. `MORPHO_BLUE_ADDRESS` is
|
|
144
|
+
gone: use `morpho_blue_address(chain_id)`.
|
|
145
|
+
|
|
136
146
|
**Markets and substrates.** Every `IporFusionMarkets` member is a `MarketId`.
|
|
137
147
|
`decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
|
|
138
148
|
|
|
@@ -210,8 +220,10 @@ except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
|
|
|
210
220
|
- It raises `MorphoApiError` for a market id or address it parses (market and V1
|
|
211
221
|
allocation ids, V1 flow-cap ids, vault/asset/owner/token/oracle/IRM/allocator
|
|
212
222
|
addresses) when the value is invalid, or absent where the field is optional in the
|
|
213
|
-
answer.
|
|
214
|
-
without `address
|
|
223
|
+
answer. Any other answer it cannot read (a missing key such as a `loanAsset`
|
|
224
|
+
without `address`, a value of the wrong type, a body that is not a JSON object) and
|
|
225
|
+
any transport failure also raise `MorphoApiError`, never `KeyError`, `TypeError` or
|
|
226
|
+
`ValueError` (see [Errors](#errors)). When matching a market's own allocation and flow cap, an id that
|
|
215
227
|
does not parse matches nothing, and a V1 flow cap without an id is skipped, as
|
|
216
228
|
before. `--market` rejects a malformed id.
|
|
217
229
|
- `LendingMarketHealth.substrate_id` for a Morpho market is `0x`-prefixed (the JSON
|
|
@@ -222,3 +234,43 @@ except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
|
|
|
222
234
|
- The on-disk caches (`~/.cache/ipor-fusion/`) key addresses lowercased; older entries
|
|
223
235
|
are rekeyed on load, so a contract name cached for one spelling now also resolves
|
|
224
236
|
for the other.
|
|
237
|
+
|
|
238
|
+
## Errors
|
|
239
|
+
|
|
240
|
+
Every exception class the SDK defines now derives from `IporFusionError` and is
|
|
241
|
+
importable from `ipor_fusion`; the builtin bases they had are kept. The rule the
|
|
242
|
+
changes below apply is: a caller's mistake (a bad argument, or an operation the
|
|
243
|
+
object was not built for) raises a plain `ValueError`; a chain, provider or remote
|
|
244
|
+
API answering something the SDK cannot use raises an `IporFusionError` subclass.
|
|
245
|
+
4.0 applies it to the paths listed here, not yet everywhere: the HyperCore NAV
|
|
246
|
+
reader (`read_hypercore_nav`) still raises `ValueError` for a failed precompile read,
|
|
247
|
+
a missing oracle price or an unsupported dex configuration,
|
|
248
|
+
`fetch_verifier_result` can still raise `KeyError` or `ValueError` on a malformed
|
|
249
|
+
indexer answer, and `erc20_balance_slot` raises `ValueError` when no slot answers.
|
|
250
|
+
|
|
251
|
+
- `MorphoApiError` (still a `RuntimeError`), `ExternalStateEventDecodeError` (still a
|
|
252
|
+
`ValueError`) and `VerifierResultUnavailable` (still a `LookupError`) now derive
|
|
253
|
+
from `IporFusionError`, so an `except IporFusionError` placed before their own
|
|
254
|
+
handlers catches them first. `MorphoApiError` lives in `ipor_fusion.errors` and is
|
|
255
|
+
exported from `ipor_fusion`; `ipor_fusion.morpho_api.MorphoApiError` still works.
|
|
256
|
+
- `EmptyCallResultError` is no longer an eth_abi `InsufficientDataBytes` (nor a
|
|
257
|
+
`DecodingError`). Code that caught either around `.call()` to tolerate a missing
|
|
258
|
+
contract or getter must catch `EmptyCallResultError`; return data that is present
|
|
259
|
+
but too short still raises eth_abi's error.
|
|
260
|
+
- Crosschain discovery (`discover_deployment`, `discover_lane_fuses`,
|
|
261
|
+
`detect_transport`, `open_lane`, `CrosschainDeployment.executor`) raises the new
|
|
262
|
+
`CrosschainDiscoveryError` instead of `ValueError` for on-chain configuration it
|
|
263
|
+
cannot use: a malformed or unknown market substrate, an executor managed by another
|
|
264
|
+
vault or of no known transport, a missing or duplicate lane fuse, or not exactly one
|
|
265
|
+
executor of a transport. An executor reporting `transportKind()` `UNDEFINED` is now
|
|
266
|
+
rejected by `detect_transport` instead of being returned. A transport kind passed in
|
|
267
|
+
that has no lane is still a `ValueError`.
|
|
268
|
+
- `Call.call()` and `Call.decode()` on a write-only `Call` raise `ValueError`, not
|
|
269
|
+
`RuntimeError`. `CrosschainSimulator.relay` raises `ValueError`, not
|
|
270
|
+
`RuntimeError`, for a message to a chain that was not added.
|
|
271
|
+
- `VaultSimulator.run` (provider error from `eth_simulateV1`, or a result count that
|
|
272
|
+
does not match the queued calls) and `CrosschainSimulator.relay` (not settled within
|
|
273
|
+
`max_rounds`) raise the new `SimulationRunError`, still a `RuntimeError`.
|
|
274
|
+
- The Morpho API client raises `MorphoApiError` for every response it cannot read and
|
|
275
|
+
for any `OSError` or `http.client.HTTPException` while fetching (it caught only
|
|
276
|
+
`URLError` and `TimeoutError` before).
|
|
@@ -174,6 +174,7 @@ from ipor_fusion.dependency_graph import (
|
|
|
174
174
|
from ipor_fusion.errors import (
|
|
175
175
|
CUSTOM_ERRORS,
|
|
176
176
|
ContractNotFoundError,
|
|
177
|
+
CrosschainDiscoveryError,
|
|
177
178
|
EmptyCallResultError,
|
|
178
179
|
IporFusionError,
|
|
179
180
|
LogScanError,
|
|
@@ -181,6 +182,7 @@ from ipor_fusion.errors import (
|
|
|
181
182
|
MorphoMarketNotFoundError,
|
|
182
183
|
NotPlasmaVaultError,
|
|
183
184
|
SimulationError,
|
|
185
|
+
SimulationRunError,
|
|
184
186
|
TransactionError,
|
|
185
187
|
UnsupportedChainError,
|
|
186
188
|
UnsupportedVaultVersionError,
|
|
@@ -716,6 +718,7 @@ __all__ = [
|
|
|
716
718
|
"market_name",
|
|
717
719
|
"DOCS",
|
|
718
720
|
"ContractNotFoundError",
|
|
721
|
+
"CrosschainDiscoveryError",
|
|
719
722
|
"EmptyCallResultError",
|
|
720
723
|
"IporFusionError",
|
|
721
724
|
"LogScanError",
|
|
@@ -724,6 +727,7 @@ __all__ = [
|
|
|
724
727
|
"NotPlasmaVaultError",
|
|
725
728
|
"TransactionError",
|
|
726
729
|
"SimulationError",
|
|
730
|
+
"SimulationRunError",
|
|
727
731
|
"UnsupportedChainError",
|
|
728
732
|
"UnsupportedVaultVersionError",
|
|
729
733
|
"CUSTOM_ERRORS",
|
|
@@ -5,48 +5,48 @@ from __future__ import annotations
|
|
|
5
5
|
from ipor_fusion.errors import UnsupportedChainError
|
|
6
6
|
from ipor_fusion.types import ChainId
|
|
7
7
|
|
|
8
|
-
CHAIN_NAMES: dict[
|
|
9
|
-
1: "ethereum",
|
|
10
|
-
42161: "arbitrum",
|
|
11
|
-
8453: "base",
|
|
12
|
-
10: "optimism",
|
|
13
|
-
130: "unichain",
|
|
14
|
-
137: "polygon",
|
|
15
|
-
56: "bsc",
|
|
16
|
-
239: "tac",
|
|
17
|
-
9745: "plasma",
|
|
18
|
-
43114: "avalanche",
|
|
19
|
-
250: "fantom",
|
|
20
|
-
143: "monad",
|
|
21
|
-
999: "hyperevm",
|
|
22
|
-
4663: "robinhood",
|
|
23
|
-
747474: "katana",
|
|
24
|
-
3637: "botanix",
|
|
25
|
-
57073: "ink",
|
|
26
|
-
14: "flare",
|
|
8
|
+
CHAIN_NAMES: dict[ChainId, str] = {
|
|
9
|
+
ChainId(1): "ethereum",
|
|
10
|
+
ChainId(42161): "arbitrum",
|
|
11
|
+
ChainId(8453): "base",
|
|
12
|
+
ChainId(10): "optimism",
|
|
13
|
+
ChainId(130): "unichain",
|
|
14
|
+
ChainId(137): "polygon",
|
|
15
|
+
ChainId(56): "bsc",
|
|
16
|
+
ChainId(239): "tac",
|
|
17
|
+
ChainId(9745): "plasma",
|
|
18
|
+
ChainId(43114): "avalanche",
|
|
19
|
+
ChainId(250): "fantom",
|
|
20
|
+
ChainId(143): "monad",
|
|
21
|
+
ChainId(999): "hyperevm",
|
|
22
|
+
ChainId(4663): "robinhood",
|
|
23
|
+
ChainId(747474): "katana",
|
|
24
|
+
ChainId(3637): "botanix",
|
|
25
|
+
ChainId(57073): "ink",
|
|
26
|
+
ChainId(14): "flare",
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
CHAIN_NAME_TO_ID: dict[str,
|
|
29
|
+
CHAIN_NAME_TO_ID: dict[str, ChainId] = {name: cid for cid, name in CHAIN_NAMES.items()}
|
|
30
30
|
|
|
31
31
|
# Chains the on-chain vault tooling (vault info/health/substrate fetch) is
|
|
32
32
|
# validated on. A provider for another chain may connect fine and still fail
|
|
33
33
|
# deeper in the stack (a missing Multicall3, an oracle quirk, an eth_getLogs
|
|
34
34
|
# cap the adaptive scan cannot page through). Extend only after the full
|
|
35
35
|
# vault_info path passes on every live vault of that chain.
|
|
36
|
-
SUPPORTED_CHAIN_IDS: frozenset[
|
|
37
|
-
|
|
36
|
+
SUPPORTED_CHAIN_IDS: frozenset[ChainId] = frozenset(
|
|
37
|
+
ChainId(cid) for cid in (1, 130, 143, 999, 8453, 9745, 42161, 43114, 747474)
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
# First eth_getLogs page size, in blocks, on chains whose providers cap the
|
|
41
41
|
# range (10,000 blocks) or time out on wide queries. The scan still shrinks a
|
|
42
42
|
# page the provider rejects; a hint only skips the whole-range attempt and the
|
|
43
43
|
# probing round trips.
|
|
44
|
-
GET_LOGS_RANGE_HINTS: dict[
|
|
45
|
-
130: 10_000,
|
|
46
|
-
9745: 10_000,
|
|
47
|
-
57073: 10_000,
|
|
48
|
-
43114: 1_000_000,
|
|
49
|
-
747474: 1_000_000,
|
|
44
|
+
GET_LOGS_RANGE_HINTS: dict[ChainId, int] = {
|
|
45
|
+
ChainId(130): 10_000,
|
|
46
|
+
ChainId(9745): 10_000,
|
|
47
|
+
ChainId(57073): 10_000,
|
|
48
|
+
ChainId(43114): 1_000_000,
|
|
49
|
+
ChainId(747474): 1_000_000,
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
|
|
@@ -15,7 +15,11 @@ from ipor_fusion.core.context import Web3Context
|
|
|
15
15
|
from ipor_fusion.core.contract import Call, ContractWrapper
|
|
16
16
|
from ipor_fusion.core.multicall import Multicall3
|
|
17
17
|
from ipor_fusion.core.plasma_vault import PlasmaVault
|
|
18
|
-
from ipor_fusion.errors import
|
|
18
|
+
from ipor_fusion.errors import (
|
|
19
|
+
ContractNotFoundError,
|
|
20
|
+
EmptyCallResultError,
|
|
21
|
+
NotPlasmaVaultError,
|
|
22
|
+
)
|
|
19
23
|
from ipor_fusion.types import AccessManagerAddress, Period, PlasmaVaultAddress, RoleId
|
|
20
24
|
|
|
21
25
|
|
|
@@ -189,8 +193,9 @@ def resolve_access_manager(
|
|
|
189
193
|
|
|
190
194
|
Raises ContractNotFoundError when no code is deployed at the address (as
|
|
191
195
|
of ctx.default_block), and NotPlasmaVaultError when the contract does not
|
|
192
|
-
expose getAccessManagerAddress()
|
|
193
|
-
(
|
|
196
|
+
expose getAccessManagerAddress(): an empty eth_call return
|
|
197
|
+
(EmptyCallResultError), return data too short to decode
|
|
198
|
+
(InsufficientDataBytes) or a revert (ContractLogicError).
|
|
194
199
|
Provider/transport errors propagate unchanged.
|
|
195
200
|
"""
|
|
196
201
|
checksum = Web3.to_checksum_address(vault_address)
|
|
@@ -210,7 +215,7 @@ def resolve_access_manager(
|
|
|
210
215
|
.get_access_manager_address()
|
|
211
216
|
.call()
|
|
212
217
|
)
|
|
213
|
-
except (InsufficientDataBytes, ContractLogicError) as exc:
|
|
218
|
+
except (EmptyCallResultError, InsufficientDataBytes, ContractLogicError) as exc:
|
|
214
219
|
raise NotPlasmaVaultError(
|
|
215
220
|
f"Address {checksum} on chain {ctx.chain_id} does not appear "
|
|
216
221
|
f"to be a Plasma Vault."
|
|
@@ -73,7 +73,7 @@ class Call(Generic[T]):
|
|
|
73
73
|
"""Execute as `eth_call`; decode and convert per `output_types`/`decoder`."""
|
|
74
74
|
actual = self._resolve_ctx(ctx)
|
|
75
75
|
if not self.output_types:
|
|
76
|
-
raise
|
|
76
|
+
raise ValueError(
|
|
77
77
|
"Call.call() on a write-only Call — use .send() instead "
|
|
78
78
|
"(no output_types declared)."
|
|
79
79
|
)
|
|
@@ -91,7 +91,7 @@ class Call(Generic[T]):
|
|
|
91
91
|
"""Decode raw return data per `output_types`/`decoder` — the second half
|
|
92
92
|
of `.call()`, for return data fetched elsewhere (e.g. `Multicall3`)."""
|
|
93
93
|
if not self.output_types:
|
|
94
|
-
raise
|
|
94
|
+
raise ValueError("Call.decode() on a write-only Call.")
|
|
95
95
|
values = tuple(decode(self.output_types, bytes(raw)))
|
|
96
96
|
single: Any = values[0] if len(values) == 1 else values
|
|
97
97
|
if self.decoder is not None:
|
|
@@ -23,7 +23,7 @@ MULTICALL3_ADDRESS: ChecksumAddress = Web3.to_checksum_address(
|
|
|
23
23
|
DEFAULT_BATCH_SIZE = 100
|
|
24
24
|
|
|
25
25
|
# A sub-call that reverts, returns nothing or returns undecodable data.
|
|
26
|
-
_CALL_FAILURES = (ContractLogicError, DecodingError)
|
|
26
|
+
_CALL_FAILURES = (ContractLogicError, EmptyCallResultError, DecodingError)
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class Multicall3(ContractWrapper):
|
|
@@ -15,7 +15,11 @@ from web3.utils.address import get_create_address
|
|
|
15
15
|
|
|
16
16
|
from ipor_fusion.core.contract import Call, _encode_calldata
|
|
17
17
|
from ipor_fusion.core.fuse_action import FuseAction
|
|
18
|
-
from ipor_fusion.errors import
|
|
18
|
+
from ipor_fusion.errors import (
|
|
19
|
+
SimulationError,
|
|
20
|
+
SimulationRunError,
|
|
21
|
+
decode_custom_error,
|
|
22
|
+
)
|
|
19
23
|
from ipor_fusion.types import PlasmaVaultAddress
|
|
20
24
|
|
|
21
25
|
#: ``n`` (the sent block's index in the run) -> account overrides for it.
|
|
@@ -570,7 +574,7 @@ class VaultSimulator:
|
|
|
570
574
|
)
|
|
571
575
|
if "error" in response:
|
|
572
576
|
err = response["error"]
|
|
573
|
-
raise
|
|
577
|
+
raise SimulationRunError(f"eth_simulateV1 failed: {err}")
|
|
574
578
|
|
|
575
579
|
return self._parse_response(response["result"])
|
|
576
580
|
|
|
@@ -597,7 +601,7 @@ class VaultSimulator:
|
|
|
597
601
|
for block_result in result:
|
|
598
602
|
raw_calls.extend(block_result.get("calls", []))
|
|
599
603
|
if len(raw_calls) != len(sources):
|
|
600
|
-
raise
|
|
604
|
+
raise SimulationRunError(
|
|
601
605
|
"eth_simulateV1 returned "
|
|
602
606
|
f"{len(raw_calls)} call results for {len(sources)} queued calls"
|
|
603
607
|
)
|
{ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/ccip/indexer.py
RENAMED
|
@@ -21,6 +21,7 @@ from web3 import Web3
|
|
|
21
21
|
|
|
22
22
|
from ipor_fusion.core.contract import Call
|
|
23
23
|
from ipor_fusion.crosschain.ccip.chainlink import CcipOffRamp
|
|
24
|
+
from ipor_fusion.errors import IporFusionError
|
|
24
25
|
from ipor_fusion.types import MessageId
|
|
25
26
|
|
|
26
27
|
#: Chainlink Labs' public mainnet indexers, tried in order.
|
|
@@ -32,9 +33,10 @@ INDEXER_URLS: tuple[str, ...] = (
|
|
|
32
33
|
_HEADERS = {"Accept": "application/json", "User-Agent": "ipor-fusion-sdk"}
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
class VerifierResultUnavailable(LookupError):
|
|
36
|
+
class VerifierResultUnavailable(IporFusionError, LookupError):
|
|
36
37
|
"""No indexer could serve a verifier result for the message (not verified
|
|
37
|
-
yet, or the indexers were unreachable); the message says which.
|
|
38
|
+
yet, or the indexers were unreachable); the message says which. Also a
|
|
39
|
+
LookupError: the result looked up is not there (yet)."""
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
@dataclass(frozen=True, slots=True)
|
|
@@ -9,6 +9,9 @@ whose ``hasDispatcher`` is true), every
|
|
|
9
9
|
turns that into one ``CrosschainLane`` per executor and spoke; ``open_lane``
|
|
10
10
|
does it for one executor address. ``LANES`` is the only place that knows the
|
|
11
11
|
concrete lane classes: a new transport is a new subpackage plus one entry.
|
|
12
|
+
|
|
13
|
+
On-chain configuration that discovery cannot use raises ``CrosschainDiscoveryError``;
|
|
14
|
+
``ValueError`` is kept for a bad argument (a transport kind with no lane).
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
17
|
from __future__ import annotations
|
|
@@ -31,7 +34,7 @@ from ipor_fusion.crosschain.lane import CrosschainLane, LaneFuses
|
|
|
31
34
|
from ipor_fusion.crosschain.messages import CrosschainTransportKind
|
|
32
35
|
from ipor_fusion.crosschain.stargate.contracts import StargateCrosschainExecutor
|
|
33
36
|
from ipor_fusion.crosschain.stargate.lane import StargateLane
|
|
34
|
-
from ipor_fusion.errors import EmptyCallResultError
|
|
37
|
+
from ipor_fusion.errors import CrosschainDiscoveryError, EmptyCallResultError
|
|
35
38
|
from ipor_fusion.fuses.crosschain.base import (
|
|
36
39
|
CrosschainSubstrateLib,
|
|
37
40
|
CrosschainSubstrateType,
|
|
@@ -75,11 +78,18 @@ def detect_transport(
|
|
|
75
78
|
non-zero ``STARGATE_POOL()`` and no ``transportKind()``."""
|
|
76
79
|
transport_kind = _probe(CcipCrosschainExecutor(ctx, executor).transport_kind())
|
|
77
80
|
if transport_kind is not None:
|
|
78
|
-
|
|
81
|
+
if transport_kind not in LANES:
|
|
82
|
+
raise CrosschainDiscoveryError(
|
|
83
|
+
f"{executor} reports transportKind {transport_kind.name}, "
|
|
84
|
+
"which has no lane"
|
|
85
|
+
)
|
|
86
|
+
return transport_kind
|
|
79
87
|
pool = _probe(StargateCrosschainExecutor(ctx, executor).stargate_pool())
|
|
80
88
|
if pool is not None and pool != ZERO_ADDRESS:
|
|
81
89
|
return CrosschainTransportKind.STARGATE_LAYERZERO
|
|
82
|
-
raise
|
|
90
|
+
raise CrosschainDiscoveryError(
|
|
91
|
+
f"{executor} is not a crosschain executor of a known transport"
|
|
92
|
+
)
|
|
83
93
|
|
|
84
94
|
|
|
85
95
|
def discover_lane_fuses(
|
|
@@ -108,14 +118,16 @@ def discover_lane_fuses(
|
|
|
108
118
|
for role, selector in selectors.items():
|
|
109
119
|
if selector in code:
|
|
110
120
|
if role in found:
|
|
111
|
-
raise
|
|
121
|
+
raise CrosschainDiscoveryError(
|
|
112
122
|
f"two {role} fuses on market {market_id}: "
|
|
113
123
|
f"{found[role]} and {fuse}"
|
|
114
124
|
)
|
|
115
125
|
found[role] = fuse
|
|
116
126
|
missing = [role for role in selectors if role not in found]
|
|
117
127
|
if missing:
|
|
118
|
-
raise
|
|
128
|
+
raise CrosschainDiscoveryError(
|
|
129
|
+
f"no {', '.join(missing)} fuse on market {market_id}"
|
|
130
|
+
)
|
|
119
131
|
return LaneFuses(found["supply"], found["command"], found["claim"])
|
|
120
132
|
|
|
121
133
|
|
|
@@ -163,7 +175,7 @@ class CrosschainDeployment:
|
|
|
163
175
|
or more than one."""
|
|
164
176
|
found = [e for e in self.executors if e.transport_kind == transport_kind]
|
|
165
177
|
if len(found) != 1:
|
|
166
|
-
raise
|
|
178
|
+
raise CrosschainDiscoveryError(
|
|
167
179
|
f"{len(found)} {transport_kind.name} executors on market "
|
|
168
180
|
f"{self.market_id} of {self.vault}"
|
|
169
181
|
)
|
|
@@ -180,17 +192,23 @@ def discover_deployment(
|
|
|
180
192
|
remote_vaults: dict[ChainId, list[ChecksumAddress]] = {}
|
|
181
193
|
for raw in plasma_vault.get_market_substrates(market_id).call():
|
|
182
194
|
raw_bytes = bytes(raw)
|
|
183
|
-
|
|
195
|
+
try:
|
|
196
|
+
substrate = CrosschainSubstrateLib.bytes32_to_substrate(raw_bytes)
|
|
197
|
+
except ValueError as exc:
|
|
198
|
+
raise CrosschainDiscoveryError(
|
|
199
|
+
f"substrate {raw_bytes.hex()} on market {market_id} does not "
|
|
200
|
+
f"decode: {exc}"
|
|
201
|
+
) from exc
|
|
184
202
|
if substrate.substrate_type == CrosschainSubstrateType.EXECUTOR:
|
|
185
203
|
if substrate.chain_id != 0:
|
|
186
|
-
raise
|
|
204
|
+
raise CrosschainDiscoveryError(
|
|
187
205
|
f"executor substrate {raw_bytes.hex()} on market {market_id} "
|
|
188
206
|
f"has nonzero chain id {substrate.chain_id}"
|
|
189
207
|
)
|
|
190
208
|
executors.append(substrate.substrate_address)
|
|
191
209
|
elif substrate.substrate_type == CrosschainSubstrateType.REMOTE_VAULT:
|
|
192
210
|
if substrate.chain_id == 0:
|
|
193
|
-
raise
|
|
211
|
+
raise CrosschainDiscoveryError(
|
|
194
212
|
f"remote vault substrate {raw_bytes.hex()} on market "
|
|
195
213
|
f"{market_id} has zero chain id"
|
|
196
214
|
)
|
|
@@ -198,7 +216,7 @@ def discover_deployment(
|
|
|
198
216
|
substrate.substrate_address
|
|
199
217
|
)
|
|
200
218
|
else:
|
|
201
|
-
raise
|
|
219
|
+
raise CrosschainDiscoveryError(
|
|
202
220
|
f"substrate {raw_bytes.hex()} on market {market_id} has type "
|
|
203
221
|
f"{substrate.substrate_type.name}"
|
|
204
222
|
)
|
|
@@ -208,7 +226,9 @@ def discover_deployment(
|
|
|
208
226
|
wrapper = CrosschainExecutor(hub_ctx, address)
|
|
209
227
|
manager = wrapper.manager().call()
|
|
210
228
|
if manager != vault:
|
|
211
|
-
raise
|
|
229
|
+
raise CrosschainDiscoveryError(
|
|
230
|
+
f"executor {address} is managed by {manager}, not {vault}"
|
|
231
|
+
)
|
|
212
232
|
transport_kind = detect_transport(hub_ctx, address)
|
|
213
233
|
infos.append(
|
|
214
234
|
CrosschainExecutorInfo(
|
{ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/crosschain/simulation.py
RENAMED
|
@@ -40,6 +40,7 @@ from ipor_fusion.crosschain.transport import (
|
|
|
40
40
|
CrosschainTransport,
|
|
41
41
|
OutboundMessage,
|
|
42
42
|
)
|
|
43
|
+
from ipor_fusion.errors import SimulationRunError
|
|
43
44
|
from ipor_fusion.types import ChainId, PlasmaVaultAddress
|
|
44
45
|
|
|
45
46
|
#: What the synthetic token source holds: enough for any test amount.
|
|
@@ -117,8 +118,9 @@ class CrosschainSimulator:
|
|
|
117
118
|
def relay(self, max_rounds: int = 8) -> dict[ChainId, SimulationResult]:
|
|
118
119
|
"""Run every chain and deliver messages until nothing is in flight.
|
|
119
120
|
|
|
120
|
-
Runs at most ``max_rounds`` rounds and raises ``
|
|
121
|
-
messages are still being produced afterward
|
|
121
|
+
Runs at most ``max_rounds`` rounds and raises ``SimulationRunError`` if
|
|
122
|
+
messages are still being produced afterward, and ``ValueError`` if a
|
|
123
|
+
message targets a chain that was not added. A delivery that reverts
|
|
122
124
|
stays in the destination call list and surfaces through that chain's
|
|
123
125
|
``SimulationResult``.
|
|
124
126
|
"""
|
|
@@ -128,7 +130,7 @@ class CrosschainSimulator:
|
|
|
128
130
|
break
|
|
129
131
|
pending = self._run_round(pending)
|
|
130
132
|
if pending:
|
|
131
|
-
raise
|
|
133
|
+
raise SimulationRunError(f"relay did not settle within {max_rounds} rounds")
|
|
132
134
|
return dict(self._results)
|
|
133
135
|
|
|
134
136
|
def _run_round(self, chain_ids: set[ChainId]) -> set[ChainId]:
|
|
@@ -147,7 +149,7 @@ class CrosschainSimulator:
|
|
|
147
149
|
def _deliver(self, message: OutboundMessage) -> None:
|
|
148
150
|
destination = self._chains.get(message.dst_chain_id)
|
|
149
151
|
if destination is None:
|
|
150
|
-
raise
|
|
152
|
+
raise ValueError(
|
|
151
153
|
f"message {message.message_id.hex()} targets chain "
|
|
152
154
|
f"{message.dst_chain_id}, which was not added to the simulator"
|
|
153
155
|
)
|
|
@@ -5,7 +5,6 @@ import re
|
|
|
5
5
|
from collections.abc import Iterable
|
|
6
6
|
|
|
7
7
|
from eth_abi import decode as abi_decode
|
|
8
|
-
from eth_abi.exceptions import InsufficientDataBytes
|
|
9
8
|
from eth_typing import ChecksumAddress
|
|
10
9
|
from eth_utils import function_signature_to_4byte_selector
|
|
11
10
|
from web3 import Web3
|
|
@@ -147,35 +146,48 @@ def get_revert_reason(web3: Web3, tx_hash: TxHash, receipt: TxReceipt) -> str |
|
|
|
147
146
|
|
|
148
147
|
|
|
149
148
|
class IporFusionError(Exception):
|
|
150
|
-
"""Base
|
|
149
|
+
"""Base of every exception the SDK defines.
|
|
150
|
+
|
|
151
|
+
The rule the SDK follows (a few older readers do not yet; see
|
|
152
|
+
MIGRATION_4.md): a subclass when the chain, a provider or a remote API
|
|
153
|
+
answers something it cannot use. A caller's mistake (a bad argument, or an
|
|
154
|
+
operation the object was not built for, such as ``.call()`` on a write-only
|
|
155
|
+
``Call``) raises a builtin ``ValueError`` instead, as the standard library
|
|
156
|
+
does for an operation on a closed file. A subclass that also describes bad
|
|
157
|
+
input (an address that is not a vault, a chain the tooling does not cover)
|
|
158
|
+
additionally derives from ``ValueError``; a few keep the builtin base they
|
|
159
|
+
had before 4.0 (``RuntimeError``, ``LookupError``) so existing handlers
|
|
160
|
+
still catch them.
|
|
161
|
+
"""
|
|
151
162
|
|
|
152
163
|
|
|
153
164
|
class ContractNotFoundError(IporFusionError, ValueError):
|
|
154
165
|
"""No contract deployed at the given address.
|
|
155
166
|
|
|
156
|
-
Also a ValueError
|
|
167
|
+
Also a ValueError: the address the caller passed holds nothing to read.
|
|
157
168
|
"""
|
|
158
169
|
|
|
159
170
|
|
|
160
171
|
class NotPlasmaVaultError(IporFusionError, ValueError):
|
|
161
172
|
"""Contract exists but does not implement the Plasma Vault interface.
|
|
162
173
|
|
|
163
|
-
Also a ValueError
|
|
174
|
+
Also a ValueError: the address the caller passed is not a vault.
|
|
164
175
|
"""
|
|
165
176
|
|
|
166
177
|
|
|
167
178
|
class UnsupportedChainError(IporFusionError, ValueError):
|
|
168
179
|
"""Chain is not (yet) supported by the on-chain vault tooling.
|
|
169
180
|
|
|
170
|
-
Also a ValueError
|
|
181
|
+
Also a ValueError: the chain the caller passed is outside what the tooling
|
|
182
|
+
covers.
|
|
171
183
|
"""
|
|
172
184
|
|
|
173
185
|
|
|
174
186
|
class UnsupportedVaultVersionError(IporFusionError, ValueError):
|
|
175
187
|
"""A Plasma Vault predates a getter the on-chain vault tooling requires.
|
|
176
188
|
|
|
177
|
-
Also a ValueError
|
|
178
|
-
|
|
189
|
+
Also a ValueError: the vault the caller passed cannot be read by this
|
|
190
|
+
tooling.
|
|
179
191
|
"""
|
|
180
192
|
|
|
181
193
|
|
|
@@ -202,21 +214,40 @@ class LogSourceUnavailable(IporFusionError):
|
|
|
202
214
|
falls back to its own `eth_getLogs` scan."""
|
|
203
215
|
|
|
204
216
|
|
|
205
|
-
class EmptyCallResultError(IporFusionError
|
|
217
|
+
class EmptyCallResultError(IporFusionError):
|
|
206
218
|
"""`eth_call` returned no data for a call that declares return values.
|
|
207
219
|
|
|
208
220
|
Almost always there is no contract at the target address on the connected
|
|
209
221
|
chain (or at the requested block); a contract whose catch-all fallback
|
|
210
|
-
returns nothing looks the same.
|
|
211
|
-
|
|
212
|
-
catch it.
|
|
222
|
+
returns nothing looks the same. Return data that is present but too short
|
|
223
|
+
still raises eth_abi's own decoding error.
|
|
213
224
|
"""
|
|
214
225
|
|
|
215
226
|
|
|
216
227
|
class MorphoMarketNotFoundError(IporFusionError, ValueError):
|
|
217
228
|
"""Morpho Blue market ID was never created on the connected chain.
|
|
218
229
|
|
|
219
|
-
Also a ValueError
|
|
230
|
+
Also a ValueError: the market id the caller passed names no market.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class MorphoApiError(IporFusionError, RuntimeError):
|
|
235
|
+
"""The Morpho API is unreachable, returned an error, or answered with a
|
|
236
|
+
response the client cannot parse (missing keys, wrong types, invalid
|
|
237
|
+
addresses or market ids). Also a RuntimeError, its base before 4.0."""
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class CrosschainDiscoveryError(IporFusionError):
|
|
241
|
+
"""A crosschain vault's on-chain configuration is not one discovery can
|
|
242
|
+
use: a malformed or unexpected market grant, an executor managed by another
|
|
243
|
+
vault or of no known transport, or a missing or duplicate lane fuse."""
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class SimulationRunError(IporFusionError, RuntimeError):
|
|
247
|
+
"""A simulation could not run to completion: the provider rejected
|
|
248
|
+
``eth_simulateV1`` or answered with a different number of call results than
|
|
249
|
+
were queued, or a crosschain relay was still producing messages after its
|
|
250
|
+
last round. Distinct from a simulated call that reverts (``SimulationError``).
|
|
220
251
|
"""
|
|
221
252
|
|
|
222
253
|
|
|
@@ -17,7 +17,9 @@ 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
|
|
|
20
|
-
SUPPORTED_CHAINS =
|
|
20
|
+
SUPPORTED_CHAINS: frozenset[ChainId] = frozenset(
|
|
21
|
+
ChainId(cid) for cid in (1, 42161, 8453, 10, 137, 56, 43114, 250)
|
|
22
|
+
)
|
|
21
23
|
|
|
22
24
|
_MAX_RETRIES = 3
|
|
23
25
|
_RETRY_DELAY = 1.0
|
{ipor_fusion-4.0.0.dev74 → ipor_fusion-4.0.0.dev83}/src/ipor_fusion/fuses/external_state_events.py
RENAMED
|
@@ -32,6 +32,7 @@ from eth_typing import ChecksumAddress
|
|
|
32
32
|
from web3 import Web3
|
|
33
33
|
from web3.types import TxReceipt
|
|
34
34
|
|
|
35
|
+
from ipor_fusion.errors import IporFusionError
|
|
35
36
|
from ipor_fusion.types import TxHash, parse_tx_hash
|
|
36
37
|
|
|
37
38
|
|
|
@@ -123,9 +124,10 @@ EXTERNAL_STATE_TOPICS: tuple[str, ...] = tuple(
|
|
|
123
124
|
)
|
|
124
125
|
|
|
125
126
|
|
|
126
|
-
class ExternalStateEventDecodeError(ValueError):
|
|
127
|
+
class ExternalStateEventDecodeError(IporFusionError, ValueError):
|
|
127
128
|
"""A log carries a listed ExternalState topic but its data does not decode
|
|
128
|
-
to the declared parameters: malformed, never a plausible event.
|
|
129
|
+
to the declared parameters: malformed, never a plausible event. Also a
|
|
130
|
+
ValueError: the log the caller passed is not a decodable event."""
|
|
129
131
|
|
|
130
132
|
|
|
131
133
|
@dataclass(frozen=True, slots=True)
|
|
@@ -87,13 +87,6 @@ class IporFusionMarkets:
|
|
|
87
87
|
# 4 BALANCE_ACCOUNT, 5 STALENESS_MAX, 6 BIG_CHANGE_BPS, 7 DUST_THRESHOLD,
|
|
88
88
|
# 8 MIN_UPDATE_INTERVAL).
|
|
89
89
|
EXTERNAL_STATE = MarketId(50)
|
|
90
|
-
# Deprecated alias of EXTERNAL_STATE, kept for backward compatibility.
|
|
91
|
-
# Reverse id->name lookups skip aliases (see _DEPRECATED_ALIASES) so the
|
|
92
|
-
# canonical name is the one displayed.
|
|
93
|
-
RWA = MarketId(50)
|
|
94
|
-
# Deprecated member names that alias a canonical member of the same value;
|
|
95
|
-
# reverse id->name lookups skip these so the canonical name wins.
|
|
96
|
-
_DEPRECATED_ALIASES = frozenset({"RWA"})
|
|
97
90
|
# substrate type: AguaSubstrateLib (VAULT or ASSET)
|
|
98
91
|
AGUA_GLOBAL_CARRY = MarketId(51)
|
|
99
92
|
# dependence graph: balance of ERC20_VAULT_BALANCE (purchase tokens return
|