ipor-fusion 4.0.0.dev103__tar.gz → 4.0.0.dev108__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.dev103 → ipor_fusion-4.0.0.dev108}/AGENTS.md +29 -15
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/MIGRATION_4.md +78 -3
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/PKG-INFO +13 -5
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/README.md +12 -4
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/pyproject.toml +1 -1
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/__init__.py +53 -37
- ipor_fusion-4.0.0.dev108/src/ipor_fusion/cache.py +204 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/vault_info_view.py +4 -1
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/context.py +15 -1
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/__init__.py +89 -10
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/ccip/__init__.py +4 -3
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/ccip/lane.py +4 -4
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/contracts.py +1 -1
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/discovery.py +4 -4
- {ipor_fusion-4.0.0.dev103/src/ipor_fusion/fuses/crosschain → ipor_fusion-4.0.0.dev108/src/ipor_fusion/crosschain/fuses}/__init__.py +5 -4
- {ipor_fusion-4.0.0.dev103/src/ipor_fusion/fuses/crosschain → ipor_fusion-4.0.0.dev108/src/ipor_fusion/crosschain/fuses}/ccip.py +2 -2
- {ipor_fusion-4.0.0.dev103/src/ipor_fusion/fuses/crosschain → ipor_fusion-4.0.0.dev108/src/ipor_fusion/crosschain/fuses}/stargate.py +5 -5
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/lane.py +3 -3
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/stargate/__init__.py +5 -4
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/stargate/lane.py +6 -6
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/explorer.py +23 -11
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/__init__.py +0 -34
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/market_ids.py +1 -1
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/mcp/server.py +4 -1
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/vault_data.py +60 -47
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_crosschain.py +1 -1
- ipor_fusion-4.0.0.dev108/tests/test_cache.py +316 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_commands.py +12 -6
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_helpers.py +15 -45
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_market_morpho_blue.py +1 -8
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_vault_info_golden.py +7 -11
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_encoding.py +5 -5
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_lane.py +1 -1
- ipor_fusion-4.0.0.dev108/tests/test_crosschain_layering.py +212 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_explorer.py +38 -18
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_hypercore_live.py +1 -5
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_public_api.py +65 -36
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/typed_client/consumer.py +12 -0
- ipor_fusion-4.0.0.dev103/src/ipor_fusion/cache.py +0 -86
- ipor_fusion-4.0.0.dev103/tests/test_cache.py +0 -99
- ipor_fusion-4.0.0.dev103/tests/test_crosschain_layering.py +0 -117
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/examples/CONTRIBUTING.md +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/examples/README.md +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/examples/advanced_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/examples/advanced_euler_v2_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/examples/external_state_margin_leg_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/examples/simple_aave_v3_supply_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/scripts/crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/skills/ipor-deploy-vault/SKILL.md +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/market_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/vault_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/access.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/contract.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/erc20.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/multicall.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/oracle.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/core/withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/ccip/chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/ccip/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/ccip/transport.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev103/src/ipor_fusion/fuses/crosschain → ipor_fusion-4.0.0.dev108/src/ipor_fusion/crosschain/fuses}/base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/stargate/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/stargate/transport.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/crosschain/transport.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/errors.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/guide/architecture.md +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/guide/invariants.md +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/guide/quickstart.md +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/oracle_mapping_reader.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/readers/unpriceable.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/vault_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/vault_managers.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/vault_positions.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/src/ipor_fusion/vault_reads.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_crosschain_recovery.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_access_manager.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_lending_health.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_pending_requests_scan.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_role_accounts.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_vault_predates_getters.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_contract_call.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_contract_encoder.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_ccip_events.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_ccip_indexer.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_chainlink.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_contracts.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_readiness.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_crosschain_transport.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_erc20.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_examples_vaults.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_external_state_executor.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_hypercore_deployment.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_hypercore_flow.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_hypercore_readers.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_hypercore_simulation.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_mcp_server.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_multicall.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_oracle.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_plasma_vault.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_reader_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_rewards_manager.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_borrow_aave_v3_mainnet.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_claim_merkl_wrapper_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_claim_morpho_rewards.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_crosschain_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_crosschain_hyperevm.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_euler_v2_batch_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_euler_v2_credit_market_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_euler_v2_swap_deploy_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_euler_v2_swap_registry_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_external_state_executor_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_fusion_factory_clone_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_looping_aave_v3_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_looping_morpho_blue_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_pilot_v3.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_pilot_v4_uniswap.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_pilot_v5_ramses.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_pilot_v5_scheduled.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_plasma_vault_setters_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_readers_integration.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_release_funds.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_swap_on_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_simulate_vault_from_scratch_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_solidity_mirrors.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_unpriceable_tokens.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_vault_simulator_blocks.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_web3_context.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/test_withdraw_manager.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/tests/typed_client/rejected.py +0 -0
- {ipor_fusion-4.0.0.dev103 → ipor_fusion-4.0.0.dev108}/uv.lock +0 -0
|
@@ -155,8 +155,8 @@ the lifecycle matrix follows that.
|
|
|
155
155
|
cases and non-obvious logic.
|
|
156
156
|
- The public API is `ipor_fusion.__all__` (README, "Public API and stability").
|
|
157
157
|
Every public symbol is imported and listed there; a symbol of a public
|
|
158
|
-
subpackage (`core`, `config`, `fuses`, `
|
|
159
|
-
`crosschain`, `crosschain.ccip`, `crosschain.stargate`) also goes in that
|
|
158
|
+
subpackage (`core`, `config`, `fuses`, `readers`, `crosschain`,
|
|
159
|
+
`crosschain.fuses`, `crosschain.ccip`, `crosschain.stargate`) also goes in that
|
|
160
160
|
subpackage's `__all__`. `ipor_fusion.guide` keeps its own namespace. The
|
|
161
161
|
public modules (`about`, `chains`, `dependency_graph`, `errors`, `explorer`,
|
|
162
162
|
`field_docs`, `market_ids`, `market_info`, `morpho_api`, `substrates`, `types`,
|
|
@@ -204,7 +204,13 @@ the lifecycle matrix follows that.
|
|
|
204
204
|
document rendered from the snapshot alone (contract names via an injected
|
|
205
205
|
`resolve_name`),
|
|
206
206
|
and `health_check_lists`; `explorer.py` Etherscan V2 (single endpoint, needs API key);
|
|
207
|
-
`cache.py` the
|
|
207
|
+
`cache.py` the `ContractCache` protocol (`get`/`put` of immutable facts keyed
|
|
208
|
+
`CacheKey(chain_id, address, field)`), `FileContractCache` (one JSON file, atomic
|
|
209
|
+
replace, corrupt/unwritable degrades to memory) and `NullCache`; every SDK use goes
|
|
210
|
+
through `cache_get`/`cache_put` (a failing cache is a miss, never an error) on
|
|
211
|
+
`ctx.cache` (`Web3Context(cache=...)`, default `default_contract_cache()` under
|
|
212
|
+
`$XDG_CACHE_HOME/ipor-fusion/`) or `get_contract_name(cache=...)`. Never cache an
|
|
213
|
+
absence (no code, no answer): it can change at a later block.
|
|
208
214
|
`morpho_api.py` the Morpho public GraphQL API client (`fetch_market`, `fetch_vault`,
|
|
209
215
|
`PUBLIC_ALLOCATOR_ADDRESSES`); `market_info.py` — `build_morpho_blue_market_json`,
|
|
210
216
|
`build_meta_morpho_json`, the `fusion market` JSON documents.
|
|
@@ -224,7 +230,7 @@ the lifecycle matrix follows that.
|
|
|
224
230
|
(`FuseAction`), `fuse` (the `Fuse` base, `StakeFuse`, the shared validators and
|
|
225
231
|
substrate packers), `address` (`_validate_not_zero_address`). `core` imports neither
|
|
226
232
|
`fuses` nor `crosschain` (`tests/test_sdk_layering.py`)
|
|
227
|
-
- `fuses/` — per-protocol fuse encoders (aave_v3, async_action, compound_v3,
|
|
233
|
+
- `fuses/` — per-protocol fuse encoders (aave_v3, async_action, compound_v3,
|
|
228
234
|
erc4626, euler_v2, external_state, fluid_instadapp, gearbox_v3, merkl, morpho, ramses_v2,
|
|
229
235
|
uniswap_v3, universal, `events.py`); `base.py` re-exports the `core/fuse.py` base and validators,
|
|
230
236
|
`FuseAction` and `ZERO_ADDRESS` (from `types`). A fuse is a value: equality and
|
|
@@ -251,15 +257,20 @@ the lifecycle matrix follows that.
|
|
|
251
257
|
(`CcipOffRamp.execute` is the permissionless delivery) and `ccip/indexer` fetches a
|
|
252
258
|
message's verifier result from Chainlink's public indexers and builds that
|
|
253
259
|
`manual_execution` call.
|
|
254
|
-
`fuses
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
The market fuse encoders live in `crosschain/fuses/` (`base`, `stargate`, `ccip`),
|
|
261
|
+
mirroring `contracts/fuses/crosschain/`; they build on `core/fuse.py`, so the
|
|
262
|
+
dependency direction is `core` <- `crosschain` and `fuses/` holds no crosschain code
|
|
263
|
+
(neither package imports the other). Names mirror the Solidity contracts and
|
|
264
|
+
libraries. Adding a transport: a new subpackage, its encoders in `crosschain/fuses/`
|
|
265
|
+
and one `LANES` entry. Layering: the encoders import the wire codecs and wrappers;
|
|
266
|
+
lanes, discovery, transports and the simulator import the encoders.
|
|
267
|
+
`ipor_fusion.crosschain` exports the whole surface (encoders, lanes, discovery,
|
|
268
|
+
transports, simulator). A package `__init__` runs before its modules and the
|
|
269
|
+
encoders import `ccip.contracts` / `stargate.layerzero`, so `.ccip` and `.stargate`
|
|
270
|
+
export only their codecs and wrappers. `tests/test_crosschain_layering.py` pins this:
|
|
271
|
+
the SDK's module-level import graph, with the implicit package-`__init__` edges, is
|
|
272
|
+
acyclic, and the crosschain package has no function-level or `TYPE_CHECKING`
|
|
273
|
+
imports; never work around a cycle with a lazy import, move the module instead.
|
|
263
274
|
- `readers/` — read side: lending_health, oracle_mapping, position_manager, aave_v3,
|
|
264
275
|
compound_v3, morpho, ramses_v2, uniswap_v3, unpriceable (middleware-priced tokens of
|
|
265
276
|
granted substrates the oracle cannot price)
|
|
@@ -383,8 +394,11 @@ byte, e.g. Ebisu ZAPPER/REGISTRY).
|
|
|
383
394
|
role-accounts, oracle-mapping). `vault info` runs independent fetch pipelines
|
|
384
395
|
(vault/asset, fees, WithdrawManager, markets, roles) concurrently on a
|
|
385
396
|
`ThreadPoolExecutor`, each a short chain of `Multicall3` batches; keep new
|
|
386
|
-
reads inside a batch rather than adding per-call round trips. Contract names
|
|
387
|
-
|
|
397
|
+
reads inside a batch rather than adding per-call round trips. Contract names,
|
|
398
|
+
token symbols and decimals and deployment blocks are cached per chain in
|
|
399
|
+
`$XDG_CACHE_HOME/ipor-fusion/contract_cache.json` (`<chain>:<address>:<field>`
|
|
400
|
+
keys); tests redirect it with `monkeypatch.setenv("XDG_CACHE_HOME", ...)` or
|
|
401
|
+
inject a cache.
|
|
388
402
|
|
|
389
403
|
Every CLI command has an MCP tool (`fusion changelog` maps to `server_info`;
|
|
390
404
|
the rest share names). Read-only: `vault_info`, `vault_validate`,
|
|
@@ -220,6 +220,27 @@ live in `ipor_fusion.core.fuse`; `ipor_fusion.fuses.base`, `ipor_fusion.fuses` a
|
|
|
220
220
|
hash now cover every attribute a fuse stores, not only its class and address (see
|
|
221
221
|
[Behavior changes](#behavior-changes)).
|
|
222
222
|
|
|
223
|
+
**Crosschain fuse encoders moved.** The crosschain market encoders moved from
|
|
224
|
+
`ipor_fusion.fuses.crosschain` into the crosschain package, and
|
|
225
|
+
`ipor_fusion.fuses` no longer re-exports them. There is no shim: the old paths
|
|
226
|
+
raise `ImportError`. The top-level names (`from ipor_fusion import ...`) are
|
|
227
|
+
unchanged.
|
|
228
|
+
|
|
229
|
+
| 3.x import | 4.0 import |
|
|
230
|
+
|---|---|
|
|
231
|
+
| `ipor_fusion.fuses.crosschain` | `ipor_fusion.crosschain.fuses` (or `ipor_fusion.crosschain`) |
|
|
232
|
+
| `ipor_fusion.fuses.crosschain.base` | `ipor_fusion.crosschain.fuses.base` |
|
|
233
|
+
| `ipor_fusion.fuses.crosschain.ccip` | `ipor_fusion.crosschain.fuses.ccip` |
|
|
234
|
+
| `ipor_fusion.fuses.crosschain.stargate` | `ipor_fusion.crosschain.fuses.stargate` |
|
|
235
|
+
| `from ipor_fusion.fuses import CcipCrosschainSupplyFuse` (and the other crosschain encoders, `SendParams`, `CrosschainSubstrate*`, `crosschain_market_id`) | `from ipor_fusion.crosschain import ...` or `from ipor_fusion import ...` |
|
|
236
|
+
|
|
237
|
+
`ipor_fusion.crosschain` now exports the whole crosschain surface: besides the
|
|
238
|
+
codecs and wrappers, the encoders, `CrosschainLane`, `CcipLane`, `StargateLane`,
|
|
239
|
+
the transports and chains, `discover_deployment`, `open_lane(s)`, `LANES` and
|
|
240
|
+
`CrosschainSimulator`, which 3.x exported only from `ipor_fusion`.
|
|
241
|
+
`ipor_fusion.crosschain.ccip` and `.stargate` keep exporting only their codecs
|
|
242
|
+
and wrappers.
|
|
243
|
+
|
|
223
244
|
**Moved, still importable.** `FuseAction` lives in `ipor_fusion.core.fuse_action` and
|
|
224
245
|
`ZERO_ADDRESS` in `ipor_fusion.types`; both are re-exported from
|
|
225
246
|
`ipor_fusion.fuses.base` and `FuseAction` from `ipor_fusion`. The CLI's internal
|
|
@@ -313,6 +334,61 @@ argument (`chain_id=`) for another chain. An external signer still needs the rea
|
|
|
313
334
|
target address (`call.to`) and builds the chain-qualified transaction itself, or
|
|
314
335
|
gets it from `call.build_transaction(ctx)`.
|
|
315
336
|
|
|
337
|
+
## Contract cache
|
|
338
|
+
|
|
339
|
+
Contract names, token symbols and decimals and deployment blocks are cached in a
|
|
340
|
+
`ContractCache`, injected instead of always written to `~/.cache`:
|
|
341
|
+
|
|
342
|
+
- `Web3Context(..., cache=...)` and `Web3Context.from_url(..., cache=...)` take one
|
|
343
|
+
(keyword-only); `ctx.cache` returns it, and `ctx.at_block(...)` keeps it. The
|
|
344
|
+
default (`None`) is `default_contract_cache()`, a `FileContractCache` at
|
|
345
|
+
`$XDG_CACHE_HOME/ipor-fusion/contract_cache.json` (`~/.cache/...` when unset),
|
|
346
|
+
resolved when used rather than at import. `NullCache()` keeps nothing;
|
|
347
|
+
`FileContractCache(path)` moves the file; any object with `get(keys)` and
|
|
348
|
+
`put(entries)` over `CacheKey(chain_id, address, field)` → `str` fits the
|
|
349
|
+
protocol (for example a shared store in a service).
|
|
350
|
+
- `resolve_token_symbol`, `resolve_token_decimals`, the snapshot's token metadata
|
|
351
|
+
(`fetch_vault_data(metadata=True)`) and `fetch_deployment_info` use `ctx.cache`.
|
|
352
|
+
`get_contract_name(chain_id, address, api_key=None, *, cache=None)` takes the cache
|
|
353
|
+
explicitly (default: the file cache); pass `cache=ctx.cache` to share the
|
|
354
|
+
context's policy. `build_vault_info_json` without `resolve_name` still names
|
|
355
|
+
contracts from the default file cache; pass `resolve_name` to avoid it.
|
|
356
|
+
- `ipor_fusion.cache` is a public module now and exports `ContractCache`,
|
|
357
|
+
`FileContractCache`, `NullCache`, `CacheKey`, `CacheField`, `cache_key` and
|
|
358
|
+
`default_contract_cache` (also from `ipor_fusion`). Its 3.x functions and constants
|
|
359
|
+
are gone: `load_contract_cache`, `save_contract_cache`, `update_contract_cache`,
|
|
360
|
+
`load_deployment_cache`, `update_deployment_cache`, `CACHE_DIR`, `CACHE_FILE` and
|
|
361
|
+
`DEPLOYMENT_CACHE_FILE`. `cache_key(chain_id, address, field)` returns a `CacheKey`
|
|
362
|
+
and raises `ValueError` for an address that is not 20 bytes of hex, so
|
|
363
|
+
`get_contract_name` does too.
|
|
364
|
+
|
|
365
|
+
**File format.** One file, keys `<chain_id>:<lowercase address>:<field>` (`name`,
|
|
366
|
+
`symbol`, `decimals`, `deployment_block`, `deployment_timestamp`), string values. On
|
|
367
|
+
load, a 3.x contract name (`<chain_id>:<address>`) is kept as that chain's `name`;
|
|
368
|
+
3.x token entries (`symbol:<address>`, `decimals:<address>`) name no chain and are
|
|
369
|
+
discarded, so symbols and decimals are read again once per chain. `deployment_cache.json`
|
|
370
|
+
is no longer read: a vault's deployment block is resolved again (it needs an
|
|
371
|
+
Etherscan key) and the old file can be deleted.
|
|
372
|
+
|
|
373
|
+
**Behavior.**
|
|
374
|
+
|
|
375
|
+
- The same address on two chains has two entries: a token's symbol or decimals read
|
|
376
|
+
on one chain is never served for another.
|
|
377
|
+
- Absence is not cached: `resolve_token_symbol` still returns `"no contract"` for an
|
|
378
|
+
address without code, but no longer stores it, so a token deployed later (or seen at
|
|
379
|
+
a later block) resolves.
|
|
380
|
+
- A write merges with the file as it is on disk and replaces it atomically (a
|
|
381
|
+
temporary file in the same directory, then `os.replace`), so a crash or two CLI/MCP
|
|
382
|
+
processes writing at once never leave a torn file; one of two concurrent writers may
|
|
383
|
+
lose its newest entry, which is read again later.
|
|
384
|
+
- A cache never fails a read: a corrupt or unreadable file reads as empty (logged
|
|
385
|
+
warning from `ipor_fusion.cache`) and is rewritten by the next write; an unwritable
|
|
386
|
+
directory (read-only file system) keeps the entries in memory for the process; a
|
|
387
|
+
custom cache that raises is treated as a miss.
|
|
388
|
+
- `fetch_deployment_info` returns `rpc-fetch-failed` only when the creation
|
|
389
|
+
transaction or its block cannot be read; a cache failure is no longer reported as
|
|
390
|
+
one (the deployment block is returned).
|
|
391
|
+
|
|
316
392
|
## Behavior changes
|
|
317
393
|
|
|
318
394
|
- Two fuses are equal (and hash alike) only when they have the same class and the
|
|
@@ -350,9 +426,8 @@ gets it from `call.build_transaction(ctx)`.
|
|
|
350
426
|
- `TransactionError.tx_hash` is `0x`-prefixed.
|
|
351
427
|
- Saved config entries are checksummed when loaded; the MCP `vault_add` tool rejects
|
|
352
428
|
a value that is not an address.
|
|
353
|
-
- The
|
|
354
|
-
|
|
355
|
-
for the other.
|
|
429
|
+
- The contract cache keys every entry by chain and lowercase address and no longer
|
|
430
|
+
caches an absence (see [Contract cache](#contract-cache)).
|
|
356
431
|
|
|
357
432
|
## Errors
|
|
358
433
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ipor-fusion
|
|
3
|
-
Version: 4.0.0.
|
|
3
|
+
Version: 4.0.0.dev108
|
|
4
4
|
Summary: The IPOR Fusion SDK for Python
|
|
5
5
|
Project-URL: homepage, https://ipor.io
|
|
6
6
|
Project-URL: repository, https://github.com/IPOR-Labs/ipor-fusion.py
|
|
@@ -377,15 +377,15 @@ renaming a name in it, or changing a signature incompatibly, waits for a major
|
|
|
377
377
|
release; adding one does not.
|
|
378
378
|
|
|
379
379
|
- **Public subpackages** declare their own `__all__`, each a subset of the top
|
|
380
|
-
level: `ipor_fusion.core`, `config`, `fuses`, `
|
|
381
|
-
`crosschain`, `crosschain.ccip` and `crosschain.stargate`. `ipor_fusion.guide`
|
|
380
|
+
level: `ipor_fusion.core`, `config`, `fuses`, `readers`, `crosschain`,
|
|
381
|
+
`crosschain.fuses`, `crosschain.ccip` and `crosschain.stargate`. `ipor_fusion.guide`
|
|
382
382
|
(the packaged docs) keeps its names in its own namespace.
|
|
383
383
|
- **Public modules** stay importable for the top-level names they define:
|
|
384
|
-
`about`, `chains`, `dependency_graph`, `errors`, `explorer`, `field_docs`,
|
|
384
|
+
`about`, `cache`, `chains`, `dependency_graph`, `errors`, `explorer`, `field_docs`,
|
|
385
385
|
`market_ids`, `market_info`, `morpho_api`, `substrates`, `types`, `validation`,
|
|
386
386
|
`vault_data` and `vault_info` (all under `ipor_fusion.`).
|
|
387
387
|
- **Internal:** `ipor_fusion.cli`, `ipor_fusion.mcp`, every other module (e.g.
|
|
388
|
-
`
|
|
388
|
+
`vault_reads`, the submodules of the public subpackages), underscore
|
|
389
389
|
names, and names in no `__all__`. They change without notice; the CLI and the
|
|
390
390
|
MCP server are stable as commands and tools, not as Python modules.
|
|
391
391
|
- **Preview features** (`PREVIEW_FEATURES` in `ipor_fusion.about`: crosschain,
|
|
@@ -495,6 +495,14 @@ fields as unavailable (`null`) rather than partial. A `LogSource` on the context
|
|
|
495
495
|
a service that caches an explorer's log API, answers those scans instead
|
|
496
496
|
(`Web3Context(log_source=…)`); when it raises `LogSourceUnavailable` the RPC scan runs.
|
|
497
497
|
|
|
498
|
+
Immutable contract facts (token symbols and decimals, contract names, deployment blocks)
|
|
499
|
+
are cached per chain and address in the context's `ContractCache`. The default,
|
|
500
|
+
`FileContractCache`, is `$XDG_CACHE_HOME/ipor-fusion/contract_cache.json`
|
|
501
|
+
(`~/.cache/...`), written atomically and ignored when corrupt or unwritable. A service
|
|
502
|
+
passes `Web3Context(..., cache=NullCache())` to keep nothing, `FileContractCache(path)`
|
|
503
|
+
to move it, or its own `get` / `put` implementation; `get_contract_name(..., cache=)`
|
|
504
|
+
takes the same object.
|
|
505
|
+
|
|
498
506
|
## Development
|
|
499
507
|
|
|
500
508
|
```bash
|
|
@@ -329,15 +329,15 @@ renaming a name in it, or changing a signature incompatibly, waits for a major
|
|
|
329
329
|
release; adding one does not.
|
|
330
330
|
|
|
331
331
|
- **Public subpackages** declare their own `__all__`, each a subset of the top
|
|
332
|
-
level: `ipor_fusion.core`, `config`, `fuses`, `
|
|
333
|
-
`crosschain`, `crosschain.ccip` and `crosschain.stargate`. `ipor_fusion.guide`
|
|
332
|
+
level: `ipor_fusion.core`, `config`, `fuses`, `readers`, `crosschain`,
|
|
333
|
+
`crosschain.fuses`, `crosschain.ccip` and `crosschain.stargate`. `ipor_fusion.guide`
|
|
334
334
|
(the packaged docs) keeps its names in its own namespace.
|
|
335
335
|
- **Public modules** stay importable for the top-level names they define:
|
|
336
|
-
`about`, `chains`, `dependency_graph`, `errors`, `explorer`, `field_docs`,
|
|
336
|
+
`about`, `cache`, `chains`, `dependency_graph`, `errors`, `explorer`, `field_docs`,
|
|
337
337
|
`market_ids`, `market_info`, `morpho_api`, `substrates`, `types`, `validation`,
|
|
338
338
|
`vault_data` and `vault_info` (all under `ipor_fusion.`).
|
|
339
339
|
- **Internal:** `ipor_fusion.cli`, `ipor_fusion.mcp`, every other module (e.g.
|
|
340
|
-
`
|
|
340
|
+
`vault_reads`, the submodules of the public subpackages), underscore
|
|
341
341
|
names, and names in no `__all__`. They change without notice; the CLI and the
|
|
342
342
|
MCP server are stable as commands and tools, not as Python modules.
|
|
343
343
|
- **Preview features** (`PREVIEW_FEATURES` in `ipor_fusion.about`: crosschain,
|
|
@@ -447,6 +447,14 @@ fields as unavailable (`null`) rather than partial. A `LogSource` on the context
|
|
|
447
447
|
a service that caches an explorer's log API, answers those scans instead
|
|
448
448
|
(`Web3Context(log_source=…)`); when it raises `LogSourceUnavailable` the RPC scan runs.
|
|
449
449
|
|
|
450
|
+
Immutable contract facts (token symbols and decimals, contract names, deployment blocks)
|
|
451
|
+
are cached per chain and address in the context's `ContractCache`. The default,
|
|
452
|
+
`FileContractCache`, is `$XDG_CACHE_HOME/ipor-fusion/contract_cache.json`
|
|
453
|
+
(`~/.cache/...`), written atomically and ignored when corrupt or unwritable. A service
|
|
454
|
+
passes `Web3Context(..., cache=NullCache())` to keep nothing, `FileContractCache(path)`
|
|
455
|
+
to move it, or its own `get` / `put` implementation; `get_contract_name(..., cache=)`
|
|
456
|
+
takes the same object.
|
|
457
|
+
|
|
450
458
|
## Development
|
|
451
459
|
|
|
452
460
|
```bash
|
|
@@ -7,6 +7,15 @@ from ipor_fusion.about import (
|
|
|
7
7
|
read_changelog,
|
|
8
8
|
repository_url,
|
|
9
9
|
)
|
|
10
|
+
from ipor_fusion.cache import (
|
|
11
|
+
CacheField,
|
|
12
|
+
CacheKey,
|
|
13
|
+
ContractCache,
|
|
14
|
+
FileContractCache,
|
|
15
|
+
NullCache,
|
|
16
|
+
cache_key,
|
|
17
|
+
default_contract_cache,
|
|
18
|
+
)
|
|
10
19
|
from ipor_fusion.chains import (
|
|
11
20
|
CHAIN_NAME_TO_ID,
|
|
12
21
|
CHAIN_NAMES,
|
|
@@ -79,17 +88,24 @@ from ipor_fusion.crosschain import (
|
|
|
79
88
|
EMPTY_COMMAND,
|
|
80
89
|
INDEXER_URLS,
|
|
81
90
|
INSUFFICIENT_NATIVE_FEE_SELECTOR,
|
|
91
|
+
LANES,
|
|
82
92
|
PACKET_SENT_TOPIC,
|
|
93
|
+
SYNTHETIC_TOKEN_SOURCE,
|
|
83
94
|
ActiveCommand,
|
|
84
95
|
Any2EVMMessage,
|
|
85
96
|
BalanceObservation,
|
|
86
97
|
BusinessAction,
|
|
98
|
+
CcipChain,
|
|
99
|
+
CcipCommandType,
|
|
87
100
|
CcipCommitteeVerifier,
|
|
101
|
+
CcipCrosschainCommandFuse,
|
|
88
102
|
CcipCrosschainDispatcher,
|
|
89
103
|
CcipCrosschainExecutor,
|
|
90
104
|
CcipCrosschainFactory,
|
|
105
|
+
CcipCrosschainSupplyFuse,
|
|
91
106
|
CcipEvent,
|
|
92
107
|
CcipEventSpec,
|
|
108
|
+
CcipLane,
|
|
93
109
|
CcipMessageSent,
|
|
94
110
|
CcipMsgType,
|
|
95
111
|
CcipObservation,
|
|
@@ -97,32 +113,57 @@ from ipor_fusion.crosschain import (
|
|
|
97
113
|
CcipOnRamp,
|
|
98
114
|
CcipRouteConfig,
|
|
99
115
|
CcipRouter,
|
|
116
|
+
CcipSendParams,
|
|
100
117
|
CcipTokenAdminRegistry,
|
|
101
118
|
CcipTokenLane,
|
|
102
119
|
CcipTokenPool,
|
|
120
|
+
CcipTransport,
|
|
103
121
|
CcipVerifierResolver,
|
|
104
122
|
CcvRequirements,
|
|
105
123
|
Command,
|
|
106
124
|
CommandStatus,
|
|
125
|
+
CrosschainClaimFuse,
|
|
126
|
+
CrosschainCommandFuse,
|
|
127
|
+
CrosschainDeployment,
|
|
107
128
|
CrosschainDispatcher,
|
|
108
129
|
CrosschainExecutor,
|
|
130
|
+
CrosschainExecutorInfo,
|
|
109
131
|
CrosschainFactory,
|
|
132
|
+
CrosschainLane,
|
|
133
|
+
CrosschainSimulator,
|
|
134
|
+
CrosschainSubstrate,
|
|
135
|
+
CrosschainSubstrateLib,
|
|
136
|
+
CrosschainSubstrateType,
|
|
137
|
+
CrosschainSupplyFuse,
|
|
138
|
+
CrosschainTransport,
|
|
110
139
|
CrosschainTransportKind,
|
|
140
|
+
DeliveryCall,
|
|
111
141
|
DeploymentStatus,
|
|
112
142
|
EnforcedOptionParam,
|
|
113
143
|
EVMTokenAmount,
|
|
114
144
|
ExecutorInitParams,
|
|
145
|
+
LaneFuses,
|
|
146
|
+
LaneObservation,
|
|
115
147
|
MessageExecutionState,
|
|
116
148
|
MessageV1,
|
|
117
149
|
MsgType,
|
|
118
150
|
Observation,
|
|
119
151
|
OptionsBuilder,
|
|
152
|
+
OutboundMessage,
|
|
120
153
|
Packet,
|
|
121
154
|
SafetyConfig,
|
|
155
|
+
SendParams,
|
|
156
|
+
StargateChain,
|
|
157
|
+
StargateCrosschainCommandFuse,
|
|
158
|
+
StargateCrosschainCommandType,
|
|
122
159
|
StargateCrosschainDispatcher,
|
|
123
160
|
StargateCrosschainExecutor,
|
|
124
161
|
StargateCrosschainFactory,
|
|
162
|
+
StargateCrosschainSupplyFuse,
|
|
163
|
+
StargateLane,
|
|
164
|
+
StargateSendParams,
|
|
125
165
|
StargateTokenMessaging,
|
|
166
|
+
StargateTransport,
|
|
126
167
|
TaxiMessage,
|
|
127
168
|
TokenTransferV1,
|
|
128
169
|
VerifierResult,
|
|
@@ -130,10 +171,14 @@ from ipor_fusion.crosschain import (
|
|
|
130
171
|
ccip_events,
|
|
131
172
|
ccip_receive_calldata,
|
|
132
173
|
ccip_token_lane,
|
|
174
|
+
crosschain_market_id,
|
|
133
175
|
decode_ccip_envelope,
|
|
134
176
|
decode_ccip_event,
|
|
135
177
|
decode_command,
|
|
136
178
|
decode_envelope,
|
|
179
|
+
detect_transport,
|
|
180
|
+
discover_deployment,
|
|
181
|
+
discover_lane_fuses,
|
|
137
182
|
encode_enforced_options,
|
|
138
183
|
encode_oft_compose_msg,
|
|
139
184
|
fetch_verifier_result,
|
|
@@ -141,29 +186,9 @@ from ipor_fusion.crosschain import (
|
|
|
141
186
|
lz_compose_calldata,
|
|
142
187
|
lz_receive_calldata,
|
|
143
188
|
manual_execution,
|
|
144
|
-
quote_ccip_native_fee,
|
|
145
|
-
)
|
|
146
|
-
from ipor_fusion.crosschain.ccip.lane import CcipLane
|
|
147
|
-
from ipor_fusion.crosschain.ccip.transport import CcipChain, CcipTransport
|
|
148
|
-
from ipor_fusion.crosschain.discovery import (
|
|
149
|
-
LANES,
|
|
150
|
-
CrosschainDeployment,
|
|
151
|
-
CrosschainExecutorInfo,
|
|
152
|
-
detect_transport,
|
|
153
|
-
discover_deployment,
|
|
154
|
-
discover_lane_fuses,
|
|
155
189
|
open_lane,
|
|
156
190
|
open_lanes,
|
|
157
|
-
|
|
158
|
-
from ipor_fusion.crosschain.lane import CrosschainLane, LaneFuses, LaneObservation
|
|
159
|
-
from ipor_fusion.crosschain.simulation import CrosschainSimulator
|
|
160
|
-
from ipor_fusion.crosschain.stargate.lane import StargateLane
|
|
161
|
-
from ipor_fusion.crosschain.stargate.transport import StargateChain, StargateTransport
|
|
162
|
-
from ipor_fusion.crosschain.transport import (
|
|
163
|
-
SYNTHETIC_TOKEN_SOURCE,
|
|
164
|
-
CrosschainTransport,
|
|
165
|
-
DeliveryCall,
|
|
166
|
-
OutboundMessage,
|
|
191
|
+
quote_ccip_native_fee,
|
|
167
192
|
)
|
|
168
193
|
from ipor_fusion.dependency_graph import (
|
|
169
194
|
compute_update_groups,
|
|
@@ -206,17 +231,7 @@ from ipor_fusion.fuses import (
|
|
|
206
231
|
AaveV3SupplyFuse,
|
|
207
232
|
AsyncActionFuse,
|
|
208
233
|
AsyncActionSubstrates,
|
|
209
|
-
CcipCommandType,
|
|
210
|
-
CcipCrosschainCommandFuse,
|
|
211
|
-
CcipCrosschainSupplyFuse,
|
|
212
|
-
CcipSendParams,
|
|
213
234
|
CompoundV3SupplyFuse,
|
|
214
|
-
CrosschainClaimFuse,
|
|
215
|
-
CrosschainCommandFuse,
|
|
216
|
-
CrosschainSubstrate,
|
|
217
|
-
CrosschainSubstrateLib,
|
|
218
|
-
CrosschainSubstrateType,
|
|
219
|
-
CrosschainSupplyFuse,
|
|
220
235
|
ERC4626SupplyFuse,
|
|
221
236
|
EulerSwapDynamicParams,
|
|
222
237
|
EulerSwapInitialState,
|
|
@@ -261,13 +276,8 @@ from ipor_fusion.fuses import (
|
|
|
261
276
|
RamsesV2CollectFuse,
|
|
262
277
|
RamsesV2ModifyPositionFuse,
|
|
263
278
|
RamsesV2NewPositionFuse,
|
|
264
|
-
SendParams,
|
|
265
279
|
SettlementMode,
|
|
266
280
|
StakeFuse,
|
|
267
|
-
StargateCrosschainCommandFuse,
|
|
268
|
-
StargateCrosschainCommandType,
|
|
269
|
-
StargateCrosschainSupplyFuse,
|
|
270
|
-
StargateSendParams,
|
|
271
281
|
TimeInForce,
|
|
272
282
|
UniswapV3ClosePositionEvent,
|
|
273
283
|
UniswapV3CollectFuse,
|
|
@@ -279,7 +289,6 @@ from ipor_fusion.fuses import (
|
|
|
279
289
|
UniversalTokenSwapperAbi,
|
|
280
290
|
UniversalTokenSwapperFuse,
|
|
281
291
|
UniversalTokenSwapperSubstrates,
|
|
282
|
-
crosschain_market_id,
|
|
283
292
|
decode_hypercore_event,
|
|
284
293
|
euler_substrate,
|
|
285
294
|
find_hypercore_events,
|
|
@@ -741,6 +750,13 @@ __all__ = [
|
|
|
741
750
|
"decode_custom_error",
|
|
742
751
|
"get_revert_reason",
|
|
743
752
|
"register_custom_errors",
|
|
753
|
+
"CacheField",
|
|
754
|
+
"CacheKey",
|
|
755
|
+
"ContractCache",
|
|
756
|
+
"FileContractCache",
|
|
757
|
+
"NullCache",
|
|
758
|
+
"cache_key",
|
|
759
|
+
"default_contract_cache",
|
|
744
760
|
"get_contract_name",
|
|
745
761
|
"get_deployment_tx",
|
|
746
762
|
"CHAIN_NAMES",
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Caches of immutable per-contract chain facts: contract names, token symbols
|
|
2
|
+
and decimals, deployment blocks. A `ContractCache` is injected through
|
|
3
|
+
`Web3Context(cache=...)` (and `get_contract_name(cache=...)`); the default is
|
|
4
|
+
`FileContractCache` at ``$XDG_CACHE_HOME/ipor-fusion/contract_cache.json``,
|
|
5
|
+
and `NullCache` disables caching.
|
|
6
|
+
|
|
7
|
+
A cache is an optimization, never a source of failure: the SDK reads and
|
|
8
|
+
writes it through `cache_get` / `cache_put`, which log and swallow any error a
|
|
9
|
+
cache raises, and `FileContractCache` itself degrades to an in-memory map when
|
|
10
|
+
its file is corrupt, unreadable or unwritable."""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
import tempfile
|
|
18
|
+
import threading
|
|
19
|
+
from collections.abc import Iterable, Mapping
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Literal, NamedTuple, Protocol, cast, get_args
|
|
22
|
+
|
|
23
|
+
from eth_typing import HexAddress
|
|
24
|
+
|
|
25
|
+
from ipor_fusion.types import ChainId, parse_hex_address
|
|
26
|
+
|
|
27
|
+
_logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
CacheField = Literal[
|
|
30
|
+
"name", "symbol", "decimals", "deployment_block", "deployment_timestamp"
|
|
31
|
+
]
|
|
32
|
+
_FIELDS: frozenset[str] = frozenset(get_args(CacheField))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CacheKey(NamedTuple):
|
|
36
|
+
"""One fact about the contract at ``address`` on ``chain_id``. Build it
|
|
37
|
+
with `cache_key`, which normalizes the address to lowercase."""
|
|
38
|
+
|
|
39
|
+
chain_id: ChainId
|
|
40
|
+
address: HexAddress
|
|
41
|
+
field: CacheField
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def cache_key(chain_id: ChainId, address: HexAddress, field: CacheField) -> CacheKey:
|
|
45
|
+
return CacheKey(chain_id, parse_hex_address(address), field)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ContractCache(Protocol):
|
|
49
|
+
"""Immutable facts keyed by chain, address and field, as strings. Store
|
|
50
|
+
only what cannot change for that key; absence (no code, no answer) is
|
|
51
|
+
never stored."""
|
|
52
|
+
|
|
53
|
+
def get(self, keys: Iterable[CacheKey]) -> dict[CacheKey, str]:
|
|
54
|
+
"""The cached values of ``keys``; a miss is absent from the result."""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
def put(self, entries: Mapping[CacheKey, str]) -> None: ...
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class NullCache:
|
|
61
|
+
"""A `ContractCache` that stores nothing: every read goes to the chain or
|
|
62
|
+
the explorer."""
|
|
63
|
+
|
|
64
|
+
def get(self, keys: Iterable[CacheKey]) -> dict[CacheKey, str]:
|
|
65
|
+
return {}
|
|
66
|
+
|
|
67
|
+
def put(self, entries: Mapping[CacheKey, str]) -> None:
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _encode(key: CacheKey) -> str:
|
|
72
|
+
return f"{key.chain_id}:{key.address}:{key.field}"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _decode(raw: str) -> CacheKey | None:
|
|
76
|
+
"""The key of one stored entry, ``None`` for an entry that cannot be
|
|
77
|
+
attributed to a chain, an address and a field. A ``<chain>:<address>`` entry is a contract
|
|
78
|
+
name (the 3.x layout); 3.x token entries (``symbol:<address>``) carry no
|
|
79
|
+
chain and are dropped."""
|
|
80
|
+
parts = raw.split(":")
|
|
81
|
+
if len(parts) == 2:
|
|
82
|
+
parts.append("name")
|
|
83
|
+
if len(parts) != 3 or not parts[0].isdigit() or parts[2] not in _FIELDS:
|
|
84
|
+
return None
|
|
85
|
+
chain, address, field = parts
|
|
86
|
+
try:
|
|
87
|
+
return cache_key(
|
|
88
|
+
ChainId(int(chain)), parse_hex_address(address), cast(CacheField, field)
|
|
89
|
+
)
|
|
90
|
+
except ValueError:
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class FileContractCache:
|
|
95
|
+
"""A `ContractCache` in one JSON file, loaded once per instance.
|
|
96
|
+
|
|
97
|
+
A write merges into the file as it is on disk (another process may have
|
|
98
|
+
added entries) and replaces it atomically (a temporary file in the same
|
|
99
|
+
directory, then `os.replace`), so a crash or a concurrent writer never
|
|
100
|
+
leaves a torn file; concurrent writers may drop each other's latest entry,
|
|
101
|
+
which is refetched later. A corrupt or unreadable file reads as empty and
|
|
102
|
+
is rewritten by the next write; a failed write is logged and the entry
|
|
103
|
+
kept in memory only."""
|
|
104
|
+
|
|
105
|
+
def __init__(self, path: Path) -> None:
|
|
106
|
+
self.path = path
|
|
107
|
+
self._lock = threading.Lock()
|
|
108
|
+
self._entries: dict[CacheKey, str] | None = None
|
|
109
|
+
|
|
110
|
+
def get(self, keys: Iterable[CacheKey]) -> dict[CacheKey, str]:
|
|
111
|
+
with self._lock:
|
|
112
|
+
entries = self._loaded()
|
|
113
|
+
return {key: entries[key] for key in keys if key in entries}
|
|
114
|
+
|
|
115
|
+
def put(self, entries: Mapping[CacheKey, str]) -> None:
|
|
116
|
+
if not entries:
|
|
117
|
+
return
|
|
118
|
+
with self._lock:
|
|
119
|
+
merged = self._read() | (self._entries or {}) | dict(entries)
|
|
120
|
+
self._entries = merged
|
|
121
|
+
try:
|
|
122
|
+
self._write(merged)
|
|
123
|
+
except OSError as exc:
|
|
124
|
+
_logger.warning("contract cache %s not written: %s", self.path, exc)
|
|
125
|
+
|
|
126
|
+
def _loaded(self) -> dict[CacheKey, str]:
|
|
127
|
+
if self._entries is None:
|
|
128
|
+
self._entries = self._read()
|
|
129
|
+
return self._entries
|
|
130
|
+
|
|
131
|
+
def _read(self) -> dict[CacheKey, str]:
|
|
132
|
+
try:
|
|
133
|
+
raw = json.loads(self.path.read_text(encoding="utf-8"))
|
|
134
|
+
except FileNotFoundError:
|
|
135
|
+
return {}
|
|
136
|
+
except (OSError, ValueError) as exc:
|
|
137
|
+
_logger.warning("contract cache %s unreadable, ignored: %s", self.path, exc)
|
|
138
|
+
return {}
|
|
139
|
+
if not isinstance(raw, dict):
|
|
140
|
+
_logger.warning(
|
|
141
|
+
"contract cache %s is not a JSON object, ignored", self.path
|
|
142
|
+
)
|
|
143
|
+
return {}
|
|
144
|
+
entries: dict[CacheKey, str] = {}
|
|
145
|
+
for raw_key, value in raw.items():
|
|
146
|
+
key = _decode(raw_key)
|
|
147
|
+
if key is not None and isinstance(value, str):
|
|
148
|
+
# A canonical entry wins over a 3.x one spelled otherwise.
|
|
149
|
+
if key not in entries or _encode(key) == raw_key:
|
|
150
|
+
entries[key] = value
|
|
151
|
+
return entries
|
|
152
|
+
|
|
153
|
+
def _write(self, entries: dict[CacheKey, str]) -> None:
|
|
154
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
155
|
+
payload = json.dumps(
|
|
156
|
+
{_encode(key): entries[key] for key in sorted(entries)}, indent=2
|
|
157
|
+
)
|
|
158
|
+
fd, tmp = tempfile.mkstemp(
|
|
159
|
+
dir=self.path.parent, prefix=f".{self.path.name}.", suffix=".tmp"
|
|
160
|
+
)
|
|
161
|
+
try:
|
|
162
|
+
with os.fdopen(fd, "w", encoding="utf-8") as fh:
|
|
163
|
+
fh.write(payload)
|
|
164
|
+
os.replace(tmp, self.path)
|
|
165
|
+
except BaseException:
|
|
166
|
+
Path(tmp).unlink(missing_ok=True)
|
|
167
|
+
raise
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def default_cache_path() -> Path:
|
|
171
|
+
"""``$XDG_CACHE_HOME/ipor-fusion/contract_cache.json``
|
|
172
|
+
(``~/.cache/...`` when unset), resolved at call time."""
|
|
173
|
+
home = Path(os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache"))
|
|
174
|
+
return home / "ipor-fusion" / "contract_cache.json"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
_defaults: dict[Path, FileContractCache] = {}
|
|
178
|
+
_defaults_lock = threading.Lock()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def default_contract_cache() -> FileContractCache:
|
|
182
|
+
"""The process-wide `FileContractCache` at `default_cache_path`."""
|
|
183
|
+
path = default_cache_path()
|
|
184
|
+
with _defaults_lock:
|
|
185
|
+
if (cache := _defaults.get(path)) is None:
|
|
186
|
+
cache = _defaults[path] = FileContractCache(path)
|
|
187
|
+
return cache
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def cache_get(cache: ContractCache, keys: Iterable[CacheKey]) -> dict[CacheKey, str]:
|
|
191
|
+
"""``cache.get(keys)``; a cache that raises reads as all misses."""
|
|
192
|
+
try:
|
|
193
|
+
return cache.get(keys)
|
|
194
|
+
except Exception as exc:
|
|
195
|
+
_logger.warning("contract cache read failed, ignored: %s", exc)
|
|
196
|
+
return {}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def cache_put(cache: ContractCache, entries: Mapping[CacheKey, str]) -> None:
|
|
200
|
+
"""``cache.put(entries)``; a cache that raises loses the entries."""
|
|
201
|
+
try:
|
|
202
|
+
cache.put(entries)
|
|
203
|
+
except Exception as exc:
|
|
204
|
+
_logger.warning("contract cache write failed, ignored: %s", exc)
|