wayfinder-paths 0.1.19__py3-none-any.whl → 0.1.20__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 -2
- wayfinder_paths/adapters/balance_adapter/README.md +59 -45
- wayfinder_paths/adapters/balance_adapter/adapter.py +0 -21
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -14
- wayfinder_paths/adapters/brap_adapter/README.md +61 -184
- wayfinder_paths/adapters/brap_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/brap_adapter/adapter.py +0 -147
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +0 -15
- wayfinder_paths/adapters/hyperlend_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +0 -9
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +0 -17
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +3 -312
- wayfinder_paths/adapters/hyperliquid_adapter/executor.py +1 -71
- wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +0 -57
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +0 -17
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +2 -42
- wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +1 -9
- wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +15 -47
- wayfinder_paths/adapters/hyperliquid_adapter/utils.py +0 -7
- wayfinder_paths/adapters/ledger_adapter/README.md +54 -74
- wayfinder_paths/adapters/ledger_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/ledger_adapter/adapter.py +0 -106
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +0 -12
- wayfinder_paths/adapters/moonwell_adapter/README.md +67 -106
- wayfinder_paths/adapters/moonwell_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +9 -121
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +84 -83
- wayfinder_paths/adapters/pool_adapter/README.md +30 -51
- wayfinder_paths/adapters/pool_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/pool_adapter/adapter.py +0 -19
- wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -8
- wayfinder_paths/adapters/token_adapter/README.md +41 -49
- wayfinder_paths/adapters/token_adapter/adapter.py +0 -32
- wayfinder_paths/adapters/token_adapter/test_adapter.py +1 -12
- wayfinder_paths/conftest.py +0 -8
- wayfinder_paths/core/__init__.py +0 -2
- wayfinder_paths/core/adapters/BaseAdapter.py +0 -22
- wayfinder_paths/core/adapters/__init__.py +0 -5
- wayfinder_paths/core/adapters/models.py +0 -5
- wayfinder_paths/core/analytics/__init__.py +0 -2
- wayfinder_paths/core/analytics/bootstrap.py +0 -16
- wayfinder_paths/core/analytics/stats.py +0 -7
- wayfinder_paths/core/analytics/test_analytics.py +5 -34
- wayfinder_paths/core/clients/BRAPClient.py +0 -35
- wayfinder_paths/core/clients/ClientManager.py +0 -51
- wayfinder_paths/core/clients/HyperlendClient.py +0 -77
- wayfinder_paths/core/clients/LedgerClient.py +2 -122
- wayfinder_paths/core/clients/PoolClient.py +0 -2
- wayfinder_paths/core/clients/TokenClient.py +0 -39
- wayfinder_paths/core/clients/WalletClient.py +0 -15
- wayfinder_paths/core/clients/WayfinderClient.py +0 -24
- wayfinder_paths/core/clients/__init__.py +0 -4
- wayfinder_paths/core/clients/protocols.py +25 -98
- wayfinder_paths/core/config.py +0 -24
- wayfinder_paths/core/constants/__init__.py +0 -7
- wayfinder_paths/core/constants/base.py +2 -9
- wayfinder_paths/core/constants/erc20_abi.py +0 -5
- wayfinder_paths/core/constants/hyperlend_abi.py +0 -7
- wayfinder_paths/core/constants/moonwell_abi.py +0 -35
- wayfinder_paths/core/engine/StrategyJob.py +0 -32
- wayfinder_paths/core/strategies/Strategy.py +0 -99
- wayfinder_paths/core/strategies/__init__.py +0 -2
- wayfinder_paths/core/utils/__init__.py +0 -1
- wayfinder_paths/core/utils/erc20_service.py +0 -1
- wayfinder_paths/core/utils/evm_helpers.py +0 -50
- wayfinder_paths/core/utils/transaction.py +0 -1
- wayfinder_paths/run_strategy.py +0 -46
- wayfinder_paths/scripts/create_strategy.py +0 -17
- wayfinder_paths/scripts/make_wallets.py +1 -4
- wayfinder_paths/strategies/basis_trading_strategy/README.md +71 -163
- wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +0 -24
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +36 -400
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +15 -64
- wayfinder_paths/strategies/basis_trading_strategy/types.py +0 -4
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +65 -56
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +4 -27
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +0 -10
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +71 -72
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +23 -227
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +120 -113
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +64 -59
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +4 -44
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +2 -35
- wayfinder_paths/templates/adapter/README.md +107 -46
- wayfinder_paths/templates/adapter/adapter.py +0 -9
- wayfinder_paths/templates/adapter/test_adapter.py +0 -19
- wayfinder_paths/templates/strategy/README.md +113 -59
- wayfinder_paths/templates/strategy/strategy.py +0 -22
- wayfinder_paths/templates/strategy/test_strategy.py +0 -28
- wayfinder_paths/tests/test_test_coverage.py +2 -12
- wayfinder_paths/tests/test_utils.py +1 -31
- wayfinder_paths-0.1.20.dist-info/METADATA +355 -0
- wayfinder_paths-0.1.20.dist-info/RECORD +129 -0
- wayfinder_paths/core/adapters/base.py +0 -5
- wayfinder_paths-0.1.19.dist-info/METADATA +0 -592
- wayfinder_paths-0.1.19.dist-info/RECORD +0 -130
- {wayfinder_paths-0.1.19.dist-info → wayfinder_paths-0.1.20.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.19.dist-info → wayfinder_paths-0.1.20.dist-info}/WHEEL +0 -0
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Moonwell Adapter for lending, borrowing, and collateral management on Moonwell protocol.
|
|
3
|
-
|
|
4
|
-
This adapter provides functionality for interacting with Moonwell on Base chain,
|
|
5
|
-
including supplying/withdrawing collateral, borrowing/repaying, and claiming rewards.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
1
|
from __future__ import annotations
|
|
9
2
|
|
|
10
3
|
import asyncio
|
|
@@ -59,37 +52,31 @@ CF_CACHE_TTL = 3600
|
|
|
59
52
|
|
|
60
53
|
# Default retry settings for rate-limited RPCs
|
|
61
54
|
DEFAULT_MAX_RETRIES = 5
|
|
62
|
-
DEFAULT_BASE_DELAY = 3.0
|
|
55
|
+
DEFAULT_BASE_DELAY = 3.0
|
|
63
56
|
|
|
64
57
|
|
|
65
58
|
def _is_rate_limit_error(error: Exception | str) -> bool:
|
|
66
|
-
"""Check if an error is a rate limit (429) error."""
|
|
67
59
|
error_str = str(error)
|
|
68
60
|
return "429" in error_str or "Too Many Requests" in error_str
|
|
69
61
|
|
|
70
62
|
|
|
71
63
|
def _timestamp_rate_to_apy(rate: float) -> float:
|
|
72
|
-
"""Convert a per-second rate to APY."""
|
|
73
64
|
return (1 + rate) ** SECONDS_PER_YEAR - 1
|
|
74
65
|
|
|
75
66
|
|
|
76
67
|
class MoonwellAdapter(BaseAdapter):
|
|
77
|
-
"""Moonwell adapter for lending/borrowing operations on Base chain."""
|
|
78
|
-
|
|
79
68
|
adapter_type = "MOONWELL"
|
|
80
69
|
|
|
81
70
|
def __init__(
|
|
82
71
|
self,
|
|
83
72
|
config: dict[str, Any] | None = None,
|
|
84
73
|
token_client: TokenClient | None = None,
|
|
85
|
-
simulation: bool = False,
|
|
86
74
|
strategy_wallet_signing_callback=None,
|
|
87
75
|
) -> None:
|
|
88
76
|
super().__init__("moonwell_adapter", config)
|
|
89
77
|
cfg = config or {}
|
|
90
78
|
adapter_cfg = cfg.get("moonwell_adapter") or {}
|
|
91
79
|
|
|
92
|
-
self.simulation = simulation
|
|
93
80
|
self.token_client = token_client
|
|
94
81
|
self.strategy_wallet_signing_callback = strategy_wallet_signing_callback
|
|
95
82
|
|
|
@@ -139,7 +126,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
139
126
|
underlying_token: str,
|
|
140
127
|
amount: int,
|
|
141
128
|
) -> tuple[bool, Any]:
|
|
142
|
-
"""Supply tokens to Moonwell by minting mTokens."""
|
|
143
129
|
strategy = self._strategy_address()
|
|
144
130
|
amount = int(amount)
|
|
145
131
|
if amount <= 0:
|
|
@@ -174,7 +160,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
174
160
|
mtoken: str,
|
|
175
161
|
amount: int,
|
|
176
162
|
) -> tuple[bool, Any]:
|
|
177
|
-
"""Withdraw tokens from Moonwell by redeeming mTokens."""
|
|
178
163
|
strategy = self._strategy_address()
|
|
179
164
|
amount = int(amount)
|
|
180
165
|
if amount <= 0:
|
|
@@ -202,13 +187,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
202
187
|
mtoken: str,
|
|
203
188
|
amount: int,
|
|
204
189
|
) -> tuple[bool, Any]:
|
|
205
|
-
"""Borrow tokens from Moonwell.
|
|
206
|
-
|
|
207
|
-
Note: Moonwell/Compound borrow() returns an error code, not a boolean.
|
|
208
|
-
Even if the transaction succeeds (status=1), the borrow may have failed
|
|
209
|
-
if the return value is non-zero. We verify success by checking that
|
|
210
|
-
the borrow balance actually increased.
|
|
211
|
-
"""
|
|
212
190
|
from loguru import logger
|
|
213
191
|
|
|
214
192
|
strategy = self._strategy_address()
|
|
@@ -218,7 +196,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
218
196
|
|
|
219
197
|
mtoken = self._checksum(mtoken)
|
|
220
198
|
|
|
221
|
-
# Get borrow balance before the transaction for verification
|
|
222
199
|
borrow_before = 0
|
|
223
200
|
try:
|
|
224
201
|
async with web3_from_chain_id(self.chain_id) as web3:
|
|
@@ -266,7 +243,7 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
266
243
|
|
|
267
244
|
# Borrow balance should have increased by approximately the amount
|
|
268
245
|
# Allow for some interest accrual
|
|
269
|
-
expected_increase = amount * 0.99
|
|
246
|
+
expected_increase = amount * 0.99
|
|
270
247
|
actual_increase = borrow_after - borrow_before
|
|
271
248
|
|
|
272
249
|
if actual_increase < expected_increase:
|
|
@@ -299,14 +276,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
299
276
|
amount: int,
|
|
300
277
|
repay_full: bool = False,
|
|
301
278
|
) -> tuple[bool, Any]:
|
|
302
|
-
"""Repay borrowed tokens to Moonwell.
|
|
303
|
-
|
|
304
|
-
Args:
|
|
305
|
-
mtoken: The mToken address
|
|
306
|
-
underlying_token: The underlying token address (e.g., WETH)
|
|
307
|
-
amount: Amount to repay (used for approval if repay_full=True)
|
|
308
|
-
repay_full: If True, uses type(uint256).max to repay exact debt
|
|
309
|
-
"""
|
|
310
279
|
strategy = self._strategy_address()
|
|
311
280
|
amount = int(amount)
|
|
312
281
|
if amount <= 0:
|
|
@@ -347,11 +316,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
347
316
|
*,
|
|
348
317
|
mtoken: str,
|
|
349
318
|
) -> tuple[bool, Any]:
|
|
350
|
-
"""Enable a market as collateral (enter market).
|
|
351
|
-
|
|
352
|
-
Note: enterMarkets returns an array of error codes. We verify success
|
|
353
|
-
by checking if the account has actually entered the market.
|
|
354
|
-
"""
|
|
355
319
|
strategy = self._strategy_address()
|
|
356
320
|
mtoken = self._checksum(mtoken)
|
|
357
321
|
|
|
@@ -401,10 +365,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
401
365
|
mtoken: str,
|
|
402
366
|
account: str | None = None,
|
|
403
367
|
) -> tuple[bool, bool | str]:
|
|
404
|
-
"""Check whether an account has entered a given market (as collateral / borrowing market)."""
|
|
405
|
-
if self.simulation:
|
|
406
|
-
return True, True
|
|
407
|
-
|
|
408
368
|
try:
|
|
409
369
|
acct = self._checksum(account) if account else self._strategy_address()
|
|
410
370
|
mtoken = self._checksum(mtoken)
|
|
@@ -425,7 +385,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
425
385
|
*,
|
|
426
386
|
mtoken: str,
|
|
427
387
|
) -> tuple[bool, Any]:
|
|
428
|
-
"""Disable a market as collateral (exit market)."""
|
|
429
388
|
strategy = self._strategy_address()
|
|
430
389
|
mtoken = self._checksum(mtoken)
|
|
431
390
|
|
|
@@ -447,21 +406,18 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
447
406
|
*,
|
|
448
407
|
min_rewards_usd: float = 0.0,
|
|
449
408
|
) -> tuple[bool, dict[str, int] | str]:
|
|
450
|
-
"""Claim WELL rewards from Moonwell. Skips if below min_rewards_usd threshold."""
|
|
451
409
|
strategy = self._strategy_address()
|
|
452
410
|
|
|
453
|
-
# Get outstanding rewards first
|
|
454
411
|
rewards = await self._get_outstanding_rewards(strategy)
|
|
455
412
|
|
|
456
413
|
# Skip if no rewards to claim
|
|
457
414
|
if not rewards:
|
|
458
415
|
return True, {}
|
|
459
416
|
|
|
460
|
-
# Check minimum threshold if token_client available
|
|
461
417
|
if min_rewards_usd > 0 and self.token_client:
|
|
462
418
|
total_usd = await self._calculate_rewards_usd(rewards)
|
|
463
419
|
if total_usd < min_rewards_usd:
|
|
464
|
-
return True, {}
|
|
420
|
+
return True, {}
|
|
465
421
|
|
|
466
422
|
# Claim via comptroller (like reference implementation)
|
|
467
423
|
tx = await self._encode_call(
|
|
@@ -478,14 +434,12 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
478
434
|
return True, rewards
|
|
479
435
|
|
|
480
436
|
async def _get_outstanding_rewards(self, account: str) -> dict[str, int]:
|
|
481
|
-
"""Get outstanding rewards for an account across all markets."""
|
|
482
437
|
try:
|
|
483
438
|
async with web3_from_chain_id(self.chain_id) as web3:
|
|
484
439
|
contract = web3.eth.contract(
|
|
485
440
|
address=self.reward_distributor_address, abi=REWARD_DISTRIBUTOR_ABI
|
|
486
441
|
)
|
|
487
442
|
|
|
488
|
-
# Call getOutstandingRewardsForUser(user)
|
|
489
443
|
all_rewards = await contract.functions.getOutstandingRewardsForUser(
|
|
490
444
|
account
|
|
491
445
|
).call(block_identifier="pending")
|
|
@@ -507,7 +461,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
507
461
|
return {}
|
|
508
462
|
|
|
509
463
|
async def _calculate_rewards_usd(self, rewards: dict[str, int]) -> float:
|
|
510
|
-
"""Calculate total USD value of rewards."""
|
|
511
464
|
if not self.token_client:
|
|
512
465
|
return 0.0
|
|
513
466
|
|
|
@@ -536,20 +489,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
536
489
|
max_retries: int = 3,
|
|
537
490
|
block_identifier: int | str | None = None,
|
|
538
491
|
) -> tuple[bool, dict[str, Any] | str]:
|
|
539
|
-
"""Get position data (balances, rewards) for an account in a market.
|
|
540
|
-
|
|
541
|
-
Args:
|
|
542
|
-
mtoken: The mToken address
|
|
543
|
-
account: Account to query (defaults to strategy wallet)
|
|
544
|
-
include_usd: Whether to include USD values
|
|
545
|
-
max_retries: Number of retry attempts
|
|
546
|
-
block_identifier: Block to query at. Can be:
|
|
547
|
-
- int: specific block number (for pinning to tx block)
|
|
548
|
-
- "safe": OP Stack safe block (data posted to L1)
|
|
549
|
-
- None/"pending": current head (default)
|
|
550
|
-
|
|
551
|
-
Includes retry logic with exponential backoff for rate-limited RPCs.
|
|
552
|
-
"""
|
|
553
492
|
mtoken = self._checksum(mtoken)
|
|
554
493
|
account = self._checksum(account) if account else self._strategy_address()
|
|
555
494
|
block_id = block_identifier if block_identifier is not None else "pending"
|
|
@@ -566,7 +505,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
566
505
|
abi=REWARD_DISTRIBUTOR_ABI,
|
|
567
506
|
)
|
|
568
507
|
|
|
569
|
-
# Fetch data sequentially to avoid overwhelming rate-limited public RPCs
|
|
570
508
|
# (parallel fetch would make 5 simultaneous calls per position)
|
|
571
509
|
bal = await mtoken_contract.functions.balanceOf(account).call(
|
|
572
510
|
block_identifier=block_id
|
|
@@ -585,12 +523,12 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
585
523
|
mtoken, account
|
|
586
524
|
).call(block_identifier=block_id)
|
|
587
525
|
)
|
|
588
|
-
break
|
|
526
|
+
break
|
|
589
527
|
except Exception as exc:
|
|
590
528
|
last_error = str(exc)
|
|
591
529
|
if "429" in last_error or "Too Many Requests" in last_error:
|
|
592
530
|
if attempt < max_retries - 1:
|
|
593
|
-
wait_time = 2 ** (attempt + 1)
|
|
531
|
+
wait_time = 2 ** (attempt + 1)
|
|
594
532
|
await asyncio.sleep(wait_time)
|
|
595
533
|
continue
|
|
596
534
|
return False, last_error
|
|
@@ -599,10 +537,8 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
599
537
|
return False, last_error
|
|
600
538
|
|
|
601
539
|
try:
|
|
602
|
-
# Process rewards
|
|
603
540
|
reward_balances = self._process_rewards(rewards)
|
|
604
541
|
|
|
605
|
-
# Build balances dict
|
|
606
542
|
mtoken_key = f"{self.chain_name}_{mtoken}"
|
|
607
543
|
underlying_key = f"{self.chain_name}_{underlying}"
|
|
608
544
|
|
|
@@ -621,7 +557,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
621
557
|
"underlying_token": underlying,
|
|
622
558
|
}
|
|
623
559
|
|
|
624
|
-
# Calculate USD values if requested and token_client available
|
|
625
560
|
if include_usd and self.token_client:
|
|
626
561
|
usd_balances = await self._calculate_usd_balances(
|
|
627
562
|
balances, underlying_key, exch
|
|
@@ -633,7 +568,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
633
568
|
return False, str(exc)
|
|
634
569
|
|
|
635
570
|
def _process_rewards(self, rewards: list) -> dict[str, int]:
|
|
636
|
-
"""Process rewards tuple into dict mapping token keys to amounts."""
|
|
637
571
|
result: dict[str, int] = {}
|
|
638
572
|
for reward_info in rewards:
|
|
639
573
|
if len(reward_info) >= 2:
|
|
@@ -647,11 +581,9 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
647
581
|
async def _calculate_usd_balances(
|
|
648
582
|
self, balances: dict[str, int], underlying_key: str, _exchange_rate: int
|
|
649
583
|
) -> dict[str, float | None]:
|
|
650
|
-
"""Calculate USD values for balances."""
|
|
651
584
|
if not self.token_client:
|
|
652
585
|
return {}
|
|
653
586
|
|
|
654
|
-
# Fetch token data for all tokens
|
|
655
587
|
tokens = set(balances.keys()) | {underlying_key}
|
|
656
588
|
token_data: dict[str, dict | None] = {}
|
|
657
589
|
for token_key in tokens:
|
|
@@ -662,7 +594,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
662
594
|
except Exception:
|
|
663
595
|
token_data[token_key] = None
|
|
664
596
|
|
|
665
|
-
# Calculate USD values
|
|
666
597
|
usd_balances: dict[str, float | None] = {}
|
|
667
598
|
for token_key, bal in balances.items():
|
|
668
599
|
data = token_data.get(token_key)
|
|
@@ -684,14 +615,8 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
684
615
|
mtoken: str,
|
|
685
616
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
686
617
|
) -> tuple[bool, float | str]:
|
|
687
|
-
"""Get the collateral factor for a market as decimal (e.g., 0.75 for 75%).
|
|
688
|
-
|
|
689
|
-
Uses a 1-hour cache since collateral factors rarely change (governance controlled).
|
|
690
|
-
Includes retry logic with exponential backoff for rate-limited RPCs.
|
|
691
|
-
"""
|
|
692
618
|
mtoken = self._checksum(mtoken)
|
|
693
619
|
|
|
694
|
-
# Check cache first
|
|
695
620
|
now = time.time()
|
|
696
621
|
if mtoken in self._cf_cache:
|
|
697
622
|
cached_value, cached_time = self._cf_cache[mtoken]
|
|
@@ -715,7 +640,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
715
640
|
if not is_listed:
|
|
716
641
|
return False, f"Market {mtoken} is not listed"
|
|
717
642
|
|
|
718
|
-
# Convert from mantissa to decimal
|
|
719
643
|
collateral_factor = collateral_factor_mantissa / MANTISSA
|
|
720
644
|
|
|
721
645
|
# Cache the result
|
|
@@ -740,10 +664,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
740
664
|
include_rewards: bool = True,
|
|
741
665
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
742
666
|
) -> tuple[bool, float | str]:
|
|
743
|
-
"""Get supply or borrow APY for a market, optionally including WELL rewards.
|
|
744
|
-
|
|
745
|
-
Includes retry logic with exponential backoff for rate-limited RPCs.
|
|
746
|
-
"""
|
|
747
667
|
mtoken = self._checksum(mtoken)
|
|
748
668
|
|
|
749
669
|
last_error = ""
|
|
@@ -756,7 +676,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
756
676
|
abi=REWARD_DISTRIBUTOR_ABI,
|
|
757
677
|
)
|
|
758
678
|
|
|
759
|
-
# Get base rate (sequential to avoid rate limits)
|
|
760
679
|
if apy_type == "supply":
|
|
761
680
|
rate_per_timestamp = await mtoken_contract.functions.supplyRatePerTimestamp().call(
|
|
762
681
|
block_identifier="pending"
|
|
@@ -786,11 +705,9 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
786
705
|
)
|
|
787
706
|
)
|
|
788
707
|
|
|
789
|
-
# Convert rate per second to APY
|
|
790
708
|
rate = rate_per_timestamp / MANTISSA
|
|
791
709
|
apy = _timestamp_rate_to_apy(rate)
|
|
792
710
|
|
|
793
|
-
# Add WELL rewards APY if requested and token_client available
|
|
794
711
|
if include_rewards and self.token_client and total_value > 0:
|
|
795
712
|
rewards_apr = await self._calculate_rewards_apr(
|
|
796
713
|
mtoken, mkt_config, total_value, apy_type
|
|
@@ -815,7 +732,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
815
732
|
total_value: int,
|
|
816
733
|
apy_type: str,
|
|
817
734
|
) -> float:
|
|
818
|
-
"""Calculate WELL rewards APR for a market."""
|
|
819
735
|
if not self.token_client:
|
|
820
736
|
return 0.0
|
|
821
737
|
|
|
@@ -830,12 +746,11 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
830
746
|
if not well_config:
|
|
831
747
|
return 0.0
|
|
832
748
|
|
|
833
|
-
# Get emission rate (supply or borrow)
|
|
834
749
|
# Config format: (mToken, rewardToken, owner, emissionCap, supplyEmissionsPerSec, borrowEmissionsPerSec, ...)
|
|
835
750
|
if apy_type == "supply":
|
|
836
|
-
well_rate = well_config[4]
|
|
751
|
+
well_rate = well_config[4]
|
|
837
752
|
else:
|
|
838
|
-
well_rate = well_config[5]
|
|
753
|
+
well_rate = well_config[5]
|
|
839
754
|
# Borrow rewards are shown as negative in some implementations
|
|
840
755
|
if well_rate < 0:
|
|
841
756
|
well_rate = -well_rate
|
|
@@ -843,14 +758,12 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
843
758
|
if well_rate == 0:
|
|
844
759
|
return 0.0
|
|
845
760
|
|
|
846
|
-
# Get underlying token for decimals
|
|
847
761
|
async with web3_from_chain_id(self.chain_id) as web3:
|
|
848
762
|
mtoken_contract = web3.eth.contract(address=mtoken, abi=MTOKEN_ABI)
|
|
849
763
|
underlying_addr = await mtoken_contract.functions.underlying().call(
|
|
850
764
|
block_identifier="pending"
|
|
851
765
|
)
|
|
852
766
|
|
|
853
|
-
# Get prices
|
|
854
767
|
well_key = f"{self.chain_name}_{self.well_token}"
|
|
855
768
|
underlying_key = f"{self.chain_name}_{underlying_addr}"
|
|
856
769
|
|
|
@@ -876,7 +789,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
876
789
|
if not well_price or not underlying_price:
|
|
877
790
|
return 0.0
|
|
878
791
|
|
|
879
|
-
# Calculate total value in USD
|
|
880
792
|
total_value_usd = (
|
|
881
793
|
total_value / (10**underlying_decimals)
|
|
882
794
|
) * underlying_price
|
|
@@ -884,7 +796,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
884
796
|
if total_value_usd == 0:
|
|
885
797
|
return 0.0
|
|
886
798
|
|
|
887
|
-
# Calculate rewards APR
|
|
888
799
|
# rewards_apr = well_price * emissions_per_second * seconds_per_year / total_value_usd
|
|
889
800
|
rewards_apr = (
|
|
890
801
|
well_price * (well_rate / MANTISSA) * SECONDS_PER_YEAR / total_value_usd
|
|
@@ -900,10 +811,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
900
811
|
account: str | None = None,
|
|
901
812
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
902
813
|
) -> tuple[bool, int | str]:
|
|
903
|
-
"""Get the maximum borrowable amount for an account (USD with 18 decimals).
|
|
904
|
-
|
|
905
|
-
Includes retry logic with exponential backoff for rate-limited RPCs.
|
|
906
|
-
"""
|
|
907
814
|
account = self._checksum(account) if account else self._strategy_address()
|
|
908
815
|
|
|
909
816
|
last_error = ""
|
|
@@ -914,7 +821,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
914
821
|
address=self.comptroller_address, abi=COMPTROLLER_ABI
|
|
915
822
|
)
|
|
916
823
|
|
|
917
|
-
# getAccountLiquidity returns (error, liquidity, shortfall)
|
|
918
824
|
(
|
|
919
825
|
error,
|
|
920
826
|
liquidity,
|
|
@@ -946,7 +852,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
946
852
|
mtoken: str,
|
|
947
853
|
account: str | None = None,
|
|
948
854
|
) -> tuple[bool, dict[str, Any] | str]:
|
|
949
|
-
"""Calculate max mTokens withdrawable without liquidation using binary search."""
|
|
950
855
|
mtoken = self._checksum(mtoken)
|
|
951
856
|
account = self._checksum(account) if account else self._strategy_address()
|
|
952
857
|
|
|
@@ -957,7 +862,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
957
862
|
)
|
|
958
863
|
mtoken_contract = web3.eth.contract(address=mtoken, abi=MTOKEN_ABI)
|
|
959
864
|
|
|
960
|
-
# Get all needed data in parallel
|
|
961
865
|
bal_raw, exch_raw, cash_raw, m_dec, u_addr = await asyncio.gather(
|
|
962
866
|
mtoken_contract.functions.balanceOf(account).call(
|
|
963
867
|
block_identifier="pending"
|
|
@@ -988,8 +892,7 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
988
892
|
"underlying_decimals": None,
|
|
989
893
|
}
|
|
990
894
|
|
|
991
|
-
|
|
992
|
-
u_dec = 18 # Default
|
|
895
|
+
u_dec = 18
|
|
993
896
|
if self.token_client:
|
|
994
897
|
try:
|
|
995
898
|
u_key = f"{self.chain_name}_{u_addr}"
|
|
@@ -1013,7 +916,7 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
1013
916
|
if err != 0:
|
|
1014
917
|
return False, f"Comptroller error {err}"
|
|
1015
918
|
if short == 0:
|
|
1016
|
-
lo = mid
|
|
919
|
+
lo = mid
|
|
1017
920
|
else:
|
|
1018
921
|
hi = mid - 1
|
|
1019
922
|
|
|
@@ -1055,7 +958,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
1055
958
|
*,
|
|
1056
959
|
amount: int,
|
|
1057
960
|
) -> tuple[bool, Any]:
|
|
1058
|
-
"""Wrap ETH to WETH."""
|
|
1059
961
|
strategy = self._strategy_address()
|
|
1060
962
|
amount = int(amount)
|
|
1061
963
|
if amount <= 0:
|
|
@@ -1079,9 +981,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
1079
981
|
MAX_UINT256 = 2**256 - 1
|
|
1080
982
|
|
|
1081
983
|
async def _send_tx(self, tx: dict[str, Any]) -> tuple[bool, Any]:
|
|
1082
|
-
"""Send transaction with simulation check."""
|
|
1083
|
-
if self.simulation:
|
|
1084
|
-
return True, {"simulation": tx}
|
|
1085
984
|
txn_hash = await send_transaction(tx, self.strategy_wallet_signing_callback)
|
|
1086
985
|
return True, txn_hash
|
|
1087
986
|
|
|
@@ -1093,14 +992,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
1093
992
|
spender: str,
|
|
1094
993
|
amount: int,
|
|
1095
994
|
) -> tuple[bool, Any]:
|
|
1096
|
-
"""Ensure token allowance is sufficient, approving if needed.
|
|
1097
|
-
|
|
1098
|
-
Approves for max uint256 to avoid precision issues with exact amounts.
|
|
1099
|
-
In simulation mode, skips the allowance check and assumes approval needed.
|
|
1100
|
-
"""
|
|
1101
|
-
if self.simulation:
|
|
1102
|
-
return True, {} # Skip allowance check in simulation
|
|
1103
|
-
|
|
1104
995
|
allowance = await get_token_allowance(
|
|
1105
996
|
token_address, self.chain_id, owner, spender
|
|
1106
997
|
)
|
|
@@ -1142,7 +1033,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
1142
1033
|
from_address: str,
|
|
1143
1034
|
value: int = 0,
|
|
1144
1035
|
) -> dict[str, Any]:
|
|
1145
|
-
"""Encode a contract call without touching the network."""
|
|
1146
1036
|
async with web3_from_chain_id(self.chain_id) as web3:
|
|
1147
1037
|
contract = web3.eth.contract(address=target, abi=abi)
|
|
1148
1038
|
|
|
@@ -1164,7 +1054,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
1164
1054
|
return tx
|
|
1165
1055
|
|
|
1166
1056
|
def _strategy_address(self) -> str:
|
|
1167
|
-
"""Get the strategy wallet address."""
|
|
1168
1057
|
addr = None
|
|
1169
1058
|
if isinstance(self.strategy_wallet, dict):
|
|
1170
1059
|
addr = self.strategy_wallet.get("address") or (
|
|
@@ -1179,7 +1068,6 @@ class MoonwellAdapter(BaseAdapter):
|
|
|
1179
1068
|
return to_checksum_address(addr)
|
|
1180
1069
|
|
|
1181
1070
|
def _checksum(self, address: str | None) -> str:
|
|
1182
|
-
"""Convert address to checksum format."""
|
|
1183
1071
|
if not address:
|
|
1184
1072
|
raise ValueError("Missing required contract address in Moonwell config")
|
|
1185
1073
|
return to_checksum_address(address)
|