ipor-fusion 4.0.0.dev63__tar.gz → 4.0.0.dev68__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.dev63 → ipor_fusion-4.0.0.dev68}/MIGRATION_4.md +21 -4
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/PKG-INFO +1 -1
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_info_view.py +2 -2
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/fusion_factory.py +64 -1
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/simulation.py +15 -12
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/quote.py +1 -3
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/simulation.py +2 -6
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/external_state_events.py +6 -1
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/mcp/server.py +2 -2
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/substrates.py +21 -16
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/types.py +16 -9
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_vault_info_golden.py +2 -2
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_external_state_events.py +7 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_external_state_executor.py +1 -1
- ipor_fusion-4.0.0.dev68/tests/test_fusion_instance.py +70 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_crosschain_hyperevm.py +1 -3
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_types_and_events.py +16 -5
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_vault_simulator_blocks.py +11 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/typed_client/consumer.py +1 -1
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/AGENTS.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/README.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_info.py +1 -1
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_helpers.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/uv.lock +0 -0
|
@@ -29,15 +29,22 @@ widths or address spelling.
|
|
|
29
29
|
| `RoleId` | a `Roles` member (accepted as is), or `RoleId(n)`; `Roles.resolve(name)` returns one |
|
|
30
30
|
| `Decimals`, `Period`, `Amount`, ... | `Decimals(8)`, `Period(0)`, `Amount(n)` (unchanged) |
|
|
31
31
|
| `ChecksumAddress` | `Web3.to_checksum_address(value)` |
|
|
32
|
-
| `HexAddress` | any `ChecksumAddress`, or `parse_hex_address(value)` (20 bytes or hex in any
|
|
32
|
+
| `HexAddress` | any `ChecksumAddress`, or `parse_hex_address(value)` (20 bytes or `0x`/`0X`-prefixed hex in any case → lowercase `0x` form) |
|
|
33
33
|
| `MorphoBlueMarketId` | `parse_morpho_market_id(value)`: 32 bytes or hex in any spelling, always `0x` + 64 lowercase digits |
|
|
34
|
-
| `TxHash` | `parse_tx_hash(value)` (32 bytes or hex in any
|
|
34
|
+
| `TxHash` | `parse_tx_hash(value)` (32 bytes or `0x`/`0X`-prefixed hex in any case) |
|
|
35
35
|
| `PlasmaVaultAddress`, `FuseAddress`, `AccessManagerAddress` | from the SDK (`PlasmaVault.address`, `get_fuses()`, `get_access_manager_address()`, `FusionInstance`, discovery), or `parse_plasma_vault_address` / `parse_fuse_address` / `parse_access_manager_address(value)` for a raw address; they label the role and check nothing on chain |
|
|
36
36
|
| crosschain ids | `CommandId(b)`, `MessageId(b)`, `AssetId(b)`, `OperationId(b)`, `TransferId(b)`, `ReturnRequestId(b)`, `PositionSetHash(b)` (bytes32); `ProposalId(n)`, `DeploymentRequestId(n)`, `StargateAssetId(n)` (int) |
|
|
37
37
|
| HyperCore ids | `HyperCoreTokenIndex(n)` (or `USDC_TOKEN_INDEX`), `HyperCoreAsset(n)` (or `hip3_action_asset(dex, index)`), `HyperCoreOrderId(n)`, `HyperCoreClientOrderId(n)`, `MarginTableId(n)`, `HyperCoreActionId(n)`, `PayloadHash(b)` |
|
|
38
38
|
| external state | `ProposalHash(b)` |
|
|
39
39
|
| `BlockIdentifier` | a block number, `"latest"` / `"pending"` / ..., or a block hash |
|
|
40
40
|
|
|
41
|
+
The address and hash parsers (`parse_hex_address`, `parse_tx_hash`,
|
|
42
|
+
`parse_plasma_vault_address`, `parse_fuse_address`, `parse_access_manager_address`)
|
|
43
|
+
validate strictly: a string needs exactly one `0x`/`0X` prefix and the exact number of
|
|
44
|
+
hex digits, with no surrounding whitespace; anything else raises `ValueError`. Strip
|
|
45
|
+
user input yourself before parsing it. `parse_morpho_market_id` still accepts hex
|
|
46
|
+
without `0x`.
|
|
47
|
+
|
|
41
48
|
`MessageId` covers both a CCIP `messageId` and a LayerZero `guid`. `AssetId` is the
|
|
42
49
|
crosschain executor's `bytes32` asset id; a Stargate pool's `uint16` asset id is
|
|
43
50
|
`StargateAssetId`. `DeploymentRequestId` is a factory's `uint256` dispatcher request;
|
|
@@ -49,8 +56,9 @@ a `bytes32` return (recall) request is `ReturnRequestId`.
|
|
|
49
56
|
`build_vault_info_json`, `ensure_supported_chain` and `fetch_vault_lending_health`
|
|
50
57
|
take `ChainId`. The vault is a `PlasmaVaultAddress` (see *Address roles*) in
|
|
51
58
|
`validate_vault` and `fetch_deployment_info`; `build_vault_info_json(ctx, plasma_vault,
|
|
52
|
-
data, chain_id, chain_label)` takes no address
|
|
53
|
-
`plasma_vault.address
|
|
59
|
+
data, *, chain_id, chain_label)` takes no address, takes the chain by keyword, and
|
|
60
|
+
labels the document with `plasma_vault.address` (a test that mocks `PlasmaVault`
|
|
61
|
+
sets `.address`). `fetch_unpriceable_priced_tokens` takes the oracle and
|
|
54
62
|
underlying as `ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
|
|
55
63
|
`get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
|
|
56
64
|
fits), and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
|
|
@@ -102,6 +110,15 @@ Every fuse constructor, `FuseAction.fuse`, `BalanceFuse.fuse`,
|
|
|
102
110
|
`FusionInstance.access_manager` return one. `resolve_access_manager` still takes any
|
|
103
111
|
`ChecksumAddress`: it is what checks that the address is a vault.
|
|
104
112
|
|
|
113
|
+
**Simulation and factory records.** `VaultSimulator(web3, *, vault=None, alpha=None,
|
|
114
|
+
block=...)` takes every argument after `web3` by keyword, and `vault` and `alpha` are
|
|
115
|
+
optional: a simulator that only observes, deploys or sends `execute_call(call,
|
|
116
|
+
from_=...)` needs neither, so drop a zero-address placeholder. `execute()` without
|
|
117
|
+
`vault` and `alpha` raises `ValueError`, as does `execute_call` without `from_` or
|
|
118
|
+
`alpha`. `FusionInstance.from_dict(data)` rebuilds a stored instance (the
|
|
119
|
+
`dataclasses.asdict` form) with every field parsed; use it instead of
|
|
120
|
+
`FusionInstance(**data)`, which passes raw strings into the role-typed fields.
|
|
121
|
+
|
|
105
122
|
**Markets and substrates.** Every `IporFusionMarkets` member is a `MarketId`.
|
|
106
123
|
`decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
|
|
107
124
|
|
|
@@ -98,8 +98,8 @@ def _print_vault_info(
|
|
|
98
98
|
ctx,
|
|
99
99
|
plasma_vault,
|
|
100
100
|
data,
|
|
101
|
-
chain_id,
|
|
102
|
-
chain_label,
|
|
101
|
+
chain_id=chain_id,
|
|
102
|
+
chain_label=chain_label,
|
|
103
103
|
resolve_name=partial(get_contract_name, chain_id, api_key=api_key),
|
|
104
104
|
)
|
|
105
105
|
click.echo(json.dumps(result, indent=2))
|
|
@@ -22,7 +22,9 @@ For external-signer flows, grab the bytes directly:
|
|
|
22
22
|
|
|
23
23
|
from __future__ import annotations
|
|
24
24
|
|
|
25
|
+
from collections.abc import Callable, Mapping
|
|
25
26
|
from dataclasses import dataclass
|
|
27
|
+
from typing import Any
|
|
26
28
|
|
|
27
29
|
from eth_abi import decode as abi_decode
|
|
28
30
|
from eth_typing import ChecksumAddress
|
|
@@ -31,7 +33,13 @@ from web3 import Web3
|
|
|
31
33
|
from web3.types import TxReceipt
|
|
32
34
|
|
|
33
35
|
from ipor_fusion.core.contract import Call, ContractWrapper
|
|
34
|
-
from ipor_fusion.types import
|
|
36
|
+
from ipor_fusion.types import (
|
|
37
|
+
AccessManagerAddress,
|
|
38
|
+
Period,
|
|
39
|
+
PlasmaVaultAddress,
|
|
40
|
+
parse_access_manager_address,
|
|
41
|
+
parse_plasma_vault_address,
|
|
42
|
+
)
|
|
35
43
|
|
|
36
44
|
|
|
37
45
|
@dataclass(slots=True, frozen=True)
|
|
@@ -70,6 +78,61 @@ class FusionInstance:
|
|
|
70
78
|
context_manager: ChecksumAddress
|
|
71
79
|
price_manager: ChecksumAddress
|
|
72
80
|
|
|
81
|
+
@classmethod
|
|
82
|
+
def from_dict(
|
|
83
|
+
cls, data: Mapping[str, Any]
|
|
84
|
+
) -> FusionInstance: # proctor: ignore[primitives-untyped-dict]
|
|
85
|
+
"""Rebuild an instance from its JSON form (``dataclasses.asdict``),
|
|
86
|
+
parsing every field: role addresses through their parsers, the rest
|
|
87
|
+
checksummed, integers and strings type-checked. Raises ``ValueError``
|
|
88
|
+
on a missing, extra or malformed field."""
|
|
89
|
+
missing = _INSTANCE_FIELDS.keys() - data.keys()
|
|
90
|
+
extra = data.keys() - _INSTANCE_FIELDS.keys()
|
|
91
|
+
if missing or extra:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
f"FusionInstance fields: missing {sorted(missing)}, extra {sorted(extra)}"
|
|
94
|
+
)
|
|
95
|
+
return cls(
|
|
96
|
+
**{name: parse(data[name]) for name, parse in _INSTANCE_FIELDS.items()}
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _integer(value: Any) -> int:
|
|
101
|
+
if type(value) is not int:
|
|
102
|
+
raise ValueError(f"expected an integer: {value!r}")
|
|
103
|
+
return value
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _text(value: Any) -> str:
|
|
107
|
+
if not isinstance(value, str):
|
|
108
|
+
raise ValueError(f"expected a string: {value!r}")
|
|
109
|
+
return value
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _address(value: Any) -> ChecksumAddress:
|
|
113
|
+
return Web3.to_checksum_address(_text(value))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
_INSTANCE_FIELDS: dict[str, Callable[[Any], Any]] = {
|
|
117
|
+
"index": _integer,
|
|
118
|
+
"version": _integer,
|
|
119
|
+
"asset_name": _text,
|
|
120
|
+
"asset_symbol": _text,
|
|
121
|
+
"asset_decimals": _integer,
|
|
122
|
+
"underlying_token": _address,
|
|
123
|
+
"underlying_token_symbol": _text,
|
|
124
|
+
"underlying_token_decimals": _integer,
|
|
125
|
+
"initial_owner": _address,
|
|
126
|
+
"plasma_vault": lambda value: parse_plasma_vault_address(_text(value)),
|
|
127
|
+
"plasma_vault_base": _address,
|
|
128
|
+
"access_manager": lambda value: parse_access_manager_address(_text(value)),
|
|
129
|
+
"fee_manager": _address,
|
|
130
|
+
"rewards_manager": _address,
|
|
131
|
+
"withdraw_manager": _address,
|
|
132
|
+
"context_manager": _address,
|
|
133
|
+
"price_manager": _address,
|
|
134
|
+
}
|
|
135
|
+
|
|
73
136
|
|
|
74
137
|
_FUSION_INSTANCE_OUTPUT_TYPES: list[str] = [
|
|
75
138
|
"uint256", # index
|
|
@@ -16,7 +16,7 @@ from web3.utils.address import get_create_address
|
|
|
16
16
|
from ipor_fusion.core.contract import Call, _encode_calldata
|
|
17
17
|
from ipor_fusion.core.fuse_action import FuseAction
|
|
18
18
|
from ipor_fusion.errors import SimulationError, decode_custom_error
|
|
19
|
-
from ipor_fusion.types import
|
|
19
|
+
from ipor_fusion.types import PlasmaVaultAddress
|
|
20
20
|
|
|
21
21
|
#: ``n`` (the sent block's index in the run) -> account overrides for it.
|
|
22
22
|
StateOverrideProvider = Callable[[int], Mapping[str, Mapping[str, Any]]]
|
|
@@ -190,15 +190,16 @@ class VaultSimulator:
|
|
|
190
190
|
def __init__(
|
|
191
191
|
self,
|
|
192
192
|
web3: Web3,
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
*,
|
|
194
|
+
vault: PlasmaVaultAddress | None = None,
|
|
195
|
+
alpha: ChecksumAddress | None = None,
|
|
195
196
|
block: BlockIdentifier = "latest",
|
|
196
197
|
validation: bool = False,
|
|
197
198
|
trace_transfers: bool = False,
|
|
198
199
|
):
|
|
199
200
|
self._web3 = web3
|
|
200
|
-
self._vault =
|
|
201
|
-
self._alpha =
|
|
201
|
+
self._vault = vault
|
|
202
|
+
self._alpha = alpha
|
|
202
203
|
self._block: BlockIdentifier = block
|
|
203
204
|
self._validation = validation
|
|
204
205
|
self._trace_transfers = trace_transfers
|
|
@@ -341,6 +342,8 @@ class VaultSimulator:
|
|
|
341
342
|
|
|
342
343
|
def execute(self, actions: list[FuseAction]) -> VaultSimulator:
|
|
343
344
|
"""Queue a default `execute((address,bytes)[])` batch on the vault, from alpha."""
|
|
345
|
+
if self._vault is None or self._alpha is None:
|
|
346
|
+
raise ValueError("execute needs a simulator built with vault= and alpha=")
|
|
344
347
|
data = FuseAction.encode_execute_payload(actions, "execute((address,bytes)[])")
|
|
345
348
|
return self._queue_execute(self._vault, data, self._alpha)
|
|
346
349
|
|
|
@@ -351,11 +354,12 @@ class VaultSimulator:
|
|
|
351
354
|
e.g. `RewardsManager.claim_rewards(...)` where the wrapper differs from
|
|
352
355
|
`PlasmaVault.execute`. `from_` defaults to alpha.
|
|
353
356
|
"""
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
357
|
+
sender = from_ or self._alpha
|
|
358
|
+
if sender is None:
|
|
359
|
+
raise ValueError(
|
|
360
|
+
"execute_call needs from_ or a simulator built with alpha="
|
|
361
|
+
)
|
|
362
|
+
return self._queue_execute(call.to, call.data, sender)
|
|
359
363
|
|
|
360
364
|
def _queue_execute(
|
|
361
365
|
self, to: ChecksumAddress, data: bytes, from_: ChecksumAddress
|
|
@@ -751,8 +755,7 @@ def erc20_balance_slot(
|
|
|
751
755
|
if max_slot < 0:
|
|
752
756
|
raise ValueError(no_slot)
|
|
753
757
|
|
|
754
|
-
|
|
755
|
-
sim = VaultSimulator(web3, vault=zero, alpha=zero, block=block)
|
|
758
|
+
sim = VaultSimulator(web3, block=block)
|
|
756
759
|
for slot in range(max_slot + 1):
|
|
757
760
|
holder = Web3.to_checksum_address(
|
|
758
761
|
"0x" + keccak(encode(["uint256"], [slot]))[-20:].hex()
|
{ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/ccip/quote.py
RENAMED
|
@@ -18,13 +18,11 @@ from web3.types import BlockIdentifier
|
|
|
18
18
|
|
|
19
19
|
from ipor_fusion.core.contract import Call
|
|
20
20
|
from ipor_fusion.core.simulation import VaultSimulator
|
|
21
|
-
from ipor_fusion.types import parse_plasma_vault_address
|
|
22
21
|
|
|
23
22
|
#: Selector of ``CcipInsufficientNativeFeeBalance(uint256 balance, uint256 required)``.
|
|
24
23
|
INSUFFICIENT_NATIVE_FEE_SELECTOR: bytes = keccak(
|
|
25
24
|
text="CcipInsufficientNativeFeeBalance(uint256,uint256)"
|
|
26
25
|
)[:4]
|
|
27
|
-
_NO_VAULT = parse_plasma_vault_address(bytes(20))
|
|
28
26
|
|
|
29
27
|
|
|
30
28
|
def quote_ccip_native_fee(
|
|
@@ -45,7 +43,7 @@ def quote_ccip_native_fee(
|
|
|
45
43
|
valid at ``block`` up to the fee check; a revert before it, or a call that
|
|
46
44
|
sends no message, raises ``ValueError``.
|
|
47
45
|
"""
|
|
48
|
-
simulator = VaultSimulator(web3,
|
|
46
|
+
simulator = VaultSimulator(web3, alpha=from_, block=block)
|
|
49
47
|
simulator.with_state_override(payer, balance=hex(0))
|
|
50
48
|
simulator.add_call(call, from_=from_, label="quote", gas=gas)
|
|
51
49
|
(result,) = simulator.run().calls
|
{ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/crosschain/simulation.py
RENAMED
|
@@ -25,7 +25,6 @@ Example -- bridge from a source vault and let the dispatcher settle::
|
|
|
25
25
|
from __future__ import annotations
|
|
26
26
|
|
|
27
27
|
from collections.abc import Mapping
|
|
28
|
-
from typing import Any
|
|
29
28
|
|
|
30
29
|
from eth_typing import ChecksumAddress
|
|
31
30
|
from web3 import Web3
|
|
@@ -41,7 +40,7 @@ from ipor_fusion.crosschain.transport import (
|
|
|
41
40
|
CrosschainTransport,
|
|
42
41
|
OutboundMessage,
|
|
43
42
|
)
|
|
44
|
-
from ipor_fusion.types import
|
|
43
|
+
from ipor_fusion.types import ChainId, PlasmaVaultAddress
|
|
45
44
|
|
|
46
45
|
#: What the synthetic token source holds: enough for any test amount.
|
|
47
46
|
SYNTHETIC_BALANCE = 2**96
|
|
@@ -89,10 +88,7 @@ class CrosschainSimulator:
|
|
|
89
88
|
raise ValueError(f"chain {chain_id} is not configured on the transport")
|
|
90
89
|
if chain_id in self._chains:
|
|
91
90
|
raise ValueError(f"chain {chain_id} already added")
|
|
92
|
-
|
|
93
|
-
sim = VaultSimulator(
|
|
94
|
-
web3, vault=vault or zero, alpha=alpha or zero, block=block
|
|
95
|
-
)
|
|
91
|
+
sim = VaultSimulator(web3, vault=vault, alpha=alpha, block=block)
|
|
96
92
|
# A chain without a real token holder to impersonate gets a synthetic
|
|
97
93
|
# one, funded once by a storage override; deliveries transfer from it.
|
|
98
94
|
for token in self._transport.synthetic_credit_tokens(chain_id):
|
{ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/src/ipor_fusion/fuses/external_state_events.py
RENAMED
|
@@ -196,7 +196,12 @@ def external_state_event_name(topic: bytes | str) -> str | None:
|
|
|
196
196
|
def _hex(value: Any) -> TxHash | None:
|
|
197
197
|
if value is None:
|
|
198
198
|
return None
|
|
199
|
-
|
|
199
|
+
raw = _as_bytes(value, "transactionHash")
|
|
200
|
+
if len(raw) != 32:
|
|
201
|
+
raise ExternalStateEventDecodeError(
|
|
202
|
+
f"transactionHash: {len(raw)} bytes, expected 32"
|
|
203
|
+
)
|
|
204
|
+
return parse_tx_hash(raw)
|
|
200
205
|
|
|
201
206
|
|
|
202
207
|
def decode_external_state_event(log: Mapping[str, Any]) -> ExternalStateEvent | None:
|
|
@@ -216,8 +216,8 @@ def vault_info(
|
|
|
216
216
|
ctx,
|
|
217
217
|
plasma_vault,
|
|
218
218
|
data,
|
|
219
|
-
chain_id,
|
|
220
|
-
chain_label,
|
|
219
|
+
chain_id=chain_id,
|
|
220
|
+
chain_label=chain_label,
|
|
221
221
|
resolve_name=partial(get_contract_name, chain_id, api_key=api_key),
|
|
222
222
|
)
|
|
223
223
|
return VaultInfoResponse.model_validate(result)
|
|
@@ -47,10 +47,15 @@ class SubstrateInfo:
|
|
|
47
47
|
# Slippage variant: [2 type chars][62 value chars]
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
def _address(hex_digits: str) -> HexAddress:
|
|
51
|
+
"""The address in 40 hex digits sliced out of a substrate word."""
|
|
52
|
+
return parse_hex_address("0x" + hex_digits)
|
|
53
|
+
|
|
54
|
+
|
|
50
55
|
def _decode_type_lshift160(hex_str: str, types: dict[int, str]) -> SubstrateInfo:
|
|
51
56
|
"""Decode type<<160 | address (Ebisu, Midas, Balancer, Velodrome)."""
|
|
52
57
|
type_byte = int(hex_str[22:24], 16)
|
|
53
|
-
addr =
|
|
58
|
+
addr = _address(hex_str[24:])
|
|
54
59
|
label = types.get(type_byte, f"type={type_byte}")
|
|
55
60
|
return SubstrateInfo(address=addr, type_label=label)
|
|
56
61
|
|
|
@@ -63,13 +68,13 @@ def _decode_type_lshift248(hex_str: str, types: dict[int, str]) -> SubstrateInfo
|
|
|
63
68
|
return SubstrateInfo(
|
|
64
69
|
raw_hex=f"0x{hex_str}", type_label=label, extra={"slippage": str(value)}
|
|
65
70
|
)
|
|
66
|
-
addr =
|
|
71
|
+
addr = _address(hex_str[24:])
|
|
67
72
|
return SubstrateInfo(address=addr, type_label=label)
|
|
68
73
|
|
|
69
74
|
|
|
70
75
|
def _decode_plain_address(hex_str: str) -> SubstrateInfo:
|
|
71
76
|
"""Decode zero-padded address: 12 zero bytes + 20 address bytes."""
|
|
72
|
-
return SubstrateInfo(address=
|
|
77
|
+
return SubstrateInfo(address=_address(hex_str[24:]))
|
|
73
78
|
|
|
74
79
|
|
|
75
80
|
def _decode_morpho(hex_str: str) -> SubstrateInfo:
|
|
@@ -84,14 +89,14 @@ def _decode_uniswap_v4(hex_str: str) -> SubstrateInfo:
|
|
|
84
89
|
substrate-as-asset (12 zero bytes + address)."""
|
|
85
90
|
if hex_str[:24] == "0" * 24:
|
|
86
91
|
return SubstrateInfo(
|
|
87
|
-
address=
|
|
92
|
+
address=_address(hex_str[24:]), type_label="uniswap_v4_token"
|
|
88
93
|
)
|
|
89
94
|
return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label="uniswap_v4_pool_id")
|
|
90
95
|
|
|
91
96
|
|
|
92
97
|
def _decode_enso(hex_str: str) -> SubstrateInfo:
|
|
93
98
|
"""Decode address<<96 | selector<<64 (Enso)."""
|
|
94
|
-
addr =
|
|
99
|
+
addr = _address(hex_str[0:40])
|
|
95
100
|
selector = f"0x{hex_str[40:48]}"
|
|
96
101
|
return SubstrateInfo(
|
|
97
102
|
address=addr, type_label="ENSO_TARGET", extra={"selector": selector}
|
|
@@ -100,7 +105,7 @@ def _decode_enso(hex_str: str) -> SubstrateInfo:
|
|
|
100
105
|
|
|
101
106
|
def _decode_dolomite(hex_str: str) -> SubstrateInfo:
|
|
102
107
|
"""Decode asset<<96 | subAccountId<<88 | canBorrow<<80 (Dolomite)."""
|
|
103
|
-
addr =
|
|
108
|
+
addr = _address(hex_str[0:40])
|
|
104
109
|
sub_account_id = int(hex_str[40:42], 16)
|
|
105
110
|
can_borrow = (int(hex_str[42:44], 16) & 0x01) == 1
|
|
106
111
|
return SubstrateInfo(
|
|
@@ -127,7 +132,7 @@ def _decode_aave_v4(hex_str: str) -> SubstrateInfo:
|
|
|
127
132
|
return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label=label)
|
|
128
133
|
flags = int(hex_str[50:52], 16)
|
|
129
134
|
return SubstrateInfo(
|
|
130
|
-
address=
|
|
135
|
+
address=_address(hex_str[2:42]),
|
|
131
136
|
type_label="AAVE_V4_RESERVE",
|
|
132
137
|
extra={
|
|
133
138
|
"reserve_id": str(int(hex_str[42:50], 16)),
|
|
@@ -145,7 +150,7 @@ def _decode_euler_v2(hex_str: str) -> SubstrateInfo:
|
|
|
145
150
|
plain-address path silently produces a malformed address (last 20 bytes are
|
|
146
151
|
flag bytes + zero padding).
|
|
147
152
|
"""
|
|
148
|
-
addr =
|
|
153
|
+
addr = _address(hex_str[0:40])
|
|
149
154
|
is_collateral = (int(hex_str[40:42], 16) & 0x01) == 1
|
|
150
155
|
can_borrow = (int(hex_str[42:44], 16) & 0x01) == 1
|
|
151
156
|
sub_account = f"0x{hex_str[44:46]}"
|
|
@@ -174,13 +179,13 @@ def _decode_async_action(hex_str: str) -> SubstrateInfo:
|
|
|
174
179
|
type_byte = int(hex_str[0:2], 16)
|
|
175
180
|
if type_byte == 0:
|
|
176
181
|
return SubstrateInfo(
|
|
177
|
-
address=
|
|
182
|
+
address=_address(hex_str[2:42]),
|
|
178
183
|
type_label="ALLOWED_AMOUNT_TO_OUTSIDE",
|
|
179
184
|
extra={"amount": str(int(hex_str[42:64], 16))},
|
|
180
185
|
)
|
|
181
186
|
if type_byte == 1:
|
|
182
187
|
return SubstrateInfo(
|
|
183
|
-
address=
|
|
188
|
+
address=_address(hex_str[16:56]),
|
|
184
189
|
type_label="ALLOWED_TARGETS",
|
|
185
190
|
extra={"selector": f"0x{hex_str[56:64]}"},
|
|
186
191
|
)
|
|
@@ -225,10 +230,10 @@ def _decode_external_state(hex_str: str) -> SubstrateInfo:
|
|
|
225
230
|
if type_byte == 0:
|
|
226
231
|
return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label="UNDEFINED")
|
|
227
232
|
if label := _EXTERNAL_STATE_ADDRESS_TYPES.get(type_byte):
|
|
228
|
-
return SubstrateInfo(address=
|
|
233
|
+
return SubstrateInfo(address=_address(hex_str[24:]), type_label=label)
|
|
229
234
|
if type_byte == 2:
|
|
230
235
|
return SubstrateInfo(
|
|
231
|
-
address=
|
|
236
|
+
address=_address(hex_str[24:]),
|
|
232
237
|
type_label="TARGET",
|
|
233
238
|
extra={"selector": f"0x{hex_str[16:24]}"},
|
|
234
239
|
)
|
|
@@ -266,7 +271,7 @@ def _decode_crosschain(hex_str: str) -> SubstrateInfo:
|
|
|
266
271
|
is_error=True,
|
|
267
272
|
extra={"chain_id": str(chain_id)},
|
|
268
273
|
)
|
|
269
|
-
info = SubstrateInfo(address=
|
|
274
|
+
info = SubstrateInfo(address=_address(hex_str[24:]), type_label=label)
|
|
270
275
|
if type_byte == 2:
|
|
271
276
|
info.extra = {"chain_id": str(chain_id)}
|
|
272
277
|
return info
|
|
@@ -326,7 +331,7 @@ def _decode_hypercore(hex_str: str) -> SubstrateInfo:
|
|
|
326
331
|
return _hypercore_invalid(hex_str, label)
|
|
327
332
|
if type_byte == 1:
|
|
328
333
|
return SubstrateInfo(
|
|
329
|
-
address=
|
|
334
|
+
address=_address(hex_str[24:]),
|
|
330
335
|
type_label=label,
|
|
331
336
|
extra={"token_index": str(int(hex_str[8:24], 16))},
|
|
332
337
|
)
|
|
@@ -347,10 +352,10 @@ def _decode_hypercore(hex_str: str) -> SubstrateInfo:
|
|
|
347
352
|
extra["read_index"] = str(read_index_of(asset))
|
|
348
353
|
return SubstrateInfo(raw_hex=f"0x{hex_str}", type_label=label, extra=extra)
|
|
349
354
|
if type_byte == 3:
|
|
350
|
-
return SubstrateInfo(address=
|
|
355
|
+
return SubstrateInfo(address=_address(hex_str[24:]), type_label=label)
|
|
351
356
|
if type_byte == 4:
|
|
352
357
|
return SubstrateInfo(
|
|
353
|
-
address=
|
|
358
|
+
address=_address(hex_str[24:]),
|
|
354
359
|
type_label=label,
|
|
355
360
|
extra={"max_fee_rate_decibps": str(int(hex_str[8:24], 16))},
|
|
356
361
|
)
|
|
@@ -145,22 +145,28 @@ def _hex_digits(value: str | bytes, size: int, what: str) -> str:
|
|
|
145
145
|
if len(value) != size:
|
|
146
146
|
raise ValueError(f"invalid {what} (expected {size} bytes): {value!r}")
|
|
147
147
|
return value.hex()
|
|
148
|
-
digits = value.
|
|
149
|
-
if
|
|
150
|
-
|
|
148
|
+
digits = value[2:].lower()
|
|
149
|
+
if (
|
|
150
|
+
value[:2] not in ("0x", "0X")
|
|
151
|
+
or len(digits) != 2 * size
|
|
152
|
+
or any(c not in "0123456789abcdef" for c in digits)
|
|
153
|
+
):
|
|
154
|
+
raise ValueError(f"invalid {what} (expected 0x and {size}-byte hex): {value!r}")
|
|
151
155
|
return digits
|
|
152
156
|
|
|
153
157
|
|
|
154
158
|
def parse_hex_address(value: str | bytes) -> HexAddress:
|
|
155
|
-
"""An address as ``0x`` and 40 lowercase hex digits, from 20 raw bytes or
|
|
156
|
-
in any
|
|
159
|
+
"""An address as ``0x`` and 40 lowercase hex digits, from 20 raw bytes or
|
|
160
|
+
``0x``-prefixed hex in any case; raises ``ValueError`` on anything else,
|
|
161
|
+
surrounding whitespace included. For a checksummed
|
|
157
162
|
address use ``Web3.to_checksum_address``."""
|
|
158
163
|
return HexAddress(HexStr("0x" + _hex_digits(value, 20, "address")))
|
|
159
164
|
|
|
160
165
|
|
|
161
166
|
def parse_tx_hash(value: str | bytes) -> TxHash:
|
|
162
167
|
"""A transaction hash as ``0x`` and 64 lowercase hex digits, from 32 raw bytes
|
|
163
|
-
or hex in any
|
|
168
|
+
or ``0x``-prefixed hex in any case; raises ``ValueError`` on anything else,
|
|
169
|
+
surrounding whitespace included."""
|
|
164
170
|
return TxHash(HexStr("0x" + _hex_digits(value, 32, "transaction hash")))
|
|
165
171
|
|
|
166
172
|
|
|
@@ -169,9 +175,10 @@ def _checksum(value: str | bytes) -> ChecksumAddress:
|
|
|
169
175
|
|
|
170
176
|
|
|
171
177
|
def parse_plasma_vault_address(value: str | bytes) -> PlasmaVaultAddress:
|
|
172
|
-
"""A Plasma Vault's address, checksummed from 20 raw bytes or
|
|
173
|
-
|
|
174
|
-
checks nothing on
|
|
178
|
+
"""A Plasma Vault's address, checksummed from 20 raw bytes or ``0x``/``0X``
|
|
179
|
+
and 40 hex digits in any case; raises ``ValueError`` on anything else,
|
|
180
|
+
surrounding whitespace included. It labels the role and checks nothing on
|
|
181
|
+
chain."""
|
|
175
182
|
return PlasmaVaultAddress(_checksum(value))
|
|
176
183
|
|
|
177
184
|
|
|
@@ -378,8 +378,8 @@ def test_public_builder_renders_the_cli_json(scenario: str) -> None:
|
|
|
378
378
|
ctx,
|
|
379
379
|
PlasmaVault(ctx, VAULT), # type: ignore[arg-type]
|
|
380
380
|
data,
|
|
381
|
-
1,
|
|
382
|
-
"ethereum",
|
|
381
|
+
chain_id=1,
|
|
382
|
+
chain_label="ethereum",
|
|
383
383
|
resolve_name=lambda address: _NAMES_BY_ADDRESS.get(address.lower(), ""),
|
|
384
384
|
)
|
|
385
385
|
expected = (FIXTURES / f"{scenario}.json").read_text(encoding="utf-8")
|
|
@@ -96,6 +96,13 @@ def test_hex_text_data_and_topics_from_an_indexer_decode_too():
|
|
|
96
96
|
assert event.transaction_hash == "0x" + "fb" * 32
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
def test_a_short_transaction_hash_is_a_decode_error():
|
|
100
|
+
log = _log("ExternalStateAssetRescued", (SIGNER,))
|
|
101
|
+
log["transactionHash"] = bytes(31)
|
|
102
|
+
with pytest.raises(ExternalStateEventDecodeError, match="transactionHash"):
|
|
103
|
+
decode_external_state_event(log)
|
|
104
|
+
|
|
105
|
+
|
|
99
106
|
def test_an_unknown_topic_is_none_and_a_malformed_listed_one_raises():
|
|
100
107
|
assert (
|
|
101
108
|
decode_external_state_event(
|
|
@@ -64,7 +64,7 @@ def _observed(call, return_data: bytes):
|
|
|
64
64
|
Feeds a synthetic `eth_simulateV1` response to `_parse_response`, so the
|
|
65
65
|
result is whatever `observe` would actually report -- including its handling
|
|
66
66
|
of a decoder that raises."""
|
|
67
|
-
sim = VaultSimulator(MagicMock()
|
|
67
|
+
sim = VaultSimulator(MagicMock())
|
|
68
68
|
sim.observe("slot", call)
|
|
69
69
|
result = sim._parse_response(
|
|
70
70
|
simulate_response(
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""`FusionInstance.from_dict` parses a stored instance back into its typed
|
|
2
|
+
fields instead of passing raw JSON strings into role-typed addresses."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from dataclasses import asdict
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from web3 import Web3
|
|
10
|
+
|
|
11
|
+
from ipor_fusion.core.fusion_factory import FusionInstance
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _stored() -> dict:
|
|
15
|
+
address = {
|
|
16
|
+
name: "0x" + f"{i:02x}" * 20
|
|
17
|
+
for i, name in enumerate(
|
|
18
|
+
[
|
|
19
|
+
"underlying_token",
|
|
20
|
+
"initial_owner",
|
|
21
|
+
"plasma_vault",
|
|
22
|
+
"plasma_vault_base",
|
|
23
|
+
"access_manager",
|
|
24
|
+
"fee_manager",
|
|
25
|
+
"rewards_manager",
|
|
26
|
+
"withdraw_manager",
|
|
27
|
+
"context_manager",
|
|
28
|
+
"price_manager",
|
|
29
|
+
],
|
|
30
|
+
start=0xA1,
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
"index": 3,
|
|
35
|
+
"version": 2,
|
|
36
|
+
"asset_name": "IPOR USDC",
|
|
37
|
+
"asset_symbol": "ipUSDC",
|
|
38
|
+
"asset_decimals": 8,
|
|
39
|
+
"underlying_token_symbol": "USDC",
|
|
40
|
+
"underlying_token_decimals": 6,
|
|
41
|
+
**address,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_from_dict_checksums_every_address_and_round_trips():
|
|
46
|
+
stored = _stored()
|
|
47
|
+
instance = FusionInstance.from_dict(stored)
|
|
48
|
+
assert instance.plasma_vault == Web3.to_checksum_address(stored["plasma_vault"])
|
|
49
|
+
assert instance.access_manager == Web3.to_checksum_address(stored["access_manager"])
|
|
50
|
+
assert instance.price_manager == Web3.to_checksum_address(stored["price_manager"])
|
|
51
|
+
assert FusionInstance.from_dict(asdict(instance)) == instance
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.mark.parametrize(
|
|
55
|
+
("change", "match"),
|
|
56
|
+
[
|
|
57
|
+
(lambda d: d.pop("plasma_vault"), "missing"),
|
|
58
|
+
(lambda d: d.update(extra=1), "extra"),
|
|
59
|
+
(lambda d: d.update(plasma_vault="0x1234"), "address"),
|
|
60
|
+
(lambda d: d.update(plasma_vault=" " + d["plasma_vault"]), "address"),
|
|
61
|
+
(lambda d: d.update(asset_decimals="8"), "integer"),
|
|
62
|
+
(lambda d: d.update(index=True), "integer"),
|
|
63
|
+
(lambda d: d.update(asset_symbol=None), "string"),
|
|
64
|
+
],
|
|
65
|
+
)
|
|
66
|
+
def test_from_dict_rejects_a_malformed_field(change, match):
|
|
67
|
+
stored = _stored()
|
|
68
|
+
change(stored)
|
|
69
|
+
with pytest.raises(ValueError, match=match):
|
|
70
|
+
FusionInstance.from_dict(stored)
|
{ipor_fusion-4.0.0.dev63 → ipor_fusion-4.0.0.dev68}/tests/test_simulate_crosschain_hyperevm.py
RENAMED
|
@@ -343,7 +343,6 @@ def test_simulate_contract_creation(web3_arb):
|
|
|
343
343
|
init_code = bytes.fromhex("600a600c600039600a6000f3") + runtime
|
|
344
344
|
simulator = VaultSimulator(
|
|
345
345
|
web3_arb,
|
|
346
|
-
vault=ZERO_ADDRESS,
|
|
347
346
|
alpha=SIMULATED_DEPLOYER,
|
|
348
347
|
block=ARBITRUM_BLOCK,
|
|
349
348
|
)
|
|
@@ -374,7 +373,6 @@ def test_compile_and_deploy_crosschain_fuses(web3_arb):
|
|
|
374
373
|
)
|
|
375
374
|
simulator = VaultSimulator(
|
|
376
375
|
web3_arb,
|
|
377
|
-
vault=ZERO_ADDRESS,
|
|
378
376
|
alpha=SIMULATED_DEPLOYER,
|
|
379
377
|
block=ARBITRUM_BLOCK,
|
|
380
378
|
)
|
|
@@ -804,7 +802,7 @@ def test_simulate_clone_htest_vault(
|
|
|
804
802
|
OWNER,
|
|
805
803
|
)
|
|
806
804
|
preview = clone.call()
|
|
807
|
-
simulator = VaultSimulator(web3,
|
|
805
|
+
simulator = VaultSimulator(web3, alpha=OWNER, block=block)
|
|
808
806
|
simulator.with_block_override(gasLimit=30_000_000)
|
|
809
807
|
simulator.add_call(clone, from_=OWNER, label="clone_vault")
|
|
810
808
|
vault = PlasmaVault(ctx, preview.plasma_vault)
|