ipor-fusion 4.0.0.dev108__tar.gz → 4.0.0.dev110__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.dev108 → ipor_fusion-4.0.0.dev110}/AGENTS.md +1 -1
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/MIGRATION_4.md +8 -3
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/PKG-INFO +1 -1
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/vault_info_view.py +5 -7
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/vault_markets_view.py +19 -26
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/vault_data.py +16 -9
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/vault_info.py +8 -6
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cache.py +46 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_commands.py +6 -18
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_helpers.py +28 -52
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_vault_info_golden.py +58 -17
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_vault_predates_getters.py +3 -1
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/README.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_public_api.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/uv.lock +0 -0
|
@@ -158,7 +158,7 @@ the lifecycle matrix follows that.
|
|
|
158
158
|
subpackage (`core`, `config`, `fuses`, `readers`, `crosschain`,
|
|
159
159
|
`crosschain.fuses`, `crosschain.ccip`, `crosschain.stargate`) also goes in that
|
|
160
160
|
subpackage's `__all__`. `ipor_fusion.guide` keeps its own namespace. The
|
|
161
|
-
public modules (`about`, `chains`, `dependency_graph`, `errors`, `explorer`,
|
|
161
|
+
public modules (`about`, `cache`, `chains`, `dependency_graph`, `errors`, `explorer`,
|
|
162
162
|
`field_docs`, `market_ids`, `market_info`, `morpho_api`, `substrates`, `types`,
|
|
163
163
|
`validation`, `vault_data`, `vault_info`) stay importable for the names they
|
|
164
164
|
define; `cli`, `mcp` and every other module are internal. Examples, the guide
|
|
@@ -351,8 +351,12 @@ Contract names, token symbols and decimals and deployment blocks are cached in a
|
|
|
351
351
|
(`fetch_vault_data(metadata=True)`) and `fetch_deployment_info` use `ctx.cache`.
|
|
352
352
|
`get_contract_name(chain_id, address, api_key=None, *, cache=None)` takes the cache
|
|
353
353
|
explicitly (default: the file cache); pass `cache=ctx.cache` to share the
|
|
354
|
-
context's policy.
|
|
355
|
-
|
|
354
|
+
context's policy. The CLI's text and JSON output name contracts through the
|
|
355
|
+
context's cache.
|
|
356
|
+
- `build_vault_info_json(data)` without `resolve_name` names no contract (every
|
|
357
|
+
`contract` is `"?"`) instead of reading names from the on-disk cache; pass
|
|
358
|
+
`resolve_name=partial(get_contract_name, chain_id, api_key=..., cache=ctx.cache)`
|
|
359
|
+
for the CLI's document.
|
|
356
360
|
- `ipor_fusion.cache` is a public module now and exports `ContractCache`,
|
|
357
361
|
`FileContractCache`, `NullCache`, `CacheKey`, `CacheField`, `cache_key` and
|
|
358
362
|
`default_contract_cache` (also from `ipor_fusion`). Its 3.x functions and constants
|
|
@@ -384,7 +388,8 @@ Etherscan key) and the old file can be deleted.
|
|
|
384
388
|
- A cache never fails a read: a corrupt or unreadable file reads as empty (logged
|
|
385
389
|
warning from `ipor_fusion.cache`) and is rewritten by the next write; an unwritable
|
|
386
390
|
directory (read-only file system) keeps the entries in memory for the process; a
|
|
387
|
-
custom cache that raises is treated as a miss
|
|
391
|
+
custom cache that raises is treated as a miss, and so is a stored number that does
|
|
392
|
+
not parse.
|
|
388
393
|
- `fetch_deployment_info` returns `rpc-fetch-failed` only when the creation
|
|
389
394
|
transaction or its block cannot be read; a cache failure is no longer reported as
|
|
390
395
|
one (the deployment block is returned).
|
{ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/vault_info_view.py
RENAMED
|
@@ -85,13 +85,11 @@ def _print_vault_info(
|
|
|
85
85
|
data.deployment_error,
|
|
86
86
|
) = fetch_deployment_info(ctx, data.vault, api_key=api_key)
|
|
87
87
|
|
|
88
|
+
resolve_name = partial(
|
|
89
|
+
get_contract_name, chain_id, api_key=api_key, cache=ctx.cache
|
|
90
|
+
)
|
|
88
91
|
if json_output:
|
|
89
|
-
result = build_vault_info_json(
|
|
90
|
-
data,
|
|
91
|
-
resolve_name=partial(
|
|
92
|
-
get_contract_name, chain_id, api_key=api_key, cache=ctx.cache
|
|
93
|
-
),
|
|
94
|
-
)
|
|
92
|
+
result = build_vault_info_json(data, resolve_name=resolve_name)
|
|
95
93
|
click.echo(json.dumps(result, indent=2))
|
|
96
94
|
return
|
|
97
95
|
|
|
@@ -105,7 +103,7 @@ def _print_vault_info(
|
|
|
105
103
|
|
|
106
104
|
_print_role_accounts(_role_accounts_json(data))
|
|
107
105
|
|
|
108
|
-
_print_markets(data,
|
|
106
|
+
_print_markets(data, resolve_name)
|
|
109
107
|
|
|
110
108
|
validation = validate_vault_state(data)
|
|
111
109
|
click.echo("ERC20 Balances (vault holdings):")
|
{ipor_fusion-4.0.0.dev108 → ipor_fusion-4.0.0.dev110}/src/ipor_fusion/cli/vault_markets_view.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from collections.abc import Sequence
|
|
3
|
+
from collections.abc import Callable, Sequence
|
|
4
4
|
from concurrent.futures import Future, ThreadPoolExecutor
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
@@ -13,12 +13,11 @@ from ipor_fusion.cli.vault_rendering import (
|
|
|
13
13
|
_print_table,
|
|
14
14
|
_substrate_details,
|
|
15
15
|
)
|
|
16
|
-
from ipor_fusion.explorer import get_contract_name
|
|
17
16
|
from ipor_fusion.substrates import (
|
|
18
17
|
decode_substrate,
|
|
19
18
|
format_market_label,
|
|
20
19
|
)
|
|
21
|
-
from ipor_fusion.types import
|
|
20
|
+
from ipor_fusion.types import FuseAddress, MarketId, parse_hex_address
|
|
22
21
|
from ipor_fusion.vault_data import (
|
|
23
22
|
GET_MARKET_SUBSTRATES,
|
|
24
23
|
VaultData,
|
|
@@ -27,6 +26,10 @@ from ipor_fusion.vault_info import (
|
|
|
27
26
|
_CAPABILITY_BALANCE_FUSES,
|
|
28
27
|
)
|
|
29
28
|
|
|
29
|
+
# Address to contract name, "" when unknown (`get_contract_name` bound to the
|
|
30
|
+
# chain, API key and the context's cache).
|
|
31
|
+
NameResolver = Callable[[HexAddress], str]
|
|
32
|
+
|
|
30
33
|
|
|
31
34
|
def _print_lending_health(data: VaultData) -> None: # noqa: C901
|
|
32
35
|
lh = data.lending_health
|
|
@@ -154,16 +157,14 @@ def _print_health_lines(market: Any, indent: str) -> None:
|
|
|
154
157
|
click.secho(f"{indent}Status: {status}", fg=color, bold=bold)
|
|
155
158
|
|
|
156
159
|
|
|
157
|
-
def _print_markets(data: VaultData,
|
|
160
|
+
def _print_markets(data: VaultData, resolve_name: NameResolver) -> None:
|
|
158
161
|
"""Fuses, balance fuses, dependency graph and substrates."""
|
|
159
|
-
chain_id = data.chain_id
|
|
160
162
|
_print_fuse_section(
|
|
161
163
|
"Fuses",
|
|
162
164
|
data.fuses,
|
|
163
165
|
data.fuse_markets,
|
|
164
166
|
data.market_substrates,
|
|
165
|
-
|
|
166
|
-
api_key,
|
|
167
|
+
resolve_name,
|
|
167
168
|
)
|
|
168
169
|
click.echo()
|
|
169
170
|
|
|
@@ -172,18 +173,17 @@ def _print_markets(data: VaultData, api_key: str | None) -> None:
|
|
|
172
173
|
data.instant_fuses,
|
|
173
174
|
data.fuse_markets,
|
|
174
175
|
data.market_substrates,
|
|
175
|
-
|
|
176
|
-
api_key,
|
|
176
|
+
resolve_name,
|
|
177
177
|
)
|
|
178
178
|
click.echo()
|
|
179
179
|
|
|
180
|
-
_print_balance_fuses_table(data,
|
|
180
|
+
_print_balance_fuses_table(data, resolve_name)
|
|
181
181
|
click.echo()
|
|
182
182
|
|
|
183
183
|
_print_dependency_graph(data)
|
|
184
184
|
|
|
185
185
|
click.echo("Substrates per Market:")
|
|
186
|
-
_print_market_substrates(data,
|
|
186
|
+
_print_market_substrates(data, resolve_name)
|
|
187
187
|
click.echo()
|
|
188
188
|
|
|
189
189
|
|
|
@@ -235,8 +235,7 @@ def _print_fuse_section(
|
|
|
235
235
|
fuses: Sequence[FuseAddress],
|
|
236
236
|
fuse_markets: dict[FuseAddress, MarketId] | None,
|
|
237
237
|
market_substrates: dict[MarketId, list[bytes]] | None,
|
|
238
|
-
|
|
239
|
-
api_key: str | None,
|
|
238
|
+
resolve_name: NameResolver,
|
|
240
239
|
) -> None:
|
|
241
240
|
"""Render a fuse section (regular or instant-withdrawal) with a unified
|
|
242
241
|
layout.
|
|
@@ -262,10 +261,7 @@ def _print_fuse_section(
|
|
|
262
261
|
return
|
|
263
262
|
|
|
264
263
|
with ThreadPoolExecutor() as pool:
|
|
265
|
-
name_futs = {
|
|
266
|
-
addr: pool.submit(get_contract_name, chain_id, addr, api_key)
|
|
267
|
-
for addr in counts
|
|
268
|
-
}
|
|
264
|
+
name_futs = {addr: pool.submit(resolve_name, addr) for addr in counts}
|
|
269
265
|
rows: list[tuple[str, ...]] = []
|
|
270
266
|
for idx, addr in enumerate(counts, 1):
|
|
271
267
|
market_label = "?"
|
|
@@ -285,7 +281,7 @@ def _print_fuse_section(
|
|
|
285
281
|
_print_table(("#", "Address", "Contract", "Market", "Substrates"), rows)
|
|
286
282
|
|
|
287
283
|
|
|
288
|
-
def _print_balance_fuses_table(data: VaultData,
|
|
284
|
+
def _print_balance_fuses_table(data: VaultData, resolve_name: NameResolver) -> None:
|
|
289
285
|
balance_fuses = data.balance_fuses
|
|
290
286
|
decimals = data.asset_decimals
|
|
291
287
|
asset_symbol = data.asset_symbol
|
|
@@ -294,9 +290,7 @@ def _print_balance_fuses_table(data: VaultData, api_key: str | None) -> None:
|
|
|
294
290
|
futures: list[tuple[int, MarketId, str, Future]] = []
|
|
295
291
|
for idx, balance_fuse in enumerate(balance_fuses, 1):
|
|
296
292
|
market_id_str = format_market_label(balance_fuse.market_id)
|
|
297
|
-
f_contract = pool.submit(
|
|
298
|
-
get_contract_name, data.chain_id, balance_fuse.fuse, api_key
|
|
299
|
-
)
|
|
293
|
+
f_contract = pool.submit(resolve_name, balance_fuse.fuse)
|
|
300
294
|
futures.append((idx, balance_fuse.market_id, market_id_str, f_contract))
|
|
301
295
|
|
|
302
296
|
venue_rows: list[tuple[str, ...]] = []
|
|
@@ -328,18 +322,18 @@ def _print_balance_fuses_table(data: VaultData, api_key: str | None) -> None:
|
|
|
328
322
|
_print_table(header, [(str(i), *row) for i, row in enumerate(zero_rows, 1)])
|
|
329
323
|
|
|
330
324
|
|
|
331
|
-
def _print_market_substrates(data: VaultData,
|
|
325
|
+
def _print_market_substrates(data: VaultData, resolve_name: NameResolver) -> set[str]:
|
|
332
326
|
if GET_MARKET_SUBSTRATES in data.unimplemented_getters:
|
|
333
327
|
click.secho(
|
|
334
328
|
f" (unavailable: the vault predates {GET_MARKET_SUBSTRATES})",
|
|
335
329
|
fg="yellow",
|
|
336
330
|
)
|
|
337
331
|
return set()
|
|
338
|
-
return _print_substrates(data,
|
|
332
|
+
return _print_substrates(data, resolve_name)
|
|
339
333
|
|
|
340
334
|
|
|
341
335
|
def _print_substrates( # noqa: C901
|
|
342
|
-
data: VaultData,
|
|
336
|
+
data: VaultData, resolve_name: NameResolver
|
|
343
337
|
) -> set[str]:
|
|
344
338
|
substrates_by_market = data.market_substrates or {}
|
|
345
339
|
balance_fuses = data.balance_fuses
|
|
@@ -364,8 +358,7 @@ def _print_substrates( # noqa: C901
|
|
|
364
358
|
resolved_symbols = data.token_symbols
|
|
365
359
|
with ThreadPoolExecutor() as pool:
|
|
366
360
|
contract_futs = {
|
|
367
|
-
addr: pool.submit(
|
|
368
|
-
for addr in all_addresses
|
|
361
|
+
addr: pool.submit(resolve_name, addr) for addr in all_addresses
|
|
369
362
|
}
|
|
370
363
|
resolved_contracts = {addr: fut.result() for addr, fut in contract_futs.items()}
|
|
371
364
|
|
|
@@ -252,9 +252,16 @@ def resolve_token_symbol(ctx: Web3Context, address: HexAddress) -> str:
|
|
|
252
252
|
return symbol
|
|
253
253
|
|
|
254
254
|
|
|
255
|
-
def
|
|
256
|
-
|
|
257
|
-
|
|
255
|
+
def _cached_int(cached: dict[CacheKey, str], key: CacheKey) -> int | None:
|
|
256
|
+
"""A cached non-negative integer; anything else (a corrupt or foreign
|
|
257
|
+
entry) is a miss."""
|
|
258
|
+
value = cached.get(key, "")
|
|
259
|
+
if not (value.isascii() and value.isdigit()):
|
|
260
|
+
return None
|
|
261
|
+
try:
|
|
262
|
+
return int(value)
|
|
263
|
+
except ValueError: # beyond the int-string conversion limit
|
|
264
|
+
return None
|
|
258
265
|
|
|
259
266
|
|
|
260
267
|
def resolve_token_decimals(ctx: Web3Context, address: HexAddress) -> int | None:
|
|
@@ -262,7 +269,7 @@ def resolve_token_decimals(ctx: Web3Context, address: HexAddress) -> int | None:
|
|
|
262
269
|
(not cached) when ``address`` has no code at the context's block or does
|
|
263
270
|
not answer."""
|
|
264
271
|
key = cache_key(ctx.chain_id, address, "decimals")
|
|
265
|
-
if (cached :=
|
|
272
|
+
if (cached := _cached_int(cache_get(ctx.cache, [key]), key)) is not None:
|
|
266
273
|
return cached
|
|
267
274
|
|
|
268
275
|
checksum = Web3.to_checksum_address(address)
|
|
@@ -696,7 +703,7 @@ def _token_metadata(
|
|
|
696
703
|
decimals: dict[HexAddress, int | None] = {
|
|
697
704
|
a: value
|
|
698
705
|
for a, k in decimals_keys.items()
|
|
699
|
-
if (value :=
|
|
706
|
+
if (value := _cached_int(cached, k)) is not None
|
|
700
707
|
}
|
|
701
708
|
missing_symbols = sorted((breakdown | substrate_addresses) - symbols.keys())
|
|
702
709
|
missing_decimals = sorted(breakdown - decimals.keys())
|
|
@@ -771,10 +778,10 @@ def fetch_deployment_info(
|
|
|
771
778
|
block_key = cache_key(chain_id, vault_address, "deployment_block")
|
|
772
779
|
time_key = cache_key(chain_id, vault_address, "deployment_timestamp")
|
|
773
780
|
cached = cache_get(ctx.cache, [block_key, time_key])
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
return
|
|
781
|
+
block = _cached_int(cached, block_key)
|
|
782
|
+
stamp = _cached_int(cached, time_key)
|
|
783
|
+
if block is not None and stamp is not None:
|
|
784
|
+
return block, stamp, None
|
|
778
785
|
|
|
779
786
|
tx_hash, error = get_deployment_tx(chain_id, vault_address, api_key)
|
|
780
787
|
if not tx_hash:
|
|
@@ -6,7 +6,6 @@ from __future__ import annotations
|
|
|
6
6
|
from collections.abc import Callable
|
|
7
7
|
from concurrent.futures import Future, ThreadPoolExecutor
|
|
8
8
|
from datetime import UTC, datetime
|
|
9
|
-
from functools import partial
|
|
10
9
|
from typing import Any
|
|
11
10
|
|
|
12
11
|
from eth_typing import HexAddress
|
|
@@ -14,7 +13,6 @@ from eth_typing import HexAddress
|
|
|
14
13
|
from ipor_fusion.chains import CHAIN_NAMES
|
|
15
14
|
from ipor_fusion.core.fee_manager import HighWaterMarkPerformanceFee, RecipientFee
|
|
16
15
|
from ipor_fusion.core.plasma_vault import BalanceFuse
|
|
17
|
-
from ipor_fusion.explorer import get_contract_name
|
|
18
16
|
from ipor_fusion.field_docs import DOCS
|
|
19
17
|
from ipor_fusion.readers.aave_v3 import AaveV3PositionBreakdown
|
|
20
18
|
from ipor_fusion.readers.hypercore import HyperCorePendingState, HyperCorePerpLeg
|
|
@@ -705,6 +703,10 @@ def _markets_json(
|
|
|
705
703
|
return markets
|
|
706
704
|
|
|
707
705
|
|
|
706
|
+
def _no_name(_address: HexAddress) -> str:
|
|
707
|
+
return ""
|
|
708
|
+
|
|
709
|
+
|
|
708
710
|
def build_vault_info_json(
|
|
709
711
|
data: VaultData,
|
|
710
712
|
*,
|
|
@@ -716,14 +718,14 @@ def build_vault_info_json(
|
|
|
716
718
|
``data`` comes from `fetch_vault_data` (with ``metadata=True`` for token
|
|
717
719
|
symbols); set its ``deployment_*`` fields first (`fetch_deployment_info`)
|
|
718
720
|
for the ``deployment`` block. ``resolve_name`` names the fuses and
|
|
719
|
-
substrate contracts (address to contract name, ``""`` when unknown)
|
|
720
|
-
|
|
721
|
-
|
|
721
|
+
substrate contracts (address to contract name, ``""`` when unknown), for
|
|
722
|
+
example `ipor_fusion.explorer.get_contract_name` bound to the chain, an
|
|
723
|
+
API key and ``ctx.cache``; without it no contract is named.
|
|
722
724
|
"""
|
|
723
725
|
chain_id = data.chain_id
|
|
724
726
|
chain_label = CHAIN_NAMES.get(chain_id, str(chain_id))
|
|
725
727
|
if resolve_name is None:
|
|
726
|
-
resolve_name =
|
|
728
|
+
resolve_name = _no_name
|
|
727
729
|
|
|
728
730
|
markets = _markets_json(data, resolve_name)
|
|
729
731
|
validation = validate_vault_state(data)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
import threading
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from types import SimpleNamespace
|
|
6
7
|
from unittest.mock import MagicMock, patch
|
|
@@ -128,6 +129,30 @@ class TestFileContractCache:
|
|
|
128
129
|
key = cache_key(BASE, LOWER, "name")
|
|
129
130
|
assert cache.get([key]) == {key: "Other"}
|
|
130
131
|
|
|
132
|
+
def test_concurrent_writers_never_tear_the_file(self, path):
|
|
133
|
+
writers = [FileContractCache(path) for _ in range(4)]
|
|
134
|
+
barrier = threading.Barrier(len(writers))
|
|
135
|
+
|
|
136
|
+
def write(index: int) -> None:
|
|
137
|
+
barrier.wait()
|
|
138
|
+
for n in range(25):
|
|
139
|
+
address = f"0x{index:02x}{n:038x}"
|
|
140
|
+
writers[index].put({cache_key(ETHEREUM, address, "name"): str(n)})
|
|
141
|
+
|
|
142
|
+
threads = [threading.Thread(target=write, args=(i,)) for i in range(4)]
|
|
143
|
+
for thread in threads:
|
|
144
|
+
thread.start()
|
|
145
|
+
for thread in threads:
|
|
146
|
+
thread.join()
|
|
147
|
+
on_disk = _on_disk(path)
|
|
148
|
+
assert on_disk
|
|
149
|
+
assert all(key.endswith(":name") for key in on_disk)
|
|
150
|
+
assert sorted(p.name for p in path.parent.iterdir()) == [path.name]
|
|
151
|
+
# Each writer still serves every entry it wrote, persisted or not.
|
|
152
|
+
for index, writer in enumerate(writers):
|
|
153
|
+
key = cache_key(ETHEREUM, f"0x{index:02x}{24:038x}", "name")
|
|
154
|
+
assert writer.get([key]) == {key: "24"}
|
|
155
|
+
|
|
131
156
|
@pytest.mark.skipif(os.geteuid() == 0, reason="root ignores permissions")
|
|
132
157
|
def test_a_read_only_directory_degrades_to_memory(self, path, caplog):
|
|
133
158
|
path.parent.mkdir(parents=True)
|
|
@@ -229,6 +254,13 @@ class TestTokenResolvers:
|
|
|
229
254
|
ctx.web3.eth.get_code.assert_not_called()
|
|
230
255
|
assert _on_disk(path) == {f"1:{LOWER}:decimals": "6"}
|
|
231
256
|
|
|
257
|
+
@pytest.mark.parametrize("value", ["\u00b2", "9" * 5000, "-1", " 6"])
|
|
258
|
+
@patch("ipor_fusion.vault_data.ERC20")
|
|
259
|
+
def test_a_corrupt_decimals_entry_is_a_miss(self, erc20, path, value):
|
|
260
|
+
_write(path, {f"1:{LOWER}:decimals": value})
|
|
261
|
+
erc20.return_value.decimals.return_value.call.return_value = 6
|
|
262
|
+
assert resolve_token_decimals(_ctx(FileContractCache(path)), CHECKSUM) == 6
|
|
263
|
+
|
|
232
264
|
def test_token_metadata_reads_and_fills_the_contexts_cache(self, path):
|
|
233
265
|
cache = FileContractCache(path)
|
|
234
266
|
cache.put({cache_key(ETHEREUM, LOWER, "symbol"): "MAINNET"})
|
|
@@ -307,6 +339,20 @@ class TestFetchDeploymentInfo:
|
|
|
307
339
|
ctx = _deployment_ctx(FileContractCache(path))
|
|
308
340
|
assert fetch_deployment_info(ctx, VAULT) == (100, 1_700_000_000, None)
|
|
309
341
|
|
|
342
|
+
@pytest.mark.parametrize("value", ["\u00b2", "9" * 5000])
|
|
343
|
+
@patch("ipor_fusion.vault_data.get_deployment_tx", return_value=(TX, None))
|
|
344
|
+
def test_a_corrupt_deployment_entry_is_a_miss(self, get_tx, path, value):
|
|
345
|
+
_write(
|
|
346
|
+
path,
|
|
347
|
+
{
|
|
348
|
+
f"1:{LOWER}:deployment_block": value,
|
|
349
|
+
f"1:{LOWER}:deployment_timestamp": "5",
|
|
350
|
+
},
|
|
351
|
+
)
|
|
352
|
+
ctx = _deployment_ctx(FileContractCache(path))
|
|
353
|
+
assert fetch_deployment_info(ctx, VAULT) == (100, 1_700_000_000, None)
|
|
354
|
+
get_tx.assert_called_once()
|
|
355
|
+
|
|
310
356
|
@patch(
|
|
311
357
|
"ipor_fusion.vault_data.get_deployment_tx",
|
|
312
358
|
return_value=(None, "no-api-key"),
|
|
@@ -418,9 +418,7 @@ class TestVaultInfo:
|
|
|
418
418
|
yield
|
|
419
419
|
|
|
420
420
|
@patch("ipor_fusion.vault_managers.WithdrawManager")
|
|
421
|
-
@patch(
|
|
422
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name", return_value="SomeFuse"
|
|
423
|
-
)
|
|
421
|
+
@patch("ipor_fusion.cli.vault_info_view.get_contract_name", return_value="SomeFuse")
|
|
424
422
|
@patch("ipor_fusion.vault_data.price_oracle_middleware")
|
|
425
423
|
@patch("ipor_fusion.vault_data.ERC20")
|
|
426
424
|
@patch("ipor_fusion.vault_data.PlasmaVault")
|
|
@@ -546,9 +544,7 @@ class TestVaultInfo:
|
|
|
546
544
|
assert f" {ADDR_FEE_RECIPIENT} 8.0000%" in result.output
|
|
547
545
|
assert "accrued, uncollected: 5" in result.output
|
|
548
546
|
|
|
549
|
-
@patch(
|
|
550
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name", return_value="SomeFuse"
|
|
551
|
-
)
|
|
547
|
+
@patch("ipor_fusion.cli.vault_info_view.get_contract_name", return_value="SomeFuse")
|
|
552
548
|
@patch("ipor_fusion.vault_data.price_oracle_middleware")
|
|
553
549
|
@patch("ipor_fusion.vault_data.ERC20")
|
|
554
550
|
@patch("ipor_fusion.vault_data.PlasmaVault")
|
|
@@ -624,9 +620,7 @@ class TestVaultInfo:
|
|
|
624
620
|
assert "99999" in result.output
|
|
625
621
|
assert mock_ctx.default_block == 99999
|
|
626
622
|
|
|
627
|
-
@patch(
|
|
628
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name", return_value="SomeFuse"
|
|
629
|
-
)
|
|
623
|
+
@patch("ipor_fusion.cli.vault_info_view.get_contract_name", return_value="SomeFuse")
|
|
630
624
|
@patch("ipor_fusion.vault_data.price_oracle_middleware")
|
|
631
625
|
@patch("ipor_fusion.vault_data.ERC20")
|
|
632
626
|
@patch("ipor_fusion.vault_data.PlasmaVault")
|
|
@@ -690,9 +684,7 @@ class TestVaultInfo:
|
|
|
690
684
|
assert result.exit_code == 0, result.output
|
|
691
685
|
assert "Supply Cap: unlimited" in result.output
|
|
692
686
|
|
|
693
|
-
@patch(
|
|
694
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name", return_value="SomeFuse"
|
|
695
|
-
)
|
|
687
|
+
@patch("ipor_fusion.cli.vault_info_view.get_contract_name", return_value="SomeFuse")
|
|
696
688
|
@patch("ipor_fusion.vault_data.price_oracle_middleware")
|
|
697
689
|
@patch("ipor_fusion.vault_data.ERC20")
|
|
698
690
|
@patch("ipor_fusion.vault_data.PlasmaVault")
|
|
@@ -766,9 +758,7 @@ class TestVaultInfo:
|
|
|
766
758
|
"ipor_fusion.vault_data.get_deployment_tx",
|
|
767
759
|
return_value=("0xdeadbeef", None),
|
|
768
760
|
)
|
|
769
|
-
@patch(
|
|
770
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name", return_value="SomeFuse"
|
|
771
|
-
)
|
|
761
|
+
@patch("ipor_fusion.cli.vault_info_view.get_contract_name", return_value="SomeFuse")
|
|
772
762
|
@patch("ipor_fusion.vault_data.price_oracle_middleware")
|
|
773
763
|
@patch("ipor_fusion.vault_data.ERC20")
|
|
774
764
|
@patch("ipor_fusion.vault_data.PlasmaVault")
|
|
@@ -836,9 +826,7 @@ class TestVaultInfo:
|
|
|
836
826
|
assert "Deployed at: block 18500000" in result.output
|
|
837
827
|
assert "2023-11-14" in result.output
|
|
838
828
|
|
|
839
|
-
@patch(
|
|
840
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name", return_value="SomeFuse"
|
|
841
|
-
)
|
|
829
|
+
@patch("ipor_fusion.cli.vault_info_view.get_contract_name", return_value="SomeFuse")
|
|
842
830
|
@patch("ipor_fusion.vault_data.price_oracle_middleware")
|
|
843
831
|
@patch("ipor_fusion.vault_data.ERC20")
|
|
844
832
|
@patch("ipor_fusion.vault_data.PlasmaVault")
|
|
@@ -703,6 +703,10 @@ class TestResolveTokenSymbol:
|
|
|
703
703
|
ADDR_1 = "0x1111111111111111111111111111111111111111"
|
|
704
704
|
|
|
705
705
|
|
|
706
|
+
def _no_name(_address: str) -> str:
|
|
707
|
+
return ""
|
|
708
|
+
|
|
709
|
+
|
|
706
710
|
def _snapshot(**fields: Any) -> Any:
|
|
707
711
|
"""The `VaultData` fields the market printers read, overridable."""
|
|
708
712
|
defaults: dict[str, Any] = {
|
|
@@ -1049,16 +1053,13 @@ class TestPrintSubstrates:
|
|
|
1049
1053
|
|
|
1050
1054
|
def test_no_substrates(self, capsys):
|
|
1051
1055
|
_print_substrates(
|
|
1052
|
-
_snapshot(balance_fuses=[FakeBalanceFuse(market_id=1, fuse=ADDR_1)]),
|
|
1056
|
+
_snapshot(balance_fuses=[FakeBalanceFuse(market_id=1, fuse=ADDR_1)]),
|
|
1057
|
+
_no_name,
|
|
1053
1058
|
)
|
|
1054
1059
|
captured = capsys.readouterr()
|
|
1055
1060
|
assert "(none)" in captured.out
|
|
1056
1061
|
|
|
1057
|
-
|
|
1058
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name",
|
|
1059
|
-
return_value="TestContract",
|
|
1060
|
-
)
|
|
1061
|
-
def test_with_substrates(self, mock_get_name, capsys):
|
|
1062
|
+
def test_with_substrates(self, capsys):
|
|
1062
1063
|
addr_bytes = bytes.fromhex("00" * 12 + "ab" * 20)
|
|
1063
1064
|
_print_substrates(
|
|
1064
1065
|
_snapshot(
|
|
@@ -1066,7 +1067,7 @@ class TestPrintSubstrates:
|
|
|
1066
1067
|
balance_fuses=[FakeBalanceFuse(market_id=7, fuse=ADDR_1)],
|
|
1067
1068
|
token_symbols={"0x" + "ab" * 20: "WETH"},
|
|
1068
1069
|
),
|
|
1069
|
-
"
|
|
1070
|
+
lambda _address: "TestContract",
|
|
1070
1071
|
)
|
|
1071
1072
|
captured = capsys.readouterr()
|
|
1072
1073
|
assert "WETH" in captured.out
|
|
@@ -1079,7 +1080,7 @@ class TestPrintSubstrates:
|
|
|
1079
1080
|
market_substrates={14: [raw_bytes32]},
|
|
1080
1081
|
balance_fuses=[FakeBalanceFuse(market_id=14, fuse=ADDR_1)],
|
|
1081
1082
|
),
|
|
1082
|
-
|
|
1083
|
+
_no_name,
|
|
1083
1084
|
)
|
|
1084
1085
|
captured = capsys.readouterr()
|
|
1085
1086
|
assert "morpho_market_id" in captured.out
|
|
@@ -1092,7 +1093,7 @@ class TestPrintSubstrates:
|
|
|
1092
1093
|
market_substrates={7: [bad_bytes]},
|
|
1093
1094
|
balance_fuses=[FakeBalanceFuse(market_id=7, fuse=ADDR_1)],
|
|
1094
1095
|
),
|
|
1095
|
-
|
|
1096
|
+
_no_name,
|
|
1096
1097
|
)
|
|
1097
1098
|
captured = capsys.readouterr()
|
|
1098
1099
|
assert "[encoding error]" in captured.out
|
|
@@ -1106,41 +1107,37 @@ class TestPrintSubstrates:
|
|
|
1106
1107
|
market_substrates={54: [malformed_executor]},
|
|
1107
1108
|
balance_fuses=[FakeBalanceFuse(market_id=54, fuse=ADDR_1)],
|
|
1108
1109
|
),
|
|
1109
|
-
|
|
1110
|
+
_no_name,
|
|
1110
1111
|
)
|
|
1111
1112
|
captured = capsys.readouterr()
|
|
1112
1113
|
assert "[encoding error: EXECUTOR, chain_id=8453]" in captured.out
|
|
1113
1114
|
|
|
1114
1115
|
|
|
1115
1116
|
class TestPrintBalanceFusesTable:
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
)
|
|
1117
|
+
def test_splits_venues_and_zero_balance(self, capsys):
|
|
1118
|
+
def resolve_name(addr):
|
|
1119
|
+
return "ZeroBalanceFuse" if addr == ADDR_2 else "MorphoBalanceFuse"
|
|
1120
|
+
|
|
1121
1121
|
fuses = [
|
|
1122
1122
|
FakeBalanceFuse(market_id=14, fuse=ADDR_1),
|
|
1123
1123
|
FakeBalanceFuse(market_id=12, fuse=ADDR_2),
|
|
1124
1124
|
]
|
|
1125
1125
|
|
|
1126
1126
|
_print_balance_fuses_table(
|
|
1127
|
-
_snapshot(balance_fuses=fuses, market_balances={14: 0, 12: 0}),
|
|
1127
|
+
_snapshot(balance_fuses=fuses, market_balances={14: 0, 12: 0}),
|
|
1128
|
+
resolve_name,
|
|
1128
1129
|
)
|
|
1129
1130
|
out = capsys.readouterr().out
|
|
1130
1131
|
assert "Balance Fuses (1):" in out
|
|
1131
1132
|
assert "Zero-Balance Fuses (1):" in out
|
|
1132
1133
|
|
|
1133
|
-
|
|
1134
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name",
|
|
1135
|
-
return_value="MorphoBalanceFuse",
|
|
1136
|
-
)
|
|
1137
|
-
def test_no_zero_balance_section_when_all_venues(self, mock_get_name, capsys):
|
|
1134
|
+
def test_no_zero_balance_section_when_all_venues(self, capsys):
|
|
1138
1135
|
_print_balance_fuses_table(
|
|
1139
1136
|
_snapshot(
|
|
1140
1137
|
balance_fuses=[FakeBalanceFuse(market_id=14, fuse=ADDR_1)],
|
|
1141
1138
|
market_balances={14: 0},
|
|
1142
1139
|
),
|
|
1143
|
-
|
|
1140
|
+
lambda _address: "MorphoBalanceFuse",
|
|
1144
1141
|
)
|
|
1145
1142
|
out = capsys.readouterr().out
|
|
1146
1143
|
assert "Balance Fuses (1):" in out
|
|
@@ -2081,19 +2078,14 @@ class TestPrintFuseSection:
|
|
|
2081
2078
|
Fuses. Dedupes by address, reports a Registrations column, and surfaces
|
|
2082
2079
|
duplicate registrations in the section header."""
|
|
2083
2080
|
|
|
2084
|
-
|
|
2085
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name",
|
|
2086
|
-
return_value="MorphoSupplyFuse",
|
|
2087
|
-
)
|
|
2088
|
-
def test_renders_market_label_and_substrate_count(self, _mock_name, capsys):
|
|
2081
|
+
def test_renders_market_label_and_substrate_count(self, capsys):
|
|
2089
2082
|
addr = ADDR_1
|
|
2090
2083
|
_print_fuse_section(
|
|
2091
2084
|
"Fuses",
|
|
2092
2085
|
[addr],
|
|
2093
2086
|
fuse_markets={addr: IporFusionMarkets.MORPHO},
|
|
2094
2087
|
market_substrates={IporFusionMarkets.MORPHO: [b"\x11" * 32, b"\x22" * 32]},
|
|
2095
|
-
|
|
2096
|
-
api_key=None,
|
|
2088
|
+
resolve_name=lambda _address: "MorphoSupplyFuse",
|
|
2097
2089
|
)
|
|
2098
2090
|
captured = capsys.readouterr()
|
|
2099
2091
|
assert "Fuses (1 unique)" in captured.out
|
|
@@ -2103,52 +2095,37 @@ class TestPrintFuseSection:
|
|
|
2103
2095
|
# Substrate count comes from market_substrates, not from registrations
|
|
2104
2096
|
assert " 2 " in captured.out or "2\n" in captured.out
|
|
2105
2097
|
|
|
2106
|
-
|
|
2107
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name",
|
|
2108
|
-
return_value="UnknownFuse",
|
|
2109
|
-
)
|
|
2110
|
-
def test_renders_question_mark_without_fuse_markets(self, _mock_name, capsys):
|
|
2098
|
+
def test_renders_question_mark_without_fuse_markets(self, capsys):
|
|
2111
2099
|
_print_fuse_section(
|
|
2112
2100
|
"Fuses",
|
|
2113
2101
|
[ADDR_1],
|
|
2114
2102
|
fuse_markets=None,
|
|
2115
2103
|
market_substrates=None,
|
|
2116
|
-
|
|
2117
|
-
api_key=None,
|
|
2104
|
+
resolve_name=lambda _address: "UnknownFuse",
|
|
2118
2105
|
)
|
|
2119
2106
|
captured = capsys.readouterr()
|
|
2120
2107
|
assert "?" in captured.out
|
|
2121
2108
|
|
|
2122
|
-
|
|
2123
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name",
|
|
2124
|
-
return_value="MorphoSupplyFuse",
|
|
2125
|
-
)
|
|
2126
|
-
def test_dedupes_repeated_fuse_address(self, _mock_name, capsys):
|
|
2109
|
+
def test_dedupes_repeated_fuse_address(self, capsys):
|
|
2127
2110
|
addr = ADDR_1
|
|
2128
2111
|
_print_fuse_section(
|
|
2129
2112
|
"Instant Withdrawal Fuses",
|
|
2130
2113
|
[addr, addr, addr],
|
|
2131
2114
|
fuse_markets={addr: IporFusionMarkets.MORPHO},
|
|
2132
2115
|
market_substrates={IporFusionMarkets.MORPHO: [b"\x11" * 32] * 3},
|
|
2133
|
-
|
|
2134
|
-
api_key=None,
|
|
2116
|
+
resolve_name=lambda _address: "MorphoSupplyFuse",
|
|
2135
2117
|
)
|
|
2136
2118
|
captured = capsys.readouterr()
|
|
2137
2119
|
assert "1 unique / 3 registrations" in captured.out
|
|
2138
2120
|
assert captured.out.count(addr) == 1
|
|
2139
2121
|
|
|
2140
|
-
|
|
2141
|
-
"ipor_fusion.cli.vault_markets_view.get_contract_name",
|
|
2142
|
-
return_value="MorphoSupplyFuse",
|
|
2143
|
-
)
|
|
2144
|
-
def test_no_registrations_suffix_when_unique(self, _mock_name, capsys):
|
|
2122
|
+
def test_no_registrations_suffix_when_unique(self, capsys):
|
|
2145
2123
|
_print_fuse_section(
|
|
2146
2124
|
"Fuses",
|
|
2147
2125
|
[ADDR_1, ADDR_2],
|
|
2148
2126
|
fuse_markets=None,
|
|
2149
2127
|
market_substrates=None,
|
|
2150
|
-
|
|
2151
|
-
api_key=None,
|
|
2128
|
+
resolve_name=lambda _address: "MorphoSupplyFuse",
|
|
2152
2129
|
)
|
|
2153
2130
|
captured = capsys.readouterr()
|
|
2154
2131
|
assert "registrations" not in captured.out
|
|
@@ -2160,8 +2137,7 @@ class TestPrintFuseSection:
|
|
|
2160
2137
|
[],
|
|
2161
2138
|
fuse_markets=None,
|
|
2162
2139
|
market_substrates=None,
|
|
2163
|
-
|
|
2164
|
-
api_key=None,
|
|
2140
|
+
resolve_name=_no_name,
|
|
2165
2141
|
)
|
|
2166
2142
|
captured = capsys.readouterr()
|
|
2167
2143
|
assert "(none)" in captured.out
|