ipor-fusion 4.0.0.dev303__tar.gz → 4.0.0.dev311__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.dev303 → ipor_fusion-4.0.0.dev311}/AGENTS.md +5 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/PKG-INFO +8 -6
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/README.md +7 -5
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/pyproject.toml +1 -1
- ipor_fusion-4.0.0.dev311/scripts/fusion_error_signatures.py +137 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/__init__.py +4 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/__init__.py +4 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/access.py +104 -1
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/errors.py +4 -0
- ipor_fusion-4.0.0.dev311/src/ipor_fusion/fusion_errors.py +1437 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_access_manager.py +98 -0
- ipor_fusion-4.0.0.dev311/tests/test_fusion_errors.py +122 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_public_api.py +4 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_claim_merkl_wrapper_base.py +3 -1
- ipor_fusion-4.0.0.dev311/tests/test_simulate_timelock_base.py +51 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/MIGRATION_4.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/address_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/preflight_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/context_manager.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/fuse_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/simulation_result.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/external_state_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/hypercore_event_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/hypercore/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/preflight.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/registry.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_event_args.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_fake_rpc.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_golden.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_live.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_oracle_mapping_fake.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/hypercore_official.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/meta_morpho_v1.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/meta_morpho_v1.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/meta_morpho_v2.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/meta_morpho_v2.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_api/market.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_api/vault_v1.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_api/vault_v2.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_api/vault_v2_not_found.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_blue.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_blue.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_blue_api_error.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_blue_api_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_blue_no_api.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/market_golden/morpho_blue_no_api.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/oracle_mapping_golden/mapping.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/oracle_mapping_golden/mapping.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/simulate_v1_request.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/clean.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/deployment_error.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/deployment_error.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/deployment_error.txt +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/fetched.cli.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/fetched.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/fetched.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/full.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/full.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/full.txt +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/rich.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_address.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_helpers.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_market_golden.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_mcp_parity.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_oracle_mapping_golden.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_preflight.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_docs_typing.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_fuse_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_governance_wrappers.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_mcp_address.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_mcp_preflight.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_preflight.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_public_type_reachability.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_registry.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_preflight_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulation_errors.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_simulation_observation.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_vault_data_block.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_vault_info_fetched_golden.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_vault_reconciliation_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev303 → ipor_fusion-4.0.0.dev311}/uv.lock +0 -0
|
@@ -237,6 +237,11 @@ the lifecycle matrix follows that.
|
|
|
237
237
|
|
|
238
238
|
- `chains.py` chain registry; `market_ids.py` `IporFusionMarkets`; `types.py`; `errors.py`
|
|
239
239
|
defines every SDK exception class (`tests/test_error_hierarchy.py` pins it)
|
|
240
|
+
- `fusion_errors.py` — `FUSION_ERROR_SIGNATURES`, every custom error of the
|
|
241
|
+
ipor-fusion contracts and their imports, registered by `errors.py` so reverts
|
|
242
|
+
decode by name; generated, never edited by hand: `uv run python
|
|
243
|
+
scripts/fusion_error_signatures.py ../ipor-fusion --source <commit>` (needs the
|
|
244
|
+
checkout's `npm ci`)
|
|
240
245
|
- `about.py` — package version, repository URL, CHANGELOG parsing (`fusion changelog`,
|
|
241
246
|
`server_info`); `field_docs.py` — `DOCS`, the JSON field descriptions shared by CLI
|
|
242
247
|
output and MCP models
|
|
@@ -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.dev311
|
|
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
|
|
@@ -369,10 +369,12 @@ A flash-loan fuse also needs `update_callback_handler(handler, sender, selector)
|
|
|
369
369
|
|
|
370
370
|
The full clone → configure → deposit → execute sequence is exercised in [`tests/test_simulate_vault_from_scratch_base.py`](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/tests/test_simulate_vault_from_scratch_base.py).
|
|
371
371
|
|
|
372
|
-
Custom errors of the
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
`SimulationResult.revert_reason` and `TransactionError`
|
|
372
|
+
Custom errors of the IPOR Fusion contracts and the libraries they import
|
|
373
|
+
(OpenZeppelin included) decode by name, so a simulated or sent call that reverts
|
|
374
|
+
reads `AccessManagedUnauthorized(0x...)`, `UnsupportedFuse()` or
|
|
375
|
+
`NavSettledStale(8453)` in `SimulationResult.revert_reason` and `TransactionError`
|
|
376
|
+
instead of a bare selector. Importing `ipor_fusion.crosschain` also registers
|
|
377
|
+
`CROSSCHAIN_ERROR_SIGNATURES`.
|
|
376
378
|
`register_custom_errors([...])` adds your own, and `SimulationResult.raise_for_failure()`
|
|
377
379
|
raises `SimulationError` naming the first failed call.
|
|
378
380
|
|
|
@@ -476,7 +478,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
|
|
|
476
478
|
| `PlasmaVault` | ERC-4626 vault — execute, deposit, withdraw, previews, fuse, market, fee and limit configuration |
|
|
477
479
|
| `VaultSimulator` | Batch `execute` + reads through `eth_simulateV1`, multi-block, no local node |
|
|
478
480
|
| `FusionFactory` | Deploy a new vault (`clone`, `clone_supervised`) |
|
|
479
|
-
| `AccessManager` | Role-based access control |
|
|
481
|
+
| `AccessManager` | Role-based access control; `read_call_plan(ctx, call, caller=)` says whether a restricted call goes now, must be scheduled (execution delay), waits, is ready or is forbidden, and gives the call to send now; `schedule`, `execute` and `cancel` build the timelock calls |
|
|
480
482
|
| `RewardsManager` | Claim and vest rewards |
|
|
481
483
|
| `WithdrawManager` | Time-windowed withdrawal requests |
|
|
482
484
|
| `FeeManager` | Deposit, performance, and management fee configuration and harvesting |
|
|
@@ -320,10 +320,12 @@ A flash-loan fuse also needs `update_callback_handler(handler, sender, selector)
|
|
|
320
320
|
|
|
321
321
|
The full clone → configure → deposit → execute sequence is exercised in [`tests/test_simulate_vault_from_scratch_base.py`](https://github.com/IPOR-Labs/ipor-fusion.py/blob/main/tests/test_simulate_vault_from_scratch_base.py).
|
|
322
322
|
|
|
323
|
-
Custom errors of the
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
`SimulationResult.revert_reason` and `TransactionError`
|
|
323
|
+
Custom errors of the IPOR Fusion contracts and the libraries they import
|
|
324
|
+
(OpenZeppelin included) decode by name, so a simulated or sent call that reverts
|
|
325
|
+
reads `AccessManagedUnauthorized(0x...)`, `UnsupportedFuse()` or
|
|
326
|
+
`NavSettledStale(8453)` in `SimulationResult.revert_reason` and `TransactionError`
|
|
327
|
+
instead of a bare selector. Importing `ipor_fusion.crosschain` also registers
|
|
328
|
+
`CROSSCHAIN_ERROR_SIGNATURES`.
|
|
327
329
|
`register_custom_errors([...])` adds your own, and `SimulationResult.raise_for_failure()`
|
|
328
330
|
raises `SimulationError` naming the first failed call.
|
|
329
331
|
|
|
@@ -427,7 +429,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
|
|
|
427
429
|
| `PlasmaVault` | ERC-4626 vault — execute, deposit, withdraw, previews, fuse, market, fee and limit configuration |
|
|
428
430
|
| `VaultSimulator` | Batch `execute` + reads through `eth_simulateV1`, multi-block, no local node |
|
|
429
431
|
| `FusionFactory` | Deploy a new vault (`clone`, `clone_supervised`) |
|
|
430
|
-
| `AccessManager` | Role-based access control |
|
|
432
|
+
| `AccessManager` | Role-based access control; `read_call_plan(ctx, call, caller=)` says whether a restricted call goes now, must be scheduled (execution delay), waits, is ready or is forbidden, and gives the call to send now; `schedule`, `execute` and `cancel` build the timelock calls |
|
|
431
433
|
| `RewardsManager` | Claim and vest rewards |
|
|
432
434
|
| `WithdrawManager` | Time-windowed withdrawal requests |
|
|
433
435
|
| `FeeManager` | Deposit, performance, and management fee configuration and harvesting |
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Regenerate ``src/ipor_fusion/fusion_errors.py`` from an ipor-fusion checkout.
|
|
3
|
+
|
|
4
|
+
Collects every ``error`` declaration of the Solidity files reachable by import
|
|
5
|
+
from ``contracts/`` (the contracts and the libraries they import, OpenZeppelin
|
|
6
|
+
included, resolved through the checkout's ``foundry.toml`` remappings) and
|
|
7
|
+
writes them as
|
|
8
|
+
canonical ``Name(type,...)`` signatures: enums become ``uint8``, contract,
|
|
9
|
+
interface and library types ``address``, ``uint``/``int`` their 256-bit form.
|
|
10
|
+
Any other parameter type (a struct, a user-defined value type) stops the run,
|
|
11
|
+
because its canonical form needs the compiler.
|
|
12
|
+
|
|
13
|
+
uv run python scripts/fusion_error_signatures.py ../ipor-fusion --source <commit>
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import re
|
|
20
|
+
import sys
|
|
21
|
+
import tomllib
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
_OUTPUT = Path(__file__).resolve().parent.parent / "src/ipor_fusion/fusion_errors.py"
|
|
25
|
+
_IMPORT = re.compile(r'^\s*import\s+(?:[^"\']*\s+from\s+)?["\']([^"\']+)["\']', re.M)
|
|
26
|
+
_ERROR = re.compile(r"\berror\s+(\w+)\s*\(([^)]*)\)\s*;")
|
|
27
|
+
_ENUM = re.compile(r"\benum\s+(\w+)")
|
|
28
|
+
_CONTRACT = re.compile(r"\b(?:abstract\s+)?(?:contract|interface|library)\s+(\w+)")
|
|
29
|
+
_ELEMENTARY = re.compile(
|
|
30
|
+
r"^(address|bool|string|bytes([1-9]|[12]\d|3[0-2])?|u?int(8|16|24|32|40|48|56|64"
|
|
31
|
+
r"|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224"
|
|
32
|
+
r"|232|240|248|256)?)$"
|
|
33
|
+
)
|
|
34
|
+
_COMMENTS = re.compile(r"//[^\n]*|/\*.*?\*/", re.S)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _Resolver:
|
|
38
|
+
"""Forge's import resolution: relative paths, then the longest matching
|
|
39
|
+
remapping, then the checkout root and each ``libs`` directory."""
|
|
40
|
+
|
|
41
|
+
def __init__(self, root: Path) -> None:
|
|
42
|
+
profile = tomllib.loads((root / "foundry.toml").read_text())["profile"]
|
|
43
|
+
default = profile["default"]
|
|
44
|
+
pairs = [entry.split("=", 1) for entry in default["remappings"]]
|
|
45
|
+
self._root = root
|
|
46
|
+
self._remappings = sorted(pairs, key=lambda pair: -len(pair[0]))
|
|
47
|
+
self._bases = [root, *(root / lib for lib in default["libs"])]
|
|
48
|
+
|
|
49
|
+
def __call__(self, spec: str, importer: Path) -> Path:
|
|
50
|
+
if spec.startswith("."):
|
|
51
|
+
return (importer.parent / spec).resolve()
|
|
52
|
+
for prefix, target in self._remappings:
|
|
53
|
+
if spec.startswith(prefix):
|
|
54
|
+
return (self._root / (target + spec.removeprefix(prefix))).resolve()
|
|
55
|
+
for base in self._bases:
|
|
56
|
+
if (base / spec).is_file():
|
|
57
|
+
return (base / spec).resolve()
|
|
58
|
+
return (self._root / spec).resolve()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _closure(root: Path) -> dict[Path, str]:
|
|
62
|
+
resolve = _Resolver(root)
|
|
63
|
+
pending = sorted((root / "contracts").rglob("*.sol"))
|
|
64
|
+
sources: dict[Path, str] = {}
|
|
65
|
+
while pending:
|
|
66
|
+
path = pending.pop()
|
|
67
|
+
if path in sources:
|
|
68
|
+
continue
|
|
69
|
+
if not path.is_file():
|
|
70
|
+
raise SystemExit(f"unresolved import: {path}")
|
|
71
|
+
text = _COMMENTS.sub("", path.read_text())
|
|
72
|
+
sources[path] = text
|
|
73
|
+
pending.extend(resolve(spec, path) for spec in _IMPORT.findall(text))
|
|
74
|
+
return sources
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _canonical(param: str, enums: set[str], contracts: set[str], where: str) -> str:
|
|
78
|
+
kind = param.split()[0].split(".")[-1]
|
|
79
|
+
if kind in ("uint", "int"):
|
|
80
|
+
return f"{kind}256"
|
|
81
|
+
if _ELEMENTARY.fullmatch(kind):
|
|
82
|
+
return kind
|
|
83
|
+
if kind in enums:
|
|
84
|
+
return "uint8"
|
|
85
|
+
if kind in contracts:
|
|
86
|
+
return "address"
|
|
87
|
+
raise SystemExit(f"{where}: no canonical form for parameter type {kind!r}")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def collect(root: Path) -> list[str]:
|
|
91
|
+
sources = _closure(root)
|
|
92
|
+
enums = {name for text in sources.values() for name in _ENUM.findall(text)}
|
|
93
|
+
contracts = {name for text in sources.values() for name in _CONTRACT.findall(text)}
|
|
94
|
+
signatures: set[str] = set()
|
|
95
|
+
for path, text in sources.items():
|
|
96
|
+
for name, params in _ERROR.findall(text):
|
|
97
|
+
types = [
|
|
98
|
+
_canonical(p.strip(), enums, contracts, f"{path}: {name}")
|
|
99
|
+
for p in params.split(",")
|
|
100
|
+
if p.strip()
|
|
101
|
+
]
|
|
102
|
+
signatures.add(f"{name}({','.join(types)})")
|
|
103
|
+
return sorted(signatures)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def render(signatures: list[str], source: str) -> str:
|
|
107
|
+
lines = "\n".join(f' "{signature}",' for signature in signatures)
|
|
108
|
+
return f'''"""Custom errors of the IPOR Fusion contracts and the libraries they import
|
|
109
|
+
(OpenZeppelin included), registered by :mod:`ipor_fusion.errors` so revert data
|
|
110
|
+
decodes by name (``AccessManagedUnauthorized(0x...)``, ``UnsupportedFuse()``)
|
|
111
|
+
instead of a bare selector.
|
|
112
|
+
|
|
113
|
+
Generated by ``scripts/fusion_error_signatures.py`` from ipor-fusion {source};
|
|
114
|
+
do not edit by hand.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
#: ``Name(type,...)`` signatures, sorted; the selector is the keccak of each.
|
|
118
|
+
FUSION_ERROR_SIGNATURES: tuple[str, ...] = (
|
|
119
|
+
{lines}
|
|
120
|
+
)
|
|
121
|
+
'''
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def main() -> None:
|
|
125
|
+
parser = argparse.ArgumentParser(description="Regenerate fusion_errors.py.")
|
|
126
|
+
parser.add_argument("checkout", type=Path, help="ipor-fusion checkout")
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
"--source", required=True, help="what the checkout is, e.g. a commit"
|
|
129
|
+
)
|
|
130
|
+
args = parser.parse_args()
|
|
131
|
+
signatures = collect(args.checkout.resolve())
|
|
132
|
+
_OUTPUT.write_text(render(signatures, args.source))
|
|
133
|
+
print(f"{len(signatures)} signatures -> {_OUTPUT}", file=sys.stderr)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
main()
|
|
@@ -27,6 +27,8 @@ from ipor_fusion.config.roles import Roles
|
|
|
27
27
|
from ipor_fusion.core.access import (
|
|
28
28
|
AccessManager,
|
|
29
29
|
CallPermission,
|
|
30
|
+
CallPlan,
|
|
31
|
+
CallPlanKind,
|
|
30
32
|
RoleAccount,
|
|
31
33
|
RoleAccountJson,
|
|
32
34
|
RoleAccountsJson,
|
|
@@ -556,6 +558,8 @@ __all__ = [
|
|
|
556
558
|
"RoleAccount",
|
|
557
559
|
"RoleAccountJson",
|
|
558
560
|
"CallPermission",
|
|
561
|
+
"CallPlan",
|
|
562
|
+
"CallPlanKind",
|
|
559
563
|
"RoleStatus",
|
|
560
564
|
"resolve_access_manager",
|
|
561
565
|
"role_account_sort_key",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from ipor_fusion.core.access import (
|
|
2
2
|
AccessManager,
|
|
3
3
|
CallPermission,
|
|
4
|
+
CallPlan,
|
|
5
|
+
CallPlanKind,
|
|
4
6
|
RoleAccount,
|
|
5
7
|
RoleAccountJson,
|
|
6
8
|
RoleAccountsJson,
|
|
@@ -107,6 +109,8 @@ __all__ = [
|
|
|
107
109
|
"RoleAccount",
|
|
108
110
|
"RoleAccountJson",
|
|
109
111
|
"CallPermission",
|
|
112
|
+
"CallPlan",
|
|
113
|
+
"CallPlanKind",
|
|
110
114
|
"RoleStatus",
|
|
111
115
|
"RewardsManager",
|
|
112
116
|
"VestingData",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from collections.abc import Callable, Sequence
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import TypedDict
|
|
3
|
+
from typing import Literal, TypedDict
|
|
4
4
|
|
|
5
5
|
from eth_abi import decode, encode
|
|
6
6
|
from eth_abi.exceptions import InsufficientDataBytes
|
|
@@ -68,6 +68,33 @@ class CallPermission:
|
|
|
68
68
|
return self.immediate or self.delay > 0
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
# schedule(when=0): as soon as the sender's execution delay allows.
|
|
72
|
+
_ASAP = Timestamp(0)
|
|
73
|
+
|
|
74
|
+
CallPlanKind = Literal["immediate", "schedule", "waiting", "ready", "forbidden"]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True, slots=True)
|
|
78
|
+
class CallPlan:
|
|
79
|
+
"""How ``caller`` gets a restricted call through the access manager now.
|
|
80
|
+
|
|
81
|
+
- ``immediate``: send the call itself (``next_call``).
|
|
82
|
+
- ``schedule``: the caller has an execution delay and nothing is scheduled;
|
|
83
|
+
``next_call`` schedules it, executable ``delay`` seconds after it lands.
|
|
84
|
+
- ``waiting``: scheduled, executable at ``scheduled_at``; nothing to send yet.
|
|
85
|
+
- ``ready``: scheduled and executable; ``next_call`` is the call itself (or
|
|
86
|
+
the manager's ``execute`` with ``via_manager``), valid until the schedule
|
|
87
|
+
expires a week after ``scheduled_at``; an expired one plans ``schedule``.
|
|
88
|
+
- ``forbidden``: the caller has no role for it, or the target is closed.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
kind: CallPlanKind
|
|
92
|
+
permission: CallPermission
|
|
93
|
+
operation: ScheduleId
|
|
94
|
+
scheduled_at: Timestamp | None
|
|
95
|
+
next_call: Call[None] | None
|
|
96
|
+
|
|
97
|
+
|
|
71
98
|
def _call_permission_decoder(values: tuple[bool, int]) -> CallPermission:
|
|
72
99
|
immediate, delay = values
|
|
73
100
|
return CallPermission(immediate=immediate, delay=Period(delay))
|
|
@@ -204,6 +231,82 @@ class AccessManager(ContractWrapper):
|
|
|
204
231
|
Web3.keccak(encode(["address", "address", "bytes"], [caller, target, data]))
|
|
205
232
|
)
|
|
206
233
|
|
|
234
|
+
def schedule(
|
|
235
|
+
self, target: ChecksumAddress, data: bytes, when: Timestamp = _ASAP
|
|
236
|
+
) -> Call[None]:
|
|
237
|
+
"""Schedule ``data`` on ``target`` for the sender, executable at ``when``;
|
|
238
|
+
0 means as soon as the sender's execution delay allows, and a nonzero
|
|
239
|
+
time before that reverts."""
|
|
240
|
+
return self._write("schedule(address,bytes,uint48)", target, data, when)
|
|
241
|
+
|
|
242
|
+
def execute(self, target: ChecksumAddress, data: bytes) -> Call[None]:
|
|
243
|
+
"""Run ``data`` on ``target`` through the manager: an immediate call, or
|
|
244
|
+
the sender's scheduled operation once it is executable (it is consumed)."""
|
|
245
|
+
return self._write("execute(address,bytes)", target, data)
|
|
246
|
+
|
|
247
|
+
def cancel(
|
|
248
|
+
self, caller: ChecksumAddress, target: ChecksumAddress, data: bytes
|
|
249
|
+
) -> Call[None]:
|
|
250
|
+
"""Cancel ``caller``'s scheduled ``data`` on ``target``: the caller itself,
|
|
251
|
+
an ADMIN_ROLE member, or the guardian of the role ``target`` maps the
|
|
252
|
+
function's selector to may."""
|
|
253
|
+
return self._write("cancel(address,address,bytes)", caller, target, data)
|
|
254
|
+
|
|
255
|
+
def read_call_plan(
|
|
256
|
+
self,
|
|
257
|
+
ctx: Web3Context,
|
|
258
|
+
call: Call[object],
|
|
259
|
+
*,
|
|
260
|
+
caller: ChecksumAddress,
|
|
261
|
+
via_manager: bool = False,
|
|
262
|
+
) -> CallPlan:
|
|
263
|
+
"""What ``caller`` sends now to get ``call`` executed: an access and
|
|
264
|
+
timelock plan from ``canCall`` and, for a delayed caller, the schedule of
|
|
265
|
+
exactly ``call``'s calldata, all read at the block ``ctx.default_block``
|
|
266
|
+
names (a number, hash or tag, resolved once). It does not run the target's own checks
|
|
267
|
+
(redemption lock, pre-hooks, balances): simulate the call for those.
|
|
268
|
+
|
|
269
|
+
A ready operation is sent as ``call`` itself, from ``caller``: the target
|
|
270
|
+
consumes the schedule and still sees ``caller`` as the sender, which
|
|
271
|
+
deposits, withdrawals and other sender-dependent calls need.
|
|
272
|
+
``via_manager`` sends it through the manager's ``execute`` instead, for a
|
|
273
|
+
target that does not consume schedules; the manager is then the sender.
|
|
274
|
+
|
|
275
|
+
Raises ``ValueError`` for a call on the access manager itself: its
|
|
276
|
+
authorization is function-specific (role grants depend on the role's
|
|
277
|
+
admin, target configuration on the target's admin delay, and the IPOR
|
|
278
|
+
manager's own restricted functions on ``canCall``), which this plan does
|
|
279
|
+
not model.
|
|
280
|
+
"""
|
|
281
|
+
if call.to == self.address:
|
|
282
|
+
raise ValueError(
|
|
283
|
+
"read_call_plan does not plan calls on the access manager itself:"
|
|
284
|
+
" their authorization is function-specific"
|
|
285
|
+
)
|
|
286
|
+
block = ctx.web3.eth.get_block(ctx.default_block)
|
|
287
|
+
ctx = ctx.at_block(block["number"])
|
|
288
|
+
selector = bytes(call.data[:4])
|
|
289
|
+
permission = self.can_call(caller, call.to, selector).call(ctx)
|
|
290
|
+
operation = self.hash_operation(caller, call.to, call.data)
|
|
291
|
+
direct = Call[None](to=call.to, data=call.data)
|
|
292
|
+
if permission.immediate:
|
|
293
|
+
return CallPlan("immediate", permission, operation, None, direct)
|
|
294
|
+
if permission.delay == 0:
|
|
295
|
+
return CallPlan("forbidden", permission, operation, None, None)
|
|
296
|
+
scheduled_at = self.get_schedule(operation).call(ctx)
|
|
297
|
+
if scheduled_at == 0:
|
|
298
|
+
return CallPlan(
|
|
299
|
+
"schedule",
|
|
300
|
+
permission,
|
|
301
|
+
operation,
|
|
302
|
+
None,
|
|
303
|
+
self.schedule(call.to, call.data),
|
|
304
|
+
)
|
|
305
|
+
if scheduled_at > block["timestamp"]:
|
|
306
|
+
return CallPlan("waiting", permission, operation, scheduled_at, None)
|
|
307
|
+
ready = self.execute(call.to, call.data) if via_manager else direct
|
|
308
|
+
return CallPlan("ready", permission, operation, scheduled_at, ready)
|
|
309
|
+
|
|
207
310
|
def can_call(
|
|
208
311
|
self, caller: ChecksumAddress, target: ChecksumAddress, selector: bytes
|
|
209
312
|
) -> Call[CallPermission]:
|
|
@@ -10,6 +10,7 @@ from eth_utils import function_signature_to_4byte_selector
|
|
|
10
10
|
from web3 import Web3
|
|
11
11
|
from web3.types import TxParams, TxReceipt
|
|
12
12
|
|
|
13
|
+
from ipor_fusion.fusion_errors import FUSION_ERROR_SIGNATURES
|
|
13
14
|
from ipor_fusion.types import TxHash
|
|
14
15
|
|
|
15
16
|
log = logging.getLogger(__name__)
|
|
@@ -50,6 +51,9 @@ def register_custom_errors(signatures: Iterable[str]) -> None:
|
|
|
50
51
|
CUSTOM_ERRORS[function_signature_to_4byte_selector(canonical)] = (name, types)
|
|
51
52
|
|
|
52
53
|
|
|
54
|
+
register_custom_errors(FUSION_ERROR_SIGNATURES)
|
|
55
|
+
|
|
56
|
+
|
|
53
57
|
def decode_custom_error(selector: bytes, payload: bytes) -> str | None:
|
|
54
58
|
"""``Name(arg, arg)`` for a registered selector, ``None`` otherwise."""
|
|
55
59
|
registered = CUSTOM_ERRORS.get(bytes(selector))
|