wayfinder-paths 0.1.22__py3-none-any.whl → 0.1.23__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 +65 -169
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +41 -113
- 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/test_adapter.py +6 -234
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +39 -86
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +5 -1
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +6 -5
- wayfinder_paths/adapters/ledger_adapter/README.md +4 -1
- wayfinder_paths/adapters/ledger_adapter/adapter.py +3 -3
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +108 -198
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +37 -23
- wayfinder_paths/adapters/token_adapter/adapter.py +14 -0
- wayfinder_paths/core/__init__.py +0 -3
- wayfinder_paths/core/clients/BRAPClient.py +3 -0
- wayfinder_paths/core/clients/ClientManager.py +0 -7
- wayfinder_paths/core/clients/LedgerClient.py +196 -172
- wayfinder_paths/core/clients/WayfinderClient.py +0 -1
- wayfinder_paths/core/clients/__init__.py +0 -5
- wayfinder_paths/core/clients/protocols.py +0 -13
- wayfinder_paths/core/config.py +0 -164
- wayfinder_paths/core/constants/__init__.py +58 -2
- wayfinder_paths/core/constants/base.py +8 -22
- wayfinder_paths/core/constants/chains.py +36 -0
- wayfinder_paths/core/constants/contracts.py +39 -0
- wayfinder_paths/core/constants/tokens.py +9 -0
- wayfinder_paths/core/strategies/Strategy.py +0 -10
- wayfinder_paths/core/utils/evm_helpers.py +5 -15
- wayfinder_paths/core/utils/tokens.py +28 -0
- wayfinder_paths/core/utils/transaction.py +13 -7
- wayfinder_paths/core/utils/web3.py +5 -3
- 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/moonwell.py +12 -7
- wayfinder_paths/policies/prjx.py +1 -3
- 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 +19 -14
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +12 -11
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +20 -33
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +21 -18
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +69 -130
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +32 -42
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.23.dist-info}/METADATA +2 -3
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.23.dist-info}/RECORD +51 -60
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.23.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/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/templates/strategy/test_strategy.py +0 -166
- wayfinder_paths/tests/test_smoke_manifest.py +0 -63
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.23.dist-info}/LICENSE +0 -0
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
# TODO: Replace MyStrategy with your actual strategy class name
|
|
5
|
-
from wayfinder_paths.strategies.your_strategy.strategy import (
|
|
6
|
-
MyStrategy, # noqa: E402
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
# Ensure wayfinder-paths is on path for tests.test_utils import
|
|
10
|
-
# This is a workaround until conftest loading order is resolved
|
|
11
|
-
_wayfinder_path_dir = Path(__file__).parent.parent.parent.parent.resolve()
|
|
12
|
-
_wayfinder_path_str = str(_wayfinder_path_dir)
|
|
13
|
-
if _wayfinder_path_str not in sys.path:
|
|
14
|
-
sys.path.insert(0, _wayfinder_path_str)
|
|
15
|
-
elif sys.path.index(_wayfinder_path_str) > 0:
|
|
16
|
-
# Move to front to take precedence
|
|
17
|
-
sys.path.remove(_wayfinder_path_str)
|
|
18
|
-
sys.path.insert(0, _wayfinder_path_str)
|
|
19
|
-
|
|
20
|
-
import pytest # noqa: E402
|
|
21
|
-
|
|
22
|
-
try:
|
|
23
|
-
from tests.test_utils import get_canonical_examples, load_strategy_examples
|
|
24
|
-
except ImportError:
|
|
25
|
-
# Fallback if path setup didn't work
|
|
26
|
-
import importlib.util
|
|
27
|
-
|
|
28
|
-
test_utils_path = Path(_wayfinder_path_dir) / "tests" / "test_utils.py"
|
|
29
|
-
spec = importlib.util.spec_from_file_location("tests.test_utils", test_utils_path)
|
|
30
|
-
test_utils = importlib.util.module_from_spec(spec)
|
|
31
|
-
spec.loader.exec_module(test_utils)
|
|
32
|
-
get_canonical_examples = test_utils.get_canonical_examples
|
|
33
|
-
load_strategy_examples = test_utils.load_strategy_examples
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@pytest.fixture
|
|
37
|
-
def strategy():
|
|
38
|
-
mock_config = {
|
|
39
|
-
"main_wallet": {"address": "0x1234567890123456789012345678901234567890"},
|
|
40
|
-
"strategy_wallet": {"address": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"},
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
s = MyStrategy(
|
|
44
|
-
config=mock_config,
|
|
45
|
-
main_wallet=mock_config["main_wallet"],
|
|
46
|
-
strategy_wallet=mock_config["strategy_wallet"],
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
# TODO: Add mocking for your adapters here if needed
|
|
50
|
-
# Example for balance_adapter:
|
|
51
|
-
# if hasattr(s, "balance_adapter") and s.balance_adapter:
|
|
52
|
-
# usdc_balance_mock = AsyncMock(return_value=(True, 60000000))
|
|
53
|
-
# gas_balance_mock = AsyncMock(return_value=(True, 2000000000000000))
|
|
54
|
-
#
|
|
55
|
-
# def get_balance_side_effect(query, wallet_address, **kwargs):
|
|
56
|
-
# token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
57
|
-
# if token_id == "usd-coin-base" or token_id == "usd-coin":
|
|
58
|
-
# elif token_id == "ethereum-base" or token_id == "ethereum":
|
|
59
|
-
#
|
|
60
|
-
# s.balance_adapter.get_balance = AsyncMock(
|
|
61
|
-
# side_effect=get_balance_side_effect
|
|
62
|
-
# )
|
|
63
|
-
|
|
64
|
-
# Example for token_adapter:
|
|
65
|
-
# if hasattr(s, "token_adapter") and s.token_adapter:
|
|
66
|
-
# default_token = {
|
|
67
|
-
# "id": "usd-coin-base",
|
|
68
|
-
# "symbol": "USDC",
|
|
69
|
-
# "name": "USD Coin",
|
|
70
|
-
# "decimals": 6,
|
|
71
|
-
# "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
72
|
-
# "chain": {"code": "base", "id": 8453, "name": "Base"},
|
|
73
|
-
# }
|
|
74
|
-
# s.token_adapter.get_token = AsyncMock(return_value=(True, default_token))
|
|
75
|
-
# s.token_adapter.get_gas_token = AsyncMock(return_value=(True, default_token))
|
|
76
|
-
|
|
77
|
-
# Example for transaction adapters:
|
|
78
|
-
# if hasattr(s, "tx_adapter") and s.tx_adapter:
|
|
79
|
-
# s.tx_adapter.move_from_main_wallet_to_strategy_wallet = AsyncMock(
|
|
80
|
-
# )
|
|
81
|
-
# s.tx_adapter.move_from_strategy_wallet_to_main_wallet = AsyncMock(
|
|
82
|
-
# )
|
|
83
|
-
|
|
84
|
-
# Example for ledger_adapter:
|
|
85
|
-
# if hasattr(s, "ledger_adapter") and s.ledger_adapter:
|
|
86
|
-
# s.ledger_adapter.get_strategy_net_deposit = AsyncMock(
|
|
87
|
-
# )
|
|
88
|
-
# s.ledger_adapter.get_strategy_transactions = AsyncMock(
|
|
89
|
-
# )
|
|
90
|
-
|
|
91
|
-
return s
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
@pytest.mark.asyncio
|
|
95
|
-
@pytest.mark.smoke
|
|
96
|
-
async def test_smoke(strategy):
|
|
97
|
-
examples = load_strategy_examples(Path(__file__))
|
|
98
|
-
smoke_data = examples["smoke"]
|
|
99
|
-
|
|
100
|
-
st = await strategy.status()
|
|
101
|
-
assert isinstance(st, dict)
|
|
102
|
-
assert "portfolio_value" in st or "net_deposit" in st or "strategy_status" in st
|
|
103
|
-
|
|
104
|
-
deposit_params = smoke_data.get("deposit", {})
|
|
105
|
-
ok, msg = await strategy.deposit(**deposit_params)
|
|
106
|
-
assert isinstance(ok, bool)
|
|
107
|
-
assert isinstance(msg, str)
|
|
108
|
-
|
|
109
|
-
ok, msg = await strategy.update(**smoke_data.get("update", {}))
|
|
110
|
-
assert isinstance(ok, bool)
|
|
111
|
-
|
|
112
|
-
ok, msg = await strategy.withdraw(**smoke_data.get("withdraw", {}))
|
|
113
|
-
assert isinstance(ok, bool)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
@pytest.mark.asyncio
|
|
117
|
-
async def test_canonical_usage(strategy):
|
|
118
|
-
examples = load_strategy_examples(Path(__file__))
|
|
119
|
-
canonical = get_canonical_examples(examples)
|
|
120
|
-
|
|
121
|
-
for example_name, example_data in canonical.items():
|
|
122
|
-
if "deposit" in example_data:
|
|
123
|
-
deposit_params = example_data.get("deposit", {})
|
|
124
|
-
ok, _ = await strategy.deposit(**deposit_params)
|
|
125
|
-
assert ok, f"Canonical example '{example_name}' deposit failed"
|
|
126
|
-
|
|
127
|
-
if "update" in example_data:
|
|
128
|
-
ok, msg = await strategy.update()
|
|
129
|
-
assert ok, f"Canonical example '{example_name}' update failed: {msg}"
|
|
130
|
-
|
|
131
|
-
if "status" in example_data:
|
|
132
|
-
st = await strategy.status()
|
|
133
|
-
assert isinstance(st, dict), (
|
|
134
|
-
f"Canonical example '{example_name}' status failed"
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
@pytest.mark.asyncio
|
|
139
|
-
async def test_error_cases(strategy):
|
|
140
|
-
examples = load_strategy_examples(Path(__file__))
|
|
141
|
-
|
|
142
|
-
for example_name, example_data in examples.items():
|
|
143
|
-
if isinstance(example_data, dict) and "expect" in example_data:
|
|
144
|
-
expect = example_data.get("expect", {})
|
|
145
|
-
|
|
146
|
-
if "deposit" in example_data:
|
|
147
|
-
deposit_params = example_data.get("deposit", {})
|
|
148
|
-
ok, _ = await strategy.deposit(**deposit_params)
|
|
149
|
-
|
|
150
|
-
if expect.get("success") is False:
|
|
151
|
-
assert ok is False, (
|
|
152
|
-
f"Expected {example_name} deposit to fail but it succeeded"
|
|
153
|
-
)
|
|
154
|
-
elif expect.get("success") is True:
|
|
155
|
-
assert ok is True, (
|
|
156
|
-
f"Expected {example_name} deposit to succeed but it failed"
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
if "update" in example_data:
|
|
160
|
-
ok, _ = await strategy.update()
|
|
161
|
-
if "success" in expect:
|
|
162
|
-
expected_success = expect.get("success")
|
|
163
|
-
assert ok == expected_success, (
|
|
164
|
-
f"Expected {example_name} update to "
|
|
165
|
-
f"{'succeed' if expected_success else 'fail'} but got opposite"
|
|
166
|
-
)
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
|
|
4
|
-
from wayfinder_paths.core.strategies.Strategy import StatusDict, StatusTuple, Strategy
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class FakeAdapter(BaseAdapter):
|
|
8
|
-
adapter_type = "FAKE"
|
|
9
|
-
|
|
10
|
-
async def connect(self) -> bool:
|
|
11
|
-
return True
|
|
12
|
-
|
|
13
|
-
async def get_balance(self, asset: str):
|
|
14
|
-
return {"asset": asset, "amount": 100}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class FakeLedgerAdapter(BaseAdapter):
|
|
18
|
-
adapter_type = "LEDGER"
|
|
19
|
-
|
|
20
|
-
async def connect(self) -> bool:
|
|
21
|
-
return True
|
|
22
|
-
|
|
23
|
-
async def record_strategy_snapshot(self, **kwargs):
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class FakeStrategy(Strategy):
|
|
28
|
-
name = "Fake Strategy"
|
|
29
|
-
|
|
30
|
-
async def deposit(self, amount: float = 0) -> StatusTuple:
|
|
31
|
-
return (True, "deposited")
|
|
32
|
-
|
|
33
|
-
async def update(self) -> StatusTuple:
|
|
34
|
-
return (True, "updated")
|
|
35
|
-
|
|
36
|
-
async def withdraw(self, amount: float = 0) -> StatusTuple:
|
|
37
|
-
return (True, "withdrew")
|
|
38
|
-
|
|
39
|
-
async def _status(self) -> StatusDict:
|
|
40
|
-
return {"total_earned": 0.0, "strategy_status": {"ok": True}}
|
|
41
|
-
|
|
42
|
-
@staticmethod
|
|
43
|
-
def policy() -> str:
|
|
44
|
-
return "wallet.id == 'TEST'"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
@pytest.mark.asyncio
|
|
48
|
-
async def test_smoke_deposit_update_withdraw_status():
|
|
49
|
-
s = FakeStrategy(
|
|
50
|
-
config={
|
|
51
|
-
"strategy_wallet": {"address": "0x1234567890123456789012345678901234567890"}
|
|
52
|
-
}
|
|
53
|
-
)
|
|
54
|
-
s.register_adapters([FakeAdapter("fake")])
|
|
55
|
-
s.ledger_adapter = FakeLedgerAdapter("ledger")
|
|
56
|
-
ok, msg = await s.deposit(amount=1)
|
|
57
|
-
assert ok
|
|
58
|
-
ok, msg = await s.update()
|
|
59
|
-
assert ok
|
|
60
|
-
ok, msg = await s.withdraw(amount=1)
|
|
61
|
-
assert ok
|
|
62
|
-
st = await s.status()
|
|
63
|
-
assert "total_earned" in st
|
|
File without changes
|