wayfinder-paths 0.1.23__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/adapters/balance_adapter/adapter.py +250 -0
- wayfinder_paths/adapters/balance_adapter/manifest.yaml +8 -0
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -11
- 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/adapter.py +1 -1
- wayfinder_paths/adapters/brap_adapter/manifest.yaml +9 -0
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +161 -26
- wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +9 -0
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +77 -13
- wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +2 -9
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +585 -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/manifest.yaml +7 -0
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +1 -2
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +592 -400
- wayfinder_paths/adapters/moonwell_adapter/manifest.yaml +14 -0
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +126 -219
- 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/examples.json +0 -4
- wayfinder_paths/adapters/token_adapter/manifest.yaml +7 -0
- wayfinder_paths/conftest.py +24 -17
- wayfinder_paths/core/adapters/BaseAdapter.py +0 -25
- wayfinder_paths/core/adapters/models.py +17 -7
- wayfinder_paths/core/clients/BRAPClient.py +1 -1
- wayfinder_paths/core/clients/TokenClient.py +47 -1
- wayfinder_paths/core/clients/WayfinderClient.py +1 -2
- wayfinder_paths/core/clients/protocols.py +21 -22
- wayfinder_paths/core/clients/test_ledger_client.py +448 -0
- wayfinder_paths/core/config.py +12 -0
- wayfinder_paths/core/constants/__init__.py +15 -0
- wayfinder_paths/core/constants/base.py +6 -1
- wayfinder_paths/core/constants/contracts.py +39 -26
- 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/engine/manifest.py +66 -0
- wayfinder_paths/core/strategies/Strategy.py +0 -61
- wayfinder_paths/core/strategies/__init__.py +10 -1
- wayfinder_paths/core/strategies/opa_loop.py +167 -0
- wayfinder_paths/core/utils/test_transaction.py +289 -0
- wayfinder_paths/core/utils/transaction.py +44 -1
- wayfinder_paths/core/utils/web3.py +3 -0
- 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/hyperliquid.py +1 -1
- wayfinder_paths/policies/lifi.py +18 -0
- wayfinder_paths/policies/util.py +8 -2
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +28 -119
- 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/strategies/boros_hype_strategy/test_strategy.py +202 -0
- 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 +3 -12
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +7 -29
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +63 -40
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +5 -15
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +0 -34
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +11 -34
- 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.23.dist-info → wayfinder_paths-0.1.24.dist-info}/WHEEL +1 -1
- wayfinder_paths/scripts/create_strategy.py +0 -139
- wayfinder_paths/scripts/make_wallets.py +0 -142
- wayfinder_paths-0.1.23.dist-info/METADATA +0 -354
- wayfinder_paths-0.1.23.dist-info/RECORD +0 -120
- /wayfinder_paths/{scripts → mcp/state}/__init__.py +0 -0
- {wayfinder_paths-0.1.23.dist-info → wayfinder_paths-0.1.24.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
schema_version: "0.1"
|
|
2
|
+
entrypoint: "adapters.moonwell_adapter.adapter.MoonwellAdapter"
|
|
3
|
+
capabilities:
|
|
4
|
+
- "lending.lend"
|
|
5
|
+
- "lending.unlend"
|
|
6
|
+
- "lending.borrow"
|
|
7
|
+
- "lending.repay"
|
|
8
|
+
- "collateral.set"
|
|
9
|
+
- "collateral.remove"
|
|
10
|
+
- "rewards.claim"
|
|
11
|
+
- "position.read"
|
|
12
|
+
- "market.apy"
|
|
13
|
+
- "market.collateral_factor"
|
|
14
|
+
dependencies: []
|
|
@@ -2,13 +2,22 @@ from contextlib import asynccontextmanager
|
|
|
2
2
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
5
|
+
from web3 import Web3
|
|
5
6
|
|
|
6
7
|
from wayfinder_paths.adapters.moonwell_adapter.adapter import (
|
|
7
|
-
|
|
8
|
+
CHAIN_NAME,
|
|
8
9
|
MANTISSA,
|
|
9
|
-
MOONWELL_DEFAULTS,
|
|
10
10
|
MoonwellAdapter,
|
|
11
11
|
)
|
|
12
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
13
|
+
BASE_USDC,
|
|
14
|
+
BASE_WETH,
|
|
15
|
+
MOONWELL_M_USDC,
|
|
16
|
+
MOONWELL_M_WETH,
|
|
17
|
+
MOONWELL_M_WSTETH,
|
|
18
|
+
MOONWELL_REWARD_DISTRIBUTOR,
|
|
19
|
+
MOONWELL_WELL_TOKEN,
|
|
20
|
+
)
|
|
12
21
|
|
|
13
22
|
|
|
14
23
|
class TestMoonwellAdapter:
|
|
@@ -22,38 +31,73 @@ class TestMoonwellAdapter:
|
|
|
22
31
|
def test_adapter_type(self, adapter):
|
|
23
32
|
assert adapter.adapter_type == "MOONWELL"
|
|
24
33
|
|
|
25
|
-
def
|
|
26
|
-
assert
|
|
27
|
-
adapter.comptroller_address.lower()
|
|
28
|
-
== MOONWELL_DEFAULTS["comptroller"].lower()
|
|
29
|
-
)
|
|
30
|
-
assert (
|
|
31
|
-
adapter.reward_distributor_address.lower()
|
|
32
|
-
== MOONWELL_DEFAULTS["reward_distributor"].lower()
|
|
33
|
-
)
|
|
34
|
-
assert adapter.m_usdc.lower() == MOONWELL_DEFAULTS["m_usdc"].lower()
|
|
35
|
-
assert adapter.m_weth.lower() == MOONWELL_DEFAULTS["m_weth"].lower()
|
|
36
|
-
assert adapter.m_wsteth.lower() == MOONWELL_DEFAULTS["m_wsteth"].lower()
|
|
37
|
-
assert adapter.well_token.lower() == MOONWELL_DEFAULTS["well_token"].lower()
|
|
34
|
+
def test_chain_name(self):
|
|
35
|
+
assert CHAIN_NAME == "base"
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
@pytest.mark.asyncio
|
|
38
|
+
async def test_get_full_user_state_basic(self, adapter):
|
|
39
|
+
w3 = Web3()
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
@asynccontextmanager
|
|
42
|
+
async def mock_web3_ctx(_chain_id):
|
|
43
|
+
yield w3
|
|
44
|
+
|
|
45
|
+
m1 = MOONWELL_M_USDC
|
|
46
|
+
m2 = MOONWELL_M_WETH
|
|
47
|
+
|
|
48
|
+
rewards = w3.codec.encode(
|
|
49
|
+
["(address,(address,uint256,uint256,uint256)[])[]"],
|
|
50
|
+
[[(m1, [(MOONWELL_WELL_TOKEN, 123, 0, 0)])]],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
stage1 = [
|
|
54
|
+
w3.codec.encode(["address[]"], [[m1, m2]]),
|
|
55
|
+
w3.codec.encode(["address[]"], [[m1]]),
|
|
56
|
+
w3.codec.encode(["uint256", "uint256", "uint256"], [0, 123, 0]),
|
|
57
|
+
rewards,
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
stage2 = [
|
|
61
|
+
# m1
|
|
62
|
+
w3.codec.encode(["uint256"], [100]),
|
|
63
|
+
w3.codec.encode(["uint256"], [2 * MANTISSA]),
|
|
64
|
+
w3.codec.encode(["uint256"], [50]),
|
|
65
|
+
w3.codec.encode(["address"], [BASE_USDC]),
|
|
66
|
+
w3.codec.encode(["uint8"], [8]),
|
|
67
|
+
w3.codec.encode(["bool", "uint256"], [True, int(0.5 * MANTISSA)]),
|
|
68
|
+
# m2 (all zeros, should be filtered out)
|
|
69
|
+
w3.codec.encode(["uint256"], [0]),
|
|
70
|
+
w3.codec.encode(["uint256"], [0]),
|
|
71
|
+
w3.codec.encode(["uint256"], [0]),
|
|
72
|
+
w3.codec.encode(["address"], [BASE_WETH]),
|
|
73
|
+
w3.codec.encode(["uint8"], [8]),
|
|
74
|
+
w3.codec.encode(["bool", "uint256"], [True, int(0.5 * MANTISSA)]),
|
|
75
|
+
]
|
|
44
76
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
77
|
+
with (
|
|
78
|
+
patch(
|
|
79
|
+
"wayfinder_paths.adapters.moonwell_adapter.adapter.web3_from_chain_id",
|
|
80
|
+
mock_web3_ctx,
|
|
81
|
+
),
|
|
82
|
+
patch.object(
|
|
83
|
+
adapter, "_multicall_chunked", new_callable=AsyncMock
|
|
84
|
+
) as mock_multicall,
|
|
85
|
+
):
|
|
86
|
+
mock_multicall.side_effect = [stage1, stage2]
|
|
87
|
+
ok, state = await adapter.get_full_user_state(
|
|
88
|
+
include_rewards=True,
|
|
89
|
+
include_usd=False,
|
|
90
|
+
include_apy=False,
|
|
91
|
+
)
|
|
51
92
|
|
|
52
|
-
@pytest.mark.asyncio
|
|
53
|
-
async def test_connect(self, adapter):
|
|
54
|
-
ok = await adapter.connect()
|
|
55
|
-
assert isinstance(ok, bool)
|
|
56
93
|
assert ok is True
|
|
94
|
+
assert state["protocol"] == "moonwell"
|
|
95
|
+
assert state["chainId"] == adapter.chain_id
|
|
96
|
+
assert state["accountLiquidity"]["liquidity"] == 123
|
|
97
|
+
assert len(state["positions"]) == 1
|
|
98
|
+
assert state["positions"][0]["enteredAsCollateral"] is True
|
|
99
|
+
assert state["positions"][0]["suppliedUnderlying"] == 200
|
|
100
|
+
assert state["rewards"][f"base_{MOONWELL_WELL_TOKEN.lower()}"] == 123
|
|
57
101
|
|
|
58
102
|
@pytest.mark.asyncio
|
|
59
103
|
async def test_lend(self, adapter):
|
|
@@ -63,8 +107,9 @@ class TestMoonwellAdapter:
|
|
|
63
107
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.ensure_allowance",
|
|
64
108
|
new_callable=AsyncMock,
|
|
65
109
|
) as mock_allowance,
|
|
66
|
-
patch
|
|
67
|
-
adapter
|
|
110
|
+
patch(
|
|
111
|
+
"wayfinder_paths.adapters.moonwell_adapter.adapter.encode_call",
|
|
112
|
+
new_callable=AsyncMock,
|
|
68
113
|
) as mock_encode,
|
|
69
114
|
patch(
|
|
70
115
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.send_transaction",
|
|
@@ -72,15 +117,12 @@ class TestMoonwellAdapter:
|
|
|
72
117
|
) as mock_send,
|
|
73
118
|
):
|
|
74
119
|
mock_allowance.return_value = (True, {})
|
|
75
|
-
mock_encode.return_value = {
|
|
76
|
-
"data": "0x1234",
|
|
77
|
-
"to": MOONWELL_DEFAULTS["m_usdc"],
|
|
78
|
-
}
|
|
120
|
+
mock_encode.return_value = {"data": "0x1234", "to": MOONWELL_M_USDC}
|
|
79
121
|
mock_send.return_value = mock_tx_hash
|
|
80
122
|
|
|
81
123
|
success, result = await adapter.lend(
|
|
82
|
-
mtoken=
|
|
83
|
-
underlying_token=
|
|
124
|
+
mtoken=MOONWELL_M_USDC,
|
|
125
|
+
underlying_token=BASE_USDC,
|
|
84
126
|
amount=10**6,
|
|
85
127
|
)
|
|
86
128
|
|
|
@@ -90,8 +132,8 @@ class TestMoonwellAdapter:
|
|
|
90
132
|
@pytest.mark.asyncio
|
|
91
133
|
async def test_lend_invalid_amount(self, adapter):
|
|
92
134
|
success, result = await adapter.lend(
|
|
93
|
-
mtoken=
|
|
94
|
-
underlying_token=
|
|
135
|
+
mtoken=MOONWELL_M_USDC,
|
|
136
|
+
underlying_token=BASE_USDC,
|
|
95
137
|
amount=0,
|
|
96
138
|
)
|
|
97
139
|
|
|
@@ -100,35 +142,22 @@ class TestMoonwellAdapter:
|
|
|
100
142
|
|
|
101
143
|
@pytest.mark.asyncio
|
|
102
144
|
async def test_unlend(self, adapter):
|
|
103
|
-
# Mock contract encoding
|
|
104
|
-
mock_contract = MagicMock()
|
|
105
|
-
mock_contract.functions.redeem = MagicMock(
|
|
106
|
-
return_value=MagicMock(
|
|
107
|
-
build_transaction=AsyncMock(return_value={"data": "0x1234"})
|
|
108
|
-
)
|
|
109
|
-
)
|
|
110
|
-
mock_web3 = MagicMock()
|
|
111
|
-
mock_web3.eth.contract = MagicMock(return_value=mock_contract)
|
|
112
|
-
|
|
113
|
-
@asynccontextmanager
|
|
114
|
-
async def mock_web3_ctx(_chain_id):
|
|
115
|
-
yield mock_web3
|
|
116
|
-
|
|
117
145
|
mock_tx_hash = {"tx_hash": "0xabc123", "status": "success"}
|
|
118
146
|
|
|
119
147
|
with (
|
|
120
148
|
patch(
|
|
121
|
-
"wayfinder_paths.adapters.moonwell_adapter.adapter.
|
|
122
|
-
|
|
123
|
-
),
|
|
149
|
+
"wayfinder_paths.adapters.moonwell_adapter.adapter.encode_call",
|
|
150
|
+
new_callable=AsyncMock,
|
|
151
|
+
) as mock_encode,
|
|
124
152
|
patch(
|
|
125
153
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.send_transaction",
|
|
126
154
|
new_callable=AsyncMock,
|
|
127
155
|
) as mock_send,
|
|
128
156
|
):
|
|
157
|
+
mock_encode.return_value = {"data": "0x1234", "to": MOONWELL_M_USDC}
|
|
129
158
|
mock_send.return_value = mock_tx_hash
|
|
130
159
|
success, result = await adapter.unlend(
|
|
131
|
-
mtoken=
|
|
160
|
+
mtoken=MOONWELL_M_USDC,
|
|
132
161
|
amount=10**8,
|
|
133
162
|
)
|
|
134
163
|
|
|
@@ -138,7 +167,7 @@ class TestMoonwellAdapter:
|
|
|
138
167
|
@pytest.mark.asyncio
|
|
139
168
|
async def test_unlend_invalid_amount(self, adapter):
|
|
140
169
|
success, result = await adapter.unlend(
|
|
141
|
-
mtoken=
|
|
170
|
+
mtoken=MOONWELL_M_USDC,
|
|
142
171
|
amount=-1,
|
|
143
172
|
)
|
|
144
173
|
|
|
@@ -147,50 +176,22 @@ class TestMoonwellAdapter:
|
|
|
147
176
|
|
|
148
177
|
@pytest.mark.asyncio
|
|
149
178
|
async def test_borrow(self, adapter):
|
|
150
|
-
# Track calls to return different values (0 before, 10**6 after)
|
|
151
|
-
borrow_balance_calls = [0]
|
|
152
|
-
|
|
153
|
-
async def mock_borrow_balance_call(**kwargs):
|
|
154
|
-
result = borrow_balance_calls[0]
|
|
155
|
-
# Next call returns increased balance
|
|
156
|
-
borrow_balance_calls[0] = 10**6
|
|
157
|
-
return result
|
|
158
|
-
|
|
159
|
-
# Mock mtoken contract for pre-check and verification
|
|
160
|
-
mock_mtoken = MagicMock()
|
|
161
|
-
mock_mtoken.functions.borrowBalanceStored = MagicMock(
|
|
162
|
-
return_value=MagicMock(call=mock_borrow_balance_call)
|
|
163
|
-
)
|
|
164
|
-
mock_mtoken.functions.borrow = MagicMock(
|
|
165
|
-
return_value=MagicMock(
|
|
166
|
-
call=AsyncMock(return_value=0),
|
|
167
|
-
_encode_transaction_data=MagicMock(return_value="0x1234"),
|
|
168
|
-
build_transaction=AsyncMock(return_value={"data": "0x1234"}),
|
|
169
|
-
)
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
mock_web3 = MagicMock()
|
|
173
|
-
mock_web3.eth.contract = MagicMock(return_value=mock_mtoken)
|
|
174
|
-
|
|
175
|
-
@asynccontextmanager
|
|
176
|
-
async def mock_web3_ctx(_chain_id):
|
|
177
|
-
yield mock_web3
|
|
178
|
-
|
|
179
179
|
mock_tx_hash = {"tx_hash": "0xabc123", "status": "success"}
|
|
180
180
|
|
|
181
181
|
with (
|
|
182
182
|
patch(
|
|
183
|
-
"wayfinder_paths.adapters.moonwell_adapter.adapter.
|
|
184
|
-
|
|
185
|
-
),
|
|
183
|
+
"wayfinder_paths.adapters.moonwell_adapter.adapter.encode_call",
|
|
184
|
+
new_callable=AsyncMock,
|
|
185
|
+
) as mock_encode,
|
|
186
186
|
patch(
|
|
187
187
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.send_transaction",
|
|
188
188
|
new_callable=AsyncMock,
|
|
189
189
|
) as mock_send,
|
|
190
190
|
):
|
|
191
|
+
mock_encode.return_value = {"data": "0x1234", "to": MOONWELL_M_USDC}
|
|
191
192
|
mock_send.return_value = mock_tx_hash
|
|
192
193
|
success, result = await adapter.borrow(
|
|
193
|
-
mtoken=
|
|
194
|
+
mtoken=MOONWELL_M_USDC,
|
|
194
195
|
amount=10**6,
|
|
195
196
|
)
|
|
196
197
|
|
|
@@ -205,8 +206,9 @@ class TestMoonwellAdapter:
|
|
|
205
206
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.ensure_allowance",
|
|
206
207
|
new_callable=AsyncMock,
|
|
207
208
|
) as mock_allowance,
|
|
208
|
-
patch
|
|
209
|
-
adapter
|
|
209
|
+
patch(
|
|
210
|
+
"wayfinder_paths.adapters.moonwell_adapter.adapter.encode_call",
|
|
211
|
+
new_callable=AsyncMock,
|
|
210
212
|
) as mock_encode,
|
|
211
213
|
patch(
|
|
212
214
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.send_transaction",
|
|
@@ -214,15 +216,12 @@ class TestMoonwellAdapter:
|
|
|
214
216
|
) as mock_send,
|
|
215
217
|
):
|
|
216
218
|
mock_allowance.return_value = (True, {})
|
|
217
|
-
mock_encode.return_value = {
|
|
218
|
-
"data": "0x1234",
|
|
219
|
-
"to": MOONWELL_DEFAULTS["m_usdc"],
|
|
220
|
-
}
|
|
219
|
+
mock_encode.return_value = {"data": "0x1234", "to": MOONWELL_M_USDC}
|
|
221
220
|
mock_send.return_value = mock_tx_hash
|
|
222
221
|
|
|
223
222
|
success, result = await adapter.repay(
|
|
224
|
-
mtoken=
|
|
225
|
-
underlying_token=
|
|
223
|
+
mtoken=MOONWELL_M_USDC,
|
|
224
|
+
underlying_token=BASE_USDC,
|
|
226
225
|
amount=10**6,
|
|
227
226
|
)
|
|
228
227
|
|
|
@@ -231,17 +230,10 @@ class TestMoonwellAdapter:
|
|
|
231
230
|
|
|
232
231
|
@pytest.mark.asyncio
|
|
233
232
|
async def test_set_collateral(self, adapter):
|
|
234
|
-
# Mock comptroller contract for verification
|
|
235
233
|
mock_comptroller = MagicMock()
|
|
236
234
|
mock_comptroller.functions.checkMembership = MagicMock(
|
|
237
235
|
return_value=MagicMock(call=AsyncMock(return_value=True))
|
|
238
236
|
)
|
|
239
|
-
mock_comptroller.functions.enterMarkets = MagicMock(
|
|
240
|
-
return_value=MagicMock(
|
|
241
|
-
_encode_transaction_data=MagicMock(return_value="0x1234"),
|
|
242
|
-
build_transaction=AsyncMock(return_value={"data": "0x1234"}),
|
|
243
|
-
)
|
|
244
|
-
)
|
|
245
237
|
|
|
246
238
|
mock_web3 = MagicMock()
|
|
247
239
|
mock_web3.eth.contract = MagicMock(return_value=mock_comptroller)
|
|
@@ -254,17 +246,22 @@ class TestMoonwellAdapter:
|
|
|
254
246
|
|
|
255
247
|
with (
|
|
256
248
|
patch(
|
|
257
|
-
"wayfinder_paths.adapters.moonwell_adapter.adapter.
|
|
258
|
-
|
|
259
|
-
),
|
|
249
|
+
"wayfinder_paths.adapters.moonwell_adapter.adapter.encode_call",
|
|
250
|
+
new_callable=AsyncMock,
|
|
251
|
+
) as mock_encode,
|
|
260
252
|
patch(
|
|
261
253
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.send_transaction",
|
|
262
254
|
new_callable=AsyncMock,
|
|
263
255
|
) as mock_send,
|
|
256
|
+
patch(
|
|
257
|
+
"wayfinder_paths.adapters.moonwell_adapter.adapter.web3_from_chain_id",
|
|
258
|
+
mock_web3_ctx,
|
|
259
|
+
),
|
|
264
260
|
):
|
|
261
|
+
mock_encode.return_value = {"data": "0x1234", "to": MOONWELL_M_WSTETH}
|
|
265
262
|
mock_send.return_value = mock_tx_hash
|
|
266
263
|
success, result = await adapter.set_collateral(
|
|
267
|
-
mtoken=
|
|
264
|
+
mtoken=MOONWELL_M_WSTETH,
|
|
268
265
|
)
|
|
269
266
|
|
|
270
267
|
assert success is True
|
|
@@ -287,7 +284,7 @@ class TestMoonwellAdapter:
|
|
|
287
284
|
)
|
|
288
285
|
|
|
289
286
|
def mock_contract(address, abi):
|
|
290
|
-
if address.lower() ==
|
|
287
|
+
if address.lower() == MOONWELL_REWARD_DISTRIBUTOR.lower():
|
|
291
288
|
return mock_reward_contract
|
|
292
289
|
return mock_comptroller
|
|
293
290
|
|
|
@@ -325,7 +322,7 @@ class TestMoonwellAdapter:
|
|
|
325
322
|
)
|
|
326
323
|
|
|
327
324
|
def mock_contract(address, abi):
|
|
328
|
-
if address.lower() ==
|
|
325
|
+
if address.lower() == MOONWELL_REWARD_DISTRIBUTOR.lower():
|
|
329
326
|
return mock_reward
|
|
330
327
|
return mock_mtoken
|
|
331
328
|
|
|
@@ -340,7 +337,7 @@ class TestMoonwellAdapter:
|
|
|
340
337
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.web3_from_chain_id",
|
|
341
338
|
mock_web3_ctx,
|
|
342
339
|
):
|
|
343
|
-
success, result = await adapter.get_pos(mtoken=
|
|
340
|
+
success, result = await adapter.get_pos(mtoken=MOONWELL_M_USDC)
|
|
344
341
|
|
|
345
342
|
assert success
|
|
346
343
|
assert "mtoken_balance" in result
|
|
@@ -353,7 +350,7 @@ class TestMoonwellAdapter:
|
|
|
353
350
|
@pytest.mark.asyncio
|
|
354
351
|
async def test_get_collateral_factor_success(self, adapter):
|
|
355
352
|
# Clear cache to ensure fresh test
|
|
356
|
-
adapter.
|
|
353
|
+
await adapter._cache.clear()
|
|
357
354
|
|
|
358
355
|
# Mock contract calls - returns (isListed, collateralFactorMantissa)
|
|
359
356
|
mock_contract = MagicMock()
|
|
@@ -374,7 +371,7 @@ class TestMoonwellAdapter:
|
|
|
374
371
|
mock_web3_ctx,
|
|
375
372
|
):
|
|
376
373
|
success, result = await adapter.get_collateral_factor(
|
|
377
|
-
mtoken=
|
|
374
|
+
mtoken=MOONWELL_M_WSTETH
|
|
378
375
|
)
|
|
379
376
|
|
|
380
377
|
assert success
|
|
@@ -407,7 +404,7 @@ class TestMoonwellAdapter:
|
|
|
407
404
|
@pytest.mark.asyncio
|
|
408
405
|
async def test_get_collateral_factor_caching(self, adapter):
|
|
409
406
|
# Clear cache to ensure fresh test
|
|
410
|
-
adapter.
|
|
407
|
+
await adapter._cache.clear()
|
|
411
408
|
|
|
412
409
|
call_count = 0
|
|
413
410
|
|
|
@@ -427,7 +424,7 @@ class TestMoonwellAdapter:
|
|
|
427
424
|
async def mock_web3_ctx(_chain_id):
|
|
428
425
|
yield mock_web3
|
|
429
426
|
|
|
430
|
-
mtoken =
|
|
427
|
+
mtoken = MOONWELL_M_WSTETH
|
|
431
428
|
|
|
432
429
|
with patch(
|
|
433
430
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.web3_from_chain_id",
|
|
@@ -452,67 +449,11 @@ class TestMoonwellAdapter:
|
|
|
452
449
|
assert call_count == 1
|
|
453
450
|
|
|
454
451
|
success4, result4 = await adapter.get_collateral_factor(
|
|
455
|
-
mtoken=
|
|
452
|
+
mtoken=MOONWELL_M_USDC
|
|
456
453
|
)
|
|
457
454
|
assert success4 is True
|
|
458
455
|
assert call_count == 2
|
|
459
456
|
|
|
460
|
-
@pytest.mark.asyncio
|
|
461
|
-
async def test_get_collateral_factor_cache_expiry(self, adapter):
|
|
462
|
-
import time
|
|
463
|
-
|
|
464
|
-
from wayfinder_paths.adapters.moonwell_adapter import adapter as adapter_module
|
|
465
|
-
|
|
466
|
-
# Clear cache to ensure fresh test
|
|
467
|
-
adapter._cf_cache.clear()
|
|
468
|
-
|
|
469
|
-
original_ttl = adapter_module.CF_CACHE_TTL
|
|
470
|
-
|
|
471
|
-
try:
|
|
472
|
-
adapter_module.CF_CACHE_TTL = 0.1
|
|
473
|
-
|
|
474
|
-
call_count = 0
|
|
475
|
-
|
|
476
|
-
async def mock_markets_call(**kwargs):
|
|
477
|
-
nonlocal call_count
|
|
478
|
-
call_count += 1
|
|
479
|
-
return (True, int(0.75 * MANTISSA))
|
|
480
|
-
|
|
481
|
-
mock_contract = MagicMock()
|
|
482
|
-
mock_contract.functions.markets = MagicMock(
|
|
483
|
-
return_value=MagicMock(call=mock_markets_call)
|
|
484
|
-
)
|
|
485
|
-
mock_web3 = MagicMock()
|
|
486
|
-
mock_web3.eth.contract = MagicMock(return_value=mock_contract)
|
|
487
|
-
|
|
488
|
-
@asynccontextmanager
|
|
489
|
-
async def mock_web3_ctx(_chain_id):
|
|
490
|
-
yield mock_web3
|
|
491
|
-
|
|
492
|
-
mtoken = MOONWELL_DEFAULTS["m_wsteth"]
|
|
493
|
-
|
|
494
|
-
with patch(
|
|
495
|
-
"wayfinder_paths.adapters.moonwell_adapter.adapter.web3_from_chain_id",
|
|
496
|
-
mock_web3_ctx,
|
|
497
|
-
):
|
|
498
|
-
# First call
|
|
499
|
-
await adapter.get_collateral_factor(mtoken=mtoken)
|
|
500
|
-
assert call_count == 1
|
|
501
|
-
|
|
502
|
-
# Immediate second call should use cache
|
|
503
|
-
await adapter.get_collateral_factor(mtoken=mtoken)
|
|
504
|
-
assert call_count == 1
|
|
505
|
-
|
|
506
|
-
# Wait for cache to expire
|
|
507
|
-
time.sleep(0.15)
|
|
508
|
-
|
|
509
|
-
await adapter.get_collateral_factor(mtoken=mtoken)
|
|
510
|
-
assert call_count == 2
|
|
511
|
-
|
|
512
|
-
finally:
|
|
513
|
-
# Restore original TTL
|
|
514
|
-
adapter_module.CF_CACHE_TTL = original_ttl
|
|
515
|
-
|
|
516
457
|
@pytest.mark.asyncio
|
|
517
458
|
async def test_get_apy_supply(self, adapter):
|
|
518
459
|
rate_per_second = int(1.5e9)
|
|
@@ -533,7 +474,7 @@ class TestMoonwellAdapter:
|
|
|
533
474
|
)
|
|
534
475
|
|
|
535
476
|
def mock_contract(address, abi):
|
|
536
|
-
if address.lower() ==
|
|
477
|
+
if address.lower() == MOONWELL_REWARD_DISTRIBUTOR.lower():
|
|
537
478
|
return mock_reward
|
|
538
479
|
return mock_mtoken
|
|
539
480
|
|
|
@@ -549,7 +490,7 @@ class TestMoonwellAdapter:
|
|
|
549
490
|
mock_web3_ctx,
|
|
550
491
|
):
|
|
551
492
|
success, result = await adapter.get_apy(
|
|
552
|
-
mtoken=
|
|
493
|
+
mtoken=MOONWELL_M_USDC,
|
|
553
494
|
apy_type="supply",
|
|
554
495
|
include_rewards=False,
|
|
555
496
|
)
|
|
@@ -578,7 +519,7 @@ class TestMoonwellAdapter:
|
|
|
578
519
|
)
|
|
579
520
|
|
|
580
521
|
def mock_contract(address, abi):
|
|
581
|
-
if address.lower() ==
|
|
522
|
+
if address.lower() == MOONWELL_REWARD_DISTRIBUTOR.lower():
|
|
582
523
|
return mock_reward
|
|
583
524
|
return mock_mtoken
|
|
584
525
|
|
|
@@ -594,7 +535,7 @@ class TestMoonwellAdapter:
|
|
|
594
535
|
mock_web3_ctx,
|
|
595
536
|
):
|
|
596
537
|
success, result = await adapter.get_apy(
|
|
597
|
-
mtoken=
|
|
538
|
+
mtoken=MOONWELL_M_USDC,
|
|
598
539
|
apy_type="borrow",
|
|
599
540
|
include_rewards=False,
|
|
600
541
|
)
|
|
@@ -649,31 +590,19 @@ class TestMoonwellAdapter:
|
|
|
649
590
|
|
|
650
591
|
@pytest.mark.asyncio
|
|
651
592
|
async def test_wrap_eth(self, adapter):
|
|
652
|
-
mock_contract = MagicMock()
|
|
653
|
-
mock_contract.functions.deposit = MagicMock(
|
|
654
|
-
return_value=MagicMock(
|
|
655
|
-
build_transaction=AsyncMock(return_value={"data": "0x1234"})
|
|
656
|
-
)
|
|
657
|
-
)
|
|
658
|
-
mock_web3 = MagicMock()
|
|
659
|
-
mock_web3.eth.contract = MagicMock(return_value=mock_contract)
|
|
660
|
-
|
|
661
|
-
@asynccontextmanager
|
|
662
|
-
async def mock_web3_ctx(_chain_id):
|
|
663
|
-
yield mock_web3
|
|
664
|
-
|
|
665
593
|
mock_tx_hash = {"tx_hash": "0xabc123", "status": "success"}
|
|
666
594
|
|
|
667
595
|
with (
|
|
668
596
|
patch(
|
|
669
|
-
"wayfinder_paths.adapters.moonwell_adapter.adapter.
|
|
670
|
-
|
|
671
|
-
),
|
|
597
|
+
"wayfinder_paths.adapters.moonwell_adapter.adapter.encode_call",
|
|
598
|
+
new_callable=AsyncMock,
|
|
599
|
+
) as mock_encode,
|
|
672
600
|
patch(
|
|
673
601
|
"wayfinder_paths.adapters.moonwell_adapter.adapter.send_transaction",
|
|
674
602
|
new_callable=AsyncMock,
|
|
675
603
|
) as mock_send,
|
|
676
604
|
):
|
|
605
|
+
mock_encode.return_value = {"data": "0x1234", "to": BASE_WETH}
|
|
677
606
|
mock_send.return_value = mock_tx_hash
|
|
678
607
|
success, result = await adapter.wrap_eth(amount=10**18)
|
|
679
608
|
|
|
@@ -681,29 +610,9 @@ class TestMoonwellAdapter:
|
|
|
681
610
|
assert result == mock_tx_hash
|
|
682
611
|
|
|
683
612
|
def test_strategy_address_missing(self):
|
|
684
|
-
with pytest.raises(
|
|
613
|
+
with pytest.raises(KeyError):
|
|
685
614
|
MoonwellAdapter(config={})
|
|
686
615
|
|
|
687
|
-
def test_config_override(self):
|
|
688
|
-
custom_comptroller = "0x1111111111111111111111111111111111111111"
|
|
689
|
-
custom_well = "0x2222222222222222222222222222222222222222"
|
|
690
|
-
config = {
|
|
691
|
-
"strategy_wallet": {
|
|
692
|
-
"address": "0x1234567890123456789012345678901234567890"
|
|
693
|
-
},
|
|
694
|
-
"moonwell_adapter": {
|
|
695
|
-
"comptroller": custom_comptroller,
|
|
696
|
-
"well_token": custom_well,
|
|
697
|
-
"chain_id": 1,
|
|
698
|
-
},
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
adapter = MoonwellAdapter(config=config)
|
|
702
|
-
|
|
703
|
-
assert adapter.comptroller_address.lower() == custom_comptroller.lower()
|
|
704
|
-
assert adapter.well_token.lower() == custom_well.lower()
|
|
705
|
-
assert adapter.chain_id == 1
|
|
706
|
-
|
|
707
616
|
@pytest.mark.asyncio
|
|
708
617
|
async def test_max_withdrawable_mtoken_zero_balance(self, adapter):
|
|
709
618
|
# Mock contracts
|
|
@@ -721,9 +630,7 @@ class TestMoonwellAdapter:
|
|
|
721
630
|
return_value=MagicMock(call=AsyncMock(return_value=8))
|
|
722
631
|
)
|
|
723
632
|
mock_mtoken.functions.underlying = MagicMock(
|
|
724
|
-
return_value=MagicMock(
|
|
725
|
-
call=AsyncMock(return_value=MOONWELL_DEFAULTS["usdc"])
|
|
726
|
-
)
|
|
633
|
+
return_value=MagicMock(call=AsyncMock(return_value=BASE_USDC))
|
|
727
634
|
)
|
|
728
635
|
|
|
729
636
|
mock_web3 = MagicMock()
|
|
@@ -738,7 +645,7 @@ class TestMoonwellAdapter:
|
|
|
738
645
|
mock_web3_ctx,
|
|
739
646
|
):
|
|
740
647
|
success, result = await adapter.max_withdrawable_mtoken(
|
|
741
|
-
mtoken=
|
|
648
|
+
mtoken=MOONWELL_M_USDC
|
|
742
649
|
)
|
|
743
650
|
|
|
744
651
|
assert success
|