ipor-fusion 4.0.0.dev88__tar.gz → 4.0.0.dev93__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.dev88 → ipor_fusion-4.0.0.dev93}/AGENTS.md +19 -8
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/MIGRATION_4.md +29 -9
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/PKG-INFO +1 -1
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/__init__.py +0 -2
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_cmd.py +4 -16
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_health.py +1 -15
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_info_view.py +16 -37
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_markets_view.py +44 -96
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/context.py +18 -5
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/contract.py +12 -3
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/mcp/server.py +4 -17
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/lending_health.py +2 -3
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/unpriceable.py +5 -1
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/validation.py +36 -273
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_data.py +204 -75
- ipor_fusion-4.0.0.dev93/src/ipor_fusion/vault_erc20.py +204 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_info.py +59 -98
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_managers.py +27 -4
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_validation.py +6 -7
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/conftest.py +1 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_commands.py +30 -14
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_helpers.py +155 -116
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_hypercore.py +8 -7
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_lending_health.py +9 -7
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_pending_requests_scan.py +11 -4
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_vault_info_golden.py +51 -16
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_vault_predates_getters.py +7 -6
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_vault_validate.py +5 -2
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_mcp_vault_validate.py +3 -8
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_public_api.py +0 -1
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_validation.py +40 -43
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_vault_info_onchain_base.py +3 -4
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +1 -1
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_web3_context.py +24 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_withdraw_manager.py +24 -2
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/typed_client/consumer.py +7 -4
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/README.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/uv.lock +0 -0
|
@@ -188,8 +188,9 @@ the lifecycle matrix follows that.
|
|
|
188
188
|
- `substrates.py` — public per-market bytes32 substrate decoding (`decode_substrate`,
|
|
189
189
|
`SubstrateInfo`, `format_market_label`, `market_name`; re-exported from `ipor_fusion`),
|
|
190
190
|
used by the CLI, MCP and `readers/lending_health`
|
|
191
|
-
- `validation.py` — public vault validation (`validate_vault
|
|
192
|
-
`validate_vault_state`
|
|
191
|
+
- `validation.py` — public vault validation (`validate_vault` fetches and validates;
|
|
192
|
+
`validate_vault_state` is a pure function of a snapshot typed by the `VaultState`
|
|
193
|
+
protocol):
|
|
193
194
|
`VaultValidation` with ordered `Finding`s, an ok/warning/critical verdict, the
|
|
194
195
|
`Reconciliation`, `Erc20Holdings` and lending health; `dependency_graph.py` holds the
|
|
195
196
|
balance-graph checks
|
|
@@ -197,9 +198,11 @@ the lifecycle matrix follows that.
|
|
|
197
198
|
`WithdrawManagerData`), `fetch_deployment_info`, cached `resolve_token_symbol` /
|
|
198
199
|
`resolve_token_decimals`; it composes `vault_reads.py` (the shared Multicall3 batch
|
|
199
200
|
and degrade-to-``None`` helpers), `vault_managers.py` (fee and WithdrawManager
|
|
200
|
-
snapshots)
|
|
201
|
-
prices)
|
|
202
|
-
`
|
|
201
|
+
snapshots), `vault_positions.py` (Morpho/Aave position breakdowns and their token
|
|
202
|
+
prices) and `vault_erc20.py` (the ERC20_VAULT_BALANCE holdings);
|
|
203
|
+
`vault_info.py` — `build_vault_info_json(data)`, the `fusion vault info --json`
|
|
204
|
+
document rendered from the snapshot alone (contract names via an injected
|
|
205
|
+
`resolve_name`),
|
|
203
206
|
and `health_check_lists`; `explorer.py` Etherscan V2 (single endpoint, needs API key);
|
|
204
207
|
`cache.py` the on-disk contract and deployment caches (`~/.cache/ipor-fusion/`).
|
|
205
208
|
`morpho_api.py` the Morpho public GraphQL API client (`fetch_market`, `fetch_vault`,
|
|
@@ -295,6 +298,9 @@ or examples.
|
|
|
295
298
|
via `self._action_raw(solidity_signature, values)`; the shared `_validate_*`
|
|
296
299
|
helpers raise before encoding. `PlasmaVault.execute([actions])` runs the
|
|
297
300
|
batch atomically.
|
|
301
|
+
- Pin reads, never mutate a shared context: `ctx.at_block(n)` is a copy whose
|
|
302
|
+
reads (and `get_logs` upper bound) default to block ``n``; `Call.call(block=n)`
|
|
303
|
+
pins one read.
|
|
298
304
|
- Simulate before sending: `VaultSimulator(web3, vault=, alpha=)` batches
|
|
299
305
|
`execute` and `observe` calls into one `eth_simulateV1` round trip, across
|
|
300
306
|
blocks via `next_block`. Integration tests and examples use it instead of a
|
|
@@ -383,9 +389,14 @@ are not validated.
|
|
|
383
389
|
|
|
384
390
|
## Domain rules (verified against the contracts)
|
|
385
391
|
|
|
386
|
-
- `fetch_vault_data(
|
|
387
|
-
|
|
388
|
-
|
|
392
|
+
- `fetch_vault_data(ctx, vault, *, block=, lending=, metadata=)` is the snapshot:
|
|
393
|
+
it resolves one block (``block``, else a numeric `ctx.default_block`, else latest),
|
|
394
|
+
runs every read on `ctx.at_block(block)` (log scans end there too) and returns
|
|
395
|
+
everything validation and the ``vault info`` document need, so neither reads the
|
|
396
|
+
chain. The chain is `ctx.chain_id`; ``lending=False`` skips lending health, the
|
|
397
|
+
position breakdowns, their prices and the unpriceable check; ``metadata=False``
|
|
398
|
+
skips token symbols/decimals. Add a new chain-state input to the snapshot, never a
|
|
399
|
+
read inside a renderer or validator.
|
|
389
400
|
- PlasmaVault serves governance/view functions via `fallback()` delegatecall to
|
|
390
401
|
PlasmaVaultBase, so Etherscan's ABI for the vault address omits callable
|
|
391
402
|
view functions such as `getDependencyBalanceGraph`. An empty dependency graph
|
|
@@ -57,20 +57,40 @@ names earlier reachable only by module path (`FusionFactory`, `FusionInstance`,
|
|
|
57
57
|
`ipor_fusion`: a module path outside the README's list (*Public API and stability*)
|
|
58
58
|
still works but is internal.
|
|
59
59
|
|
|
60
|
-
**Vault reads and validation.**
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
**Vault reads and validation.** The vault read is one pinned snapshot, and the
|
|
61
|
+
chain is always the context's:
|
|
62
|
+
|
|
63
|
+
- `fetch_vault_data(ctx, vault, *, block=None, lending=True, metadata=True)` takes
|
|
64
|
+
the vault address (not a `PlasmaVault`) and no `chain_id`. It resolves one block
|
|
65
|
+
(``block``, else a numeric `ctx.default_block`, else the latest) and runs every
|
|
66
|
+
read, log scans included, on `ctx.at_block(block)`; `ctx` is not modified. The old
|
|
67
|
+
``chain_id=0`` switch is ``lending=False``; ``metadata=False`` skips token symbols
|
|
68
|
+
and decimals. `VaultData` gains `vault`, `chain_id`, `market_balances`, `erc20`,
|
|
69
|
+
`token_symbols` and `token_decimals`; `WithdrawManagerData` gains
|
|
70
|
+
`shares_to_release_assets` and `pending_request_assets`.
|
|
71
|
+
- `validate_vault(ctx, vault, *, block=None)` fetches with ``metadata=False`` and
|
|
72
|
+
validates; it no longer takes ``resolve_symbol``. `validate_vault_state(state)` is
|
|
73
|
+
pure: a custom `VaultState` also provides `vault`, `chain_id`, `market_balances`,
|
|
74
|
+
`erc20` and the queue's `shares_to_release_assets`. `read_erc20_holdings` is gone:
|
|
75
|
+
the holdings are `VaultData.erc20`.
|
|
76
|
+
- `build_vault_info_json(data, *, resolve_name=None)` renders from the snapshot and
|
|
77
|
+
reads nothing; the chain and its label come from `data.chain_id`.
|
|
78
|
+
- `fetch_deployment_info(ctx, vault, *, api_key=None)` and
|
|
79
|
+
`fetch_vault_lending_health(ctx, vault, *, balance_fuse_market_ids,
|
|
80
|
+
market_substrates, aave_pools=None)` take the chain from `ctx`.
|
|
81
|
+
- `Web3Context(web3, chain_id, *, ...)` and `Web3Context.from_url(url, *, ...)` take
|
|
82
|
+
their options by keyword. New: `Web3Context.at_block(block)` (a pinned copy) and
|
|
83
|
+
`Call.call(ctx=None, *, block=None)`. `Web3Context.get_logs(to_block=None)` ends at
|
|
84
|
+
the context's default block and `Web3Context.get_block(block=None)` returns it,
|
|
85
|
+
both instead of ``"latest"``.
|
|
86
|
+
|
|
87
|
+
`ensure_supported_chain` takes `ChainId`. `fetch_unpriceable_priced_tokens` takes the oracle and
|
|
67
88
|
underlying as `ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
|
|
68
89
|
`get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
|
|
69
90
|
fits), and the `resolve_name` / `resolve_symbol` callbacks receive one (a callback
|
|
70
91
|
typed for `str` still fits). Market-keyed data is keyed by `MarketId`:
|
|
71
92
|
`VaultData.market_substrates`, `dependency_graph`, `morpho_positions`,
|
|
72
|
-
`aave_positions`; the `VaultState` protocol, `
|
|
73
|
-
(`market_balances`), `fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens`
|
|
93
|
+
`aave_positions`, `market_balances`; the `VaultState` protocol, `fetch_vault_lending_health`, `fetch_unpriceable_priced_tokens`
|
|
74
94
|
and the `dependency_graph` helpers (`compute_update_reach`, `compute_update_groups`,
|
|
75
95
|
`find_orphan_fuse_markets`, `find_markets_missing_erc20_dependency`).
|
|
76
96
|
|
|
@@ -445,7 +445,6 @@ from ipor_fusion.validation import (
|
|
|
445
445
|
VaultState,
|
|
446
446
|
VaultValidation,
|
|
447
447
|
WithdrawQueue,
|
|
448
|
-
read_erc20_holdings,
|
|
449
448
|
validate_vault,
|
|
450
449
|
validate_vault_state,
|
|
451
450
|
)
|
|
@@ -861,7 +860,6 @@ __all__ = [
|
|
|
861
860
|
"VaultState",
|
|
862
861
|
"VaultValidation",
|
|
863
862
|
"WithdrawQueue",
|
|
864
|
-
"read_erc20_holdings",
|
|
865
863
|
"validate_vault",
|
|
866
864
|
"validate_vault_state",
|
|
867
865
|
"compute_update_groups",
|
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import sys
|
|
5
|
-
from functools import partial
|
|
6
5
|
from typing import Any
|
|
7
6
|
|
|
8
7
|
import click
|
|
@@ -50,7 +49,6 @@ from ipor_fusion.validation import (
|
|
|
50
49
|
from ipor_fusion.vault_data import (
|
|
51
50
|
_ROLE_SCAN_ERRORS,
|
|
52
51
|
fetch_vault_data,
|
|
53
|
-
resolve_token_symbol,
|
|
54
52
|
)
|
|
55
53
|
from ipor_fusion.vault_info import (
|
|
56
54
|
_erc20_balances_json,
|
|
@@ -207,7 +205,6 @@ def info(
|
|
|
207
205
|
"""
|
|
208
206
|
cfg = load_config()
|
|
209
207
|
chain_id, ctx = _build_ctx(cfg, vault_address, chain_id, block_number)
|
|
210
|
-
checksum_address = vault_address
|
|
211
208
|
|
|
212
209
|
# Cheap single-call probe: friendly errors for "nothing deployed here" and
|
|
213
210
|
# "not a Plasma Vault" (revert and empty-return flavors alike) before the
|
|
@@ -217,17 +214,13 @@ def info(
|
|
|
217
214
|
except (ContractNotFoundError, NotPlasmaVaultError) as exc:
|
|
218
215
|
raise click.UsageError(str(exc)) from exc
|
|
219
216
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
_auto_save_vault(cfg, vault_address, chain_id, plasma_vault)
|
|
217
|
+
_auto_save_vault(cfg, vault_address, chain_id, PlasmaVault(ctx, vault_address))
|
|
223
218
|
|
|
224
219
|
try:
|
|
225
|
-
data = fetch_vault_data(ctx,
|
|
220
|
+
data = fetch_vault_data(ctx, vault_address, block=block_number)
|
|
226
221
|
except UnsupportedVaultVersionError as exc:
|
|
227
222
|
raise click.ClickException(str(exc)) from exc
|
|
228
|
-
_print_vault_info(
|
|
229
|
-
ctx, plasma_vault, cfg, data, vault_address, chain_id, json_output
|
|
230
|
-
)
|
|
223
|
+
_print_vault_info(ctx, cfg, data, json_output)
|
|
231
224
|
|
|
232
225
|
|
|
233
226
|
@vault.command("validate")
|
|
@@ -264,12 +257,7 @@ def validate(
|
|
|
264
257
|
raise click.UsageError(str(exc)) from exc
|
|
265
258
|
|
|
266
259
|
try:
|
|
267
|
-
validation = validate_vault(
|
|
268
|
-
ctx,
|
|
269
|
-
vault_address,
|
|
270
|
-
block_number,
|
|
271
|
-
resolve_symbol=partial(resolve_token_symbol, ctx),
|
|
272
|
-
)
|
|
260
|
+
validation = validate_vault(ctx, vault_address, block=block_number)
|
|
273
261
|
except UnsupportedVaultVersionError as exc:
|
|
274
262
|
raise click.ClickException(str(exc)) from exc
|
|
275
263
|
|
|
@@ -3,32 +3,18 @@
|
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
from functools import partial
|
|
7
|
-
|
|
8
6
|
import click
|
|
9
7
|
|
|
10
8
|
from ipor_fusion.cli.vault_rendering import _format_amount, _format_usd, _print_table
|
|
11
|
-
from ipor_fusion.core.context import Web3Context
|
|
12
|
-
from ipor_fusion.core.plasma_vault import PlasmaVault
|
|
13
9
|
from ipor_fusion.substrates import market_name
|
|
14
10
|
from ipor_fusion.validation import (
|
|
15
11
|
Erc20Holdings,
|
|
16
12
|
VaultValidation,
|
|
17
|
-
read_erc20_holdings,
|
|
18
13
|
)
|
|
19
|
-
from ipor_fusion.vault_data import VaultData
|
|
14
|
+
from ipor_fusion.vault_data import VaultData
|
|
20
15
|
from ipor_fusion.vault_info import health_check_lists
|
|
21
16
|
|
|
22
17
|
|
|
23
|
-
def _read_erc20_holdings(
|
|
24
|
-
ctx: Web3Context, plasma_vault: PlasmaVault, data: VaultData
|
|
25
|
-
) -> Erc20Holdings:
|
|
26
|
-
"""ERC20 holdings with token symbols from the CLI's contract cache."""
|
|
27
|
-
return read_erc20_holdings(
|
|
28
|
-
ctx, plasma_vault, data, partial(resolve_token_symbol, ctx)
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
18
|
def _print_erc20_balances(data: VaultData, erc20: Erc20Holdings) -> None:
|
|
33
19
|
if not erc20.tokens:
|
|
34
20
|
has_erc20_market = any(
|
|
@@ -14,7 +14,6 @@ from ipor_fusion.cli.vault_health import (
|
|
|
14
14
|
_print_erc20_balances,
|
|
15
15
|
_print_health_check,
|
|
16
16
|
_print_reconciliation,
|
|
17
|
-
_read_erc20_holdings,
|
|
18
17
|
)
|
|
19
18
|
from ipor_fusion.cli.vault_markets_view import _print_lending_health, _print_markets
|
|
20
19
|
from ipor_fusion.cli.vault_rendering import (
|
|
@@ -26,7 +25,6 @@ from ipor_fusion.cli.vault_rendering import (
|
|
|
26
25
|
)
|
|
27
26
|
from ipor_fusion.core.context import Web3Context
|
|
28
27
|
from ipor_fusion.core.fee_manager import RecipientFee
|
|
29
|
-
from ipor_fusion.core.plasma_vault import PlasmaVault
|
|
30
28
|
from ipor_fusion.explorer import get_contract_name
|
|
31
29
|
from ipor_fusion.readers.hypercore import HyperCorePendingState
|
|
32
30
|
from ipor_fusion.substrates import (
|
|
@@ -48,7 +46,6 @@ from ipor_fusion.vault_info import (
|
|
|
48
46
|
_wad_to_usd,
|
|
49
47
|
build_vault_info_json,
|
|
50
48
|
)
|
|
51
|
-
from ipor_fusion.vault_reads import _safe_call
|
|
52
49
|
|
|
53
50
|
|
|
54
51
|
def _print_role_accounts_table(role_accounts: list[dict[str, Any]]) -> None:
|
|
@@ -76,38 +73,28 @@ def _print_role_accounts(role_accounts: list[dict[str, Any]] | None) -> None:
|
|
|
76
73
|
|
|
77
74
|
|
|
78
75
|
def _print_vault_info(
|
|
79
|
-
ctx: Web3Context,
|
|
80
|
-
plasma_vault: PlasmaVault,
|
|
81
|
-
cfg: FusionConfig,
|
|
82
|
-
data: VaultData,
|
|
83
|
-
vault_address: PlasmaVaultAddress,
|
|
84
|
-
chain_id: ChainId,
|
|
85
|
-
json_output: bool = False,
|
|
76
|
+
ctx: Web3Context, cfg: FusionConfig, data: VaultData, json_output: bool = False
|
|
86
77
|
) -> None:
|
|
87
78
|
api_key = cfg.etherscan_api_key
|
|
79
|
+
chain_id = data.chain_id
|
|
88
80
|
chain_label = CHAIN_NAMES.get(chain_id, str(chain_id))
|
|
89
81
|
|
|
90
82
|
(
|
|
91
83
|
data.deployment_block,
|
|
92
84
|
data.deployment_timestamp,
|
|
93
85
|
data.deployment_error,
|
|
94
|
-
) = fetch_deployment_info(ctx,
|
|
86
|
+
) = fetch_deployment_info(ctx, data.vault, api_key=api_key)
|
|
95
87
|
|
|
96
88
|
if json_output:
|
|
97
89
|
result = build_vault_info_json(
|
|
98
|
-
|
|
99
|
-
plasma_vault,
|
|
100
|
-
data,
|
|
101
|
-
chain_id=chain_id,
|
|
102
|
-
chain_label=chain_label,
|
|
103
|
-
resolve_name=partial(get_contract_name, chain_id, api_key=api_key),
|
|
90
|
+
data, resolve_name=partial(get_contract_name, chain_id, api_key=api_key)
|
|
104
91
|
)
|
|
105
92
|
click.echo(json.dumps(result, indent=2))
|
|
106
93
|
return
|
|
107
94
|
|
|
108
|
-
_print_identity(data,
|
|
95
|
+
_print_identity(data, data.vault, chain_id, chain_label)
|
|
109
96
|
_print_supply(data)
|
|
110
|
-
_print_managers(data
|
|
97
|
+
_print_managers(data)
|
|
111
98
|
click.echo()
|
|
112
99
|
|
|
113
100
|
_print_fees(data)
|
|
@@ -115,15 +102,9 @@ def _print_vault_info(
|
|
|
115
102
|
|
|
116
103
|
_print_role_accounts(_role_accounts_json(data))
|
|
117
104
|
|
|
118
|
-
|
|
105
|
+
_print_markets(data, api_key)
|
|
119
106
|
|
|
120
|
-
validation = validate_vault_state(
|
|
121
|
-
ctx,
|
|
122
|
-
plasma_vault,
|
|
123
|
-
data,
|
|
124
|
-
market_balances,
|
|
125
|
-
_read_erc20_holdings(ctx, plasma_vault, data),
|
|
126
|
-
)
|
|
107
|
+
validation = validate_vault_state(data)
|
|
127
108
|
click.echo("ERC20 Balances (vault holdings):")
|
|
128
109
|
_print_erc20_balances(data, validation.erc20)
|
|
129
110
|
click.echo()
|
|
@@ -131,7 +112,7 @@ def _print_vault_info(
|
|
|
131
112
|
_print_reconciliation(data, validation)
|
|
132
113
|
click.echo()
|
|
133
114
|
|
|
134
|
-
_print_lending_health(
|
|
115
|
+
_print_lending_health(data)
|
|
135
116
|
click.echo()
|
|
136
117
|
_print_hypercore(data)
|
|
137
118
|
|
|
@@ -207,7 +188,7 @@ def _print_supply(data: VaultData) -> None:
|
|
|
207
188
|
)
|
|
208
189
|
|
|
209
190
|
|
|
210
|
-
def _print_managers(data: VaultData
|
|
191
|
+
def _print_managers(data: VaultData) -> None:
|
|
211
192
|
click.echo(f"Access Manager: {data.access_manager}")
|
|
212
193
|
click.echo(f"Price Oracle: {data.price_oracle_addr}")
|
|
213
194
|
click.echo(f"Rewards Manager: {data.rewards_manager or 'N/A'}")
|
|
@@ -222,7 +203,7 @@ def _print_managers(data: VaultData, plasma_vault: PlasmaVault) -> None:
|
|
|
222
203
|
if wmd.last_release_funds_timestamp > 0:
|
|
223
204
|
last_iso = _unix_to_iso(wmd.last_release_funds_timestamp)
|
|
224
205
|
click.echo(f" Last release: {last_iso}")
|
|
225
|
-
_print_pending_requests(data
|
|
206
|
+
_print_pending_requests(data)
|
|
226
207
|
|
|
227
208
|
|
|
228
209
|
def _format_fee_percent(value: float | None) -> str:
|
|
@@ -375,7 +356,7 @@ def _print_hypercore_pending(pending: HyperCorePendingState | None) -> None:
|
|
|
375
356
|
)
|
|
376
357
|
|
|
377
358
|
|
|
378
|
-
def _print_pending_requests(data: VaultData
|
|
359
|
+
def _print_pending_requests(data: VaultData) -> None:
|
|
379
360
|
if (wmd := data.withdraw_manager_data) is None:
|
|
380
361
|
return
|
|
381
362
|
if wmd.pending_requests is None:
|
|
@@ -389,16 +370,14 @@ def _print_pending_requests(data: VaultData, plasma_vault: PlasmaVault) -> None:
|
|
|
389
370
|
return
|
|
390
371
|
|
|
391
372
|
total_shares = Shares(sum(r.shares for r in requests))
|
|
392
|
-
|
|
393
|
-
|
|
373
|
+
request_assets = [wmd.pending_request_assets.get(r.account) for r in requests]
|
|
374
|
+
total_assets = (
|
|
375
|
+
None if None in request_assets else sum(a for a in request_assets if a)
|
|
394
376
|
)
|
|
395
377
|
|
|
396
378
|
click.echo(f" Pending requests ({len(requests)}):")
|
|
397
379
|
rows: list[tuple[str, ...]] = []
|
|
398
|
-
for req in requests:
|
|
399
|
-
assets: int | None = _safe_call(
|
|
400
|
-
lambda s=req.shares: plasma_vault.convert_to_assets(s).call()
|
|
401
|
-
)
|
|
380
|
+
for req, assets in zip(requests, request_assets, strict=True):
|
|
402
381
|
assets_str = (
|
|
403
382
|
f"{_format_amount(assets, data.asset_decimals)} {data.asset_symbol}"
|
|
404
383
|
if assets is not None
|
{ipor_fusion-4.0.0.dev88 → ipor_fusion-4.0.0.dev93}/src/ipor_fusion/cli/vault_markets_view.py
RENAMED
|
@@ -13,8 +13,6 @@ from ipor_fusion.cli.vault_rendering import (
|
|
|
13
13
|
_print_table,
|
|
14
14
|
_substrate_details,
|
|
15
15
|
)
|
|
16
|
-
from ipor_fusion.core.context import Web3Context
|
|
17
|
-
from ipor_fusion.core.plasma_vault import BalanceFuse, PlasmaVault
|
|
18
16
|
from ipor_fusion.explorer import get_contract_name
|
|
19
17
|
from ipor_fusion.substrates import (
|
|
20
18
|
decode_substrate,
|
|
@@ -24,17 +22,13 @@ from ipor_fusion.types import ChainId, FuseAddress, MarketId, parse_hex_address
|
|
|
24
22
|
from ipor_fusion.vault_data import (
|
|
25
23
|
GET_MARKET_SUBSTRATES,
|
|
26
24
|
VaultData,
|
|
27
|
-
resolve_token_decimals,
|
|
28
|
-
resolve_token_symbol,
|
|
29
25
|
)
|
|
30
26
|
from ipor_fusion.vault_info import (
|
|
31
27
|
_CAPABILITY_BALANCE_FUSES,
|
|
32
28
|
)
|
|
33
29
|
|
|
34
30
|
|
|
35
|
-
def _print_lending_health( # noqa: C901
|
|
36
|
-
ctx: Web3Context, data: VaultData
|
|
37
|
-
) -> None:
|
|
31
|
+
def _print_lending_health(data: VaultData) -> None: # noqa: C901
|
|
38
32
|
lh = data.lending_health
|
|
39
33
|
has_health = lh is not None and lh.has_lending_positions
|
|
40
34
|
has_breakdown = bool(data.morpho_positions) or bool(data.aave_positions)
|
|
@@ -46,22 +40,21 @@ def _print_lending_health( # noqa: C901
|
|
|
46
40
|
morpho_health, aave_health = _index_lending_health(lh)
|
|
47
41
|
consumed_morpho_subs: set[str] = set()
|
|
48
42
|
consumed_aave_mids: set[int | str] = set()
|
|
49
|
-
prices = data.token_prices_usd or {}
|
|
50
43
|
|
|
51
44
|
for ipor_mid, positions in (data.morpho_positions or {}).items():
|
|
52
45
|
click.echo(f" {format_market_label(ipor_mid)}:")
|
|
53
46
|
for pb in positions:
|
|
54
|
-
coll_sym =
|
|
55
|
-
loan_sym =
|
|
47
|
+
coll_sym = _token_symbol(data, pb.collateral_token)
|
|
48
|
+
loan_sym = _token_symbol(data, pb.loan_token)
|
|
56
49
|
click.echo(f" morpho market {pb.market_id} ({coll_sym}/{loan_sym}):")
|
|
57
50
|
click.echo(
|
|
58
|
-
f" Collateral: {_format_token_amount(
|
|
51
|
+
f" Collateral: {_format_token_amount(data, int(pb.collateral), pb.collateral_token)}"
|
|
59
52
|
)
|
|
60
53
|
click.echo(
|
|
61
|
-
f" Borrow: {_format_token_amount(
|
|
54
|
+
f" Borrow: {_format_token_amount(data, int(pb.borrow_assets), pb.loan_token)}"
|
|
62
55
|
)
|
|
63
56
|
click.echo(
|
|
64
|
-
f" Supply: {_format_token_amount(
|
|
57
|
+
f" Supply: {_format_token_amount(data, int(pb.supply_assets), pb.loan_token)}"
|
|
65
58
|
)
|
|
66
59
|
sid = str(pb.market_id).lower().removeprefix("0x")
|
|
67
60
|
consumed_morpho_subs.add(sid)
|
|
@@ -71,17 +64,17 @@ def _print_lending_health( # noqa: C901
|
|
|
71
64
|
for ipor_mid, aave_positions in (data.aave_positions or {}).items():
|
|
72
65
|
click.echo(f" {format_market_label(ipor_mid)}:")
|
|
73
66
|
for ab in aave_positions:
|
|
74
|
-
asset_symbol =
|
|
67
|
+
asset_symbol = _token_symbol(data, ab.asset)
|
|
75
68
|
click.echo(f" asset {ab.asset} ({asset_symbol}):")
|
|
76
69
|
click.echo(
|
|
77
|
-
f" Supply: {_format_token_amount(
|
|
70
|
+
f" Supply: {_format_token_amount(data, int(ab.supply), ab.asset)}"
|
|
78
71
|
)
|
|
79
72
|
click.echo(
|
|
80
|
-
f" Variable Debt: {_format_token_amount(
|
|
73
|
+
f" Variable Debt: {_format_token_amount(data, int(ab.variable_debt), ab.asset)}"
|
|
81
74
|
)
|
|
82
75
|
if ab.stable_debt > 0:
|
|
83
76
|
click.echo(
|
|
84
|
-
f" Stable Debt: {_format_token_amount(
|
|
77
|
+
f" Stable Debt: {_format_token_amount(data, int(ab.stable_debt), ab.asset)}"
|
|
85
78
|
)
|
|
86
79
|
consumed_aave_mids.add(ipor_mid)
|
|
87
80
|
if m := aave_health.get(ipor_mid):
|
|
@@ -161,15 +154,9 @@ def _print_health_lines(market: Any, indent: str) -> None:
|
|
|
161
154
|
click.secho(f"{indent}Status: {status}", fg=color, bold=bold)
|
|
162
155
|
|
|
163
156
|
|
|
164
|
-
def _print_markets(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
data: VaultData,
|
|
168
|
-
chain_id: ChainId,
|
|
169
|
-
api_key: str | None,
|
|
170
|
-
) -> dict[MarketId, int]:
|
|
171
|
-
"""Fuses, balance fuses, dependency graph and substrates; returns the
|
|
172
|
-
per-market balances read for the balance-fuse table."""
|
|
157
|
+
def _print_markets(data: VaultData, api_key: str | None) -> None:
|
|
158
|
+
"""Fuses, balance fuses, dependency graph and substrates."""
|
|
159
|
+
chain_id = data.chain_id
|
|
173
160
|
_print_fuse_section(
|
|
174
161
|
"Fuses",
|
|
175
162
|
data.fuses,
|
|
@@ -190,46 +177,31 @@ def _print_markets(
|
|
|
190
177
|
)
|
|
191
178
|
click.echo()
|
|
192
179
|
|
|
193
|
-
|
|
194
|
-
plasma_vault,
|
|
195
|
-
data.balance_fuses,
|
|
196
|
-
data.asset_decimals,
|
|
197
|
-
data.asset_symbol,
|
|
198
|
-
data.asset_price_usd,
|
|
199
|
-
chain_id,
|
|
200
|
-
api_key,
|
|
201
|
-
)
|
|
180
|
+
_print_balance_fuses_table(data, api_key)
|
|
202
181
|
click.echo()
|
|
203
182
|
|
|
204
183
|
_print_dependency_graph(data)
|
|
205
184
|
|
|
206
185
|
click.echo("Substrates per Market:")
|
|
207
|
-
_print_market_substrates(
|
|
186
|
+
_print_market_substrates(data, api_key)
|
|
208
187
|
click.echo()
|
|
209
|
-
return market_balances
|
|
210
188
|
|
|
211
189
|
|
|
212
|
-
def
|
|
213
|
-
|
|
214
|
-
raw: int,
|
|
215
|
-
token_address: HexAddress,
|
|
216
|
-
prices_usd: dict[HexAddress, float] | None = None,
|
|
217
|
-
) -> str:
|
|
218
|
-
"""Format a raw on-chain amount using cached ERC-20 symbol + decimals.
|
|
190
|
+
def _token_symbol(data: VaultData, token_address: HexAddress) -> str:
|
|
191
|
+
return data.token_symbols.get(parse_hex_address(token_address)) or "?"
|
|
219
192
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
"""
|
|
223
|
-
|
|
224
|
-
|
|
193
|
+
|
|
194
|
+
def _format_token_amount(data: VaultData, raw: int, token_address: HexAddress) -> str:
|
|
195
|
+
"""Format a raw amount with the snapshot's symbol and decimals, suffixed
|
|
196
|
+
with ` ($X.XX)` when the snapshot has the token's USD price."""
|
|
197
|
+
key = parse_hex_address(token_address)
|
|
198
|
+
symbol = data.token_symbols.get(key) or "?"
|
|
199
|
+
decimals = data.token_decimals.get(key)
|
|
225
200
|
if decimals is None:
|
|
226
201
|
return f"{raw} raw ({symbol})"
|
|
227
202
|
base = f"{_format_amount(raw, decimals)} {symbol}"
|
|
228
|
-
if prices_usd:
|
|
229
|
-
|
|
230
|
-
raw, decimals, prices_usd.get(parse_hex_address(token_address))
|
|
231
|
-
)
|
|
232
|
-
return f"{base}{usd_suffix}"
|
|
203
|
+
if prices_usd := data.token_prices_usd:
|
|
204
|
+
return f"{base}{_format_usd(raw, decimals, prices_usd.get(key))}"
|
|
233
205
|
return base
|
|
234
206
|
|
|
235
207
|
|
|
@@ -313,37 +285,24 @@ def _print_fuse_section(
|
|
|
313
285
|
_print_table(("#", "Address", "Contract", "Market", "Substrates"), rows)
|
|
314
286
|
|
|
315
287
|
|
|
316
|
-
def _print_balance_fuses_table(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
asset_price_usd: float | None,
|
|
322
|
-
chain_id: ChainId,
|
|
323
|
-
api_key: str | None,
|
|
324
|
-
) -> dict[MarketId, int]:
|
|
325
|
-
"""Print the table; return each market's cached balance, once per market
|
|
326
|
-
(several balance fuses can share a market's single storage slot)."""
|
|
327
|
-
market_balances: dict[MarketId, int] = {}
|
|
288
|
+
def _print_balance_fuses_table(data: VaultData, api_key: str | None) -> None:
|
|
289
|
+
balance_fuses = data.balance_fuses
|
|
290
|
+
decimals = data.asset_decimals
|
|
291
|
+
asset_symbol = data.asset_symbol
|
|
292
|
+
asset_price_usd = data.asset_price_usd
|
|
328
293
|
with ThreadPoolExecutor() as pool:
|
|
329
|
-
futures: list[tuple[int, MarketId, str, Future
|
|
294
|
+
futures: list[tuple[int, MarketId, str, Future]] = []
|
|
330
295
|
for idx, balance_fuse in enumerate(balance_fuses, 1):
|
|
331
296
|
market_id_str = format_market_label(balance_fuse.market_id)
|
|
332
|
-
f_balance = pool.submit(
|
|
333
|
-
plasma_vault.total_assets_in_market(balance_fuse.market_id).call
|
|
334
|
-
)
|
|
335
297
|
f_contract = pool.submit(
|
|
336
|
-
get_contract_name, chain_id, balance_fuse.fuse, api_key
|
|
337
|
-
)
|
|
338
|
-
futures.append(
|
|
339
|
-
(idx, balance_fuse.market_id, market_id_str, f_balance, f_contract)
|
|
298
|
+
get_contract_name, data.chain_id, balance_fuse.fuse, api_key
|
|
340
299
|
)
|
|
300
|
+
futures.append((idx, balance_fuse.market_id, market_id_str, f_contract))
|
|
341
301
|
|
|
342
302
|
venue_rows: list[tuple[str, ...]] = []
|
|
343
303
|
zero_rows: list[tuple[str, ...]] = []
|
|
344
|
-
for idx, market_id, market_id_str,
|
|
345
|
-
assets_in_market =
|
|
346
|
-
market_balances.setdefault(market_id, assets_in_market)
|
|
304
|
+
for idx, market_id, market_id_str, f_contract in futures:
|
|
305
|
+
assets_in_market = data.market_balances[market_id]
|
|
347
306
|
contract_name = f_contract.result() or "?"
|
|
348
307
|
balance_str = (
|
|
349
308
|
f"{_format_amount(assets_in_market, decimals)} {asset_symbol}"
|
|
@@ -367,30 +326,23 @@ def _print_balance_fuses_table(
|
|
|
367
326
|
click.echo()
|
|
368
327
|
click.echo(f"Zero-Balance Fuses ({len(zero_rows)}):")
|
|
369
328
|
_print_table(header, [(str(i), *row) for i, row in enumerate(zero_rows, 1)])
|
|
370
|
-
return market_balances
|
|
371
329
|
|
|
372
330
|
|
|
373
|
-
def _print_market_substrates(
|
|
374
|
-
ctx: Web3Context, data: VaultData, chain_id: ChainId, api_key: str | None
|
|
375
|
-
) -> set[str]:
|
|
331
|
+
def _print_market_substrates(data: VaultData, api_key: str | None) -> set[str]:
|
|
376
332
|
if GET_MARKET_SUBSTRATES in data.unimplemented_getters:
|
|
377
333
|
click.secho(
|
|
378
334
|
f" (unavailable: the vault predates {GET_MARKET_SUBSTRATES})",
|
|
379
335
|
fg="yellow",
|
|
380
336
|
)
|
|
381
337
|
return set()
|
|
382
|
-
return _print_substrates(
|
|
383
|
-
ctx, data.market_substrates or {}, data.balance_fuses, chain_id, api_key
|
|
384
|
-
)
|
|
338
|
+
return _print_substrates(data, api_key)
|
|
385
339
|
|
|
386
340
|
|
|
387
341
|
def _print_substrates( # noqa: C901
|
|
388
|
-
|
|
389
|
-
substrates_by_market: dict[MarketId, list[bytes]],
|
|
390
|
-
balance_fuses: Sequence[BalanceFuse],
|
|
391
|
-
chain_id: ChainId,
|
|
392
|
-
api_key: str | None,
|
|
342
|
+
data: VaultData, api_key: str | None
|
|
393
343
|
) -> set[str]:
|
|
344
|
+
substrates_by_market = data.market_substrates or {}
|
|
345
|
+
balance_fuses = data.balance_fuses
|
|
394
346
|
# Phase 1: collect each balance fuse's substrates and the addresses to resolve
|
|
395
347
|
market_substrates: list[tuple[str, MarketId, list[bytes]]] = []
|
|
396
348
|
all_addresses: set[HexAddress] = set()
|
|
@@ -408,17 +360,13 @@ def _print_substrates( # noqa: C901
|
|
|
408
360
|
click.echo(" (none)")
|
|
409
361
|
return {a.lower() for a in all_addresses}
|
|
410
362
|
|
|
411
|
-
# Phase 2: resolve
|
|
363
|
+
# Phase 2: resolve contract names in parallel; symbols are in the snapshot
|
|
364
|
+
resolved_symbols = data.token_symbols
|
|
412
365
|
with ThreadPoolExecutor() as pool:
|
|
413
|
-
symbol_futs = {
|
|
414
|
-
addr: pool.submit(resolve_token_symbol, ctx, addr) for addr in all_addresses
|
|
415
|
-
}
|
|
416
366
|
contract_futs = {
|
|
417
|
-
addr: pool.submit(get_contract_name, chain_id, addr, api_key)
|
|
367
|
+
addr: pool.submit(get_contract_name, data.chain_id, addr, api_key)
|
|
418
368
|
for addr in all_addresses
|
|
419
369
|
}
|
|
420
|
-
|
|
421
|
-
resolved_symbols = {addr: fut.result() for addr, fut in symbol_futs.items()}
|
|
422
370
|
resolved_contracts = {addr: fut.result() for addr, fut in contract_futs.items()}
|
|
423
371
|
|
|
424
372
|
# Phase 3: print
|