ipor-fusion 4.0.0.dev110__tar.gz → 4.0.0.dev116__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.dev110 → ipor_fusion-4.0.0.dev116}/AGENTS.md +3 -1
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/MIGRATION_4.md +20 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/PKG-INFO +1 -1
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_info_view.py +1 -1
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/context.py +43 -3
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/plasma_vault.py +11 -10
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/validation.py +17 -8
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_data.py +19 -16
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_erc20.py +28 -32
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_info.py +1 -1
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_managers.py +18 -18
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/conftest.py +1 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/fixtures/vault_info_golden/clean.txt +1 -1
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_helpers.py +2 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_web3_context.py +43 -1
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/README.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_public_api.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/uv.lock +0 -0
|
@@ -394,7 +394,9 @@ byte, e.g. Ebisu ZAPPER/REGISTRY).
|
|
|
394
394
|
role-accounts, oracle-mapping). `vault info` runs independent fetch pipelines
|
|
395
395
|
(vault/asset, fees, WithdrawManager, markets, roles) concurrently on a
|
|
396
396
|
`ThreadPoolExecutor`, each a short chain of `Multicall3` batches; keep new
|
|
397
|
-
reads inside a batch rather than adding per-call round trips.
|
|
397
|
+
reads inside a batch rather than adding per-call round trips. A `from_url` context
|
|
398
|
+
bounds requests in flight (`max_concurrent_requests`, default 8) whatever the pool
|
|
399
|
+
nesting, so a new fan-out does not need its own limit. Contract names,
|
|
398
400
|
token symbols and decimals and deployment blocks are cached per chain in
|
|
399
401
|
`$XDG_CACHE_HOME/ipor-fusion/contract_cache.json` (`<chain>:<address>:<field>`
|
|
400
402
|
keys); tests redirect it with `monkeypatch.setenv("XDG_CACHE_HOME", ...)` or
|
|
@@ -84,6 +84,26 @@ chain is always the context's:
|
|
|
84
84
|
the context's default block and `Web3Context.get_block(block=None)` returns it,
|
|
85
85
|
both instead of ``"latest"``.
|
|
86
86
|
|
|
87
|
+
Snapshot fields carry their units: `VaultData.total_assets`,
|
|
88
|
+
`underlying_balance_on_vault` and `market_balances` values are `Amount`;
|
|
89
|
+
`total_supply` and `supply_cap` are `Shares`; `share_decimals` / `asset_decimals` are `Decimals`;
|
|
90
|
+
`block_timestamp` and `deployment_timestamp` are `Timestamp`. `FeeData` fee fields
|
|
91
|
+
are `Fee`, `management_fee_last_update` a `Timestamp`, `unrealized_management_fee` an
|
|
92
|
+
`Amount`; `WithdrawManagerData.withdraw_window` is a `Period`, its fees `Fee`,
|
|
93
|
+
`shares_to_release` `Shares`, `last_release_funds_timestamp` a `Timestamp` and the
|
|
94
|
+
converted amounts `Amount`. A custom `VaultState` annotates the same.
|
|
95
|
+
`PlasmaVault.total_supply()`, `balance_of()` and `get_total_supply_cap()` return
|
|
96
|
+
`Shares` (vault shares), and `transfer` / `approve` / `transfer_from` /
|
|
97
|
+
`set_total_supply_cap` take `Shares`; `ERC20` keeps `Amount`. The supply cap is
|
|
98
|
+
now shown in share decimals labelled `shares` (``fusion vault info``) and its JSON
|
|
99
|
+
``formatted`` uses `share_decimals`; it was formatted with the asset's decimals.
|
|
100
|
+
|
|
101
|
+
`Web3Context.from_url(..., max_concurrent_requests=8)` bounds the RPC requests in
|
|
102
|
+
flight across every thread using the context (``None`` lifts the bound); the vault
|
|
103
|
+
reads fan out on nested thread pools, and the bound is what a rate-limited provider
|
|
104
|
+
sees. A context built from your own `Web3` object is not bounded unless you add the
|
|
105
|
+
same middleware.
|
|
106
|
+
|
|
87
107
|
`ensure_supported_chain` takes `ChainId`. `fetch_unpriceable_priced_tokens` takes the oracle and
|
|
88
108
|
underlying as `ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
|
|
89
109
|
`get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
|
{ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_info_view.py
RENAMED
|
@@ -185,7 +185,7 @@ def _print_supply(data: VaultData) -> None:
|
|
|
185
185
|
else:
|
|
186
186
|
click.echo(
|
|
187
187
|
f"Supply Cap: "
|
|
188
|
-
f"{_format_amount(data.supply_cap, data.
|
|
188
|
+
f"{_format_amount(data.supply_cap, data.share_decimals)} shares"
|
|
189
189
|
)
|
|
190
190
|
|
|
191
191
|
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import copy
|
|
4
4
|
import logging
|
|
5
|
+
import threading
|
|
6
|
+
from typing import Any
|
|
5
7
|
|
|
6
8
|
from eth_account import Account
|
|
7
9
|
from eth_typing import ChecksumAddress, HexStr
|
|
@@ -11,12 +13,21 @@ from requests import ConnectionError as RequestsConnectionError
|
|
|
11
13
|
from requests import HTTPError, Timeout
|
|
12
14
|
from web3 import Web3
|
|
13
15
|
from web3.exceptions import ContractLogicError, Web3RPCError
|
|
14
|
-
from web3.middleware import ExtraDataToPOAMiddleware
|
|
16
|
+
from web3.middleware import ExtraDataToPOAMiddleware, Web3Middleware
|
|
15
17
|
from web3.providers.rpc.utils import (
|
|
16
18
|
REQUEST_RETRY_ALLOWLIST,
|
|
17
19
|
ExceptionRetryConfiguration,
|
|
18
20
|
)
|
|
19
|
-
from web3.types import
|
|
21
|
+
from web3.types import (
|
|
22
|
+
BlockIdentifier,
|
|
23
|
+
LogReceipt,
|
|
24
|
+
MakeRequestFn,
|
|
25
|
+
RPCEndpoint,
|
|
26
|
+
RPCResponse,
|
|
27
|
+
TxParams,
|
|
28
|
+
TxReceipt,
|
|
29
|
+
Wei,
|
|
30
|
+
)
|
|
20
31
|
|
|
21
32
|
from ipor_fusion.cache import ContractCache, default_contract_cache
|
|
22
33
|
from ipor_fusion.chains import GET_LOGS_RANGE_HINTS
|
|
@@ -70,6 +81,11 @@ class Web3Context:
|
|
|
70
81
|
# multi-call read (vault fetch, health check) fans that out into minutes of
|
|
71
82
|
# wall clock. Long-running services should pass something tighter.
|
|
72
83
|
DEFAULT_RPC_TIMEOUT_S = 30.0
|
|
84
|
+
# RPC requests in flight at once through a `from_url` context. The vault
|
|
85
|
+
# reads fan out on nested thread pools; this bounds what reaches the
|
|
86
|
+
# provider, whatever the nesting, so a rate-limited endpoint sees a steady
|
|
87
|
+
# stream instead of bursts.
|
|
88
|
+
DEFAULT_MAX_CONCURRENT_REQUESTS = 8
|
|
73
89
|
|
|
74
90
|
def __init__(
|
|
75
91
|
self,
|
|
@@ -142,7 +158,7 @@ class Web3Context:
|
|
|
142
158
|
return self._signer
|
|
143
159
|
|
|
144
160
|
@classmethod
|
|
145
|
-
def from_url(
|
|
161
|
+
def from_url( # proctor: ignore[primitives-too-many-params]
|
|
146
162
|
cls,
|
|
147
163
|
url: str,
|
|
148
164
|
*,
|
|
@@ -153,7 +169,11 @@ class Web3Context:
|
|
|
153
169
|
log_scan_max_requests: int | None = DEFAULT_LOG_SCAN_MAX_REQUESTS,
|
|
154
170
|
log_source: LogSource | None = None,
|
|
155
171
|
cache: ContractCache | None = None,
|
|
172
|
+
max_concurrent_requests: int | None = DEFAULT_MAX_CONCURRENT_REQUESTS,
|
|
156
173
|
) -> Web3Context:
|
|
174
|
+
"""A context on an HTTP provider at ``url``. ``max_concurrent_requests``
|
|
175
|
+
bounds the RPC requests in flight across every thread using it
|
|
176
|
+
(``None``: unbounded)."""
|
|
157
177
|
web3 = Web3(
|
|
158
178
|
Web3.HTTPProvider(
|
|
159
179
|
url,
|
|
@@ -171,6 +191,8 @@ class Web3Context:
|
|
|
171
191
|
# with a ValueError. The middleware moves it to proofOfAuthorityData
|
|
172
192
|
# and leaves every other block untouched.
|
|
173
193
|
web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
|
|
194
|
+
if max_concurrent_requests is not None:
|
|
195
|
+
web3.middleware_onion.add(_in_flight_limit(max_concurrent_requests))
|
|
174
196
|
chain_id = ChainId(web3.eth.chain_id)
|
|
175
197
|
|
|
176
198
|
return cls(
|
|
@@ -385,3 +407,21 @@ class Web3Context:
|
|
|
385
407
|
@staticmethod
|
|
386
408
|
def _percent_of(value: int, percentage: int) -> int:
|
|
387
409
|
return value * percentage // 100
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _in_flight_limit(limit: int) -> type[Web3Middleware]:
|
|
413
|
+
"""A middleware class (web3 instantiates it per Web3) holding a semaphore
|
|
414
|
+
slot for the duration of each provider request."""
|
|
415
|
+
if limit < 1:
|
|
416
|
+
raise ValueError(f"max_concurrent_requests must be at least 1, got {limit}")
|
|
417
|
+
slots = threading.BoundedSemaphore(limit)
|
|
418
|
+
|
|
419
|
+
class InFlightLimit(Web3Middleware):
|
|
420
|
+
def wrap_make_request(self, make_request: MakeRequestFn) -> MakeRequestFn:
|
|
421
|
+
def limited(method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
422
|
+
with slots:
|
|
423
|
+
return make_request(method, params)
|
|
424
|
+
|
|
425
|
+
return limited
|
|
426
|
+
|
|
427
|
+
return InFlightLimit
|
|
@@ -191,7 +191,7 @@ class PlasmaVault(ContractWrapper):
|
|
|
191
191
|
`addFuses` steps must be reverted before the vault is usable."""
|
|
192
192
|
return self._write("removeFuses(address[])", fuses)
|
|
193
193
|
|
|
194
|
-
def set_total_supply_cap(self, cap:
|
|
194
|
+
def set_total_supply_cap(self, cap: Shares) -> Call[None]:
|
|
195
195
|
return self._write("setTotalSupplyCap(uint256)", cap)
|
|
196
196
|
|
|
197
197
|
def convert_to_public_vault(self) -> Call[None]:
|
|
@@ -362,25 +362,26 @@ class PlasmaVault(ContractWrapper):
|
|
|
362
362
|
"configureInstantWithdrawalFuses((address,bytes32[])[])", normalized
|
|
363
363
|
)
|
|
364
364
|
|
|
365
|
-
def transfer(self, to: ChecksumAddress, value:
|
|
365
|
+
def transfer(self, to: ChecksumAddress, value: Shares) -> Call[None]:
|
|
366
366
|
return self._write("transfer(address,uint256)", to, value)
|
|
367
367
|
|
|
368
|
-
def approve(self, account: ChecksumAddress, amount:
|
|
368
|
+
def approve(self, account: ChecksumAddress, amount: Shares) -> Call[None]:
|
|
369
369
|
return self._write("approve(address,uint256)", account, amount)
|
|
370
370
|
|
|
371
371
|
def transfer_from(
|
|
372
|
-
self, _from: ChecksumAddress, to: ChecksumAddress, amount:
|
|
372
|
+
self, _from: ChecksumAddress, to: ChecksumAddress, amount: Shares
|
|
373
373
|
) -> Call[None]:
|
|
374
374
|
return self._write("transferFrom(address,address,uint256)", _from, to, amount)
|
|
375
375
|
|
|
376
|
-
def balance_of(self, account: ChecksumAddress) -> Call[
|
|
376
|
+
def balance_of(self, account: ChecksumAddress) -> Call[Shares]:
|
|
377
377
|
return self._view(
|
|
378
|
-
"balanceOf(address)", account, output_types=["uint256"], decoder=
|
|
378
|
+
"balanceOf(address)", account, output_types=["uint256"], decoder=Shares
|
|
379
379
|
)
|
|
380
380
|
|
|
381
|
-
def get_total_supply_cap(self) -> Call[
|
|
381
|
+
def get_total_supply_cap(self) -> Call[Shares]:
|
|
382
|
+
"""The maximum total supply, in shares (``2**256 - 1``: uncapped)."""
|
|
382
383
|
return self._view(
|
|
383
|
-
"getTotalSupplyCap()", output_types=["uint256"], decoder=
|
|
384
|
+
"getTotalSupplyCap()", output_types=["uint256"], decoder=Shares
|
|
384
385
|
)
|
|
385
386
|
|
|
386
387
|
def max_withdraw(self, account: ChecksumAddress) -> Call[Amount]:
|
|
@@ -418,8 +419,8 @@ class PlasmaVault(ContractWrapper):
|
|
|
418
419
|
def total_assets(self) -> Call[Amount]:
|
|
419
420
|
return self._view("totalAssets()", output_types=["uint256"], decoder=Amount)
|
|
420
421
|
|
|
421
|
-
def total_supply(self) -> Call[
|
|
422
|
-
return self._view("totalSupply()", output_types=["uint256"], decoder=
|
|
422
|
+
def total_supply(self) -> Call[Shares]:
|
|
423
|
+
return self._view("totalSupply()", output_types=["uint256"], decoder=Shares)
|
|
423
424
|
|
|
424
425
|
def name(self) -> Call[str]:
|
|
425
426
|
return self._view("name()", output_types=["string"])
|
|
@@ -13,6 +13,7 @@ from dataclasses import dataclass, field
|
|
|
13
13
|
from typing import Protocol, TypeVar
|
|
14
14
|
|
|
15
15
|
from eth_typing import ChecksumAddress
|
|
16
|
+
from web3.types import Timestamp
|
|
16
17
|
|
|
17
18
|
from ipor_fusion.core.context import Web3Context
|
|
18
19
|
from ipor_fusion.core.plasma_vault import BalanceFuse
|
|
@@ -24,7 +25,15 @@ from ipor_fusion.dependency_graph import (
|
|
|
24
25
|
from ipor_fusion.market_ids import IporFusionMarkets
|
|
25
26
|
from ipor_fusion.readers.lending_health import VaultLendingHealth
|
|
26
27
|
from ipor_fusion.substrates import decode_substrate, format_market_label
|
|
27
|
-
from ipor_fusion.types import
|
|
28
|
+
from ipor_fusion.types import (
|
|
29
|
+
Amount,
|
|
30
|
+
ChainId,
|
|
31
|
+
Decimals,
|
|
32
|
+
FuseAddress,
|
|
33
|
+
MarketId,
|
|
34
|
+
PlasmaVaultAddress,
|
|
35
|
+
Shares,
|
|
36
|
+
)
|
|
28
37
|
from ipor_fusion.types import Check as Check
|
|
29
38
|
from ipor_fusion.types import Severity as Severity
|
|
30
39
|
from ipor_fusion.types import Verdict as Verdict
|
|
@@ -57,9 +66,9 @@ class WithdrawQueue(Protocol):
|
|
|
57
66
|
"""The WithdrawManager state the reconciliation needs."""
|
|
58
67
|
|
|
59
68
|
@property
|
|
60
|
-
def shares_to_release(self) ->
|
|
69
|
+
def shares_to_release(self) -> Shares: ...
|
|
61
70
|
@property
|
|
62
|
-
def shares_to_release_assets(self) ->
|
|
71
|
+
def shares_to_release_assets(self) -> Amount | None: ...
|
|
63
72
|
|
|
64
73
|
|
|
65
74
|
class UnpriceableToken(Protocol):
|
|
@@ -97,19 +106,19 @@ class VaultState(Protocol):
|
|
|
97
106
|
@property
|
|
98
107
|
def block_number(self) -> int: ...
|
|
99
108
|
@property
|
|
100
|
-
def block_timestamp(self) ->
|
|
109
|
+
def block_timestamp(self) -> Timestamp: ...
|
|
101
110
|
@property
|
|
102
111
|
def asset(self) -> ChecksumAddress: ...
|
|
103
112
|
@property
|
|
104
113
|
def asset_symbol(self) -> str: ...
|
|
105
114
|
@property
|
|
106
|
-
def asset_decimals(self) ->
|
|
115
|
+
def asset_decimals(self) -> Decimals: ...
|
|
107
116
|
@property
|
|
108
117
|
def asset_price_usd(self) -> float | None: ...
|
|
109
118
|
@property
|
|
110
|
-
def total_assets(self) ->
|
|
119
|
+
def total_assets(self) -> Amount: ...
|
|
111
120
|
@property
|
|
112
|
-
def underlying_balance_on_vault(self) ->
|
|
121
|
+
def underlying_balance_on_vault(self) -> Amount: ...
|
|
113
122
|
@property
|
|
114
123
|
def price_oracle_addr(self) -> ChecksumAddress: ...
|
|
115
124
|
@property
|
|
@@ -129,7 +138,7 @@ class VaultState(Protocol):
|
|
|
129
138
|
@property
|
|
130
139
|
def unimplemented_getters(self) -> Sequence[str]: ...
|
|
131
140
|
@property
|
|
132
|
-
def market_balances(self) -> Mapping[MarketId,
|
|
141
|
+
def market_balances(self) -> Mapping[MarketId, Amount]: ...
|
|
133
142
|
@property
|
|
134
143
|
def erc20(self) -> Erc20Holdings: ...
|
|
135
144
|
|
|
@@ -18,7 +18,7 @@ from typing import Any, TypeVar
|
|
|
18
18
|
from eth_typing import HexAddress
|
|
19
19
|
from eth_utils import function_signature_to_4byte_selector
|
|
20
20
|
from web3 import Web3
|
|
21
|
-
from web3.types import ChecksumAddress
|
|
21
|
+
from web3.types import ChecksumAddress, Timestamp
|
|
22
22
|
|
|
23
23
|
from ipor_fusion.cache import CacheKey, cache_get, cache_key, cache_put
|
|
24
24
|
from ipor_fusion.chains import ensure_supported_chain
|
|
@@ -57,10 +57,13 @@ from ipor_fusion.readers.unpriceable import (
|
|
|
57
57
|
from ipor_fusion.substrates import decode_substrate
|
|
58
58
|
from ipor_fusion.types import (
|
|
59
59
|
AccessManagerAddress,
|
|
60
|
+
Amount,
|
|
60
61
|
ChainId,
|
|
62
|
+
Decimals,
|
|
61
63
|
FuseAddress,
|
|
62
64
|
MarketId,
|
|
63
65
|
PlasmaVaultAddress,
|
|
66
|
+
Shares,
|
|
64
67
|
parse_hex_address,
|
|
65
68
|
)
|
|
66
69
|
from ipor_fusion.vault_erc20 import Erc20Holdings, _Asset, _read_erc20_holdings
|
|
@@ -115,12 +118,12 @@ class VaultData:
|
|
|
115
118
|
chain_id: ChainId
|
|
116
119
|
block_number: int
|
|
117
120
|
is_latest: bool
|
|
118
|
-
block_timestamp:
|
|
119
|
-
share_decimals:
|
|
120
|
-
asset_decimals:
|
|
121
|
-
total_assets:
|
|
122
|
-
total_supply:
|
|
123
|
-
supply_cap:
|
|
121
|
+
block_timestamp: Timestamp
|
|
122
|
+
share_decimals: Decimals
|
|
123
|
+
asset_decimals: Decimals
|
|
124
|
+
total_assets: Amount
|
|
125
|
+
total_supply: Shares
|
|
126
|
+
supply_cap: Shares
|
|
124
127
|
asset: ChecksumAddress
|
|
125
128
|
asset_symbol: str
|
|
126
129
|
access_manager: AccessManagerAddress
|
|
@@ -135,10 +138,10 @@ class VaultData:
|
|
|
135
138
|
# totalAssets via ERC4626 base accounting; by design NOT tracked as an
|
|
136
139
|
# ERC20_VAULT_BALANCE substrate, so it is read directly in
|
|
137
140
|
# fetch_vault_data rather than derived from substrates.
|
|
138
|
-
underlying_balance_on_vault:
|
|
141
|
+
underlying_balance_on_vault: Amount = Amount(0)
|
|
139
142
|
vault_name: str = ""
|
|
140
143
|
deployment_block: int | None = None
|
|
141
|
-
deployment_timestamp:
|
|
144
|
+
deployment_timestamp: Timestamp | None = None
|
|
142
145
|
# Short error code from `fetch_deployment_info` when block/timestamp could
|
|
143
146
|
# not be resolved. Surfaced in CLI output and the JSON `deployment` field
|
|
144
147
|
# so users can distinguish "lookup not attempted/failed" from
|
|
@@ -186,7 +189,7 @@ class VaultData:
|
|
|
186
189
|
unimplemented_getters: list[str] = field(default_factory=list)
|
|
187
190
|
# totalAssetsInMarket of every balance-fuse market, once per market, in
|
|
188
191
|
# balance-fuse order.
|
|
189
|
-
market_balances: dict[MarketId,
|
|
192
|
+
market_balances: dict[MarketId, Amount] = field(default_factory=dict)
|
|
190
193
|
# The ERC20_VAULT_BALANCE tokens read directly (empty without that market).
|
|
191
194
|
erc20: Erc20Holdings = field(default_factory=Erc20Holdings)
|
|
192
195
|
# Symbols (lowercase address keys) of the substrate and breakdown tokens,
|
|
@@ -215,7 +218,7 @@ def _fuse_market_id_call(fuse: ChecksumAddress) -> Call[int]:
|
|
|
215
218
|
)
|
|
216
219
|
|
|
217
220
|
|
|
218
|
-
_UNCAPPED = 2**256 - 1
|
|
221
|
+
_UNCAPPED = Shares(2**256 - 1)
|
|
219
222
|
|
|
220
223
|
|
|
221
224
|
def _missing_required_getter(
|
|
@@ -285,7 +288,7 @@ def resolve_token_decimals(ctx: Web3Context, address: HexAddress) -> int | None:
|
|
|
285
288
|
return decimals
|
|
286
289
|
|
|
287
290
|
|
|
288
|
-
def _block_timestamp(ctx: Web3Context, block: int) ->
|
|
291
|
+
def _block_timestamp(ctx: Web3Context, block: int) -> Timestamp:
|
|
289
292
|
return ctx.web3.eth.get_block(block)["timestamp"]
|
|
290
293
|
|
|
291
294
|
|
|
@@ -435,7 +438,7 @@ class _MarketReads:
|
|
|
435
438
|
# Only markets with at least one substrate.
|
|
436
439
|
market_substrates: dict[MarketId, list[bytes]]
|
|
437
440
|
aave_pools: dict[MarketId, ChecksumAddress]
|
|
438
|
-
market_balances: dict[MarketId,
|
|
441
|
+
market_balances: dict[MarketId, Amount]
|
|
439
442
|
# False when the vault predates getMarketSubstrates (substrates unknown).
|
|
440
443
|
substrates_available: bool = True
|
|
441
444
|
|
|
@@ -763,7 +766,7 @@ def fetch_deployment_info(
|
|
|
763
766
|
vault_address: PlasmaVaultAddress,
|
|
764
767
|
*,
|
|
765
768
|
api_key: str | None = None,
|
|
766
|
-
) -> tuple[int | None,
|
|
769
|
+
) -> tuple[int | None, Timestamp | None, str | None]:
|
|
767
770
|
"""Return ``(block, timestamp, error)`` for the vault deployment, cached in
|
|
768
771
|
``ctx.cache``.
|
|
769
772
|
|
|
@@ -781,7 +784,7 @@ def fetch_deployment_info(
|
|
|
781
784
|
block = _cached_int(cached, block_key)
|
|
782
785
|
stamp = _cached_int(cached, time_key)
|
|
783
786
|
if block is not None and stamp is not None:
|
|
784
|
-
return block, stamp, None
|
|
787
|
+
return block, Timestamp(stamp), None
|
|
785
788
|
|
|
786
789
|
tx_hash, error = get_deployment_tx(chain_id, vault_address, api_key)
|
|
787
790
|
if not tx_hash:
|
|
@@ -790,7 +793,7 @@ def fetch_deployment_info(
|
|
|
790
793
|
try:
|
|
791
794
|
tx = ctx.web3.eth.get_transaction(tx_hash)
|
|
792
795
|
block_number: int = tx["blockNumber"]
|
|
793
|
-
timestamp:
|
|
796
|
+
timestamp: Timestamp = ctx.web3.eth.get_block(block_number)["timestamp"]
|
|
794
797
|
except Exception:
|
|
795
798
|
return None, None, "rpc-fetch-failed"
|
|
796
799
|
cache_put(ctx.cache, {block_key: str(block_number), time_key: str(timestamp)})
|
|
@@ -5,21 +5,21 @@ middleware price per token, valued in the underlying asset. Part of the
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
from collections.abc import Callable, Mapping, Sequence
|
|
8
|
-
from concurrent.futures import Future, ThreadPoolExecutor
|
|
9
8
|
from dataclasses import dataclass
|
|
10
|
-
from
|
|
9
|
+
from typing import Any
|
|
11
10
|
|
|
12
11
|
from eth_typing import ChecksumAddress, HexAddress
|
|
13
12
|
from web3 import Web3
|
|
14
13
|
|
|
15
14
|
from ipor_fusion.core.context import Web3Context
|
|
15
|
+
from ipor_fusion.core.contract import Call
|
|
16
16
|
from ipor_fusion.core.erc20 import ERC20
|
|
17
|
+
from ipor_fusion.core.multicall import Multicall3
|
|
17
18
|
from ipor_fusion.core.oracle import PriceOracleMiddleware
|
|
18
19
|
from ipor_fusion.core.plasma_vault import BalanceFuse
|
|
19
20
|
from ipor_fusion.market_ids import IporFusionMarkets
|
|
20
21
|
from ipor_fusion.substrates import decode_substrate
|
|
21
22
|
from ipor_fusion.types import MarketId, PlasmaVaultAddress
|
|
22
|
-
from ipor_fusion.vault_reads import _safe_call
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
@dataclass(frozen=True)
|
|
@@ -113,36 +113,32 @@ def _read_tokens(
|
|
|
113
113
|
addresses: list[HexAddress],
|
|
114
114
|
resolve_symbol: Callable[[HexAddress], str],
|
|
115
115
|
) -> list[_TokenRead]:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
116
|
+
"""Every token's decimals, the vault's balance and the middleware price in
|
|
117
|
+
one Multicall3 batch (a read that fails is ``None``); symbols through
|
|
118
|
+
``resolve_symbol``."""
|
|
119
|
+
calls: list[Call[Any]] = []
|
|
120
|
+
for address in addresses:
|
|
121
|
+
checksum = Web3.to_checksum_address(address)
|
|
122
|
+
token = ERC20(checksum)
|
|
123
|
+
calls += [
|
|
124
|
+
token.decimals(),
|
|
125
|
+
token.balance_of(vault),
|
|
126
|
+
oracle.get_asset_price(checksum),
|
|
127
|
+
]
|
|
128
|
+
results = Multicall3(ctx).try_aggregate(calls)
|
|
129
|
+
reads: list[_TokenRead] = []
|
|
130
|
+
for index, address in enumerate(addresses):
|
|
131
|
+
decimals, balance, price = results[3 * index : 3 * index + 3]
|
|
132
|
+
reads.append(
|
|
133
|
+
_TokenRead(
|
|
134
|
+
address=address,
|
|
135
|
+
symbol=resolve_symbol(address) or "?",
|
|
136
|
+
decimals=decimals,
|
|
137
|
+
balance=balance,
|
|
138
|
+
price_usd=price.readable() if price else None,
|
|
132
139
|
)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
price_value = price.result()
|
|
136
|
-
reads.append(
|
|
137
|
-
_TokenRead(
|
|
138
|
-
address=address,
|
|
139
|
-
symbol=symbol.result() or "?",
|
|
140
|
-
decimals=decimals.result(),
|
|
141
|
-
balance=balance.result(),
|
|
142
|
-
price_usd=price_value.readable() if price_value else None,
|
|
143
|
-
)
|
|
144
|
-
)
|
|
145
|
-
return reads
|
|
140
|
+
)
|
|
141
|
+
return reads
|
|
146
142
|
|
|
147
143
|
|
|
148
144
|
def _value_tokens(
|
|
@@ -645,7 +645,7 @@ def _supply_cap_json(data: VaultData) -> dict[str, Any]:
|
|
|
645
645
|
"formatted": (
|
|
646
646
|
"unlimited"
|
|
647
647
|
if data.supply_cap == UINT256_MAX
|
|
648
|
-
else _format_amount(data.supply_cap, data.
|
|
648
|
+
else _format_amount(data.supply_cap, data.share_decimals)
|
|
649
649
|
),
|
|
650
650
|
}
|
|
651
651
|
|
|
@@ -7,7 +7,7 @@ from dataclasses import dataclass, field
|
|
|
7
7
|
from functools import partial
|
|
8
8
|
|
|
9
9
|
from eth_abi.exceptions import DecodingError
|
|
10
|
-
from web3.types import ChecksumAddress
|
|
10
|
+
from web3.types import ChecksumAddress, Timestamp
|
|
11
11
|
|
|
12
12
|
from ipor_fusion.core.context import Web3Context
|
|
13
13
|
from ipor_fusion.core.fee_manager import (
|
|
@@ -19,7 +19,7 @@ from ipor_fusion.core.fee_manager import (
|
|
|
19
19
|
from ipor_fusion.core.plasma_vault import PlasmaVault
|
|
20
20
|
from ipor_fusion.core.withdraw_manager import AccountRequest, WithdrawManager
|
|
21
21
|
from ipor_fusion.errors import EmptyCallResultError
|
|
22
|
-
from ipor_fusion.types import ZERO_ADDRESS, Shares
|
|
22
|
+
from ipor_fusion.types import ZERO_ADDRESS, Amount, Fee, Period, Shares
|
|
23
23
|
from ipor_fusion.vault_reads import _read_batch, _safe_call, _scan_or_none
|
|
24
24
|
|
|
25
25
|
|
|
@@ -41,15 +41,15 @@ class FeeData:
|
|
|
41
41
|
configurations have drifted.
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
|
-
performance_fee_vault_bps:
|
|
45
|
-
management_fee_vault_bps:
|
|
46
|
-
management_fee_last_update:
|
|
47
|
-
unrealized_management_fee:
|
|
44
|
+
performance_fee_vault_bps: Fee | None = None
|
|
45
|
+
management_fee_vault_bps: Fee | None = None
|
|
46
|
+
management_fee_last_update: Timestamp | None = None
|
|
47
|
+
unrealized_management_fee: Amount | None = None
|
|
48
48
|
fee_manager: ChecksumAddress | None = None
|
|
49
49
|
ipor_dao_fee_recipient: ChecksumAddress | None = None
|
|
50
|
-
deposit_fee_wad:
|
|
51
|
-
performance_fee_manager_bps:
|
|
52
|
-
management_fee_manager_bps:
|
|
50
|
+
deposit_fee_wad: Fee | None = None
|
|
51
|
+
performance_fee_manager_bps: Fee | None = None
|
|
52
|
+
management_fee_manager_bps: Fee | None = None
|
|
53
53
|
performance_fee_recipients: list[RecipientFee] | None = None
|
|
54
54
|
management_fee_recipients: list[RecipientFee] | None = None
|
|
55
55
|
high_water_mark: HighWaterMarkPerformanceFee | None = None
|
|
@@ -59,21 +59,21 @@ class FeeData:
|
|
|
59
59
|
class WithdrawManagerData:
|
|
60
60
|
"""On-chain state snapshot from the WithdrawManager contract."""
|
|
61
61
|
|
|
62
|
-
withdraw_window:
|
|
62
|
+
withdraw_window: Period
|
|
63
63
|
# None (not 0) when the getter failed: a zero fee and an unreadable fee
|
|
64
64
|
# are different facts, and the fees section reports both.
|
|
65
|
-
request_fee:
|
|
66
|
-
withdraw_fee:
|
|
67
|
-
shares_to_release:
|
|
68
|
-
last_release_funds_timestamp:
|
|
65
|
+
request_fee: Fee | None
|
|
66
|
+
withdraw_fee: Fee | None
|
|
67
|
+
shares_to_release: Shares
|
|
68
|
+
last_release_funds_timestamp: Timestamp
|
|
69
69
|
# None when the WithdrawRequestUpdated scan or its reads failed.
|
|
70
70
|
pending_requests: list[AccountRequest] | None
|
|
71
71
|
# convertToAssets of shares_to_release at the snapshot block; None when
|
|
72
72
|
# the read failed (or there is nothing to release: 0).
|
|
73
|
-
shares_to_release_assets:
|
|
73
|
+
shares_to_release_assets: Amount | None = None
|
|
74
74
|
# convertToAssets of each pending request's shares, by account; a failed
|
|
75
75
|
# read is None.
|
|
76
|
-
pending_request_assets: dict[ChecksumAddress,
|
|
76
|
+
pending_request_assets: dict[ChecksumAddress, Amount | None] = field(
|
|
77
77
|
default_factory=dict
|
|
78
78
|
)
|
|
79
79
|
|
|
@@ -108,11 +108,11 @@ def _fetch_withdraw_manager(
|
|
|
108
108
|
],
|
|
109
109
|
)
|
|
110
110
|
return address, WithdrawManagerData(
|
|
111
|
-
withdraw_window=window or 0,
|
|
111
|
+
withdraw_window=window or Period(0),
|
|
112
112
|
request_fee=request_fee,
|
|
113
113
|
withdraw_fee=withdraw_fee,
|
|
114
114
|
shares_to_release=to_release,
|
|
115
|
-
last_release_funds_timestamp=last_ts or 0,
|
|
115
|
+
last_release_funds_timestamp=last_ts or Timestamp(0),
|
|
116
116
|
pending_requests=pending,
|
|
117
117
|
shares_to_release_assets=release_assets,
|
|
118
118
|
pending_request_assets={
|
|
@@ -49,6 +49,7 @@ def sequential_multicall():
|
|
|
49
49
|
patch("ipor_fusion.vault_reads.Multicall3", SequentialMulticall),
|
|
50
50
|
patch("ipor_fusion.vault_positions.Multicall3", SequentialMulticall),
|
|
51
51
|
patch("ipor_fusion.vault_data.Multicall3", SequentialMulticall),
|
|
52
|
+
patch("ipor_fusion.vault_erc20.Multicall3", SequentialMulticall),
|
|
52
53
|
):
|
|
53
54
|
yield
|
|
54
55
|
|
{ipor_fusion-4.0.0.dev110 → ipor_fusion-4.0.0.dev116}/tests/fixtures/vault_info_golden/clean.txt
RENAMED
|
@@ -11,7 +11,7 @@ Share decimals: 6
|
|
|
11
11
|
Asset price: N/A
|
|
12
12
|
Total Assets: 0.0 USDC
|
|
13
13
|
Total Supply: 0.0 shares
|
|
14
|
-
Supply Cap: 1,000,000.0
|
|
14
|
+
Supply Cap: 1,000,000.0 shares
|
|
15
15
|
Access Manager: 0x00000000000000000000000000000000000000AC
|
|
16
16
|
Price Oracle: 0x000000000000000000000000000000000000000C
|
|
17
17
|
Rewards Manager: N/A
|
|
@@ -752,6 +752,7 @@ class FakeBalanceFuse:
|
|
|
752
752
|
fuse: str
|
|
753
753
|
|
|
754
754
|
|
|
755
|
+
@pytest.mark.usefixtures("sequential_multicall")
|
|
755
756
|
class TestPrintErc20Balances:
|
|
756
757
|
@pytest.fixture(autouse=True)
|
|
757
758
|
def _tmp_config(self, tmp_path, monkeypatch):
|
|
@@ -1227,6 +1228,7 @@ class TestPrintReconciliation:
|
|
|
1227
1228
|
assert "MISMATCH" in captured.out
|
|
1228
1229
|
|
|
1229
1230
|
|
|
1231
|
+
@pytest.mark.usefixtures("sequential_multicall")
|
|
1230
1232
|
class TestErc20BalancesNotes:
|
|
1231
1233
|
@pytest.fixture(autouse=True)
|
|
1232
1234
|
def _tmp_config(self, tmp_path, monkeypatch):
|