wayfinder-paths 0.1.29__tar.gz → 0.1.30__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.
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/PKG-INFO +1 -1
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/pyproject.toml +1 -1
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/adapter.py +313 -12
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/test_adapter.py +125 -14
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +2 -2
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/exchange.py +2 -2
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/local_signer.py +2 -33
- wayfinder_paths-0.1.30/wayfinder_paths/core/constants/hype_oft_abi.py +151 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/strategies/Strategy.py +1 -2
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/quotes.py +4 -125
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/basis_trading_strategy/strategy.py +1 -2
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/boros_ops_mixin.py +57 -201
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/constants.py +1 -152
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +2 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +2 -0
- wayfinder_paths-0.1.29/wayfinder_paths/core/types.py +0 -19
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/LICENSE +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/balance_adapter/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/balance_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/balance_adapter/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/balance_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/client.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/parsers.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/test_golden.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/types.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/utils.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/brap_adapter/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/brap_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/brap_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/brap_adapter/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/brap_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/brap_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperlend_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperlend_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/executor.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/util.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/hyperliquid_adapter/utils.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/ledger_adapter/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/ledger_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/ledger_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/ledger_adapter/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/ledger_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/ledger_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/moonwell_adapter/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/moonwell_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/moonwell_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/moonwell_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/multicall_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/multicall_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/multicall_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/multicall_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pendle_adapter/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pendle_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pendle_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pendle_adapter/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pendle_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pendle_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pool_adapter/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pool_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pool_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pool_adapter/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pool_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/token_adapter/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/token_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/token_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/token_adapter/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/token_adapter/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/token_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/conftest.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/adapters/BaseAdapter.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/adapters/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/adapters/models.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/analytics/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/analytics/bootstrap.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/analytics/stats.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/analytics/test_analytics.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/BRAPClient.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/BalanceClient.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/ClientManager.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/HyperlendClient.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/LedgerClient.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/PoolClient.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/TokenClient.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/WayfinderClient.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/protocols.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/clients/test_ledger_client.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/config.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/constants/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/constants/base.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/constants/chains.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/constants/contracts.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/constants/erc20_abi.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/constants/hyperlend_abi.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/constants/hyperliquid.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/constants/moonwell_abi.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/constants/tokens.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/engine/manifest.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/strategies/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/strategies/base.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/strategies/descriptors.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/strategies/opa_loop.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/utils/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/utils/evm_helpers.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/utils/test_transaction.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/utils/tokens.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/utils/transaction.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/utils/wallets.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/core/utils/web3.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/preview.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/scripting.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/server.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/state/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/state/profile_store.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/state/store.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/test_scripting.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/balances.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/discovery.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/execute.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/hyperliquid.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/run_script.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/strategies.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/tokens.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/tools/wallets.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/mcp/utils.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/enso.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/erc20.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/evm.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/hyper_evm.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/hyperlend.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/hyperliquid.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/lifi.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/moonwell.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/prjx.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/policies/util.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/run_strategy.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/basis_trading_strategy/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/basis_trading_strategy/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/basis_trading_strategy/constants.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/basis_trading_strategy/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/basis_trading_strategy/types.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/hyperevm_ops_mixin.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/hyperliquid_ops_mixin.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/manifest.yaml +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/planner.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/risk_ops_mixin.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/snapshot_mixin.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/strategy.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/test_planner_golden.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/test_strategy.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/types.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/boros_hype_strategy/withdraw_mixin.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/stablecoin_yield_strategy/examples.json +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/tests/__init__.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/tests/test_mcp_quote_swap.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/tests/test_test_coverage.py +0 -0
- {wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/tests/test_utils.py +0 -0
{wayfinder_paths-0.1.29 → wayfinder_paths-0.1.30}/wayfinder_paths/adapters/boros_adapter/adapter.py
RENAMED
|
@@ -10,9 +10,15 @@ from eth_utils import function_signature_to_4byte_selector, to_checksum_address
|
|
|
10
10
|
from loguru import logger
|
|
11
11
|
|
|
12
12
|
from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
|
|
13
|
-
from wayfinder_paths.core.constants.
|
|
13
|
+
from wayfinder_paths.core.constants.chains import CHAIN_ID_HYPEREVM
|
|
14
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
15
|
+
BOROS_MARKET_HUB,
|
|
16
|
+
BOROS_ROUTER,
|
|
17
|
+
HYPE_OFT_ADDRESS,
|
|
18
|
+
)
|
|
19
|
+
from wayfinder_paths.core.constants.hype_oft_abi import HYPE_OFT_ABI
|
|
14
20
|
from wayfinder_paths.core.utils.tokens import build_approve_transaction
|
|
15
|
-
from wayfinder_paths.core.utils.transaction import send_transaction
|
|
21
|
+
from wayfinder_paths.core.utils.transaction import encode_call, send_transaction
|
|
16
22
|
from wayfinder_paths.core.utils.web3 import web3_from_chain_id
|
|
17
23
|
|
|
18
24
|
from .client import BorosClient
|
|
@@ -114,6 +120,11 @@ class BorosAdapter(BaseAdapter):
|
|
|
114
120
|
}
|
|
115
121
|
]
|
|
116
122
|
|
|
123
|
+
@staticmethod
|
|
124
|
+
def _pad_address_bytes32(address: str) -> bytes:
|
|
125
|
+
checksum = to_checksum_address(address)
|
|
126
|
+
return bytes.fromhex(checksum[2:]).rjust(32, b"\x00")
|
|
127
|
+
|
|
117
128
|
async def get_cash_fee_data(self, *, token_id: int) -> tuple[bool, dict[str, Any]]:
|
|
118
129
|
"""Read MarketHub.getCashFeeData(tokenId) from chain.
|
|
119
130
|
|
|
@@ -168,7 +179,6 @@ class BorosAdapter(BaseAdapter):
|
|
|
168
179
|
*,
|
|
169
180
|
token_id: int,
|
|
170
181
|
market_id: int | None = None,
|
|
171
|
-
token_decimals: int = 18,
|
|
172
182
|
) -> tuple[bool, dict[str, Any]]:
|
|
173
183
|
"""Sweep isolated cash -> cross cash for a given token (optionally per-market).
|
|
174
184
|
|
|
@@ -176,22 +186,17 @@ class BorosAdapter(BaseAdapter):
|
|
|
176
186
|
this helper moves that isolated cash back to cross margin using cash_transfer.
|
|
177
187
|
|
|
178
188
|
Notes:
|
|
189
|
+
- Uses `get_account_balances()` (collaterals endpoint) to find isolated cash.
|
|
179
190
|
- cash_transfer uses 1e18 internal cash units, not token native decimals.
|
|
180
191
|
- This does NOT touch isolated positions for other markets unless market_id is None.
|
|
181
192
|
"""
|
|
182
193
|
if self.simulation:
|
|
183
194
|
return True, {"status": "simulated"}
|
|
184
195
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
include_open_orders=False,
|
|
189
|
-
include_withdrawal_status=False,
|
|
190
|
-
)
|
|
191
|
-
if not ok_state or not isinstance(state, dict):
|
|
192
|
-
return False, {"error": f"Failed to read Boros state: {state}"}
|
|
196
|
+
ok_bal, balances = await self.get_account_balances(token_id=int(token_id))
|
|
197
|
+
if not ok_bal or not isinstance(balances, dict):
|
|
198
|
+
return False, {"error": f"Failed to read Boros balances: {balances}"}
|
|
193
199
|
|
|
194
|
-
balances = state.get("balances") or {}
|
|
195
200
|
isolated_positions = balances.get("isolated_positions") or []
|
|
196
201
|
if not isinstance(isolated_positions, list):
|
|
197
202
|
isolated_positions = []
|
|
@@ -1304,6 +1309,302 @@ class BorosAdapter(BaseAdapter):
|
|
|
1304
1309
|
logger.error(f"Failed to cash transfer: {e}")
|
|
1305
1310
|
return False, {"error": str(e)}
|
|
1306
1311
|
|
|
1312
|
+
async def bridge_hype_oft_hyperevm_to_arbitrum(
|
|
1313
|
+
self,
|
|
1314
|
+
*,
|
|
1315
|
+
amount_wei: int,
|
|
1316
|
+
max_value_wei: int | None = None,
|
|
1317
|
+
to_address: str | None = None,
|
|
1318
|
+
from_address: str | None = None,
|
|
1319
|
+
dst_eid: int = 30110,
|
|
1320
|
+
min_amount_wei: int = 0,
|
|
1321
|
+
) -> tuple[bool, dict[str, Any]]:
|
|
1322
|
+
"""Bridge native HYPE from HyperEVM -> Arbitrum via LayerZero OFT.
|
|
1323
|
+
|
|
1324
|
+
Notes:
|
|
1325
|
+
- Uses HyperEVM chain id (999) for the transaction.
|
|
1326
|
+
- `amount_wei` is in 1e18 (native HYPE).
|
|
1327
|
+
- The OFT bridge requires `msg.value = amount + nativeFee`.
|
|
1328
|
+
- Amount must be rounded down to `decimalConversionRate()`.
|
|
1329
|
+
- If `max_value_wei` is provided, clamps amount so that (amount + fee) <= max_value_wei.
|
|
1330
|
+
"""
|
|
1331
|
+
if amount_wei <= 0:
|
|
1332
|
+
return True, {"status": "no_op", "amount_wei": 0}
|
|
1333
|
+
|
|
1334
|
+
if self.simulation:
|
|
1335
|
+
return True, {
|
|
1336
|
+
"status": "simulated",
|
|
1337
|
+
"amount_wei": int(amount_wei),
|
|
1338
|
+
"dst_eid": int(dst_eid),
|
|
1339
|
+
"to": to_address or self.user_address,
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if not self.sign_callback:
|
|
1343
|
+
return False, {"error": "sign_callback not configured"}
|
|
1344
|
+
|
|
1345
|
+
sender = from_address or self.user_address
|
|
1346
|
+
recipient = to_address or self.user_address
|
|
1347
|
+
if not sender or not recipient:
|
|
1348
|
+
return False, {"error": "from_address/to_address not configured"}
|
|
1349
|
+
|
|
1350
|
+
try:
|
|
1351
|
+
async with web3_from_chain_id(CHAIN_ID_HYPEREVM) as w3:
|
|
1352
|
+
contract = w3.eth.contract(
|
|
1353
|
+
address=w3.to_checksum_address(HYPE_OFT_ADDRESS),
|
|
1354
|
+
abi=HYPE_OFT_ABI,
|
|
1355
|
+
)
|
|
1356
|
+
|
|
1357
|
+
conversion_rate = int(
|
|
1358
|
+
await contract.functions.decimalConversionRate().call()
|
|
1359
|
+
)
|
|
1360
|
+
if conversion_rate > 0:
|
|
1361
|
+
amount_wei = (int(amount_wei) // conversion_rate) * conversion_rate
|
|
1362
|
+
else:
|
|
1363
|
+
amount_wei = int(amount_wei)
|
|
1364
|
+
|
|
1365
|
+
if amount_wei <= 0:
|
|
1366
|
+
return True, {"status": "no_op", "amount_wei": 0}
|
|
1367
|
+
|
|
1368
|
+
to_bytes32 = self._pad_address_bytes32(recipient)
|
|
1369
|
+
|
|
1370
|
+
def _send_params(amount_ld: int) -> tuple[Any, ...]:
|
|
1371
|
+
return (
|
|
1372
|
+
int(dst_eid),
|
|
1373
|
+
to_bytes32,
|
|
1374
|
+
int(amount_ld),
|
|
1375
|
+
int(min_amount_wei),
|
|
1376
|
+
b"",
|
|
1377
|
+
b"",
|
|
1378
|
+
b"",
|
|
1379
|
+
)
|
|
1380
|
+
|
|
1381
|
+
send_params = _send_params(int(amount_wei))
|
|
1382
|
+
fee = await contract.functions.quoteSend(send_params, False).call()
|
|
1383
|
+
native_fee_wei = int(fee[0])
|
|
1384
|
+
lz_token_fee_wei = int(fee[1])
|
|
1385
|
+
|
|
1386
|
+
if max_value_wei is not None:
|
|
1387
|
+
max_send_amount_wei = max(0, int(max_value_wei) - native_fee_wei)
|
|
1388
|
+
if conversion_rate > 0:
|
|
1389
|
+
max_send_amount_wei = (
|
|
1390
|
+
max_send_amount_wei // conversion_rate
|
|
1391
|
+
) * conversion_rate
|
|
1392
|
+
if amount_wei > max_send_amount_wei:
|
|
1393
|
+
amount_wei = int(max_send_amount_wei)
|
|
1394
|
+
if amount_wei <= 0:
|
|
1395
|
+
return False, {
|
|
1396
|
+
"error": "Insufficient balance to cover OFT fee",
|
|
1397
|
+
"native_fee_wei": native_fee_wei,
|
|
1398
|
+
"max_value_wei": int(max_value_wei),
|
|
1399
|
+
}
|
|
1400
|
+
send_params = _send_params(int(amount_wei))
|
|
1401
|
+
fee = await contract.functions.quoteSend(
|
|
1402
|
+
send_params, False
|
|
1403
|
+
).call()
|
|
1404
|
+
native_fee_wei = int(fee[0])
|
|
1405
|
+
lz_token_fee_wei = int(fee[1])
|
|
1406
|
+
|
|
1407
|
+
total_value_wei = int(amount_wei) + int(native_fee_wei)
|
|
1408
|
+
if max_value_wei is not None and total_value_wei > int(max_value_wei):
|
|
1409
|
+
return False, {
|
|
1410
|
+
"error": "Insufficient balance after fee quote",
|
|
1411
|
+
"amount_wei": int(amount_wei),
|
|
1412
|
+
"native_fee_wei": int(native_fee_wei),
|
|
1413
|
+
"total_value_wei": int(total_value_wei),
|
|
1414
|
+
"max_value_wei": int(max_value_wei),
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
tx = await encode_call(
|
|
1418
|
+
target=HYPE_OFT_ADDRESS,
|
|
1419
|
+
abi=HYPE_OFT_ABI,
|
|
1420
|
+
fn_name="send",
|
|
1421
|
+
args=[
|
|
1422
|
+
send_params,
|
|
1423
|
+
(int(native_fee_wei), int(lz_token_fee_wei)),
|
|
1424
|
+
to_checksum_address(sender),
|
|
1425
|
+
],
|
|
1426
|
+
from_address=sender,
|
|
1427
|
+
chain_id=CHAIN_ID_HYPEREVM,
|
|
1428
|
+
value=int(total_value_wei),
|
|
1429
|
+
)
|
|
1430
|
+
tx_hash = await send_transaction(
|
|
1431
|
+
tx, self.sign_callback, wait_for_receipt=True
|
|
1432
|
+
)
|
|
1433
|
+
return True, {
|
|
1434
|
+
"status": "ok",
|
|
1435
|
+
"tx_hash": tx_hash,
|
|
1436
|
+
"amount_wei": int(amount_wei),
|
|
1437
|
+
"native_fee_wei": int(native_fee_wei),
|
|
1438
|
+
"lz_token_fee_wei": int(lz_token_fee_wei),
|
|
1439
|
+
"total_value_wei": int(total_value_wei),
|
|
1440
|
+
"dst_eid": int(dst_eid),
|
|
1441
|
+
"to": recipient,
|
|
1442
|
+
"from": sender,
|
|
1443
|
+
"layerzeroscan": f"https://layerzeroscan.com/tx/{tx_hash}",
|
|
1444
|
+
}
|
|
1445
|
+
except Exception as exc: # noqa: BLE001
|
|
1446
|
+
return False, {"error": str(exc)}
|
|
1447
|
+
|
|
1448
|
+
async def close_positions_except(
|
|
1449
|
+
self,
|
|
1450
|
+
*,
|
|
1451
|
+
keep_market_id: int,
|
|
1452
|
+
token_id: int = 3,
|
|
1453
|
+
market_ids: list[int] | None = None,
|
|
1454
|
+
best_effort: bool = True,
|
|
1455
|
+
) -> tuple[bool, dict[str, Any]]:
|
|
1456
|
+
"""Close all Boros positions except `keep_market_id` (best-effort by default)."""
|
|
1457
|
+
if self.simulation:
|
|
1458
|
+
return True, {
|
|
1459
|
+
"status": "simulated",
|
|
1460
|
+
"keep_market_id": int(keep_market_id),
|
|
1461
|
+
"markets": market_ids or [],
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
if market_ids is None:
|
|
1465
|
+
ok_pos, positions = await self.get_active_positions()
|
|
1466
|
+
if not ok_pos:
|
|
1467
|
+
return False, {"error": f"Failed to get positions: {positions}"}
|
|
1468
|
+
market_ids = sorted(
|
|
1469
|
+
{
|
|
1470
|
+
int(p.get("marketId"))
|
|
1471
|
+
for p in positions
|
|
1472
|
+
if p.get("marketId") is not None
|
|
1473
|
+
}
|
|
1474
|
+
)
|
|
1475
|
+
|
|
1476
|
+
results: list[dict[str, Any]] = []
|
|
1477
|
+
failures: list[dict[str, Any]] = []
|
|
1478
|
+
for mid in market_ids:
|
|
1479
|
+
if int(mid) == int(keep_market_id):
|
|
1480
|
+
continue
|
|
1481
|
+
ok_close, res_close = await self.close_positions_market(
|
|
1482
|
+
int(mid), token_id=int(token_id)
|
|
1483
|
+
)
|
|
1484
|
+
entry = {"market_id": int(mid), "ok": bool(ok_close), "res": res_close}
|
|
1485
|
+
results.append(entry)
|
|
1486
|
+
if not ok_close:
|
|
1487
|
+
failures.append(entry)
|
|
1488
|
+
if not best_effort:
|
|
1489
|
+
return False, {
|
|
1490
|
+
"error": f"Failed to close position for market {mid}",
|
|
1491
|
+
"results": results,
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
return True, {"status": "ok", "results": results, "failures": failures}
|
|
1495
|
+
|
|
1496
|
+
async def ensure_position_size_yu(
|
|
1497
|
+
self,
|
|
1498
|
+
*,
|
|
1499
|
+
market_id: int,
|
|
1500
|
+
token_id: int,
|
|
1501
|
+
target_size_yu: float,
|
|
1502
|
+
tif: str = "IOC",
|
|
1503
|
+
min_resize_excess_usd: float | None = None,
|
|
1504
|
+
yu_to_usd: float | None = None,
|
|
1505
|
+
) -> tuple[bool, dict[str, Any]]:
|
|
1506
|
+
"""Ensure the Boros position size (YU) for a market.
|
|
1507
|
+
|
|
1508
|
+
- Uses `place_rate_order(... side="short")` to increase the position.
|
|
1509
|
+
- Uses `close_positions_market(..., size_yu_wei=...)` to decrease it.
|
|
1510
|
+
- If `min_resize_excess_usd` is provided, uses `yu_to_usd` to skip small resizes.
|
|
1511
|
+
"""
|
|
1512
|
+
if self.simulation:
|
|
1513
|
+
return True, {
|
|
1514
|
+
"status": "simulated",
|
|
1515
|
+
"market_id": int(market_id),
|
|
1516
|
+
"token_id": int(token_id),
|
|
1517
|
+
"target_size_yu": float(target_size_yu),
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
ok_pos, positions = await self.get_active_positions(market_id=int(market_id))
|
|
1521
|
+
if not ok_pos:
|
|
1522
|
+
return False, {"error": f"Failed to get positions: {positions}"}
|
|
1523
|
+
|
|
1524
|
+
if positions:
|
|
1525
|
+
current_size_yu = abs(float(positions[0].get("size", 0) or 0.0))
|
|
1526
|
+
else:
|
|
1527
|
+
current_size_yu = 0.0
|
|
1528
|
+
|
|
1529
|
+
diff_yu = float(target_size_yu) - float(current_size_yu)
|
|
1530
|
+
diff_abs_yu = abs(diff_yu)
|
|
1531
|
+
|
|
1532
|
+
if min_resize_excess_usd is not None:
|
|
1533
|
+
if yu_to_usd is None or yu_to_usd <= 0:
|
|
1534
|
+
return False, {
|
|
1535
|
+
"error": "yu_to_usd required when min_resize_excess_usd is set",
|
|
1536
|
+
"yu_to_usd": yu_to_usd,
|
|
1537
|
+
}
|
|
1538
|
+
diff_usd_equiv = diff_abs_yu * float(yu_to_usd)
|
|
1539
|
+
if diff_usd_equiv < float(min_resize_excess_usd):
|
|
1540
|
+
return True, {
|
|
1541
|
+
"status": "ok",
|
|
1542
|
+
"action": "no_op",
|
|
1543
|
+
"market_id": int(market_id),
|
|
1544
|
+
"token_id": int(token_id),
|
|
1545
|
+
"current_size_yu": float(current_size_yu),
|
|
1546
|
+
"target_size_yu": float(target_size_yu),
|
|
1547
|
+
"diff_yu": float(diff_yu),
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
if diff_abs_yu < 1e-9:
|
|
1551
|
+
return True, {
|
|
1552
|
+
"status": "ok",
|
|
1553
|
+
"action": "no_op",
|
|
1554
|
+
"market_id": int(market_id),
|
|
1555
|
+
"token_id": int(token_id),
|
|
1556
|
+
"current_size_yu": float(current_size_yu),
|
|
1557
|
+
"target_size_yu": float(target_size_yu),
|
|
1558
|
+
"diff_yu": float(diff_yu),
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
size_yu_wei = int(diff_abs_yu * 1e18)
|
|
1562
|
+
|
|
1563
|
+
if diff_yu > 0:
|
|
1564
|
+
ok_open, res_open = await self.place_rate_order(
|
|
1565
|
+
market_id=int(market_id),
|
|
1566
|
+
token_id=int(token_id),
|
|
1567
|
+
size_yu_wei=int(size_yu_wei),
|
|
1568
|
+
side="short",
|
|
1569
|
+
tif=tif,
|
|
1570
|
+
)
|
|
1571
|
+
if not ok_open:
|
|
1572
|
+
return False, {
|
|
1573
|
+
"error": f"Failed to open/increase position: {res_open}",
|
|
1574
|
+
"market_id": int(market_id),
|
|
1575
|
+
}
|
|
1576
|
+
return True, {
|
|
1577
|
+
"status": "ok",
|
|
1578
|
+
"action": "increase_short",
|
|
1579
|
+
"market_id": int(market_id),
|
|
1580
|
+
"token_id": int(token_id),
|
|
1581
|
+
"current_size_yu": float(current_size_yu),
|
|
1582
|
+
"target_size_yu": float(target_size_yu),
|
|
1583
|
+
"diff_yu": float(diff_yu),
|
|
1584
|
+
"tx": res_open,
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
ok_close, res_close = await self.close_positions_market(
|
|
1588
|
+
market_id=int(market_id),
|
|
1589
|
+
token_id=int(token_id),
|
|
1590
|
+
size_yu_wei=int(size_yu_wei),
|
|
1591
|
+
)
|
|
1592
|
+
if not ok_close:
|
|
1593
|
+
return False, {
|
|
1594
|
+
"error": f"Failed to close/decrease position: {res_close}",
|
|
1595
|
+
"market_id": int(market_id),
|
|
1596
|
+
}
|
|
1597
|
+
return True, {
|
|
1598
|
+
"status": "ok",
|
|
1599
|
+
"action": "decrease",
|
|
1600
|
+
"market_id": int(market_id),
|
|
1601
|
+
"token_id": int(token_id),
|
|
1602
|
+
"current_size_yu": float(current_size_yu),
|
|
1603
|
+
"target_size_yu": float(target_size_yu),
|
|
1604
|
+
"diff_yu": float(diff_yu),
|
|
1605
|
+
"tx": res_close,
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1307
1608
|
async def place_rate_order(
|
|
1308
1609
|
self,
|
|
1309
1610
|
*,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Tests for BorosAdapter."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
6
7
|
from eth_abi import encode as abi_encode
|
|
@@ -384,16 +385,14 @@ class TestBorosAdapter:
|
|
|
384
385
|
async def test_sweep_isolated_to_cross_filters_by_market(self, adapter):
|
|
385
386
|
"""Test isolated -> cross sweep only affects the specified market."""
|
|
386
387
|
adapter.simulation = False
|
|
387
|
-
adapter.
|
|
388
|
+
adapter.get_account_balances = AsyncMock(
|
|
388
389
|
return_value=(
|
|
389
390
|
True,
|
|
390
391
|
{
|
|
391
|
-
"
|
|
392
|
-
"
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
]
|
|
396
|
-
}
|
|
392
|
+
"isolated_positions": [
|
|
393
|
+
{"market_id": 18, "balance_wei": 111},
|
|
394
|
+
{"market_id": 19, "balance_wei": 222},
|
|
395
|
+
]
|
|
397
396
|
},
|
|
398
397
|
)
|
|
399
398
|
)
|
|
@@ -414,15 +413,13 @@ class TestBorosAdapter:
|
|
|
414
413
|
async def test_sweep_isolated_to_cross_errors_on_failed_transfer(self, adapter):
|
|
415
414
|
"""Test sweep fails fast when an isolated->cross transfer fails."""
|
|
416
415
|
adapter.simulation = False
|
|
417
|
-
adapter.
|
|
416
|
+
adapter.get_account_balances = AsyncMock(
|
|
418
417
|
return_value=(
|
|
419
418
|
True,
|
|
420
419
|
{
|
|
421
|
-
"
|
|
422
|
-
"
|
|
423
|
-
|
|
424
|
-
]
|
|
425
|
-
}
|
|
420
|
+
"isolated_positions": [
|
|
421
|
+
{"market_id": 18, "balance_wei": 111},
|
|
422
|
+
]
|
|
426
423
|
},
|
|
427
424
|
)
|
|
428
425
|
)
|
|
@@ -487,6 +484,120 @@ class TestBorosAdapter:
|
|
|
487
484
|
|
|
488
485
|
mock_sweep.assert_awaited_once_with(token_id=3, market_id=18)
|
|
489
486
|
|
|
487
|
+
@pytest.mark.asyncio
|
|
488
|
+
async def test_close_positions_except_skips_keep_market(self, adapter):
|
|
489
|
+
adapter.simulation = False
|
|
490
|
+
adapter.close_positions_market = AsyncMock(
|
|
491
|
+
return_value=(True, {"status": "ok"})
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
ok, res = await adapter.close_positions_except(
|
|
495
|
+
keep_market_id=19, token_id=3, market_ids=[18, 19, 20]
|
|
496
|
+
)
|
|
497
|
+
assert ok is True
|
|
498
|
+
assert res["status"] == "ok"
|
|
499
|
+
|
|
500
|
+
calls = adapter.close_positions_market.await_args_list
|
|
501
|
+
assert len(calls) == 2
|
|
502
|
+
assert calls[0].args[0] == 18
|
|
503
|
+
assert calls[1].args[0] == 20
|
|
504
|
+
|
|
505
|
+
@pytest.mark.asyncio
|
|
506
|
+
async def test_ensure_position_size_yu_increases_short(self, adapter):
|
|
507
|
+
adapter.simulation = False
|
|
508
|
+
adapter.get_active_positions = AsyncMock(return_value=(True, []))
|
|
509
|
+
adapter.place_rate_order = AsyncMock(return_value=(True, {"tx_hash": "0xopen"}))
|
|
510
|
+
|
|
511
|
+
ok, res = await adapter.ensure_position_size_yu(
|
|
512
|
+
market_id=18, token_id=3, target_size_yu=1.5
|
|
513
|
+
)
|
|
514
|
+
assert ok is True
|
|
515
|
+
assert res["action"] == "increase_short"
|
|
516
|
+
adapter.place_rate_order.assert_awaited_once()
|
|
517
|
+
|
|
518
|
+
_, kwargs = adapter.place_rate_order.await_args
|
|
519
|
+
assert kwargs["market_id"] == 18
|
|
520
|
+
assert kwargs["token_id"] == 3
|
|
521
|
+
assert kwargs["side"] == "short"
|
|
522
|
+
assert kwargs["tif"] == "IOC"
|
|
523
|
+
assert kwargs["size_yu_wei"] == int(1.5 * 1e18)
|
|
524
|
+
|
|
525
|
+
@pytest.mark.asyncio
|
|
526
|
+
async def test_ensure_position_size_yu_decreases(self, adapter):
|
|
527
|
+
adapter.simulation = False
|
|
528
|
+
adapter.get_active_positions = AsyncMock(
|
|
529
|
+
return_value=(True, [{"size": 2.0, "sizeWei": int(2e18)}])
|
|
530
|
+
)
|
|
531
|
+
adapter.close_positions_market = AsyncMock(
|
|
532
|
+
return_value=(True, {"tx_hash": "0xclose"})
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
ok, res = await adapter.ensure_position_size_yu(
|
|
536
|
+
market_id=18, token_id=3, target_size_yu=1.0
|
|
537
|
+
)
|
|
538
|
+
assert ok is True
|
|
539
|
+
assert res["action"] == "decrease"
|
|
540
|
+
adapter.close_positions_market.assert_awaited_once_with(
|
|
541
|
+
market_id=18, token_id=3, size_yu_wei=int(1.0 * 1e18)
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
@pytest.mark.asyncio
|
|
545
|
+
async def test_bridge_hype_oft_rounds_amount_and_builds_tx(self, adapter):
|
|
546
|
+
adapter.simulation = False
|
|
547
|
+
adapter.sign_callback = object()
|
|
548
|
+
|
|
549
|
+
mock_contract = SimpleNamespace()
|
|
550
|
+
mock_dec_fn = SimpleNamespace(call=AsyncMock(return_value=10))
|
|
551
|
+
mock_quote_fn = SimpleNamespace(call=AsyncMock(return_value=(5, 0)))
|
|
552
|
+
mock_functions = SimpleNamespace(
|
|
553
|
+
decimalConversionRate=MagicMock(return_value=mock_dec_fn),
|
|
554
|
+
quoteSend=MagicMock(return_value=mock_quote_fn),
|
|
555
|
+
)
|
|
556
|
+
mock_contract.functions = mock_functions
|
|
557
|
+
|
|
558
|
+
mock_web3 = SimpleNamespace(
|
|
559
|
+
eth=SimpleNamespace(contract=MagicMock(return_value=mock_contract)),
|
|
560
|
+
to_checksum_address=lambda x: x,
|
|
561
|
+
)
|
|
562
|
+
mock_cm = AsyncMock()
|
|
563
|
+
mock_cm.__aenter__.return_value = mock_web3
|
|
564
|
+
mock_cm.__aexit__.return_value = False
|
|
565
|
+
|
|
566
|
+
with (
|
|
567
|
+
patch(
|
|
568
|
+
"wayfinder_paths.adapters.boros_adapter.adapter.web3_from_chain_id",
|
|
569
|
+
return_value=mock_cm,
|
|
570
|
+
),
|
|
571
|
+
patch(
|
|
572
|
+
"wayfinder_paths.adapters.boros_adapter.adapter.encode_call",
|
|
573
|
+
new=AsyncMock(return_value={"chainId": 999}),
|
|
574
|
+
) as mock_encode,
|
|
575
|
+
patch(
|
|
576
|
+
"wayfinder_paths.adapters.boros_adapter.adapter.send_transaction",
|
|
577
|
+
new=AsyncMock(return_value="0xtx"),
|
|
578
|
+
),
|
|
579
|
+
):
|
|
580
|
+
ok, res = await adapter.bridge_hype_oft_hyperevm_to_arbitrum(
|
|
581
|
+
amount_wei=123,
|
|
582
|
+
max_value_wei=1000,
|
|
583
|
+
to_address=adapter.user_address,
|
|
584
|
+
from_address=adapter.user_address,
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
assert ok is True
|
|
588
|
+
assert res["status"] == "ok"
|
|
589
|
+
assert res["tx_hash"] == "0xtx"
|
|
590
|
+
assert res["amount_wei"] == 120 # rounded down by conversion rate 10
|
|
591
|
+
assert res["native_fee_wei"] == 5
|
|
592
|
+
assert res["total_value_wei"] == 125
|
|
593
|
+
|
|
594
|
+
_, kwargs = mock_encode.await_args
|
|
595
|
+
assert kwargs["fn_name"] == "send"
|
|
596
|
+
assert kwargs["value"] == 125
|
|
597
|
+
send_params = kwargs["args"][0]
|
|
598
|
+
assert send_params[0] == 30110
|
|
599
|
+
assert send_params[2] == 120
|
|
600
|
+
|
|
490
601
|
@pytest.mark.asyncio
|
|
491
602
|
async def test_withdraw_collateral_simulation(self, adapter):
|
|
492
603
|
"""Test withdraw in simulation mode."""
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import time
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
5
6
|
from decimal import ROUND_DOWN, Decimal, getcontext
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
@@ -22,7 +23,6 @@ from wayfinder_paths.core.constants.hyperliquid import (
|
|
|
22
23
|
from wayfinder_paths.core.constants.hyperliquid import (
|
|
23
24
|
HYPERLIQUID_BRIDGE_ADDRESS as _HYPERLIQUID_BRIDGE_ADDRESS,
|
|
24
25
|
)
|
|
25
|
-
from wayfinder_paths.core.types import HyperliquidSignCallback
|
|
26
26
|
|
|
27
27
|
# Re-export Bridge2 constants for backwards compatibility.
|
|
28
28
|
HYPERLIQUID_BRIDGE_ADDRESS = _HYPERLIQUID_BRIDGE_ADDRESS
|
|
@@ -68,7 +68,7 @@ class HyperliquidAdapter(BaseAdapter):
|
|
|
68
68
|
config: dict[str, Any] | None = None,
|
|
69
69
|
*,
|
|
70
70
|
simulation: bool = False,
|
|
71
|
-
sign_callback:
|
|
71
|
+
sign_callback: Callable[[dict], Awaitable[str]] | None = None,
|
|
72
72
|
) -> None:
|
|
73
73
|
super().__init__("hyperliquid_adapter", config)
|
|
74
74
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from collections.abc import Awaitable, Callable
|
|
1
2
|
from decimal import Decimal
|
|
2
3
|
from typing import Any, Literal
|
|
3
4
|
|
|
@@ -24,7 +25,6 @@ from loguru import logger
|
|
|
24
25
|
from web3 import Web3
|
|
25
26
|
|
|
26
27
|
from wayfinder_paths.adapters.hyperliquid_adapter.util import Util
|
|
27
|
-
from wayfinder_paths.core.types import HyperliquidSignCallback
|
|
28
28
|
|
|
29
29
|
ARBITRUM_CHAIN_ID = "0xa4b1"
|
|
30
30
|
MAINNET = "Mainnet"
|
|
@@ -39,7 +39,7 @@ class Exchange:
|
|
|
39
39
|
self,
|
|
40
40
|
info: Info,
|
|
41
41
|
util: Util,
|
|
42
|
-
sign_callback:
|
|
42
|
+
sign_callback: Callable[[dict], Awaitable[str]],
|
|
43
43
|
signing_type: Literal["eip712", "local"],
|
|
44
44
|
):
|
|
45
45
|
self.info = info
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
from collections.abc import Awaitable, Callable
|
|
1
2
|
from typing import Any
|
|
2
3
|
|
|
3
4
|
from eth_account import Account
|
|
4
5
|
|
|
5
|
-
from wayfinder_paths.core.types import HyperliquidSignCallback
|
|
6
|
-
|
|
7
6
|
|
|
8
7
|
def _resolve_private_key(config: dict[str, Any]) -> str | None:
|
|
9
8
|
"""Extract private key from config."""
|
|
@@ -26,23 +25,7 @@ def _resolve_private_key(config: dict[str, Any]) -> str | None:
|
|
|
26
25
|
return None
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
def create_local_signer(config: dict[str, Any]) ->
|
|
30
|
-
"""
|
|
31
|
-
Create a Hyperliquid signing callback using private key from config.
|
|
32
|
-
|
|
33
|
-
For local signing, the payload is a keccak hash (0x...) of the encoded EIP-712 typed data.
|
|
34
|
-
The callback signs with the private key and returns a signature dict.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
config: Configuration dict containing private key in strategy_wallet or main_wallet
|
|
38
|
-
|
|
39
|
-
Returns:
|
|
40
|
-
HyperliquidSignCallback that signs payloads with the local private key
|
|
41
|
-
|
|
42
|
-
Raises:
|
|
43
|
-
ValueError: If no private key found in config
|
|
44
|
-
"""
|
|
45
|
-
# Extract private key from config
|
|
28
|
+
def create_local_signer(config: dict[str, Any]) -> Callable[[dict], Awaitable[str]]:
|
|
46
29
|
private_key = _resolve_private_key(config)
|
|
47
30
|
if not private_key:
|
|
48
31
|
raise ValueError(
|
|
@@ -57,20 +40,6 @@ def create_local_signer(config: dict[str, Any]) -> HyperliquidSignCallback:
|
|
|
57
40
|
async def sign(
|
|
58
41
|
action: dict[str, Any], payload: str, address: str
|
|
59
42
|
) -> dict[str, str] | None:
|
|
60
|
-
"""
|
|
61
|
-
Sign a Hyperliquid action with local private key.
|
|
62
|
-
|
|
63
|
-
For local signing, payload is keccak hash (0x...) of encoded typed data.
|
|
64
|
-
Sign with account and return signature dict.
|
|
65
|
-
|
|
66
|
-
Args:
|
|
67
|
-
action: The action being signed (not used for local signing)
|
|
68
|
-
payload: Keccak hash (0x...) of encoded EIP-712 typed data
|
|
69
|
-
address: The address signing (validation check)
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
Signature dict {"r": "0x...", "s": "0x...", "v": 28} or None if validation fails
|
|
73
|
-
"""
|
|
74
43
|
# Verify address matches account
|
|
75
44
|
if address.lower() != account.address.lower():
|
|
76
45
|
return None
|