ipor-fusion 4.0.0.dev114__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.dev114 → ipor_fusion-4.0.0.dev116}/AGENTS.md +3 -1
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/MIGRATION_4.md +6 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/PKG-INFO +1 -1
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/context.py +43 -3
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_erc20.py +28 -32
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/conftest.py +1 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_helpers.py +2 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_web3_context.py +43 -1
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/README.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/lane.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/discovery.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/lane.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/lane.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/mcp/server.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_data.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_commands.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_vault_info_golden.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_lane.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_live.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_public_api.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/typed_client/consumer.py +0 -0
- {ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev114 → 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
|
|
@@ -98,6 +98,12 @@ converted amounts `Amount`. A custom `VaultState` annotates the same.
|
|
|
98
98
|
now shown in share decimals labelled `shares` (``fusion vault info``) and its JSON
|
|
99
99
|
``formatted`` uses `share_decimals`; it was formatted with the asset's decimals.
|
|
100
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
|
+
|
|
101
107
|
`ensure_supported_chain` takes `ChainId`. `fetch_unpriceable_priced_tokens` takes the oracle and
|
|
102
108
|
underlying as `ChecksumAddress`. `resolve_token_symbol`, `resolve_token_decimals`,
|
|
103
109
|
`get_contract_name` and `get_deployment_tx` take `HexAddress` (a checksum address
|
|
@@ -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
|
|
@@ -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(
|
|
@@ -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
|
|
|
@@ -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):
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# pyright: reportAttributeAccessIssue=false
|
|
2
2
|
"""Unit tests for Web3Context — mock Web3 calls, no network required."""
|
|
3
3
|
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
4
7
|
from unittest.mock import MagicMock, patch
|
|
5
8
|
|
|
6
9
|
import pytest
|
|
@@ -126,7 +129,7 @@ class TestFromUrl:
|
|
|
126
129
|
|
|
127
130
|
def make_request(_provider, method, _params):
|
|
128
131
|
methods.append(method)
|
|
129
|
-
result = "0x1"
|
|
132
|
+
result = "0x1" + "00" * 32
|
|
130
133
|
return {"jsonrpc": "2.0", "id": 1, "result": result}
|
|
131
134
|
|
|
132
135
|
with patch.object(HTTPProvider, "make_request", make_request):
|
|
@@ -635,3 +638,42 @@ class TestPinnedContext:
|
|
|
635
638
|
assert ctx.call.call_args.args == (ADDR, b"\x01")
|
|
636
639
|
call.call(ctx, block=55)
|
|
637
640
|
assert ctx.call.call_args.args == (ADDR, b"\x01", 55)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
class TestConcurrencyLimit:
|
|
644
|
+
def _peak(self, limit: int | None) -> int:
|
|
645
|
+
in_flight = 0
|
|
646
|
+
peak = 0
|
|
647
|
+
lock = threading.Lock()
|
|
648
|
+
|
|
649
|
+
def make_request(provider, method, params):
|
|
650
|
+
nonlocal in_flight, peak
|
|
651
|
+
with lock:
|
|
652
|
+
in_flight += 1
|
|
653
|
+
peak = max(peak, in_flight)
|
|
654
|
+
time.sleep(0.02)
|
|
655
|
+
with lock:
|
|
656
|
+
in_flight -= 1
|
|
657
|
+
result = "0x1"
|
|
658
|
+
return {"jsonrpc": "2.0", "id": 1, "result": result}
|
|
659
|
+
|
|
660
|
+
with patch.object(HTTPProvider, "make_request", make_request):
|
|
661
|
+
ctx = Web3Context.from_url(
|
|
662
|
+
"http://localhost:8545", max_concurrent_requests=limit
|
|
663
|
+
)
|
|
664
|
+
with ThreadPoolExecutor(16) as pool:
|
|
665
|
+
list(pool.map(lambda _: ctx.web3.eth.block_number, range(32)))
|
|
666
|
+
return peak
|
|
667
|
+
|
|
668
|
+
def test_requests_in_flight_never_exceed_the_limit(self):
|
|
669
|
+
assert self._peak(3) <= 3
|
|
670
|
+
|
|
671
|
+
def test_none_leaves_requests_unbounded(self):
|
|
672
|
+
assert self._peak(None) > 3
|
|
673
|
+
|
|
674
|
+
def test_a_limit_below_one_is_rejected(self):
|
|
675
|
+
with (
|
|
676
|
+
patch.object(HTTPProvider, "make_request"),
|
|
677
|
+
pytest.raises(ValueError, match="at least 1"),
|
|
678
|
+
):
|
|
679
|
+
Web3Context.from_url("http://localhost:8545", max_concurrent_requests=0)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/advanced_euler_v2_looping_arbitrum.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/external_state_margin_leg_base.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/examples/simple_aave_v3_supply_base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/oracle_mapping_view.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_info_view.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_markets_view.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/cli/vault_rendering.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/fusion_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/rewards_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/core/withdraw_manager.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/__init__.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/__init__.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/chainlink.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/codec.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/contracts.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/events.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/indexer.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/lane.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/quote.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/ccip/transport.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/contracts.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/discovery.py
RENAMED
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/__init__.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/base.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/ccip.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/fuses/stargate.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/messages.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/simulation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/stargate/lane.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/crosschain/transport.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/external_state.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/external_state_events.py
RENAMED
|
File without changes
|
{ipor_fusion-4.0.0.dev114 → ipor_fusion-4.0.0.dev116}/src/ipor_fusion/fuses/fluid_instadapp.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|