wayfinder-paths 0.1.22__py3-none-any.whl → 0.1.24__py3-none-any.whl
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.
Potentially problematic release.
This version of wayfinder-paths might be problematic. Click here for more details.
- wayfinder_paths/__init__.py +0 -4
- wayfinder_paths/adapters/balance_adapter/README.md +0 -1
- wayfinder_paths/adapters/balance_adapter/adapter.py +313 -167
- wayfinder_paths/adapters/balance_adapter/manifest.yaml +8 -0
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +41 -124
- wayfinder_paths/adapters/boros_adapter/__init__.py +17 -0
- wayfinder_paths/adapters/boros_adapter/adapter.py +1574 -0
- wayfinder_paths/adapters/boros_adapter/client.py +476 -0
- wayfinder_paths/adapters/boros_adapter/manifest.yaml +10 -0
- wayfinder_paths/adapters/boros_adapter/parsers.py +88 -0
- wayfinder_paths/adapters/boros_adapter/test_adapter.py +460 -0
- wayfinder_paths/adapters/boros_adapter/test_golden.py +156 -0
- wayfinder_paths/adapters/boros_adapter/types.py +70 -0
- wayfinder_paths/adapters/boros_adapter/utils.py +85 -0
- wayfinder_paths/adapters/brap_adapter/README.md +22 -75
- wayfinder_paths/adapters/brap_adapter/adapter.py +187 -576
- wayfinder_paths/adapters/brap_adapter/examples.json +21 -140
- wayfinder_paths/adapters/brap_adapter/manifest.yaml +9 -0
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +6 -234
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +180 -92
- wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +9 -0
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +82 -14
- wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +2 -9
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +586 -61
- wayfinder_paths/adapters/hyperliquid_adapter/executor.py +47 -68
- wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +14 -0
- wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +2 -3
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +17 -21
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +3 -6
- wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +4 -8
- wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +2 -2
- wayfinder_paths/adapters/ledger_adapter/README.md +4 -1
- wayfinder_paths/adapters/ledger_adapter/adapter.py +3 -3
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +7 -0
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +1 -2
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +649 -547
- wayfinder_paths/adapters/moonwell_adapter/manifest.yaml +14 -0
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +160 -239
- wayfinder_paths/adapters/multicall_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/multicall_adapter/adapter.py +166 -0
- wayfinder_paths/adapters/multicall_adapter/manifest.yaml +5 -0
- wayfinder_paths/adapters/multicall_adapter/test_adapter.py +97 -0
- wayfinder_paths/adapters/pendle_adapter/README.md +102 -0
- wayfinder_paths/adapters/pendle_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/pendle_adapter/adapter.py +1992 -0
- wayfinder_paths/adapters/pendle_adapter/examples.json +11 -0
- wayfinder_paths/adapters/pendle_adapter/manifest.yaml +21 -0
- wayfinder_paths/adapters/pendle_adapter/test_adapter.py +666 -0
- wayfinder_paths/adapters/pool_adapter/manifest.yaml +6 -0
- wayfinder_paths/adapters/token_adapter/adapter.py +14 -0
- wayfinder_paths/adapters/token_adapter/examples.json +0 -4
- wayfinder_paths/adapters/token_adapter/manifest.yaml +7 -0
- wayfinder_paths/conftest.py +24 -17
- wayfinder_paths/core/__init__.py +0 -3
- wayfinder_paths/core/adapters/BaseAdapter.py +0 -25
- wayfinder_paths/core/adapters/models.py +17 -7
- wayfinder_paths/core/clients/BRAPClient.py +4 -1
- wayfinder_paths/core/clients/ClientManager.py +0 -7
- wayfinder_paths/core/clients/LedgerClient.py +196 -172
- wayfinder_paths/core/clients/TokenClient.py +47 -1
- wayfinder_paths/core/clients/WayfinderClient.py +1 -3
- wayfinder_paths/core/clients/__init__.py +0 -5
- wayfinder_paths/core/clients/protocols.py +21 -35
- wayfinder_paths/core/clients/test_ledger_client.py +448 -0
- wayfinder_paths/core/config.py +10 -162
- wayfinder_paths/core/constants/__init__.py +73 -2
- wayfinder_paths/core/constants/base.py +8 -17
- wayfinder_paths/core/constants/chains.py +36 -0
- wayfinder_paths/core/constants/contracts.py +52 -0
- wayfinder_paths/core/constants/erc20_abi.py +0 -1
- wayfinder_paths/core/constants/hyperlend_abi.py +0 -4
- wayfinder_paths/core/constants/hyperliquid.py +16 -0
- wayfinder_paths/core/constants/moonwell_abi.py +0 -15
- wayfinder_paths/core/constants/tokens.py +9 -0
- wayfinder_paths/core/engine/manifest.py +66 -0
- wayfinder_paths/core/strategies/Strategy.py +0 -71
- wayfinder_paths/core/strategies/__init__.py +10 -1
- wayfinder_paths/core/strategies/opa_loop.py +167 -0
- wayfinder_paths/core/utils/evm_helpers.py +5 -15
- wayfinder_paths/core/utils/test_transaction.py +289 -0
- wayfinder_paths/core/utils/tokens.py +28 -0
- wayfinder_paths/core/utils/transaction.py +57 -8
- wayfinder_paths/core/utils/web3.py +8 -3
- wayfinder_paths/mcp/__init__.py +5 -0
- wayfinder_paths/mcp/preview.py +185 -0
- wayfinder_paths/mcp/scripting.py +84 -0
- wayfinder_paths/mcp/server.py +52 -0
- wayfinder_paths/mcp/state/profile_store.py +195 -0
- wayfinder_paths/mcp/state/store.py +89 -0
- wayfinder_paths/mcp/test_scripting.py +267 -0
- wayfinder_paths/mcp/tools/__init__.py +0 -0
- wayfinder_paths/mcp/tools/balances.py +290 -0
- wayfinder_paths/mcp/tools/discovery.py +158 -0
- wayfinder_paths/mcp/tools/execute.py +770 -0
- wayfinder_paths/mcp/tools/hyperliquid.py +931 -0
- wayfinder_paths/mcp/tools/quotes.py +288 -0
- wayfinder_paths/mcp/tools/run_script.py +286 -0
- wayfinder_paths/mcp/tools/strategies.py +188 -0
- wayfinder_paths/mcp/tools/tokens.py +46 -0
- wayfinder_paths/mcp/tools/wallets.py +354 -0
- wayfinder_paths/mcp/utils.py +129 -0
- wayfinder_paths/policies/enso.py +1 -2
- wayfinder_paths/policies/hyper_evm.py +6 -3
- wayfinder_paths/policies/hyperlend.py +1 -2
- wayfinder_paths/policies/hyperliquid.py +1 -1
- wayfinder_paths/policies/lifi.py +18 -0
- wayfinder_paths/policies/moonwell.py +12 -7
- wayfinder_paths/policies/prjx.py +1 -3
- wayfinder_paths/policies/util.py +8 -2
- wayfinder_paths/run_strategy.py +97 -300
- wayfinder_paths/strategies/basis_trading_strategy/constants.py +3 -1
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +47 -133
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +24 -53
- wayfinder_paths/strategies/boros_hype_strategy/__init__.py +3 -0
- wayfinder_paths/strategies/boros_hype_strategy/boros_ops_mixin.py +450 -0
- wayfinder_paths/strategies/boros_hype_strategy/constants.py +255 -0
- wayfinder_paths/strategies/boros_hype_strategy/examples.json +37 -0
- wayfinder_paths/strategies/boros_hype_strategy/hyperevm_ops_mixin.py +114 -0
- wayfinder_paths/strategies/boros_hype_strategy/hyperliquid_ops_mixin.py +642 -0
- wayfinder_paths/strategies/boros_hype_strategy/manifest.yaml +36 -0
- wayfinder_paths/strategies/boros_hype_strategy/planner.py +460 -0
- wayfinder_paths/strategies/boros_hype_strategy/risk_ops_mixin.py +886 -0
- wayfinder_paths/strategies/boros_hype_strategy/snapshot_mixin.py +494 -0
- wayfinder_paths/strategies/boros_hype_strategy/strategy.py +1194 -0
- wayfinder_paths/strategies/boros_hype_strategy/test_planner_golden.py +374 -0
- wayfinder_paths/{templates/strategy → strategies/boros_hype_strategy}/test_strategy.py +99 -63
- wayfinder_paths/strategies/boros_hype_strategy/types.py +365 -0
- wayfinder_paths/strategies/boros_hype_strategy/withdraw_mixin.py +997 -0
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +15 -23
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +27 -62
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +84 -58
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +5 -15
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +69 -164
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +43 -76
- wayfinder_paths/tests/test_mcp_quote_swap.py +165 -0
- wayfinder_paths/tests/test_test_coverage.py +1 -4
- wayfinder_paths-0.1.24.dist-info/METADATA +378 -0
- wayfinder_paths-0.1.24.dist-info/RECORD +185 -0
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.24.dist-info}/WHEEL +1 -1
- wayfinder_paths/core/clients/WalletClient.py +0 -41
- wayfinder_paths/core/engine/StrategyJob.py +0 -110
- wayfinder_paths/core/services/test_local_evm_txn.py +0 -145
- wayfinder_paths/scripts/create_strategy.py +0 -139
- wayfinder_paths/scripts/make_wallets.py +0 -142
- wayfinder_paths/templates/adapter/README.md +0 -150
- wayfinder_paths/templates/adapter/adapter.py +0 -16
- wayfinder_paths/templates/adapter/examples.json +0 -8
- wayfinder_paths/templates/adapter/test_adapter.py +0 -30
- wayfinder_paths/templates/strategy/README.md +0 -186
- wayfinder_paths/templates/strategy/examples.json +0 -11
- wayfinder_paths/templates/strategy/strategy.py +0 -35
- wayfinder_paths/tests/test_smoke_manifest.py +0 -63
- wayfinder_paths-0.1.22.dist-info/METADATA +0 -355
- wayfinder_paths-0.1.22.dist-info/RECORD +0 -129
- /wayfinder_paths/{scripts → mcp/state}/__init__.py +0 -0
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.24.dist-info}/LICENSE +0 -0
|
@@ -1,36 +1,17 @@
|
|
|
1
|
-
import sys
|
|
2
1
|
from pathlib import Path
|
|
3
2
|
from unittest.mock import AsyncMock
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
_wayfinder_path_str = str(_wayfinder_path_dir)
|
|
9
|
-
if _wayfinder_path_str not in sys.path:
|
|
10
|
-
sys.path.insert(0, _wayfinder_path_str)
|
|
11
|
-
elif sys.path.index(_wayfinder_path_str) > 0:
|
|
12
|
-
# Move to front to take precedence
|
|
13
|
-
sys.path.remove(_wayfinder_path_str)
|
|
14
|
-
sys.path.insert(0, _wayfinder_path_str)
|
|
15
|
-
|
|
16
|
-
import pytest # noqa: E402
|
|
17
|
-
|
|
18
|
-
try:
|
|
19
|
-
from tests.test_utils import get_canonical_examples, load_strategy_examples
|
|
20
|
-
except ImportError:
|
|
21
|
-
# Fallback if path setup didn't work
|
|
22
|
-
import importlib.util
|
|
23
|
-
|
|
24
|
-
test_utils_path = Path(_wayfinder_path_dir) / "tests" / "test_utils.py"
|
|
25
|
-
spec = importlib.util.spec_from_file_location("tests.test_utils", test_utils_path)
|
|
26
|
-
test_utils = importlib.util.module_from_spec(spec)
|
|
27
|
-
spec.loader.exec_module(test_utils)
|
|
28
|
-
get_canonical_examples = test_utils.get_canonical_examples
|
|
29
|
-
load_strategy_examples = test_utils.load_strategy_examples
|
|
30
|
-
|
|
31
|
-
from wayfinder_paths.strategies.stablecoin_yield_strategy.strategy import ( # noqa: E402
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from wayfinder_paths.strategies.stablecoin_yield_strategy.strategy import (
|
|
32
7
|
StablecoinYieldStrategy,
|
|
33
8
|
)
|
|
9
|
+
from wayfinder_paths.tests.test_utils import (
|
|
10
|
+
get_canonical_examples,
|
|
11
|
+
load_strategy_examples,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
_StablecoinYieldStrategy = StablecoinYieldStrategy
|
|
34
15
|
|
|
35
16
|
|
|
36
17
|
@pytest.fixture
|
|
@@ -47,17 +28,14 @@ def strategy():
|
|
|
47
28
|
)
|
|
48
29
|
|
|
49
30
|
if hasattr(s, "balance_adapter") and s.balance_adapter:
|
|
50
|
-
usdc_balance_mock = AsyncMock(return_value=(True, 60000000))
|
|
51
|
-
gas_balance_mock = AsyncMock(return_value=(True, 2000000000000000))
|
|
52
31
|
|
|
53
|
-
def get_balance_side_effect(
|
|
54
|
-
token_id =
|
|
55
|
-
|
|
56
|
-
)
|
|
32
|
+
def get_balance_side_effect(
|
|
33
|
+
*, wallet_address, token_id=None, token_address=None, chain_id=None
|
|
34
|
+
):
|
|
57
35
|
if token_id == "usd-coin-base" or token_id == "usd-coin":
|
|
58
|
-
return
|
|
36
|
+
return (True, 60000000)
|
|
59
37
|
elif token_id == "ethereum-base" or token_id == "ethereum":
|
|
60
|
-
return
|
|
38
|
+
return (True, 2000000000000000)
|
|
61
39
|
return (True, 1000000000)
|
|
62
40
|
|
|
63
41
|
s.balance_adapter.get_balance = AsyncMock(side_effect=get_balance_side_effect)
|
|
@@ -111,10 +89,10 @@ def strategy():
|
|
|
111
89
|
|
|
112
90
|
if hasattr(s, "balance_adapter") and s.balance_adapter:
|
|
113
91
|
s.balance_adapter.move_from_main_wallet_to_strategy_wallet = AsyncMock(
|
|
114
|
-
return_value=(True, "
|
|
92
|
+
return_value=(True, "0xtxhash_transfer")
|
|
115
93
|
)
|
|
116
94
|
s.balance_adapter.move_from_strategy_wallet_to_main_wallet = AsyncMock(
|
|
117
|
-
return_value=(True, "
|
|
95
|
+
return_value=(True, "0xtxhash_transfer")
|
|
118
96
|
)
|
|
119
97
|
|
|
120
98
|
if hasattr(s, "ledger_adapter") and s.ledger_adapter:
|
|
@@ -154,46 +132,36 @@ def strategy():
|
|
|
154
132
|
|
|
155
133
|
if hasattr(s, "brap_adapter") and s.brap_adapter:
|
|
156
134
|
|
|
157
|
-
def
|
|
135
|
+
def best_quote_side_effect(*args, **kwargs):
|
|
158
136
|
to_token_address = kwargs.get("to_token_address", "")
|
|
159
137
|
if to_token_address == "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913":
|
|
160
|
-
return (
|
|
161
|
-
True,
|
|
162
|
-
{
|
|
163
|
-
"quotes": {
|
|
164
|
-
"best_quote": {
|
|
165
|
-
"output_amount": "99900000",
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
)
|
|
138
|
+
return (True, {"output_amount": "99900000"})
|
|
170
139
|
return (
|
|
171
140
|
True,
|
|
172
141
|
{
|
|
173
|
-
"
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
"fromToken": {"symbol": "USDC"},
|
|
181
|
-
"toToken": {"symbol": "POOL"},
|
|
182
|
-
}
|
|
183
|
-
}
|
|
142
|
+
"output_amount": "105000000",
|
|
143
|
+
"input_amount": "50000000000000",
|
|
144
|
+
"toAmount": "105000000",
|
|
145
|
+
"estimatedGas": "1000000000",
|
|
146
|
+
"fromAmount": "100000000",
|
|
147
|
+
"fromToken": {"symbol": "USDC"},
|
|
148
|
+
"toToken": {"symbol": "POOL"},
|
|
184
149
|
},
|
|
185
150
|
)
|
|
186
151
|
|
|
187
|
-
s.brap_adapter.
|
|
188
|
-
side_effect=get_swap_quote_side_effect
|
|
189
|
-
)
|
|
152
|
+
s.brap_adapter.best_quote = AsyncMock(side_effect=best_quote_side_effect)
|
|
190
153
|
|
|
191
154
|
if (
|
|
192
155
|
hasattr(s, "brap_adapter")
|
|
193
156
|
and s.brap_adapter
|
|
194
157
|
and hasattr(s.brap_adapter, "swap_from_quote")
|
|
195
158
|
):
|
|
196
|
-
s.brap_adapter.swap_from_quote = AsyncMock(
|
|
159
|
+
s.brap_adapter.swap_from_quote = AsyncMock(
|
|
160
|
+
return_value=(
|
|
161
|
+
True,
|
|
162
|
+
{"tx_hash": "0xmockhash", "from_amount": "100", "to_amount": "99"},
|
|
163
|
+
)
|
|
164
|
+
)
|
|
197
165
|
|
|
198
166
|
s.DEPOSIT_USDC = 0
|
|
199
167
|
s.usdc_token_info = {
|
|
@@ -408,13 +376,9 @@ async def test_deposit_tracks_usdc(strategy):
|
|
|
408
376
|
|
|
409
377
|
@pytest.mark.asyncio
|
|
410
378
|
async def test_sweep_wallet_uses_tracked_tokens(strategy):
|
|
411
|
-
from wayfinder_paths.strategies.stablecoin_yield_strategy.strategy import (
|
|
412
|
-
StablecoinYieldStrategy,
|
|
413
|
-
)
|
|
414
|
-
|
|
415
379
|
# Restore the real _sweep_wallet method (fixture mocks it as a no-op)
|
|
416
|
-
strategy._sweep_wallet =
|
|
417
|
-
strategy,
|
|
380
|
+
strategy._sweep_wallet = _StablecoinYieldStrategy._sweep_wallet.__get__(
|
|
381
|
+
strategy, _StablecoinYieldStrategy
|
|
418
382
|
)
|
|
419
383
|
|
|
420
384
|
strategy._track_token("token-1", 1000000)
|
|
@@ -426,8 +390,9 @@ async def test_sweep_wallet_uses_tracked_tokens(strategy):
|
|
|
426
390
|
assert "token-2" in strategy.tracked_token_ids
|
|
427
391
|
|
|
428
392
|
# Mock balance adapter to return fresh balances
|
|
429
|
-
|
|
430
|
-
token_id =
|
|
393
|
+
def get_balance_mock(
|
|
394
|
+
*, wallet_address, token_id=None, token_address=None, chain_id=None
|
|
395
|
+
):
|
|
431
396
|
balance = strategy.tracked_balances.get(token_id, 0)
|
|
432
397
|
return (True, int(balance) if balance else 0)
|
|
433
398
|
|
|
@@ -468,8 +433,9 @@ async def test_get_non_gas_balances_uses_tracked_state(strategy):
|
|
|
468
433
|
strategy._track_token(pool_token_id, 50000000000000000000)
|
|
469
434
|
|
|
470
435
|
# Mock refresh
|
|
471
|
-
def _get_balance_effect(
|
|
472
|
-
token_id =
|
|
436
|
+
def _get_balance_effect(
|
|
437
|
+
*, wallet_address, token_id=None, token_address=None, chain_id=None
|
|
438
|
+
):
|
|
473
439
|
return (True, strategy.tracked_balances.get(token_id, 0))
|
|
474
440
|
|
|
475
441
|
strategy.balance_adapter.get_balance = AsyncMock(side_effect=_get_balance_effect)
|
|
@@ -488,8 +454,9 @@ async def test_partial_liquidate_uses_tracked_tokens(strategy):
|
|
|
488
454
|
strategy._track_token("test-pool-base", 100000000000000000000)
|
|
489
455
|
|
|
490
456
|
# Mock balance and token adapters
|
|
491
|
-
def _get_balance_effect_partial(
|
|
492
|
-
token_id =
|
|
457
|
+
def _get_balance_effect_partial(
|
|
458
|
+
*, wallet_address, token_id=None, token_address=None, chain_id=None
|
|
459
|
+
):
|
|
493
460
|
return (True, strategy.tracked_balances.get(token_id, 0))
|
|
494
461
|
|
|
495
462
|
strategy.balance_adapter.get_balance = AsyncMock(
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import AsyncMock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from wayfinder_paths.mcp.tools.quotes import quote_swap
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.asyncio
|
|
11
|
+
async def test_quote_swap_returns_compact_best_quote_by_default():
|
|
12
|
+
fake_wallet = {"address": "0x000000000000000000000000000000000000dEaD"}
|
|
13
|
+
|
|
14
|
+
class FakeTokenClient:
|
|
15
|
+
async def get_gas_token(self, chain_code: str):
|
|
16
|
+
assert chain_code == "arbitrum"
|
|
17
|
+
return {
|
|
18
|
+
"token_id": "ethereum-arbitrum",
|
|
19
|
+
"asset_id": "ethereum",
|
|
20
|
+
"symbol": "ETH",
|
|
21
|
+
"decimals": 18,
|
|
22
|
+
"chain_id": 42161,
|
|
23
|
+
"address": "0x0000000000000000000000000000000000000000",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async def get_token_details(self, query: str):
|
|
27
|
+
assert query == "usd-coin-arbitrum"
|
|
28
|
+
return {
|
|
29
|
+
"token_id": "usd-coin-arbitrum",
|
|
30
|
+
"asset_id": "usd-coin",
|
|
31
|
+
"symbol": "USDC",
|
|
32
|
+
"decimals": 6,
|
|
33
|
+
"chain_id": 42161,
|
|
34
|
+
"address": "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class FakeBRAPClient:
|
|
38
|
+
async def get_quote(self, **kwargs): # noqa: ANN003
|
|
39
|
+
assert kwargs["from_chain_id"] == 42161
|
|
40
|
+
assert kwargs["to_chain_id"] == 42161
|
|
41
|
+
assert kwargs["slippage"] == pytest.approx(0.005)
|
|
42
|
+
|
|
43
|
+
calldata = "0x" + ("ab" * 4096)
|
|
44
|
+
return {
|
|
45
|
+
"quotes": {
|
|
46
|
+
"quote_count": 3,
|
|
47
|
+
"best_quote": {
|
|
48
|
+
"provider": "brap_best",
|
|
49
|
+
"input_amount": kwargs["amount1"],
|
|
50
|
+
"output_amount": "1234567",
|
|
51
|
+
"input_amount_usd": 5.0,
|
|
52
|
+
"output_amount_usd": 4.99,
|
|
53
|
+
"gas_estimate": 210000,
|
|
54
|
+
"fee_estimate": {"total_usd": 0.01},
|
|
55
|
+
"native_input": True,
|
|
56
|
+
"native_output": False,
|
|
57
|
+
"calldata": calldata,
|
|
58
|
+
"wrap_transaction": None,
|
|
59
|
+
"unwrap_transaction": None,
|
|
60
|
+
},
|
|
61
|
+
"all_quotes": [
|
|
62
|
+
{"provider": "brap_best"},
|
|
63
|
+
{"provider": "brap_alt"},
|
|
64
|
+
{"provider": "brap_alt"},
|
|
65
|
+
],
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
with (
|
|
70
|
+
patch(
|
|
71
|
+
"wayfinder_paths.mcp.tools.quotes.find_wallet_by_label",
|
|
72
|
+
return_value=fake_wallet,
|
|
73
|
+
),
|
|
74
|
+
patch(
|
|
75
|
+
"wayfinder_paths.mcp.tools.quotes.TokenClient",
|
|
76
|
+
return_value=FakeTokenClient(),
|
|
77
|
+
),
|
|
78
|
+
patch(
|
|
79
|
+
"wayfinder_paths.mcp.tools.quotes.BRAPClient", return_value=FakeBRAPClient()
|
|
80
|
+
),
|
|
81
|
+
):
|
|
82
|
+
out = await quote_swap(
|
|
83
|
+
wallet_label="main",
|
|
84
|
+
from_token="ethereum-arbitrum",
|
|
85
|
+
to_token="usd-coin-arbitrum",
|
|
86
|
+
amount="0.0017",
|
|
87
|
+
slippage_bps=50,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
assert out["ok"] is True
|
|
91
|
+
res = out["result"]
|
|
92
|
+
assert "raw" not in res["quote"]
|
|
93
|
+
|
|
94
|
+
best = res["quote"]["best_quote"]
|
|
95
|
+
assert best["provider"] == "brap_best"
|
|
96
|
+
assert best["output_amount"] == "1234567"
|
|
97
|
+
assert best["calldata_len"] > 0
|
|
98
|
+
assert "calldata" not in best
|
|
99
|
+
assert res["quote"]["quote_count"] == 3
|
|
100
|
+
assert res["quote"]["providers"] == ["brap_best", "brap_alt"]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.asyncio
|
|
104
|
+
async def test_quote_swap_can_include_calldata_when_requested():
|
|
105
|
+
fake_wallet = {"address": "0x000000000000000000000000000000000000dEaD"}
|
|
106
|
+
token_client = AsyncMock()
|
|
107
|
+
token_client.get_gas_token = AsyncMock(
|
|
108
|
+
return_value={
|
|
109
|
+
"token_id": "ethereum-arbitrum",
|
|
110
|
+
"asset_id": "ethereum",
|
|
111
|
+
"symbol": "ETH",
|
|
112
|
+
"decimals": 18,
|
|
113
|
+
"chain_id": 42161,
|
|
114
|
+
"address": "0x0000000000000000000000000000000000000000",
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
token_client.get_token_details = AsyncMock(
|
|
118
|
+
return_value={
|
|
119
|
+
"token_id": "usd-coin-arbitrum",
|
|
120
|
+
"asset_id": "usd-coin",
|
|
121
|
+
"symbol": "USDC",
|
|
122
|
+
"decimals": 6,
|
|
123
|
+
"chain_id": 42161,
|
|
124
|
+
"address": "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
calldata = "0x" + ("cd" * 1024)
|
|
129
|
+
brap_client = AsyncMock()
|
|
130
|
+
brap_client.get_quote = AsyncMock(
|
|
131
|
+
return_value={
|
|
132
|
+
"quotes": {
|
|
133
|
+
"quote_count": 1,
|
|
134
|
+
"best_quote": {
|
|
135
|
+
"provider": "brap_best",
|
|
136
|
+
"output_amount": "1",
|
|
137
|
+
"calldata": calldata,
|
|
138
|
+
},
|
|
139
|
+
"all_quotes": [{"provider": "brap_best"}],
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
with (
|
|
145
|
+
patch(
|
|
146
|
+
"wayfinder_paths.mcp.tools.quotes.find_wallet_by_label",
|
|
147
|
+
return_value=fake_wallet,
|
|
148
|
+
),
|
|
149
|
+
patch(
|
|
150
|
+
"wayfinder_paths.mcp.tools.quotes.TokenClient", return_value=token_client
|
|
151
|
+
),
|
|
152
|
+
patch("wayfinder_paths.mcp.tools.quotes.BRAPClient", return_value=brap_client),
|
|
153
|
+
):
|
|
154
|
+
out = await quote_swap(
|
|
155
|
+
wallet_label="main",
|
|
156
|
+
from_token="ethereum-arbitrum",
|
|
157
|
+
to_token="usd-coin-arbitrum",
|
|
158
|
+
amount="0.0017",
|
|
159
|
+
slippage_bps=50,
|
|
160
|
+
include_calldata=True,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
assert out["ok"] is True
|
|
164
|
+
best = out["result"]["quote"]["best_quote"]
|
|
165
|
+
assert best["calldata"] == calldata
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import json
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
@@ -105,7 +106,6 @@ def test_strategy_tests_use_examples_json():
|
|
|
105
106
|
if not test_file.exists():
|
|
106
107
|
continue
|
|
107
108
|
|
|
108
|
-
# Read the test file content
|
|
109
109
|
try:
|
|
110
110
|
content = test_file.read_text()
|
|
111
111
|
|
|
@@ -162,8 +162,6 @@ def test_strategy_examples_have_smoke():
|
|
|
162
162
|
if not strategies_dir.exists():
|
|
163
163
|
pytest.skip("Strategies directory not found")
|
|
164
164
|
|
|
165
|
-
import json
|
|
166
|
-
|
|
167
165
|
missing_smoke = []
|
|
168
166
|
|
|
169
167
|
# Find all strategy directories
|
|
@@ -174,7 +172,6 @@ def test_strategy_examples_have_smoke():
|
|
|
174
172
|
strategy_py = strategy_dir / "strategy.py"
|
|
175
173
|
examples_json = strategy_dir / "examples.json"
|
|
176
174
|
|
|
177
|
-
# Only check strategies that exist
|
|
178
175
|
if not strategy_py.exists():
|
|
179
176
|
continue
|
|
180
177
|
|