ipor-fusion 4.0.0.dev93__tar.gz → 4.0.0.dev103__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.dev93 → ipor_fusion-4.0.0.dev103}/AGENTS.md +32 -14
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/MIGRATION_4.md +92 -28
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/PKG-INFO +16 -13
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/README.md +15 -12
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/examples/CONTRIBUTING.md +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/examples/README.md +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/examples/advanced_euler_v2_credit_market_base.py +17 -17
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/examples/advanced_euler_v2_looping_arbitrum.py +12 -15
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/examples/composed_hypercore_flow_arbitrum_hyperevm.py +23 -25
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/examples/crosschain_ccip_usdc_arbitrum_hyperevm.py +4 -4
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/examples/external_state_margin_leg_base.py +21 -21
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/examples/simple_aave_v3_supply_base.py +13 -13
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/pyproject.toml +4 -2
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/scripts/crosschain_readiness.py +22 -21
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/skills/ipor-deploy-vault/SKILL.md +10 -10
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/market_cmd.py +3 -3
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/vault_cmd.py +6 -6
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/access.py +26 -24
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/contract.py +37 -64
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/erc20.py +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/external_state_executor.py +30 -53
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/multicall.py +37 -22
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/oracle.py +16 -13
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/plasma_vault.py +35 -30
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/rewards_manager.py +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/simulation.py +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/withdraw_manager.py +16 -11
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/ccip/chainlink.py +10 -12
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/ccip/contracts.py +3 -3
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/ccip/lane.py +22 -14
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/ccip/transport.py +4 -9
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/discovery.py +17 -18
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/lane.py +26 -11
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/stargate/contracts.py +2 -2
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/stargate/lane.py +7 -6
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/stargate/transport.py +4 -9
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/transport.py +2 -7
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/errors.py +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/guide/architecture.md +4 -4
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/guide/invariants.md +3 -3
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/guide/quickstart.md +7 -7
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/mcp/server.py +6 -6
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/aave_v3.py +10 -11
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/hypercore.py +6 -10
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/hypercore_outcome.py +5 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/lending_health.py +9 -7
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/morpho.py +21 -18
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/oracle_mapping.py +4 -4
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/oracle_mapping_reader.py +24 -25
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/unpriceable.py +4 -6
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/vault_data.py +16 -17
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/vault_erc20.py +8 -4
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/vault_managers.py +6 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/vault_positions.py +8 -10
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/vault_reads.py +5 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_crosschain_lifecycle.py +10 -10
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_crosschain_recovery.py +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_euler_v2.py +14 -18
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_multicall.py +2 -2
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_access_manager.py +24 -23
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_commands.py +20 -20
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_helpers.py +11 -12
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_lending_health.py +7 -7
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_pending_requests_scan.py +3 -3
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_role_accounts.py +9 -9
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_vault_info_golden.py +4 -4
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_vault_predates_getters.py +2 -2
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_contract_call.py +15 -21
- ipor_fusion-4.0.0.dev103/tests/test_contract_encoder.py +158 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_ccip_events.py +2 -2
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_ccip_indexer.py +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_chainlink.py +15 -13
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_contracts.py +28 -27
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_lane.py +125 -20
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_readiness.py +11 -13
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_transport.py +4 -8
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_erc20.py +9 -9
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_examples_vaults.py +2 -2
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_external_state_executor.py +191 -187
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_fee_manager.py +12 -12
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_hypercore_deployment.py +5 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_hypercore_flow.py +3 -3
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_hypercore_live.py +17 -15
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_hypercore_readers.py +7 -9
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_hypercore_simulation.py +2 -2
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_mcp_server.py +12 -10
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_multicall.py +48 -3
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_oracle.py +65 -23
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_oracle_mapping.py +3 -3
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_plasma_vault.py +62 -60
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_reader_encoding.py +22 -22
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_rewards_manager.py +12 -12
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_borrow_aave_v3_mainnet.py +11 -15
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_claim_merkl_wrapper_base.py +8 -12
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_claim_morpho_rewards.py +3 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_crosschain_hypercore.py +4 -6
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_crosschain_hyperevm.py +63 -62
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_euler_v2_batch_base.py +5 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_euler_v2_credit_market_base.py +4 -8
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_euler_v2_swap_deploy_base.py +6 -6
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_euler_v2_swap_reconfigure_base.py +4 -4
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_euler_v2_swap_registry_base.py +5 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_external_state_executor_base.py +17 -7
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_fusion_factory_clone_base.py +12 -16
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_looping_aave_v3_base.py +12 -14
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_looping_morpho_blue_base.py +9 -11
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_morpho_blue.py +5 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_pilot_v3.py +34 -42
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_pilot_v4_uniswap.py +26 -38
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_pilot_v5_ramses.py +18 -26
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_pilot_v5_scheduled.py +30 -42
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_plasma_vault_setters_base.py +4 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_readers_integration.py +26 -24
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_release_funds.py +11 -15
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_swap_on_base.py +9 -11
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_vault_from_scratch_base.py +5 -5
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_solidity_mirrors.py +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_unpriceable_tokens.py +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_vault_info_onchain_base_no_withdraw_manager.py +3 -3
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_vault_simulator_blocks.py +1 -1
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_web3_context.py +11 -11
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_withdraw_manager.py +25 -27
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/typed_client/consumer.py +69 -28
- ipor_fusion-4.0.0.dev103/tests/typed_client/rejected.py +51 -0
- ipor_fusion-4.0.0.dev93/tests/test_contract_encoder.py +0 -118
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.claude-plugin/marketplace.json +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.claude-plugin/plugin.json +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.env.example +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.gemini/settings.json +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.git-blame-ignore-revs +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.github/workflows/cd.yml +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.github/workflows/ci.yml +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.github/workflows/commit-lint.yml +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.github/workflows/notify-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.github/workflows/python-build.yml +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.github/workflows/python-publish.yml +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.github/workflows/release.yml +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.github/workflows/report-slack.yml +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.gitignore +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.mcp.json +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.pre-commit-config.yaml +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/.python-version +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/CHANGELOG.md +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/CLAUDE.md +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/CODEOWNERS +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/LICENSE +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/build.sh +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/pytest.ini +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/scripts/commit-check.sh +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/about.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cache.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/chains.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/changelog_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/config_cmd.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/config_store.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/main.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/options.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/oracle_mapping_view.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/vault_health.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/vault_info_view.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/vault_markets_view.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/cli/vault_rendering.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/config/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/config/roles.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/address.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/context.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/fee_manager.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/fuse.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/fuse_action.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/fusion_factory.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/core/logs.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/ccip/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/ccip/codec.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/ccip/events.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/ccip/indexer.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/ccip/quote.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/contracts.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/errors.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/logs.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/messages.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/simulation.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/stargate/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/crosschain/stargate/layerzero.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/dependency_graph.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/entry_cli.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/entry_mcp.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/explorer.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/field_docs.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/aave_v3.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/async_action.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/base.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/crosschain/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/crosschain/base.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/crosschain/ccip.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/crosschain/stargate.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/erc4626.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/euler_v2.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/events.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/external_state.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/fluid_instadapp.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/gearbox_v3.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/merkl.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/morpho.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/fuses/universal.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/guide/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/guide/glossary.md +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/market_ids.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/market_info.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/mcp/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/mcp/guide.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/mcp/models.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/morpho_api.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/py.typed +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/__init__.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/aave_v4.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/compound_v3.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/oracle_mapping_nodes.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/position_manager.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/ramses_v2.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/readers/uniswap_v3.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/substrates.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/types.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/validation.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/src/ipor_fusion/vault_info.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/UniswapV3UniversalRouter.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_foundry.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_guide.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_hypercore_model.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_hypercore_shadow.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_simulate.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/_validation.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/addresses.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/conftest.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/constants.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/fixtures/hypercore_hip3_flow.json +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/fixtures/hypercore_market55.json +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/fixtures/vault_info_golden/clean.json +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/fixtures/vault_info_golden/clean.txt +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/fixtures/vault_info_golden/rich.json +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/fixtures/vault_info_golden/rich.txt +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_about.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cache.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_chains.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_changelog.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_config_store.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_market_morpho_blue.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_oracle_mapping.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_cli_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_ccip_quote.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_errors.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_layering.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_crosschain_readiness_report.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_error_hierarchy.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_examples_composed_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_examples_euler_looping_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_explorer.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_external_state_events.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_fuse_encoding.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_fusion_instance.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_guide.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_hypercore_events.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_hypercore_outcome.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_logs.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_mcp_hypercore.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_mcp_models.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_mcp_vault_validate.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_public_api.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_readiness_selection.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_revert_decoding.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_roles.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_sdk_layering.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_crosschain_canary.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_crosschain_lifecycle.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_examples_arbitrum.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_examples_base.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_simulate_examples_crosschain.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_solidity_compiler.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_substrates.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_types_and_events.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_validation.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_vault_dep_graph.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/tests/test_vault_info_onchain_base.py +0 -0
- {ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/uv.lock +0 -0
|
@@ -216,8 +216,8 @@ the lifecycle matrix follows that.
|
|
|
216
216
|
time and request budget, and raises `LogScanError` rather than return partial logs;
|
|
217
217
|
`LogSource`: an optional index `Web3Context.get_logs` asks first for a topic0-only
|
|
218
218
|
filter, falling back to the scan on `LogSourceUnavailable`;
|
|
219
|
-
prefer a storage or view read over a scan, as `
|
|
220
|
-
`
|
|
219
|
+
prefer a storage or view read over a scan, as `read_balance_fuses` and
|
|
220
|
+
`read_withdraw_manager_address` do), `plasma_vault`, `access`, `withdraw_manager`, `rewards_manager`, `fee_manager`,
|
|
221
221
|
`simulation` (`VaultSimulator`, eth_simulateV1, `erc20_balance_slot` and ERC-20 balance
|
|
222
222
|
overrides), `oracle`, `fusion_factory`,
|
|
223
223
|
`external_state_executor` (NAV marks for market 50), `erc20`, `fuse_action`
|
|
@@ -279,18 +279,36 @@ Two patterns explain most of the code; know both before writing SDK code
|
|
|
279
279
|
or examples.
|
|
280
280
|
|
|
281
281
|
- `Call[T]` (`core/contract.py`): every `ContractWrapper` method (one per
|
|
282
|
-
Solidity function) returns a `Call` instead of executing.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
`
|
|
290
|
-
|
|
282
|
+
Solidity function) returns a `Call` instead of executing. Construction and
|
|
283
|
+
Call building are offline: a wrapper is its address (plus genuine contract
|
|
284
|
+
parameters such as a legacy oracle's decimals) and holds no `Web3Context`;
|
|
285
|
+
a `Call` holds `to`, `data`, output types and decoder, never a context.
|
|
286
|
+
Execution takes the context as a required argument: `.call(ctx)` runs
|
|
287
|
+
`eth_call` (at the context's, possibly pinned, block; `block=` overrides)
|
|
288
|
+
and decodes to `T`; `.send(ctx)` signs and submits, returning a `TxReceipt`;
|
|
289
|
+
`.build_transaction(ctx)` / `.estimate_gas(ctx)` preview without signing
|
|
290
|
+
(default `"latest"`). `.calldata`, `.decode(raw)` and `.map(f)` stay
|
|
291
|
+
offline. A `Call` without `output_types` (a write) raises on `.call(ctx)`.
|
|
292
|
+
The same `Call` feeds `VaultSimulator.observe`, so reads, sends and
|
|
293
|
+
simulations share one definition.
|
|
294
|
+
- A wrapper method that must execute to answer (storage, a log scan, a read
|
|
295
|
+
that depends on another read) is named `read_*` and takes `ctx` first:
|
|
296
|
+
`PlasmaVault.read_balance_fuses(ctx)`, `AccessManager.read_all_role_accounts(ctx)`,
|
|
297
|
+
`MorphoReader.read_rates(ctx, id)`. Decide by the I/O a method performs, not
|
|
298
|
+
by the class name. Resolvers that probe the chain to pick a wrapper
|
|
299
|
+
(`price_oracle_middleware(ctx, a)`, `resolve_access_manager`,
|
|
300
|
+
`ExternalStateExecutor.for_vault`) return context-free wrappers. Executors
|
|
301
|
+
and orchestrators own their contexts: `Multicall3(ctx)`, `VaultSimulator`,
|
|
302
|
+
`CrosschainSimulator`, `OracleMappingReader(ctx, oracle)` and
|
|
303
|
+
`CrosschainLane` (`hub_ctx` for the executor, `spoke_ctx` for the
|
|
304
|
+
dispatcher at the same address: run a lane's Calls on the matching one).
|
|
305
|
+
A chain-dependent decode takes the chain explicitly
|
|
306
|
+
(`ExternalStateExecutor.pending_proposals(account, chain_id=)`); a decoder
|
|
307
|
+
never consults the executing context.
|
|
291
308
|
- Batch independent reads: `Multicall3(ctx).aggregate(calls)` (raises as
|
|
292
|
-
`.call()` would) or `.try_aggregate(calls)` (``None`` per failed read) runs
|
|
293
|
-
many `Call`s in one `eth_call` at one block
|
|
309
|
+
`.call(ctx)` would) or `.try_aggregate(calls)` (``None`` per failed read) runs
|
|
310
|
+
many `Call`s in one `eth_call` at one block; every batch, re-run and
|
|
311
|
+
no-Multicall fallback runs on its `ctx`. A loop of `.call(ctx)`s, or a
|
|
294
312
|
thread pool of them, costs one round trip each; RPC latency, not decoding,
|
|
295
313
|
is what makes the vault tooling slow.
|
|
296
314
|
- Fuses (`fuses/`) are stateless encoders: a method returns an immutable
|
|
@@ -299,7 +317,7 @@ or examples.
|
|
|
299
317
|
helpers raise before encoding. `PlasmaVault.execute([actions])` runs the
|
|
300
318
|
batch atomically.
|
|
301
319
|
- Pin reads, never mutate a shared context: `ctx.at_block(n)` is a copy whose
|
|
302
|
-
reads (and `get_logs` upper bound) default to block ``n``; `Call.call(block=n)`
|
|
320
|
+
reads (and `get_logs` upper bound) default to block ``n``; `Call.call(ctx, block=n)`
|
|
303
321
|
pins one read.
|
|
304
322
|
- Simulate before sending: `VaultSimulator(web3, vault=, alpha=)` batches
|
|
305
323
|
`execute` and `observe` calls into one `eth_simulateV1` round trip, across
|
|
@@ -80,7 +80,7 @@ chain is always the context's:
|
|
|
80
80
|
market_substrates, aave_pools=None)` take the chain from `ctx`.
|
|
81
81
|
- `Web3Context(web3, chain_id, *, ...)` and `Web3Context.from_url(url, *, ...)` take
|
|
82
82
|
their options by keyword. New: `Web3Context.at_block(block)` (a pinned copy) and
|
|
83
|
-
`Call.call(ctx
|
|
83
|
+
`Call.call(ctx, *, block=None)` (see [Context-free wrappers](#context-free-wrappers)). `Web3Context.get_logs(to_block=None)` ends at
|
|
84
84
|
the context's default block and `Web3Context.get_block(block=None)` returns it,
|
|
85
85
|
both instead of ``"latest"``.
|
|
86
86
|
|
|
@@ -113,7 +113,7 @@ The Uniswap V3 / Ramses V2 position events' `version`, `sender` and `recipient`
|
|
|
113
113
|
`quote_ccip_native_fee(block=)` and `OracleMappingReader.asset_source_events` take a
|
|
114
114
|
`BlockIdentifier`.
|
|
115
115
|
|
|
116
|
-
**Address roles.** `PlasmaVault(
|
|
116
|
+
**Address roles.** `PlasmaVault(address)`,
|
|
117
117
|
`validate_vault`, `fetch_deployment_info`, the HyperCore, lending-health and
|
|
118
118
|
oracle-mapping readers, `read_external_state`, `VaultSimulator(vault=)`,
|
|
119
119
|
`CrosschainSimulator`, crosschain discovery, the hub-manager inputs
|
|
@@ -130,7 +130,7 @@ Every fuse constructor, `FuseAction.fuse`, `BalanceFuse.fuse`,
|
|
|
130
130
|
`LaneFuses` and the HyperCore balance-fuse argument take a `FuseAddress`, and
|
|
131
131
|
`get_fuses()`, `get_instant_withdrawal_fuses()`, `get_rewards_fuses()` and
|
|
132
132
|
`VaultData.fuses` / `instant_fuses` / `fuse_markets` keys return it.
|
|
133
|
-
`AccessManager(
|
|
133
|
+
`AccessManager(address)` takes an `AccessManagerAddress`;
|
|
134
134
|
`get_access_manager_address()`, `VaultData.access_manager` and
|
|
135
135
|
`FusionInstance.access_manager` return one. `resolve_access_manager` still takes any
|
|
136
136
|
`ChecksumAddress`: it is what checks that the address is a vault.
|
|
@@ -167,7 +167,7 @@ gone: use `morpho_blue_address(chain_id)`.
|
|
|
167
167
|
`decode_substrate`, `market_name`, `format_market_label` take `MarketId`.
|
|
168
168
|
|
|
169
169
|
**Access.** `AccessManager.has_role`, `grant_role`, `revoke_role`,
|
|
170
|
-
`
|
|
170
|
+
`read_accounts_with_role` take `RoleId | Roles`; `RoleAccount.role_id` is `RoleId`.
|
|
171
171
|
|
|
172
172
|
**Morpho.** `MorphoBlueMarketId` now always carries `0x`; build it with
|
|
173
173
|
`parse_morpho_market_id` instead of `MorphoBlueMarketId("<64 hex>")`. `fetch_market`,
|
|
@@ -214,29 +214,6 @@ are `ProposalHash`; `ExternalStateEvent.transaction_hash` is a `TxHash`.
|
|
|
214
214
|
except below): `chain_id` fields are `ChainId`, `role_id` `RoleId`, `market_id`
|
|
215
215
|
`MarketId`. Calling an MCP tool function directly from Python takes `ChainId`.
|
|
216
216
|
|
|
217
|
-
**Offline wrappers.** `ContractWrapper.encoder()` (and the `PlasmaVault` /
|
|
218
|
-
`AccessManager` overrides) is gone. Every wrapper constructor takes
|
|
219
|
-
`ctx: Web3Context | None`, so build an offline wrapper with the normal
|
|
220
|
-
constructor and an address: `PlasmaVault(None, vault)`, `ERC20(None, token)`,
|
|
221
|
-
`FusionFactory(None, factory)`, `LegacyPriceOracleMiddleware(None, oracle,
|
|
222
|
-
Decimals(8))`. The address is required (there is no zero-address placeholder) and
|
|
223
|
-
subclass parameters stay required, so the wrapper is fully initialized:
|
|
224
|
-
`.encoder()` skipped the constructor, and a legacy oracle built that way failed to
|
|
225
|
-
decode `get_asset_price`. `.calldata` still needs no context; `.call()`, `.send()`,
|
|
226
|
-
`.build_transaction()` and `.estimate_gas()` raise `ValueError` until one is passed
|
|
227
|
-
to them, and a method that reads the chain itself (log scans such as
|
|
228
|
-
`get_balance_fuses` or `get_pending_requests`, `withdraw_manager_address`,
|
|
229
|
-
`Multicall3.aggregate` / `try_aggregate`, the Aave V3 and Morpho position reads)
|
|
230
|
-
raises `ValueError("Web3Context required: <wrapper> at <address> ...")` instead of
|
|
231
|
-
an `AttributeError` on `None`. `ExternalStateExecutor` still refuses the zero
|
|
232
|
-
address for its hash and event readers.
|
|
233
|
-
|
|
234
|
-
| 3.x | 4.0 |
|
|
235
|
-
|---|---|
|
|
236
|
-
| `PlasmaVault.encoder(vault)` | `PlasmaVault(None, vault)` |
|
|
237
|
-
| `ERC20.encoder()` (no address) | `ERC20(None, token)`: pass the real address |
|
|
238
|
-
| `Cls.__new__(Cls)` + `_ctx` / `_address` | `Cls(None, address)` |
|
|
239
|
-
|
|
240
217
|
**Fuse base.** `Fuse`, `StakeFuse` and the shared validators and substrate packers
|
|
241
218
|
live in `ipor_fusion.core.fuse`; `ipor_fusion.fuses.base`, `ipor_fusion.fuses` and
|
|
242
219
|
`ipor_fusion` re-export them, so existing imports keep working. Fuse equality and
|
|
@@ -249,6 +226,93 @@ hash now cover every attribute a fuse stores, not only its class and address (se
|
|
|
249
226
|
`ipor_fusion.cli.vault_cmd` was split into `options`, `vault_info_view`,
|
|
250
227
|
`vault_markets_view` and `oracle_mapping_view` (private names, not API).
|
|
251
228
|
|
|
229
|
+
## Context-free wrappers
|
|
230
|
+
|
|
231
|
+
A contract wrapper is its address (plus genuine contract parameters, such as a legacy
|
|
232
|
+
oracle's decimals) and nothing chain-bound. Construction and `Call` building are
|
|
233
|
+
offline; the `Web3Context` is a required argument where something executes. In 3.x
|
|
234
|
+
the context was stored on the wrapper and copied into every `Call`; that is gone,
|
|
235
|
+
together with `ContractWrapper.encoder()`, `Call.ctx` and the zero-address
|
|
236
|
+
placeholder wrapper.
|
|
237
|
+
|
|
238
|
+
- Build every wrapper from its address: `PlasmaVault(vault)`, `ERC20(token)`,
|
|
239
|
+
`FusionFactory(factory)`, `AccessManager(manager)`,
|
|
240
|
+
`LegacyPriceOracleMiddleware(oracle, Decimals(8))`, the crosschain executor,
|
|
241
|
+
dispatcher and factory wrappers, the Chainlink CCIP wrappers, the readers
|
|
242
|
+
(`MorphoReader(morpho)`, `AaveV3Reader(pool)`, ...). `HyperCoreReader()` takes no
|
|
243
|
+
argument. The nominal address types are unchanged.
|
|
244
|
+
- Execute a `Call` on a context: `.call(ctx, *, block=None)` (reads default to the
|
|
245
|
+
context's, possibly pinned, block), `.send(ctx)`, `.build_transaction(ctx, *,
|
|
246
|
+
block="latest")`, `.estimate_gas(ctx, *, block="latest")`. `ctx` may be passed by
|
|
247
|
+
keyword. Without it Python raises `TypeError` (and pyright reports the call),
|
|
248
|
+
instead of the 3.x `ValueError("Web3Context required ...")`. `.calldata`,
|
|
249
|
+
`.decode(raw)` and `.map(f)` need no context. `Call(...)` no longer takes `ctx=`.
|
|
250
|
+
- A wrapper method that executes to answer (storage, log scans, dependent reads) is
|
|
251
|
+
named `read_*` and takes the context first (table below).
|
|
252
|
+
- Executors and orchestrators keep their contexts: `Multicall3(ctx)` (every batch,
|
|
253
|
+
every re-run of a failed sub-call and the per-call fallback run on its `ctx`, so a
|
|
254
|
+
pinned context keeps them all at its block; it is no longer a `ContractWrapper`,
|
|
255
|
+
`aggregate3(calls)` still returns the raw `Call`), `VaultSimulator`,
|
|
256
|
+
`CrosschainSimulator` and `OracleMappingReader(ctx, oracle)`.
|
|
257
|
+
- Resolvers that probe the chain stay eager and return context-free wrappers:
|
|
258
|
+
`price_oracle_middleware(ctx, oracle)`, `resolve_access_manager(ctx, vault)`,
|
|
259
|
+
`ExternalStateExecutor.for_vault(ctx, vault)`. A resolved oracle carries the ABI
|
|
260
|
+
found on that chain and block; resolve again for another one.
|
|
261
|
+
- Crosschain lanes own both contexts: `CcipLane` / `StargateLane` take `hub_ctx=` and
|
|
262
|
+
`spoke_ctx=` instead of `spoke_chain_id=` (it is `spoke_ctx.chain_id`), and expose
|
|
263
|
+
them as `lane.hub_ctx` / `lane.spoke_ctx`. `needs_attestation`,
|
|
264
|
+
`command_in_flight` and `transfer_in_flight` read on `hub_ctx`. The Calls a lane
|
|
265
|
+
returns are unexecuted, and the executor and dispatcher share one address, so pick
|
|
266
|
+
the context yourself: `lane.get_balance().call(lane.hub_ctx)`,
|
|
267
|
+
`lane.observation().call(lane.spoke_ctx)`. `CrosschainTransport.executor(address)`
|
|
268
|
+
and `.dispatcher(address)` no longer take a context.
|
|
269
|
+
- Chain-bound decoding takes the chain explicitly:
|
|
270
|
+
`ExternalStateExecutor.pending_proposals(account, *, chain_id)`,
|
|
271
|
+
`parse_balance_proposed(log, *, chain_id)` and
|
|
272
|
+
`find_balance_proposed(logs, account=None, *, chain_id)` require it (3.x fell back
|
|
273
|
+
to the wrapper's context). The decoder captures it, so `.call(ctx)`, `Multicall3`
|
|
274
|
+
and `VaultSimulator.observe` stamp the same hash for the same bytes, whatever
|
|
275
|
+
context executes them. Pass `ctx.chain_id` for the chain you read from.
|
|
276
|
+
`pending_proposals` now refuses an executor at the zero address when the Call is
|
|
277
|
+
built, not when it is decoded. `mark_nav` takes the chain from its contexts.
|
|
278
|
+
|
|
279
|
+
| 3.x | 4.0 |
|
|
280
|
+
|---|---|
|
|
281
|
+
| `PlasmaVault(ctx, vault)`, `ERC20(ctx, token)`, any `Wrapper(ctx, address, ...)` | `PlasmaVault(vault)`, `ERC20(token)`, `Wrapper(address, ...)` |
|
|
282
|
+
| `PlasmaVault.encoder(vault)`, `ERC20.encoder()` | `PlasmaVault(vault)`, `ERC20(token)` (the real address) |
|
|
283
|
+
| `LegacyPriceOracleMiddleware(ctx, oracle, decimals)` | `LegacyPriceOracleMiddleware(oracle, decimals)` |
|
|
284
|
+
| `HyperCoreReader(ctx)` | `HyperCoreReader()` |
|
|
285
|
+
| `Call(to=, data=, ..., ctx=ctx)` | `Call(to=, data=, ...)` |
|
|
286
|
+
| `call.call()` / `call.call(block=n)` | `call.call(ctx)` / `call.call(ctx, block=n)` |
|
|
287
|
+
| `call.send()` | `call.send(ctx)` |
|
|
288
|
+
| `call.build_transaction(block=b)`, `call.estimate_gas(block=b)` | `call.build_transaction(ctx, block=b)`, `call.estimate_gas(ctx, block=b)` |
|
|
289
|
+
| `call.ctx` | gone: keep the context yourself |
|
|
290
|
+
| `PlasmaVault.get_balance_fuses()` | `PlasmaVault.read_balance_fuses(ctx)` |
|
|
291
|
+
| `PlasmaVault.withdraw_manager_address()` | `PlasmaVault.read_withdraw_manager_address(ctx)` |
|
|
292
|
+
| `PlasmaVault.price_oracle_address()` | `PlasmaVault.read_price_oracle_address(ctx)` |
|
|
293
|
+
| `AccessManager.owner()` / `owners()` / `atomists()` | `read_owner(ctx)` / `read_owners(ctx)` / `read_atomists(ctx)` |
|
|
294
|
+
| `AccessManager.get_accounts_with_role(role)` | `AccessManager.read_accounts_with_role(ctx, role)` |
|
|
295
|
+
| `AccessManager.get_all_role_accounts()` | `AccessManager.read_all_role_accounts(ctx)` |
|
|
296
|
+
| `PriceOracleMiddleware.get_assets_price_sources()` | `PriceOracleMiddleware.read_assets_price_sources(ctx)` |
|
|
297
|
+
| `WithdrawManager.get_pending_requests(from_block)` | `WithdrawManager.read_pending_requests(ctx, from_block)` |
|
|
298
|
+
| `WithdrawManager.get_pending_requests_info(from_block)` | `WithdrawManager.read_pending_requests_info(ctx, from_block)` |
|
|
299
|
+
| `MorphoReader.position_breakdown(id, user)` | `MorphoReader.read_position_breakdown(ctx, id, user)` |
|
|
300
|
+
| `MorphoReader.require_market(id)` | `MorphoReader.read_existing_market(ctx, id)` |
|
|
301
|
+
| `MorphoReader.rates(id)` | `MorphoReader.read_rates(ctx, id)` |
|
|
302
|
+
| `MorphoReader.rates_from(market, params)` | `MorphoReader.read_rates_from(ctx, market, params)` (it reads the IRM) |
|
|
303
|
+
| `AaveV3Reader.position_breakdown(asset, user)` | `AaveV3Reader.read_position_breakdown(ctx, asset, user)` |
|
|
304
|
+
| `AaveV3FuseReader.pool()` | `AaveV3FuseReader.read_pool(ctx)` |
|
|
305
|
+
| `executor.pending_proposals(account)` | `executor.pending_proposals(account, chain_id=ctx.chain_id)` |
|
|
306
|
+
| `executor.parse_balance_proposed(log)` / `find_balance_proposed(logs)` | `...(log, chain_id=...)` / `...(logs, chain_id=...)` |
|
|
307
|
+
| `CcipLane(executor=, dispatcher=, spoke_chain_id=, ...)` | `CcipLane(hub_ctx=, spoke_ctx=, executor=, dispatcher=, ...)` (same for `StargateLane`) |
|
|
308
|
+
| `transport.executor(ctx, address)` / `transport.dispatcher(ctx, address)` | `transport.executor(address)` / `transport.dispatcher(address)` |
|
|
309
|
+
|
|
310
|
+
A service that caches wrappers per chain can now share one wrapper across chains and
|
|
311
|
+
snapshots; what it must not share is a `Call` result decoded with a chain-bound
|
|
312
|
+
argument (`chain_id=`) for another chain. An external signer still needs the real
|
|
313
|
+
target address (`call.to`) and builds the chain-qualified transaction itself, or
|
|
314
|
+
gets it from `call.build_transaction(ctx)`.
|
|
315
|
+
|
|
252
316
|
## Behavior changes
|
|
253
317
|
|
|
254
318
|
- Two fuses are equal (and hash alike) only when they have the same class and the
|
|
@@ -325,7 +389,7 @@ is the wrong one.
|
|
|
325
389
|
executor of a transport. An executor reporting `transportKind()` `UNDEFINED` is now
|
|
326
390
|
rejected by `detect_transport` instead of being returned. A transport kind passed in
|
|
327
391
|
that has no lane is still a `ValueError`.
|
|
328
|
-
- `Call.call()` and `Call.decode()` on a write-only `Call` raise `ValueError`, not
|
|
392
|
+
- `Call.call(ctx)` and `Call.decode()` on a write-only `Call` raise `ValueError`, not
|
|
329
393
|
`RuntimeError`. `CrosschainSimulator.relay` raises `ValueError`, not
|
|
330
394
|
`RuntimeError`, for a message to a chain that was not added.
|
|
331
395
|
- `VaultSimulator.run` (provider error from `eth_simulateV1`, or a result count that
|
|
@@ -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.dev103
|
|
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
|
|
@@ -141,8 +141,8 @@ ctx = Web3Context.from_url(
|
|
|
141
141
|
private_key="0x...",
|
|
142
142
|
)
|
|
143
143
|
|
|
144
|
-
# 2. Wrap the PlasmaVault contract
|
|
145
|
-
vault = PlasmaVault(
|
|
144
|
+
# 2. Wrap the PlasmaVault contract (an address only; contexts are passed at execution)
|
|
145
|
+
vault = PlasmaVault(Web3.to_checksum_address("0xVAULT_ADDRESS"))
|
|
146
146
|
|
|
147
147
|
# 3. Build a fuse action (e.g. supply USDC to Aave V3)
|
|
148
148
|
fuse = AaveV3SupplyFuse(Web3.to_checksum_address("0xFUSE_ADDRESS"))
|
|
@@ -151,25 +151,28 @@ action = fuse.supply(
|
|
|
151
151
|
amount=1_000_000, # 1 USDC (6 decimals)
|
|
152
152
|
)
|
|
153
153
|
|
|
154
|
-
# 4. Execute on-chain (execute() returns a Call; .send() signs and submits it)
|
|
155
|
-
receipt = vault.execute([action]).send()
|
|
154
|
+
# 4. Execute on-chain (execute() returns a Call; .send(ctx) signs and submits it)
|
|
155
|
+
receipt = vault.execute([action]).send(ctx)
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
Fuse, factory and manager addresses per chain are published in
|
|
159
159
|
[ipor-abi](https://github.com/IPOR-Labs/ipor-abi) (`mainnet/mainnet-<chain>-fusion/addresses.json`).
|
|
160
|
-
A fuse must also be registered on the vault; `vault.get_fuses().call()` lists the registered ones.
|
|
160
|
+
A fuse must also be registered on the vault; `vault.get_fuses().call(ctx)` lists the registered ones.
|
|
161
161
|
|
|
162
162
|
Amounts are raw on-chain integers (`Amount`, `Shares` in `ipor_fusion.types`); the SDK never scales by decimals.
|
|
163
163
|
|
|
164
164
|
### Read, send, or simulate
|
|
165
165
|
|
|
166
|
-
Every wrapper method returns a `Call` instead of executing.
|
|
166
|
+
Every wrapper method returns a `Call` instead of executing. Wrappers and `Call`s hold no
|
|
167
|
+
`Web3Context`: the context is a required argument wherever something runs, and methods that
|
|
168
|
+
must read the chain to answer are named `read_*` and take it first
|
|
169
|
+
(`vault.read_balance_fuses(ctx)`). The same `Call` powers all three modes:
|
|
167
170
|
|
|
168
171
|
```python
|
|
169
172
|
from ipor_fusion import VaultSimulator
|
|
170
173
|
|
|
171
|
-
total = vault.total_assets().call() # eth_call -> Amount
|
|
172
|
-
receipt = vault.execute([action]).send() # signed tx -> TxReceipt
|
|
174
|
+
total = vault.total_assets().call(ctx) # eth_call -> Amount
|
|
175
|
+
receipt = vault.execute([action]).send(ctx) # signed tx -> TxReceipt
|
|
173
176
|
payload = vault.execute([action]).calldata # raw bytes for an external signer
|
|
174
177
|
|
|
175
178
|
# Simulate first via eth_simulateV1 (no local node); alpha is the account allowed to call execute()
|
|
@@ -312,7 +315,7 @@ Reverts on the deploy-and-configure path, keyed by selector so a failed transact
|
|
|
312
315
|
| `0x9996b315` | `AddressEmptyCode(address)` | `execute()` touched a market with no balance fuse | `add_balance_fuse(market_id, balance_fuse)` before the first `execute` on that market |
|
|
313
316
|
| `0x4bf4de4e` | `HandlerNotFound()` | A protocol called back into the vault during `execute()` (a flash loan) and no handler is registered for its address and selector | `update_callback_handler(handler, sender, selector)` before the first `execute` that triggers the callback |
|
|
314
317
|
| `0x068ca9d8` | `AccessManagedUnauthorized(address)` | The caller lacks the role the function requires: `FUSE_MANAGER` for `add_fuses`, `grant_market_substrates`, `add_balance_fuse`, `update_callback_handler`; `ALPHA` for `execute`; `WHITELIST` for `deposit` and `mint` on a private vault | `AccessManager.grant_role(role, account, 0)` from the role's admin (`OWNER` grants `ATOMIST`, `ATOMIST` grants the rest); for a reverting `deposit`, whitelist the depositor or convert the vault to public |
|
|
315
|
-
| `ValueError: Private key required for sending transactions` | SDK, before any transaction | `.send()` on a `Web3Context` without a key | `Web3Context(w3, chain_id, signer=..., private_key=...)` or `Web3Context.from_url(url, private_key=...)` |
|
|
318
|
+
| `ValueError: Private key required for sending transactions` | SDK, before any transaction | `.send(ctx)` on a `Web3Context` without a key | `Web3Context(w3, chain_id, signer=..., private_key=...)` or `Web3Context.from_url(url, private_key=...)` |
|
|
316
319
|
|
|
317
320
|
`clone()` grants the owner only `Roles.OWNER_ROLE` (1). OWNER grants `Roles.ATOMIST_ROLE` (100), which administers `Roles.FUSE_MANAGER_ROLE` (300, configuration), `Roles.ALPHA_ROLE` (200, `execute`) and `Roles.WHITELIST_ROLE` (800, `deposit` on a private vault).
|
|
318
321
|
|
|
@@ -398,10 +401,10 @@ The SDK uses a **fuse adapter pattern**:
|
|
|
398
401
|
- **Fuses** encode protocol-specific calls into `FuseAction` objects (pure calldata, no state)
|
|
399
402
|
- **PlasmaVault** batches and executes `FuseAction` sequences on-chain via `execute()`; a batch is atomic
|
|
400
403
|
- **Web3Context** manages provider connections, signing, and transaction dispatch
|
|
401
|
-
- **Call** is the lazy result of every wrapper method: `.call()`, `.send()`, `.calldata`, or feed it to `VaultSimulator`
|
|
404
|
+
- **Call** is the lazy result of every wrapper method: `.call(ctx)`, `.send(ctx)`, `.calldata`, or feed it to `VaultSimulator`
|
|
402
405
|
|
|
403
406
|
```
|
|
404
|
-
Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call --> .send() / simulate
|
|
407
|
+
Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call --> .send(ctx) / simulate
|
|
405
408
|
```
|
|
406
409
|
|
|
407
410
|
### Core modules (`ipor_fusion.core`)
|
|
@@ -409,7 +412,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
|
|
|
409
412
|
| Module | Purpose |
|
|
410
413
|
|--------|---------|
|
|
411
414
|
| `Web3Context` | Provider connection, signing, tx dispatch, gas estimation |
|
|
412
|
-
| `Call` | Pre-encoded contract call; `.call()`, `.send()`, `.calldata`, `.build_transaction()` |
|
|
415
|
+
| `Call` | Pre-encoded contract call; `.call(ctx)`, `.send(ctx)`, `.calldata`, `.build_transaction(ctx)` |
|
|
413
416
|
| `PlasmaVault` | ERC-4626 vault — execute, deposit, withdraw, fuse and market configuration |
|
|
414
417
|
| `VaultSimulator` | Batch `execute` + reads through `eth_simulateV1`, multi-block, no local node |
|
|
415
418
|
| `FusionFactory` | Deploy a new vault (`clone`, `clone_supervised`) |
|
|
@@ -93,8 +93,8 @@ ctx = Web3Context.from_url(
|
|
|
93
93
|
private_key="0x...",
|
|
94
94
|
)
|
|
95
95
|
|
|
96
|
-
# 2. Wrap the PlasmaVault contract
|
|
97
|
-
vault = PlasmaVault(
|
|
96
|
+
# 2. Wrap the PlasmaVault contract (an address only; contexts are passed at execution)
|
|
97
|
+
vault = PlasmaVault(Web3.to_checksum_address("0xVAULT_ADDRESS"))
|
|
98
98
|
|
|
99
99
|
# 3. Build a fuse action (e.g. supply USDC to Aave V3)
|
|
100
100
|
fuse = AaveV3SupplyFuse(Web3.to_checksum_address("0xFUSE_ADDRESS"))
|
|
@@ -103,25 +103,28 @@ action = fuse.supply(
|
|
|
103
103
|
amount=1_000_000, # 1 USDC (6 decimals)
|
|
104
104
|
)
|
|
105
105
|
|
|
106
|
-
# 4. Execute on-chain (execute() returns a Call; .send() signs and submits it)
|
|
107
|
-
receipt = vault.execute([action]).send()
|
|
106
|
+
# 4. Execute on-chain (execute() returns a Call; .send(ctx) signs and submits it)
|
|
107
|
+
receipt = vault.execute([action]).send(ctx)
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
Fuse, factory and manager addresses per chain are published in
|
|
111
111
|
[ipor-abi](https://github.com/IPOR-Labs/ipor-abi) (`mainnet/mainnet-<chain>-fusion/addresses.json`).
|
|
112
|
-
A fuse must also be registered on the vault; `vault.get_fuses().call()` lists the registered ones.
|
|
112
|
+
A fuse must also be registered on the vault; `vault.get_fuses().call(ctx)` lists the registered ones.
|
|
113
113
|
|
|
114
114
|
Amounts are raw on-chain integers (`Amount`, `Shares` in `ipor_fusion.types`); the SDK never scales by decimals.
|
|
115
115
|
|
|
116
116
|
### Read, send, or simulate
|
|
117
117
|
|
|
118
|
-
Every wrapper method returns a `Call` instead of executing.
|
|
118
|
+
Every wrapper method returns a `Call` instead of executing. Wrappers and `Call`s hold no
|
|
119
|
+
`Web3Context`: the context is a required argument wherever something runs, and methods that
|
|
120
|
+
must read the chain to answer are named `read_*` and take it first
|
|
121
|
+
(`vault.read_balance_fuses(ctx)`). The same `Call` powers all three modes:
|
|
119
122
|
|
|
120
123
|
```python
|
|
121
124
|
from ipor_fusion import VaultSimulator
|
|
122
125
|
|
|
123
|
-
total = vault.total_assets().call() # eth_call -> Amount
|
|
124
|
-
receipt = vault.execute([action]).send() # signed tx -> TxReceipt
|
|
126
|
+
total = vault.total_assets().call(ctx) # eth_call -> Amount
|
|
127
|
+
receipt = vault.execute([action]).send(ctx) # signed tx -> TxReceipt
|
|
125
128
|
payload = vault.execute([action]).calldata # raw bytes for an external signer
|
|
126
129
|
|
|
127
130
|
# Simulate first via eth_simulateV1 (no local node); alpha is the account allowed to call execute()
|
|
@@ -264,7 +267,7 @@ Reverts on the deploy-and-configure path, keyed by selector so a failed transact
|
|
|
264
267
|
| `0x9996b315` | `AddressEmptyCode(address)` | `execute()` touched a market with no balance fuse | `add_balance_fuse(market_id, balance_fuse)` before the first `execute` on that market |
|
|
265
268
|
| `0x4bf4de4e` | `HandlerNotFound()` | A protocol called back into the vault during `execute()` (a flash loan) and no handler is registered for its address and selector | `update_callback_handler(handler, sender, selector)` before the first `execute` that triggers the callback |
|
|
266
269
|
| `0x068ca9d8` | `AccessManagedUnauthorized(address)` | The caller lacks the role the function requires: `FUSE_MANAGER` for `add_fuses`, `grant_market_substrates`, `add_balance_fuse`, `update_callback_handler`; `ALPHA` for `execute`; `WHITELIST` for `deposit` and `mint` on a private vault | `AccessManager.grant_role(role, account, 0)` from the role's admin (`OWNER` grants `ATOMIST`, `ATOMIST` grants the rest); for a reverting `deposit`, whitelist the depositor or convert the vault to public |
|
|
267
|
-
| `ValueError: Private key required for sending transactions` | SDK, before any transaction | `.send()` on a `Web3Context` without a key | `Web3Context(w3, chain_id, signer=..., private_key=...)` or `Web3Context.from_url(url, private_key=...)` |
|
|
270
|
+
| `ValueError: Private key required for sending transactions` | SDK, before any transaction | `.send(ctx)` on a `Web3Context` without a key | `Web3Context(w3, chain_id, signer=..., private_key=...)` or `Web3Context.from_url(url, private_key=...)` |
|
|
268
271
|
|
|
269
272
|
`clone()` grants the owner only `Roles.OWNER_ROLE` (1). OWNER grants `Roles.ATOMIST_ROLE` (100), which administers `Roles.FUSE_MANAGER_ROLE` (300, configuration), `Roles.ALPHA_ROLE` (200, `execute`) and `Roles.WHITELIST_ROLE` (800, `deposit` on a private vault).
|
|
270
273
|
|
|
@@ -350,10 +353,10 @@ The SDK uses a **fuse adapter pattern**:
|
|
|
350
353
|
- **Fuses** encode protocol-specific calls into `FuseAction` objects (pure calldata, no state)
|
|
351
354
|
- **PlasmaVault** batches and executes `FuseAction` sequences on-chain via `execute()`; a batch is atomic
|
|
352
355
|
- **Web3Context** manages provider connections, signing, and transaction dispatch
|
|
353
|
-
- **Call** is the lazy result of every wrapper method: `.call()`, `.send()`, `.calldata`, or feed it to `VaultSimulator`
|
|
356
|
+
- **Call** is the lazy result of every wrapper method: `.call(ctx)`, `.send(ctx)`, `.calldata`, or feed it to `VaultSimulator`
|
|
354
357
|
|
|
355
358
|
```
|
|
356
|
-
Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call --> .send() / simulate
|
|
359
|
+
Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call --> .send(ctx) / simulate
|
|
357
360
|
```
|
|
358
361
|
|
|
359
362
|
### Core modules (`ipor_fusion.core`)
|
|
@@ -361,7 +364,7 @@ Fuse.method() --> FuseAction --> PlasmaVault.execute([actions]) --> Call
|
|
|
361
364
|
| Module | Purpose |
|
|
362
365
|
|--------|---------|
|
|
363
366
|
| `Web3Context` | Provider connection, signing, tx dispatch, gas estimation |
|
|
364
|
-
| `Call` | Pre-encoded contract call; `.call()`, `.send()`, `.calldata`, `.build_transaction()` |
|
|
367
|
+
| `Call` | Pre-encoded contract call; `.call(ctx)`, `.send(ctx)`, `.calldata`, `.build_transaction(ctx)` |
|
|
365
368
|
| `PlasmaVault` | ERC-4626 vault — execute, deposit, withdraw, fuse and market configuration |
|
|
366
369
|
| `VaultSimulator` | Batch `execute` + reads through `eth_simulateV1`, multi-block, no local node |
|
|
367
370
|
| `FusionFactory` | Deploy a new vault (`clone`, `clone_supervised`) |
|
|
@@ -35,7 +35,7 @@ own examples.
|
|
|
35
35
|
The default (and only) behavior is preview plus `eth_simulateV1` simulation. An example must not
|
|
36
36
|
sign or broadcast a transaction, and must not require a private key to run.
|
|
37
37
|
|
|
38
|
-
- Show the read-only path with `.call()`, the unsigned-payload path with `.calldata`, and simulate
|
|
38
|
+
- Show the read-only path with `.call(ctx)`, the unsigned-payload path with `.calldata`, and simulate
|
|
39
39
|
the whole flow with `VaultSimulator`.
|
|
40
40
|
- For "how to actually submit this on-chain", point the reader at their signer via `.calldata`, and
|
|
41
41
|
at the deployment/operation repos -- do not add a runnable broadcast path.
|
|
@@ -104,7 +104,7 @@ default run also logs the unsigned `clone(...)` creation calldata.
|
|
|
104
104
|
|
|
105
105
|
These examples only simulate; they never broadcast. To submit a flow on-chain, take
|
|
106
106
|
the `.calldata` each builder produces and sign it with your own signer -- a multisig,
|
|
107
|
-
hardware wallet, or key-management service. (`Call.send()` performs a direct send from a
|
|
107
|
+
hardware wallet, or key-management service. (`Call.send(ctx)` performs a direct send from a
|
|
108
108
|
configured signer, but a production deployment should not sign with a hot key.) Always
|
|
109
109
|
review production configuration independently before using it with real funds.
|
|
110
110
|
|
|
@@ -6,7 +6,7 @@ collateralized borrow. It shows, end to end and without reading the test suite,
|
|
|
6
6
|
how to:
|
|
7
7
|
|
|
8
8
|
1. connect a ``Web3Context`` to Base;
|
|
9
|
-
2. preview a vault deployment with ``FusionFactory.clone(...).call()`` and read
|
|
9
|
+
2. preview a vault deployment with ``FusionFactory.clone(...).call(ctx)`` and read
|
|
10
10
|
its state-dependent CREATE addresses for the atomic simulation, plus the
|
|
11
11
|
unsigned creation ``.calldata``;
|
|
12
12
|
3. bootstrap the vault's roles in the correct order;
|
|
@@ -222,7 +222,7 @@ def _euler_account(plasma_vault: ChecksumAddress) -> ChecksumAddress:
|
|
|
222
222
|
|
|
223
223
|
|
|
224
224
|
def _evault_debt_of(
|
|
225
|
-
|
|
225
|
+
euler_vault: ChecksumAddress, account: ChecksumAddress
|
|
226
226
|
) -> Call[int]:
|
|
227
227
|
"""Read an Euler eVault's outstanding debt for an account (underlying units).
|
|
228
228
|
|
|
@@ -234,7 +234,7 @@ def _evault_debt_of(
|
|
|
234
234
|
data = function_signature_to_4byte_selector("debtOf(address)") + encode(
|
|
235
235
|
["address"], [account]
|
|
236
236
|
)
|
|
237
|
-
return Call(to=euler_vault, data=data, output_types=["uint256"]
|
|
237
|
+
return Call(to=euler_vault, data=data, output_types=["uint256"])
|
|
238
238
|
|
|
239
239
|
|
|
240
240
|
# ── Pure builders (no chain access -- safe to import and unit-test offline) ───
|
|
@@ -261,12 +261,12 @@ def clone_args() -> dict:
|
|
|
261
261
|
def unsigned_clone_calldata() -> bytes:
|
|
262
262
|
"""Unsigned creation calldata (selector + ABI-encoded args) for the deploy.
|
|
263
263
|
|
|
264
|
-
Built
|
|
265
|
-
an external signer / multisig instead of calling ``.send()``. NOTE: the owner
|
|
264
|
+
Built from an address-only wrapper, so it needs no provider. Hand these bytes to
|
|
265
|
+
an external signer / multisig instead of calling ``.send(ctx)``. NOTE: the owner
|
|
266
266
|
encoded here is the simulation ``OWNER`` (a throwaway EOA) -- set your real
|
|
267
267
|
owner in ``clone_args()`` before deploying, or the vault is owned by it.
|
|
268
268
|
"""
|
|
269
|
-
return FusionFactory(
|
|
269
|
+
return FusionFactory(BASE_FUSION_FACTORY).clone(**clone_args()).calldata
|
|
270
270
|
|
|
271
271
|
|
|
272
272
|
def market_substrates() -> list[bytes]:
|
|
@@ -369,10 +369,10 @@ def run_simulation(web3: Web3) -> SimulationResult:
|
|
|
369
369
|
"""Build, fund and run the full Euler V2 credit lifecycle in one batch."""
|
|
370
370
|
ctx = Web3Context(web3=web3, chain_id=BASE_CHAIN_ID, signer=OWNER)
|
|
371
371
|
ctx.default_block = PINNED_BLOCK
|
|
372
|
-
factory = FusionFactory(
|
|
372
|
+
factory = FusionFactory(BASE_FUSION_FACTORY)
|
|
373
373
|
|
|
374
374
|
# 1. Preview the deterministic addresses (eth_call, no gas, no state change).
|
|
375
|
-
preview = factory.clone(**clone_args()).call()
|
|
375
|
+
preview = factory.clone(**clone_args()).call(ctx)
|
|
376
376
|
log.info(
|
|
377
377
|
"predicted vault=%s access_manager=%s price_manager=%s (index=%d)",
|
|
378
378
|
preview.plasma_vault,
|
|
@@ -383,19 +383,19 @@ def run_simulation(web3: Web3) -> SimulationResult:
|
|
|
383
383
|
# 2. The unsigned creation calldata (external-signer path).
|
|
384
384
|
log.info("unsigned clone calldata: 0x%s", unsigned_clone_calldata().hex())
|
|
385
385
|
|
|
386
|
-
plasma_vault = PlasmaVault(
|
|
387
|
-
access_manager = AccessManager(
|
|
388
|
-
price_manager = PriceOracleMiddlewareManager(
|
|
389
|
-
cbeth = ERC20(
|
|
386
|
+
plasma_vault = PlasmaVault(preview.plasma_vault)
|
|
387
|
+
access_manager = AccessManager(preview.access_manager)
|
|
388
|
+
price_manager = PriceOracleMiddlewareManager(preview.price_manager)
|
|
389
|
+
cbeth = ERC20(BASE_CBETH)
|
|
390
390
|
euler_account = _euler_account(preview.plasma_vault)
|
|
391
391
|
|
|
392
392
|
# The whole flow runs as ONE eth_simulateV1 batch; nothing here is signed or
|
|
393
393
|
# broadcast (see the module docstring). Each simulator primitive stands in for
|
|
394
394
|
# a production action, read as "simulation -> production":
|
|
395
|
-
# - sim.add_call(call=X, from_=OWNER) -> X.send() by the owner/atomist
|
|
395
|
+
# - sim.add_call(call=X, from_=OWNER) -> X.send(ctx) by the owner/atomist
|
|
396
396
|
# (or X.calldata to a multisig / timelock).
|
|
397
|
-
# - sim.execute([action]) -> plasma_vault.execute([action]).send() by the alpha.
|
|
398
|
-
# - sim.observe("key", X) -> X.call(), a plain read.
|
|
397
|
+
# - sim.execute([action]) -> plasma_vault.execute([action]).send(ctx) by the alpha.
|
|
398
|
+
# - sim.observe("key", X) -> X.call(ctx), a plain read.
|
|
399
399
|
sim = VaultSimulator(
|
|
400
400
|
web3=web3, vault=preview.plasma_vault, alpha=ALPHA, block=PINNED_BLOCK
|
|
401
401
|
)
|
|
@@ -499,7 +499,7 @@ def run_simulation(web3: Web3) -> SimulationResult:
|
|
|
499
499
|
|
|
500
500
|
# Borrow WETH against the cbETH collateral; debt is now open.
|
|
501
501
|
sim.execute([borrow_action(BORROW_AMOUNT)])
|
|
502
|
-
sim.observe("debt_after_borrow", _evault_debt_of(
|
|
502
|
+
sim.observe("debt_after_borrow", _evault_debt_of(EVAULT_WETH, euler_account))
|
|
503
503
|
plan.append("borrow 1 WETH against the cbETH collateral")
|
|
504
504
|
|
|
505
505
|
# Re-value the market while the position is open so the balance fuse's
|
|
@@ -512,7 +512,7 @@ def run_simulation(web3: Web3) -> SimulationResult:
|
|
|
512
512
|
# advance in this run, interest is negligible and the borrowed WETH covers it.
|
|
513
513
|
# (Advancing time would accrue interest, requiring a small extra WETH buffer.)
|
|
514
514
|
sim.execute([repay_action(Amount(MAX_UINT256))])
|
|
515
|
-
sim.observe("debt_after_repay", _evault_debt_of(
|
|
515
|
+
sim.observe("debt_after_repay", _evault_debt_of(EVAULT_WETH, euler_account))
|
|
516
516
|
plan.append("repay the full WETH debt")
|
|
517
517
|
|
|
518
518
|
# Unwind in reverse of setup: disable the WETH controller, then the cbETH
|
{ipor_fusion-4.0.0.dev93 → ipor_fusion-4.0.0.dev103}/examples/advanced_euler_v2_looping_arbitrum.py
RENAMED
|
@@ -183,14 +183,13 @@ def _euler_account(plasma_vault: ChecksumAddress) -> ChecksumAddress:
|
|
|
183
183
|
|
|
184
184
|
|
|
185
185
|
def _debt_of(
|
|
186
|
-
ctx: Web3Context,
|
|
187
186
|
contract: ChecksumAddress,
|
|
188
187
|
account: ChecksumAddress,
|
|
189
188
|
) -> Call[int]:
|
|
190
189
|
data = function_signature_to_4byte_selector("debtOf(address)") + encode(
|
|
191
190
|
["address"], [account]
|
|
192
191
|
)
|
|
193
|
-
return Call(to=contract, data=data, output_types=["uint256"]
|
|
192
|
+
return Call(to=contract, data=data, output_types=["uint256"])
|
|
194
193
|
|
|
195
194
|
|
|
196
195
|
# ── Pure builders (no chain access) ──────────────────────────────────────────
|
|
@@ -208,7 +207,7 @@ def clone_args() -> dict:
|
|
|
208
207
|
|
|
209
208
|
|
|
210
209
|
def unsigned_clone_calldata() -> bytes:
|
|
211
|
-
return FusionFactory(
|
|
210
|
+
return FusionFactory(ARBITRUM_FUSION_FACTORY).clone(**clone_args()).calldata
|
|
212
211
|
|
|
213
212
|
|
|
214
213
|
def euler_market_substrates() -> list[bytes]:
|
|
@@ -260,9 +259,7 @@ def enable_euler_actions() -> list[FuseAction]:
|
|
|
260
259
|
def build_swap_action() -> FuseAction:
|
|
261
260
|
"""Swap the borrowed USDC through Uniswap V3 and require flash repayment."""
|
|
262
261
|
transfer_data = (
|
|
263
|
-
ERC20(
|
|
264
|
-
.transfer(UNISWAP_UNIVERSAL_ROUTER, BORROW_USDC)
|
|
265
|
-
.calldata
|
|
262
|
+
ERC20(ARBITRUM_USDC).transfer(UNISWAP_UNIVERSAL_ROUTER, BORROW_USDC).calldata
|
|
266
263
|
)
|
|
267
264
|
path = encode_packed(
|
|
268
265
|
["address", "uint24", "address"],
|
|
@@ -320,8 +317,8 @@ def run_simulation(web3: Web3) -> SimulationResult:
|
|
|
320
317
|
"""Build, configure and run the WBTC/USDC leveraged loop without broadcasting."""
|
|
321
318
|
ctx = Web3Context(web3=web3, chain_id=ARBITRUM_CHAIN_ID, signer=OWNER)
|
|
322
319
|
ctx.default_block = PINNED_BLOCK
|
|
323
|
-
factory = FusionFactory(
|
|
324
|
-
preview = factory.clone(**clone_args()).call()
|
|
320
|
+
factory = FusionFactory(ARBITRUM_FUSION_FACTORY)
|
|
321
|
+
preview = factory.clone(**clone_args()).call(ctx)
|
|
325
322
|
|
|
326
323
|
log.info(
|
|
327
324
|
"predicted vault=%s access_manager=%s price_manager=%s",
|
|
@@ -331,11 +328,11 @@ def run_simulation(web3: Web3) -> SimulationResult:
|
|
|
331
328
|
)
|
|
332
329
|
log.info("unsigned clone calldata: 0x%s", unsigned_clone_calldata().hex())
|
|
333
330
|
|
|
334
|
-
vault = PlasmaVault(
|
|
335
|
-
access_manager = AccessManager(
|
|
336
|
-
price_manager = PriceOracleMiddlewareManager(
|
|
337
|
-
wbtc = ERC20(
|
|
338
|
-
usdc = ERC20(
|
|
331
|
+
vault = PlasmaVault(preview.plasma_vault)
|
|
332
|
+
access_manager = AccessManager(preview.access_manager)
|
|
333
|
+
price_manager = PriceOracleMiddlewareManager(preview.price_manager)
|
|
334
|
+
wbtc = ERC20(ARBITRUM_WBTC)
|
|
335
|
+
usdc = ERC20(ARBITRUM_USDC)
|
|
339
336
|
euler_account = _euler_account(preview.plasma_vault)
|
|
340
337
|
|
|
341
338
|
sim = VaultSimulator(
|
|
@@ -427,11 +424,11 @@ def run_simulation(web3: Web3) -> SimulationResult:
|
|
|
427
424
|
sim.add_call(call=vault.update_markets_balances([EULER_MARKET]), from_=ALPHA)
|
|
428
425
|
sim.observe(
|
|
429
426
|
"collateral_shares",
|
|
430
|
-
ERC20(
|
|
427
|
+
ERC20(EVAULT_WBTC).balance_of(euler_account),
|
|
431
428
|
)
|
|
432
429
|
sim.observe(
|
|
433
430
|
"usdc_debt",
|
|
434
|
-
_debt_of(
|
|
431
|
+
_debt_of(EVAULT_USDC, euler_account),
|
|
435
432
|
)
|
|
436
433
|
sim.observe("vault_wbtc", wbtc.balance_of(preview.plasma_vault))
|
|
437
434
|
sim.observe("vault_usdc", usdc.balance_of(preview.plasma_vault))
|