ipor-fusion 4.0.0.dev286__tar.gz → 4.0.0.dev293__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.dev286 → ipor_fusion-4.0.0.dev293}/AGENTS.md +11 -1
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/MIGRATION_4.md +22 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/PKG-INFO +8 -4
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/README.md +7 -3
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/__init__.py +10 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/about.py +2 -1
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/external_state_executor.py +64 -10
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/__init__.py +8 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/external_state.py +11 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/external_state_event_decoding.py +48 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/mcp/models.py +3 -2
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/types.py +5 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_external_state_events.py +23 -3
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_external_state_executor.py +21 -4
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_fuse_encoding.py +7 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_public_api.py +10 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_solidity_mirrors.py +8 -6
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/address_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/context_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/simulation_result.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/hypercore/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/registry.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_event_args.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_fake_rpc.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_golden.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_live.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_oracle_mapping_fake.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/hypercore_official.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_blue.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/simulate_v1_request.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/fetched.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/full.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/full.txt +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_address.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_helpers.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_market_golden.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_mcp_parity.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_oracle_mapping_golden.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_docs_typing.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_governance_wrappers.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_mcp_address.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_registry.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulation_errors.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_simulation_observation.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_vault_data_block.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_vault_info_fetched_golden.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_vault_reconciliation_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/uv.lock +0 -0
|
@@ -154,6 +154,15 @@ the lifecycle matrix follows that.
|
|
|
154
154
|
`commit-lint.yml` fails a PR whose commit subjects or title break it. Never
|
|
155
155
|
hand-bump `project.version`; the release workflow stamps it and re-locks
|
|
156
156
|
`uv.lock`.
|
|
157
|
+
- Preview features (`ipor_fusion.crosschain` with its subpackages, and
|
|
158
|
+
`ipor_fusion.hypercore`) keep no backward compatibility: they follow the
|
|
159
|
+
current official contracts only, and support for retired deployments (the
|
|
160
|
+
pilot executors and factories, canaries) is removed rather than kept beside
|
|
161
|
+
them. From 4.1 on, a breaking change confined to them ships as `feat`/`fix`
|
|
162
|
+
without `!` or a `BREAKING CHANGE:` footer, so it never forces a major
|
|
163
|
+
release; anything it changes outside those packages (the top level, the
|
|
164
|
+
`vault info` document, the CLI and MCP) is still breaking by the usual rules.
|
|
165
|
+
4.0 itself keeps its MIGRATION_4.md entries for them, for 3.x users.
|
|
157
166
|
- This repository is public, and commit messages also ship in `CHANGELOG.md`
|
|
158
167
|
inside the wheel. No issue-tracker keys (`IL-` plus a number), internal hosts,
|
|
159
168
|
dashboard links or private repository names in commits, branch names, PR
|
|
@@ -207,7 +216,8 @@ the lifecycle matrix follows that.
|
|
|
207
216
|
path. Adding or removing a
|
|
208
217
|
name updates the snapshots in `tests/test_public_api.py` (every `__all__`, and
|
|
209
218
|
the names each public module keeps by path); moving a name out of a public
|
|
210
|
-
module leaves a re-export there. A removal or rename is a breaking change
|
|
219
|
+
module leaves a re-export there. A removal or rename is a breaking change,
|
|
220
|
+
except inside a preview package (see Conventions).
|
|
211
221
|
A project type used in a public signature or field is exported too
|
|
212
222
|
(`tests/test_public_type_reachability.py`). The package is `py.typed`, so public APIs stay fully
|
|
213
223
|
annotated.
|
|
@@ -427,6 +427,28 @@ the modules are internal.
|
|
|
427
427
|
`MAX_MARK_ORACLE_DEVIATION_BPS`, `NAV_JUMP_FLOOR_USD6` (7..11); `decode_substrate`
|
|
428
428
|
decodes them instead of flagging them as errors.
|
|
429
429
|
|
|
430
|
+
#### ExternalState: two executor builds, one custodian marker
|
|
431
|
+
|
|
432
|
+
The 2026-10-21 contracts release (on Arbitrum in `ipor-abi`) changes what the
|
|
433
|
+
balance fuse and the pre-hook compare to detect an unprocessed custodian
|
|
434
|
+
update: the executor's confirm count instead of the last confirmed update's
|
|
435
|
+
timestamp. HyperEVM, Base and Ethereum still publish the earlier build, so both
|
|
436
|
+
exist; the comparison is equality either way.
|
|
437
|
+
|
|
438
|
+
- `ExternalStateVaultState.last_checked_custodian_timestamp` is
|
|
439
|
+
`last_checked_custodian_marker` and `BalanceFuseSnapshot.last_custodian_update_timestamp`
|
|
440
|
+
is `custodian_marker`, both a `CustodianMarker` (an opaque value the
|
|
441
|
+
contracts only compare for equality): a timestamp on an earlier executor, the
|
|
442
|
+
confirm count on a released one. `ExternalStateExecutor.confirm_count()`
|
|
443
|
+
exists only on the released executor (it reverts on an earlier one), and
|
|
444
|
+
`last_custodian_update_timestamp()` reads the timestamp on both.
|
|
445
|
+
- New on the released build: `ExternalStateExecutor.unforwarded_credit(asset)`,
|
|
446
|
+
`proposal_base_balance(account)`, `ExternalStateOperationFuse.sweep_surplus(asset)`,
|
|
447
|
+
and the events `ExternalStateBalanceFuseLastCustodianConfirmCountUpdated` and
|
|
448
|
+
`ExternalStateOperationFuseSweepSurplus` (the released balance fuse no longer
|
|
449
|
+
emits `...LastCustodianTimestampUpdated`; it stays registered for the earlier
|
|
450
|
+
build).
|
|
451
|
+
|
|
430
452
|
**Removed.**
|
|
431
453
|
|
|
432
454
|
- `IporFusionMarkets.RWA`: market 50 is `IporFusionMarkets.EXTERNAL_STATE`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ipor-fusion
|
|
3
|
-
Version: 4.0.0.
|
|
3
|
+
Version: 4.0.0.dev293
|
|
4
4
|
Summary: The IPOR Fusion SDK for Python
|
|
5
5
|
Project-URL: homepage, https://ipor.io
|
|
6
6
|
Project-URL: repository, https://github.com/IPOR-Labs/ipor-fusion.py
|
|
@@ -418,7 +418,8 @@ release; adding one does not.
|
|
|
418
418
|
- **Preview features** (`PREVIEW_FEATURES` in `ipor_fusion.about`) export only
|
|
419
419
|
from their own subpackages, `ipor_fusion.crosschain` (with `crosschain.fuses`,
|
|
420
420
|
`crosschain.ccip` and `crosschain.stargate`) and `ipor_fusion.hypercore`, never
|
|
421
|
-
from the top level, and
|
|
421
|
+
from the top level, and keep no backward compatibility: a minor release may
|
|
422
|
+
change or remove them (see [Preview features](#preview-features-ipor_fusioncrosschain-ipor_fusionhypercore)). Their exception classes
|
|
422
423
|
stay in `ipor_fusion.errors`, and the `vault info` document's HyperCore blocks
|
|
423
424
|
(`HyperCoreJson`, ...) stay with the document.
|
|
424
425
|
- **Third-party types** are not re-exported: import `ChecksumAddress` and
|
|
@@ -463,7 +464,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
|
|
|
463
464
|
| `PriceOracleMiddleware` | Asset price feeds |
|
|
464
465
|
| `PriceOracleMiddlewareManager` | Per-vault price-source overrides |
|
|
465
466
|
| `ExternalStateExecutor` | NAV propose/confirm for off-vault capital (market 50); the executor's thresholds (`staleness_max`, `big_change_bps`, `dust_threshold`, `min_update_interval`), the live aggregate (`get_balance_fuse_snapshot`) and the pre-hook's staleness clock (`get_oldest_update_timestamp`) |
|
|
466
|
-
| `read_external_state`, `ExternalStateVaultState` | The market's vault-scoped state from the vault's ERC-7201 storage at one block: executor, the balance fuse's cached total, the custodian
|
|
467
|
+
| `read_external_state`, `ExternalStateVaultState` | The market's vault-scoped state from the vault's ERC-7201 storage at one block: executor, the balance fuse's cached total, the custodian marker it last checked (a timestamp on the earlier executor build, the confirm count on the 2026-10-21 one) and the pause flag the pre-hook enforces (no getter exposes them) |
|
|
467
468
|
| `decode_external_state_event`, `EXTERNAL_STATE_TOPICS` | The ExternalState events the vault emits (big change, cache updates, unpause, rescue, executor creation and deployment, operation enter/exit), decoded by topic with their raw log identity; a malformed listed log raises |
|
|
468
469
|
| `erc20_balance_slot`, `VaultSimulator.with_erc20_balance` | Fund any address with any ERC-20 in a simulation by a storage override (slot found by probing), no holder to impersonate |
|
|
469
470
|
| `ERC20` | Token reads and approvals |
|
|
@@ -488,7 +489,10 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
|
|
|
488
489
|
### Preview features (`ipor_fusion.crosschain`, `ipor_fusion.hypercore`)
|
|
489
490
|
|
|
490
491
|
Not production (see the note at the top) and outside the stable top level:
|
|
491
|
-
import these from their subpackage
|
|
492
|
+
import these from their subpackage. They track the current official contracts
|
|
493
|
+
only, with no backward compatibility: from 4.1 on, a minor release may change or
|
|
494
|
+
remove any of these names and drops support for retired deployments (such as
|
|
495
|
+
the pilot executors and factories). Pin an exact version if you depend on them.
|
|
492
496
|
|
|
493
497
|
| Name | Purpose |
|
|
494
498
|
|------|---------|
|
|
@@ -369,7 +369,8 @@ release; adding one does not.
|
|
|
369
369
|
- **Preview features** (`PREVIEW_FEATURES` in `ipor_fusion.about`) export only
|
|
370
370
|
from their own subpackages, `ipor_fusion.crosschain` (with `crosschain.fuses`,
|
|
371
371
|
`crosschain.ccip` and `crosschain.stargate`) and `ipor_fusion.hypercore`, never
|
|
372
|
-
from the top level, and
|
|
372
|
+
from the top level, and keep no backward compatibility: a minor release may
|
|
373
|
+
change or remove them (see [Preview features](#preview-features-ipor_fusioncrosschain-ipor_fusionhypercore)). Their exception classes
|
|
373
374
|
stay in `ipor_fusion.errors`, and the `vault info` document's HyperCore blocks
|
|
374
375
|
(`HyperCoreJson`, ...) stay with the document.
|
|
375
376
|
- **Third-party types** are not re-exported: import `ChecksumAddress` and
|
|
@@ -414,7 +415,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
|
|
|
414
415
|
| `PriceOracleMiddleware` | Asset price feeds |
|
|
415
416
|
| `PriceOracleMiddlewareManager` | Per-vault price-source overrides |
|
|
416
417
|
| `ExternalStateExecutor` | NAV propose/confirm for off-vault capital (market 50); the executor's thresholds (`staleness_max`, `big_change_bps`, `dust_threshold`, `min_update_interval`), the live aggregate (`get_balance_fuse_snapshot`) and the pre-hook's staleness clock (`get_oldest_update_timestamp`) |
|
|
417
|
-
| `read_external_state`, `ExternalStateVaultState` | The market's vault-scoped state from the vault's ERC-7201 storage at one block: executor, the balance fuse's cached total, the custodian
|
|
418
|
+
| `read_external_state`, `ExternalStateVaultState` | The market's vault-scoped state from the vault's ERC-7201 storage at one block: executor, the balance fuse's cached total, the custodian marker it last checked (a timestamp on the earlier executor build, the confirm count on the 2026-10-21 one) and the pause flag the pre-hook enforces (no getter exposes them) |
|
|
418
419
|
| `decode_external_state_event`, `EXTERNAL_STATE_TOPICS` | The ExternalState events the vault emits (big change, cache updates, unpause, rescue, executor creation and deployment, operation enter/exit), decoded by topic with their raw log identity; a malformed listed log raises |
|
|
419
420
|
| `erc20_balance_slot`, `VaultSimulator.with_erc20_balance` | Fund any address with any ERC-20 in a simulation by a storage override (slot found by probing), no holder to impersonate |
|
|
420
421
|
| `ERC20` | Token reads and approvals |
|
|
@@ -439,7 +440,10 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
|
|
|
439
440
|
### Preview features (`ipor_fusion.crosschain`, `ipor_fusion.hypercore`)
|
|
440
441
|
|
|
441
442
|
Not production (see the note at the top) and outside the stable top level:
|
|
442
|
-
import these from their subpackage
|
|
443
|
+
import these from their subpackage. They track the current official contracts
|
|
444
|
+
only, with no backward compatibility: from 4.1 on, a minor release may change or
|
|
445
|
+
remove any of these names and drops support for retired deployments (such as
|
|
446
|
+
the pilot executors and factories). Pin an exact version if you depend on them.
|
|
443
447
|
|
|
444
448
|
| Name | Purpose |
|
|
445
449
|
|------|---------|
|
|
@@ -196,10 +196,12 @@ from ipor_fusion.fuses.external_state_event_decoding import (
|
|
|
196
196
|
EXTERNAL_STATE_TOPICS,
|
|
197
197
|
ExternalStateAssetRescuedArgs,
|
|
198
198
|
ExternalStateAssetRescuedEvent,
|
|
199
|
+
ExternalStateBalanceFuseLastCustodianConfirmCountUpdatedEvent,
|
|
199
200
|
ExternalStateBalanceFuseLastCustodianTimestampUpdatedEvent,
|
|
200
201
|
ExternalStateBalanceFuseLastTotalBalanceUpdatedEvent,
|
|
201
202
|
ExternalStateBigChangeDetectedArgs,
|
|
202
203
|
ExternalStateBigChangeDetectedEvent,
|
|
204
|
+
ExternalStateCustodianConfirmCountUpdatedArgs,
|
|
203
205
|
ExternalStateCustodianTimestampUpdatedArgs,
|
|
204
206
|
ExternalStateEvent,
|
|
205
207
|
ExternalStateEventArgs,
|
|
@@ -210,6 +212,8 @@ from ipor_fusion.fuses.external_state_event_decoding import (
|
|
|
210
212
|
ExternalStateOperationArgs,
|
|
211
213
|
ExternalStateOperationFuseEnterEvent,
|
|
212
214
|
ExternalStateOperationFuseExitEvent,
|
|
215
|
+
ExternalStateOperationFuseSweepSurplusArgs,
|
|
216
|
+
ExternalStateOperationFuseSweepSurplusEvent,
|
|
213
217
|
ExternalStateTotalBalanceUpdatedArgs,
|
|
214
218
|
ExternalStateUnpausedArgs,
|
|
215
219
|
ExternalStateUnpausedEvent,
|
|
@@ -333,6 +337,7 @@ from ipor_fusion.types import (
|
|
|
333
337
|
AssetSource,
|
|
334
338
|
ChainId,
|
|
335
339
|
Check,
|
|
340
|
+
CustodianMarker,
|
|
336
341
|
Decimals,
|
|
337
342
|
Fee,
|
|
338
343
|
FuseAddress,
|
|
@@ -437,6 +442,8 @@ __all__ = [
|
|
|
437
442
|
"EXTERNAL_STATE_EVENT_CLASSES",
|
|
438
443
|
"ExternalStateBigChangeDetectedEvent",
|
|
439
444
|
"ExternalStateBalanceFuseLastCustodianTimestampUpdatedEvent",
|
|
445
|
+
"ExternalStateBalanceFuseLastCustodianConfirmCountUpdatedEvent",
|
|
446
|
+
"ExternalStateOperationFuseSweepSurplusEvent",
|
|
440
447
|
"ExternalStateBalanceFuseLastTotalBalanceUpdatedEvent",
|
|
441
448
|
"ExternalStateUnpausedEvent",
|
|
442
449
|
"ExternalStateAssetRescuedEvent",
|
|
@@ -447,6 +454,8 @@ __all__ = [
|
|
|
447
454
|
"ExternalStateAssetRescuedArgs",
|
|
448
455
|
"ExternalStateBigChangeDetectedArgs",
|
|
449
456
|
"ExternalStateCustodianTimestampUpdatedArgs",
|
|
457
|
+
"ExternalStateCustodianConfirmCountUpdatedArgs",
|
|
458
|
+
"ExternalStateOperationFuseSweepSurplusArgs",
|
|
450
459
|
"ExternalStateEventArgs",
|
|
451
460
|
"ExternalStateExecutorArgs",
|
|
452
461
|
"ExternalStateOperationArgs",
|
|
@@ -664,6 +673,7 @@ __all__ = [
|
|
|
664
673
|
"format_amount",
|
|
665
674
|
"format_shares",
|
|
666
675
|
"Price",
|
|
676
|
+
"CustodianMarker",
|
|
667
677
|
"Period",
|
|
668
678
|
"MAX_UINT256",
|
|
669
679
|
"ZERO_ADDRESS",
|
|
@@ -41,7 +41,8 @@ PREVIEW_FEATURES: tuple[PreviewFeature, ...] = (
|
|
|
41
41
|
PREVIEW_STATEMENT = (
|
|
42
42
|
"Preview, not production: the contracts are under active development, the "
|
|
43
43
|
"mainnet deployments are a proof of concept and IPOR Labs canaries, and "
|
|
44
|
-
"interfaces may change between minor versions
|
|
44
|
+
"interfaces may change or be removed between minor versions, with no "
|
|
45
|
+
"backward compatibility."
|
|
45
46
|
)
|
|
46
47
|
|
|
47
48
|
_DISTRIBUTION = "ipor-fusion"
|
|
@@ -35,6 +35,7 @@ from ipor_fusion.types import (
|
|
|
35
35
|
ZERO_ADDRESS,
|
|
36
36
|
Amount,
|
|
37
37
|
ChainId,
|
|
38
|
+
CustodianMarker,
|
|
38
39
|
Period,
|
|
39
40
|
PlasmaVaultAddress,
|
|
40
41
|
ProposalHash,
|
|
@@ -127,10 +128,15 @@ class ExternalStateVaultState:
|
|
|
127
128
|
`last_total_balance` is the balance fuse's cache of the executor's total in
|
|
128
129
|
raw underlying units, as of the fuse's last refresh -- not the live total,
|
|
129
130
|
which `ExternalStateExecutor.get_balance_fuse_snapshot` reads;
|
|
130
|
-
`
|
|
131
|
-
balance fuse last processed --
|
|
131
|
+
`last_checked_custodian_marker` is the executor's custodian marker the
|
|
132
|
+
balance fuse last processed -- the last confirmed custodian update's block
|
|
133
|
+
timestamp on executors of the contracts before the 2026-10-21 release, the
|
|
134
|
+
executor's confirm count (`ExternalStateExecutor.confirm_count`) on the
|
|
135
|
+
executors of that release; either way not necessarily the latest update
|
|
132
136
|
and not the pre-hook's staleness clock, which is
|
|
133
|
-
`ExternalStateExecutor.get_oldest_update_timestamp
|
|
137
|
+
`ExternalStateExecutor.get_oldest_update_timestamp`. The fuse and the
|
|
138
|
+
pre-hook only compare it for equality with
|
|
139
|
+
`BalanceFuseSnapshot.custodian_marker`; `paused` is the flag
|
|
134
140
|
the balance fuse sets on a big change and `ExternalStateUnpauseFuse` clears
|
|
135
141
|
with an atomist signature. `ExternalStatePausePreHook` blocks the vault's
|
|
136
142
|
gated user operations while the flag is set, and also, flag or not, while
|
|
@@ -141,7 +147,7 @@ class ExternalStateVaultState:
|
|
|
141
147
|
|
|
142
148
|
executor: ChecksumAddress | None
|
|
143
149
|
last_total_balance: Amount
|
|
144
|
-
|
|
150
|
+
last_checked_custodian_marker: CustodianMarker
|
|
145
151
|
paused: bool
|
|
146
152
|
block: int
|
|
147
153
|
|
|
@@ -150,11 +156,14 @@ class ExternalStateVaultState:
|
|
|
150
156
|
class BalanceFuseSnapshot:
|
|
151
157
|
"""`ExternalStateExecutor.getBalanceFuseSnapshot()`: the live sum of the
|
|
152
158
|
tracked balances in raw underlying units, the cached big-change threshold
|
|
153
|
-
in basis points and the
|
|
159
|
+
in basis points and the custodian marker: the last confirmed custodian
|
|
160
|
+
update's timestamp on executors before the 2026-10-21 contracts release,
|
|
161
|
+
the confirm count on the executors of that release (two confirms in one
|
|
162
|
+
block then still differ)."""
|
|
154
163
|
|
|
155
164
|
total_balance: Amount
|
|
156
165
|
big_change_bps: int
|
|
157
|
-
|
|
166
|
+
custodian_marker: CustodianMarker
|
|
158
167
|
|
|
159
168
|
|
|
160
169
|
def _storage_word(raw: bytes) -> int:
|
|
@@ -226,7 +235,7 @@ def read_external_state(
|
|
|
226
235
|
return ExternalStateVaultState(
|
|
227
236
|
executor=Web3.to_checksum_address(f"0x{words[0]:040x}") if words[0] else None,
|
|
228
237
|
last_total_balance=Amount(words[1]),
|
|
229
|
-
|
|
238
|
+
last_checked_custodian_marker=CustodianMarker(words[2]),
|
|
230
239
|
paused=words[3] == 1,
|
|
231
240
|
block=block,
|
|
232
241
|
)
|
|
@@ -344,18 +353,63 @@ class ExternalStateExecutor(ContractWrapper):
|
|
|
344
353
|
|
|
345
354
|
def get_balance_fuse_snapshot(self) -> Call[BalanceFuseSnapshot]:
|
|
346
355
|
"""The live aggregate the balance fuse reads: the sum of the tracked
|
|
347
|
-
balances, the big-change threshold and the
|
|
348
|
-
|
|
356
|
+
balances, the big-change threshold and the custodian marker, in one
|
|
357
|
+
call. Pin the context's block to the one
|
|
349
358
|
`read_external_state` returned to compare live against cached."""
|
|
350
359
|
return self._view(
|
|
351
360
|
"getBalanceFuseSnapshot()",
|
|
352
361
|
output_types=["uint256", "uint256", "uint256"],
|
|
353
362
|
).map(
|
|
354
363
|
lambda values: BalanceFuseSnapshot(
|
|
355
|
-
Amount(values[0]), int(values[1]),
|
|
364
|
+
Amount(values[0]), int(values[1]), CustodianMarker(values[2])
|
|
356
365
|
)
|
|
357
366
|
)
|
|
358
367
|
|
|
368
|
+
def confirm_count(self) -> Call[CustodianMarker]:
|
|
369
|
+
"""Executors of the 2026-10-21 contracts release only: the count of
|
|
370
|
+
confirmed custodian updates across every balance account, the marker
|
|
371
|
+
`get_balance_fuse_snapshot` reports. An earlier executor has no such
|
|
372
|
+
function and the call reverts."""
|
|
373
|
+
return self._view(
|
|
374
|
+
"confirmCount()", output_types=["uint256"], decoder=CustodianMarker
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
def last_custodian_update_timestamp(self) -> Call[Timestamp]:
|
|
378
|
+
"""Block timestamp of the last confirmed custodian update on any
|
|
379
|
+
balance account."""
|
|
380
|
+
return self._view(
|
|
381
|
+
"lastCustodianUpdateTimestamp()",
|
|
382
|
+
output_types=["uint256"],
|
|
383
|
+
decoder=Timestamp,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
def unforwarded_credit(self, asset: ChecksumAddress) -> Call[Amount]:
|
|
387
|
+
"""Executors of the 2026-10-21 contracts release only: tokens of
|
|
388
|
+
`asset` credited through the vault path that have not left the
|
|
389
|
+
executor. The executor's `asset` balance above it is surplus (a
|
|
390
|
+
donation or a payout sent straight to the executor):
|
|
391
|
+
`ExternalStateOperationFuse.sweep_surplus` moves it to the vault, and
|
|
392
|
+
custodians should not propose while it exists unless they know whether
|
|
393
|
+
their report counts it."""
|
|
394
|
+
return self._view(
|
|
395
|
+
"unforwardedCredit(address)",
|
|
396
|
+
asset,
|
|
397
|
+
output_types=["uint256"],
|
|
398
|
+
decoder=Amount,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
def proposal_base_balance(self, balance_account: ChecksumAddress) -> Call[Amount]:
|
|
402
|
+
"""Executors of the 2026-10-21 contracts release only: the account
|
|
403
|
+
balance when the pending proposal was made. `confirmBalance` reverts
|
|
404
|
+
`ExternalStateExecutorBalanceChangedSinceProposal` once the balance
|
|
405
|
+
differs from it (an alpha flow ran in between): propose again."""
|
|
406
|
+
return self._view(
|
|
407
|
+
"proposalBaseBalance(address)",
|
|
408
|
+
balance_account,
|
|
409
|
+
output_types=["uint256"],
|
|
410
|
+
decoder=Amount,
|
|
411
|
+
)
|
|
412
|
+
|
|
359
413
|
def get_oldest_update_timestamp(self) -> Call[Timestamp]:
|
|
360
414
|
"""The pre-hook's staleness clock: the oldest non-zero `lastUpdated`
|
|
361
415
|
across the balance accounts, 0 when no account was ever updated. The
|
|
@@ -30,10 +30,12 @@ from ipor_fusion.fuses.external_state_event_decoding import (
|
|
|
30
30
|
EXTERNAL_STATE_TOPICS,
|
|
31
31
|
ExternalStateAssetRescuedArgs,
|
|
32
32
|
ExternalStateAssetRescuedEvent,
|
|
33
|
+
ExternalStateBalanceFuseLastCustodianConfirmCountUpdatedEvent,
|
|
33
34
|
ExternalStateBalanceFuseLastCustodianTimestampUpdatedEvent,
|
|
34
35
|
ExternalStateBalanceFuseLastTotalBalanceUpdatedEvent,
|
|
35
36
|
ExternalStateBigChangeDetectedArgs,
|
|
36
37
|
ExternalStateBigChangeDetectedEvent,
|
|
38
|
+
ExternalStateCustodianConfirmCountUpdatedArgs,
|
|
37
39
|
ExternalStateCustodianTimestampUpdatedArgs,
|
|
38
40
|
ExternalStateEvent,
|
|
39
41
|
ExternalStateEventArgs,
|
|
@@ -44,6 +46,8 @@ from ipor_fusion.fuses.external_state_event_decoding import (
|
|
|
44
46
|
ExternalStateOperationArgs,
|
|
45
47
|
ExternalStateOperationFuseEnterEvent,
|
|
46
48
|
ExternalStateOperationFuseExitEvent,
|
|
49
|
+
ExternalStateOperationFuseSweepSurplusArgs,
|
|
50
|
+
ExternalStateOperationFuseSweepSurplusEvent,
|
|
47
51
|
ExternalStateTotalBalanceUpdatedArgs,
|
|
48
52
|
ExternalStateUnpausedArgs,
|
|
49
53
|
ExternalStateUnpausedEvent,
|
|
@@ -146,10 +150,14 @@ __all__ = [
|
|
|
146
150
|
"ExternalStateAssetRescuedArgs",
|
|
147
151
|
"ExternalStateAssetRescuedEvent",
|
|
148
152
|
"ExternalStateBalanceFuseLastCustodianTimestampUpdatedEvent",
|
|
153
|
+
"ExternalStateBalanceFuseLastCustodianConfirmCountUpdatedEvent",
|
|
154
|
+
"ExternalStateOperationFuseSweepSurplusEvent",
|
|
149
155
|
"ExternalStateBalanceFuseLastTotalBalanceUpdatedEvent",
|
|
150
156
|
"ExternalStateBigChangeDetectedArgs",
|
|
151
157
|
"ExternalStateBigChangeDetectedEvent",
|
|
152
158
|
"ExternalStateCustodianTimestampUpdatedArgs",
|
|
159
|
+
"ExternalStateCustodianConfirmCountUpdatedArgs",
|
|
160
|
+
"ExternalStateOperationFuseSweepSurplusArgs",
|
|
153
161
|
"ExternalStateEvent",
|
|
154
162
|
"ExternalStateEventArgs",
|
|
155
163
|
"ExternalStateEventSpec",
|
{ipor_fusion-4.0.0.dev286 → ipor_fusion-4.0.0.dev293}/src/ipor_fusion/fuses/external_state.py
RENAMED
|
@@ -92,6 +92,17 @@ class ExternalStateOperationFuse(Fuse):
|
|
|
92
92
|
"""
|
|
93
93
|
return self._action_raw("createExecutor()", [])
|
|
94
94
|
|
|
95
|
+
def sweep_surplus(self, asset: ChecksumAddress) -> FuseAction:
|
|
96
|
+
"""Operation fuses of the 2026-10-21 contracts release only: move to
|
|
97
|
+
the vault the executor's ``asset`` balance above its unforwarded
|
|
98
|
+
credit (tokens that did not arrive through the vault path, such as a
|
|
99
|
+
donation), emitting ``ExternalStateOperationFuseSweepSurplus``. The
|
|
100
|
+
swept tokens enter NAV at once only when ``asset`` is the underlying.
|
|
101
|
+
Use it for tokens the confirmed custodian report no longer counts; a
|
|
102
|
+
payout it still counts is booked with ``exit`` instead."""
|
|
103
|
+
self._validate_address(asset, "asset")
|
|
104
|
+
return self._action_raw("sweepSurplus(address)", [asset])
|
|
105
|
+
|
|
95
106
|
def _build(
|
|
96
107
|
self,
|
|
97
108
|
method: str,
|
|
@@ -65,6 +65,10 @@ EXTERNAL_STATE_EVENT_SPECS: tuple[ExternalStateEventSpec, ...] = (
|
|
|
65
65
|
"ExternalStateBalanceFuseLastCustodianTimestampUpdated",
|
|
66
66
|
(("uint256", "oldTimestamp"), ("uint256", "newTimestamp")),
|
|
67
67
|
),
|
|
68
|
+
ExternalStateEventSpec(
|
|
69
|
+
"ExternalStateBalanceFuseLastCustodianConfirmCountUpdated",
|
|
70
|
+
(("uint256", "oldConfirmCount"), ("uint256", "newConfirmCount")),
|
|
71
|
+
),
|
|
68
72
|
ExternalStateEventSpec(
|
|
69
73
|
"ExternalStateBalanceFuseLastTotalBalanceUpdated",
|
|
70
74
|
(("uint256", "oldTotalBalance"), ("uint256", "newTotalBalance")),
|
|
@@ -106,6 +110,10 @@ EXTERNAL_STATE_EVENT_SPECS: tuple[ExternalStateEventSpec, ...] = (
|
|
|
106
110
|
("uint256", "actionsCount"),
|
|
107
111
|
),
|
|
108
112
|
),
|
|
113
|
+
ExternalStateEventSpec(
|
|
114
|
+
"ExternalStateOperationFuseSweepSurplus",
|
|
115
|
+
(("address", "version"), ("address", "asset"), ("uint256", "amount")),
|
|
116
|
+
),
|
|
109
117
|
# ExternalStateExecutorStorageLib
|
|
110
118
|
ExternalStateEventSpec(
|
|
111
119
|
"ExternalStateExecutorDeployed",
|
|
@@ -127,6 +135,24 @@ class ExternalStateCustodianTimestampUpdatedArgs(TypedDict):
|
|
|
127
135
|
newTimestamp: Timestamp
|
|
128
136
|
|
|
129
137
|
|
|
138
|
+
class ExternalStateCustodianConfirmCountUpdatedArgs(TypedDict):
|
|
139
|
+
"""``ExternalStateBalanceFuseLastCustodianConfirmCountUpdated`` (balance
|
|
140
|
+
fuses of the 2026-10-21 contracts release; earlier ones emit the
|
|
141
|
+
timestamp event)."""
|
|
142
|
+
|
|
143
|
+
oldConfirmCount: int
|
|
144
|
+
newConfirmCount: int
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ExternalStateOperationFuseSweepSurplusArgs(TypedDict):
|
|
148
|
+
"""``ExternalStateOperationFuseSweepSurplus``: ``amount`` of ``asset``
|
|
149
|
+
swept from the executor to the vault."""
|
|
150
|
+
|
|
151
|
+
version: FuseAddress
|
|
152
|
+
asset: ChecksumAddress
|
|
153
|
+
amount: Amount
|
|
154
|
+
|
|
155
|
+
|
|
130
156
|
class ExternalStateTotalBalanceUpdatedArgs(TypedDict):
|
|
131
157
|
"""``ExternalStateBalanceFuseLastTotalBalanceUpdated``."""
|
|
132
158
|
|
|
@@ -213,6 +239,24 @@ class ExternalStateBalanceFuseLastCustodianTimestampUpdatedEvent(
|
|
|
213
239
|
args: ExternalStateCustodianTimestampUpdatedArgs
|
|
214
240
|
|
|
215
241
|
|
|
242
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
243
|
+
class ExternalStateBalanceFuseLastCustodianConfirmCountUpdatedEvent(
|
|
244
|
+
_ExternalStateEventBase
|
|
245
|
+
):
|
|
246
|
+
name: Literal["ExternalStateBalanceFuseLastCustodianConfirmCountUpdated"] = (
|
|
247
|
+
"ExternalStateBalanceFuseLastCustodianConfirmCountUpdated"
|
|
248
|
+
)
|
|
249
|
+
args: ExternalStateCustodianConfirmCountUpdatedArgs
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
253
|
+
class ExternalStateOperationFuseSweepSurplusEvent(_ExternalStateEventBase):
|
|
254
|
+
name: Literal["ExternalStateOperationFuseSweepSurplus"] = (
|
|
255
|
+
"ExternalStateOperationFuseSweepSurplus"
|
|
256
|
+
)
|
|
257
|
+
args: ExternalStateOperationFuseSweepSurplusArgs
|
|
258
|
+
|
|
259
|
+
|
|
216
260
|
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
217
261
|
class ExternalStateBalanceFuseLastTotalBalanceUpdatedEvent(_ExternalStateEventBase):
|
|
218
262
|
name: Literal["ExternalStateBalanceFuseLastTotalBalanceUpdated"] = (
|
|
@@ -262,6 +306,8 @@ class ExternalStateExecutorDeployedEvent(_ExternalStateEventBase):
|
|
|
262
306
|
ExternalStateEvent: TypeAlias = (
|
|
263
307
|
ExternalStateBigChangeDetectedEvent
|
|
264
308
|
| ExternalStateBalanceFuseLastCustodianTimestampUpdatedEvent
|
|
309
|
+
| ExternalStateBalanceFuseLastCustodianConfirmCountUpdatedEvent
|
|
310
|
+
| ExternalStateOperationFuseSweepSurplusEvent
|
|
265
311
|
| ExternalStateBalanceFuseLastTotalBalanceUpdatedEvent
|
|
266
312
|
| ExternalStateUnpausedEvent
|
|
267
313
|
| ExternalStateAssetRescuedEvent
|
|
@@ -275,6 +321,8 @@ ExternalStateEvent: TypeAlias = (
|
|
|
275
321
|
EXTERNAL_STATE_EVENT_CLASSES: dict[str, type[ExternalStateEvent]] = {
|
|
276
322
|
"ExternalStateBigChangeDetected": ExternalStateBigChangeDetectedEvent,
|
|
277
323
|
"ExternalStateBalanceFuseLastCustodianTimestampUpdated": ExternalStateBalanceFuseLastCustodianTimestampUpdatedEvent,
|
|
324
|
+
"ExternalStateBalanceFuseLastCustodianConfirmCountUpdated": ExternalStateBalanceFuseLastCustodianConfirmCountUpdatedEvent,
|
|
325
|
+
"ExternalStateOperationFuseSweepSurplus": ExternalStateOperationFuseSweepSurplusEvent,
|
|
278
326
|
"ExternalStateBalanceFuseLastTotalBalanceUpdated": ExternalStateBalanceFuseLastTotalBalanceUpdatedEvent,
|
|
279
327
|
"ExternalStateUnpaused": ExternalStateUnpausedEvent,
|
|
280
328
|
"ExternalStateAssetRescued": ExternalStateAssetRescuedEvent,
|
|
@@ -1176,8 +1176,9 @@ class PreviewFeatureModel(_Base):
|
|
|
1176
1176
|
default="preview",
|
|
1177
1177
|
description="Always 'preview': not production. The contracts are under "
|
|
1178
1178
|
"active development, the mainnet deployments are a proof of concept "
|
|
1179
|
-
"and IPOR Labs canaries, and interfaces may change
|
|
1180
|
-
"versions
|
|
1179
|
+
"and IPOR Labs canaries, and interfaces may change or be removed "
|
|
1180
|
+
"between minor versions, with no backward compatibility. Route no "
|
|
1181
|
+
"production capital through these features.",
|
|
1181
1182
|
)
|
|
1182
1183
|
|
|
1183
1184
|
|
|
@@ -17,6 +17,11 @@ RoleId = NewType("RoleId", int)
|
|
|
17
17
|
ChainId = NewType("ChainId", int)
|
|
18
18
|
# An 18-decimal fixed-point fraction: 10**18 is 1, i.e. 100% (a market limit).
|
|
19
19
|
Wad = NewType("Wad", int)
|
|
20
|
+
# The ExternalState executor's custodian marker the balance fuse and pre-hook
|
|
21
|
+
# compare for equality only: the last confirmed update's block timestamp on an
|
|
22
|
+
# executor before the 2026-10-21 contracts release, the confirm count on one of
|
|
23
|
+
# that release. Opaque: never compare it with a clock or do arithmetic on it.
|
|
24
|
+
CustodianMarker = NewType("CustodianMarker", int)
|
|
20
25
|
# A Morpho Blue market id (bytes32) in its canonical spelling: "0x" and 64
|
|
21
26
|
# lowercase hex digits, as the Morpho API reports it. Build one with
|
|
22
27
|
# parse_morpho_market_id.
|
|
@@ -7,7 +7,7 @@ import re
|
|
|
7
7
|
import pytest
|
|
8
8
|
from _event_args import assert_event_class_mirrors_spec
|
|
9
9
|
from eth_abi import encode
|
|
10
|
-
from test_solidity_mirrors import _solidity_source
|
|
10
|
+
from test_solidity_mirrors import IPOR_FUSION_REF, _solidity_source
|
|
11
11
|
from web3 import Web3
|
|
12
12
|
|
|
13
13
|
from ipor_fusion import (
|
|
@@ -28,6 +28,17 @@ _SOURCES = (
|
|
|
28
28
|
"contracts/fuses/external_state/ExternalStateOperationFuse.sol",
|
|
29
29
|
"contracts/fuses/external_state/lib/ExternalStateExecutorStorageLib.sol",
|
|
30
30
|
)
|
|
31
|
+
# Both published builds: the release pinned in test_solidity_mirrors (Arbitrum)
|
|
32
|
+
# and the earlier one HyperEVM, Base and Ethereum still publish. The released
|
|
33
|
+
# balance fuse declares its events in a library of its own.
|
|
34
|
+
_EARLIER_REF = "a1f79d6c3d0d426ace4df55ed0fb8ea340580739"
|
|
35
|
+
_BUILDS = (
|
|
36
|
+
(
|
|
37
|
+
IPOR_FUSION_REF,
|
|
38
|
+
(*_SOURCES, "contracts/fuses/external_state/lib/ExternalStateBigChangeLib.sol"),
|
|
39
|
+
),
|
|
40
|
+
(_EARLIER_REF, _SOURCES),
|
|
41
|
+
)
|
|
31
42
|
_EVENT_RE = re.compile(r"\bevent\s+(\w+)\s*\(([^)]*)\)\s*;", re.S)
|
|
32
43
|
|
|
33
44
|
VAULT = "0xdBEe6621Febce4C51Fa9DD1dB12dCd9d4CC3e7b9"
|
|
@@ -35,9 +46,18 @@ SIGNER = "0x00000000000000000000000000000000000000a5"
|
|
|
35
46
|
|
|
36
47
|
|
|
37
48
|
def _declared():
|
|
49
|
+
"""Every event either build declares; a name both declare must agree."""
|
|
50
|
+
out = {}
|
|
51
|
+
for ref, paths in _BUILDS:
|
|
52
|
+
for name, params in _declared_at(ref, paths).items():
|
|
53
|
+
assert out.setdefault(name, params) == params, name
|
|
54
|
+
return out
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _declared_at(ref, paths):
|
|
38
58
|
out = {}
|
|
39
|
-
for path in
|
|
40
|
-
for name, body in _EVENT_RE.findall(_solidity_source(path)):
|
|
59
|
+
for path in paths:
|
|
60
|
+
for name, body in _EVENT_RE.findall(_solidity_source(path, ref)):
|
|
41
61
|
params = []
|
|
42
62
|
for raw in [p.strip() for p in body.split(",") if p.strip()]:
|
|
43
63
|
words = raw.split()
|
|
@@ -1203,7 +1203,7 @@ class TestReadExternalState:
|
|
|
1203
1203
|
|
|
1204
1204
|
assert state.executor == EXECUTOR_ADDR
|
|
1205
1205
|
assert state.last_total_balance == 32_928_135
|
|
1206
|
-
assert state.
|
|
1206
|
+
assert state.last_checked_custodian_marker == 1_791_458_983
|
|
1207
1207
|
assert state.paused is True
|
|
1208
1208
|
assert state.block == 4_805_000 # "latest" resolved once, before the reads
|
|
1209
1209
|
assert [c.args for c in ctx.get_storage_at.call_args_list] == [
|
|
@@ -1256,7 +1256,7 @@ class TestReadExternalState:
|
|
|
1256
1256
|
self._ctx([0, (1 << 256) - 1, (1 << 256) - 1, 0]), VAULT_ADDR
|
|
1257
1257
|
)
|
|
1258
1258
|
assert top.last_total_balance == (1 << 256) - 1
|
|
1259
|
-
assert top.
|
|
1259
|
+
assert top.last_checked_custodian_marker == (1 << 256) - 1
|
|
1260
1260
|
|
|
1261
1261
|
def test_a_pinned_context_block_is_used_as_is(self):
|
|
1262
1262
|
from ipor_fusion import read_external_state
|
|
@@ -1267,7 +1267,7 @@ class TestReadExternalState:
|
|
|
1267
1267
|
|
|
1268
1268
|
assert state.block == 4_700_000
|
|
1269
1269
|
assert state.executor is None and state.paused is False
|
|
1270
|
-
assert (state.last_total_balance, state.
|
|
1270
|
+
assert (state.last_total_balance, state.last_checked_custodian_marker) == (
|
|
1271
1271
|
0,
|
|
1272
1272
|
0,
|
|
1273
1273
|
)
|
|
@@ -1292,7 +1292,7 @@ class TestReadExternalState:
|
|
|
1292
1292
|
5,
|
|
1293
1293
|
True,
|
|
1294
1294
|
)
|
|
1295
|
-
assert state.
|
|
1295
|
+
assert state.last_checked_custodian_marker == 0
|
|
1296
1296
|
|
|
1297
1297
|
def test_malformed_words_raise_instead_of_passing_as_values(self):
|
|
1298
1298
|
from ipor_fusion import read_external_state
|
|
@@ -1320,6 +1320,8 @@ class TestExecutorViews:
|
|
|
1320
1320
|
("dust_threshold", "dustThreshold()"),
|
|
1321
1321
|
("min_update_interval", "minUpdateInterval()"),
|
|
1322
1322
|
("get_oldest_update_timestamp", "getOldestUpdateTimestamp()"),
|
|
1323
|
+
("confirm_count", "confirmCount()"),
|
|
1324
|
+
("last_custodian_update_timestamp", "lastCustodianUpdateTimestamp()"),
|
|
1323
1325
|
],
|
|
1324
1326
|
)
|
|
1325
1327
|
def test_uint256_views(self, method, signature):
|
|
@@ -1338,3 +1340,18 @@ class TestExecutorViews:
|
|
|
1338
1340
|
assert call.decoder is not None
|
|
1339
1341
|
snap = call.decoder((32_928_135, 50_000, 1_791_458_983))
|
|
1340
1342
|
assert snap == BalanceFuseSnapshot(32_928_135, 50_000, 1_791_458_983)
|
|
1343
|
+
# a 2026-10-21 executor reports its confirm count in the same slot
|
|
1344
|
+
assert call.decoder((32_928_135, 50_000, 7)).custodian_marker == 7
|
|
1345
|
+
|
|
1346
|
+
@pytest.mark.parametrize(
|
|
1347
|
+
("method", "signature"),
|
|
1348
|
+
[
|
|
1349
|
+
("unforwarded_credit", "unforwardedCredit(address)"),
|
|
1350
|
+
("proposal_base_balance", "proposalBaseBalance(address)"),
|
|
1351
|
+
],
|
|
1352
|
+
)
|
|
1353
|
+
def test_released_executor_per_address_views(self, method, signature):
|
|
1354
|
+
executor = ExternalStateExecutor(EXECUTOR_ADDR)
|
|
1355
|
+
call = getattr(executor, method)(EXECUTOR_ADDR)
|
|
1356
|
+
assert bytes(call.data)[:4] == Web3.keccak(text=signature)[:4]
|
|
1357
|
+
assert call.output_types == ("uint256",)
|
|
@@ -1777,6 +1777,13 @@ class TestExternalStateOperationFuse:
|
|
|
1777
1777
|
actions=[(ZERO_ADDRESS, b"\x00")],
|
|
1778
1778
|
)
|
|
1779
1779
|
|
|
1780
|
+
def test_sweep_surplus_encodes_the_asset(self):
|
|
1781
|
+
action = ExternalStateOperationFuse(FUSE_ADDR).sweep_surplus(TOKEN_A)
|
|
1782
|
+
assert action.data[:4] == _selector("sweepSurplus(address)")
|
|
1783
|
+
assert decode(["address"], action.data[4:])[0].lower() == TOKEN_A.lower()
|
|
1784
|
+
with pytest.raises(ValueError, match="asset"):
|
|
1785
|
+
ExternalStateOperationFuse(FUSE_ADDR).sweep_surplus(ZERO_ADDRESS)
|
|
1786
|
+
|
|
1780
1787
|
def test_create_executor_is_bare_selector(self):
|
|
1781
1788
|
action = ExternalStateOperationFuse(FUSE_ADDR).create_executor()
|
|
1782
1789
|
assert action.fuse == FUSE_ADDR
|