wayfinder-paths 0.1.13__py3-none-any.whl → 0.1.14__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/README.md +13 -14
- wayfinder_paths/adapters/balance_adapter/adapter.py +33 -32
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +123 -0
- wayfinder_paths/adapters/brap_adapter/README.md +11 -16
- wayfinder_paths/adapters/brap_adapter/adapter.py +78 -63
- wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +121 -59
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +16 -14
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
- wayfinder_paths/adapters/pool_adapter/README.md +9 -10
- wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
- wayfinder_paths/adapters/token_adapter/README.md +2 -14
- wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
- wayfinder_paths/adapters/token_adapter/examples.json +4 -8
- wayfinder_paths/adapters/token_adapter/test_adapter.py +5 -3
- wayfinder_paths/core/clients/BRAPClient.py +102 -61
- wayfinder_paths/core/clients/ClientManager.py +1 -68
- wayfinder_paths/core/clients/HyperlendClient.py +125 -64
- wayfinder_paths/core/clients/LedgerClient.py +1 -4
- wayfinder_paths/core/clients/PoolClient.py +122 -48
- wayfinder_paths/core/clients/TokenClient.py +91 -36
- wayfinder_paths/core/clients/WalletClient.py +26 -56
- wayfinder_paths/core/clients/WayfinderClient.py +28 -160
- wayfinder_paths/core/clients/__init__.py +0 -2
- wayfinder_paths/core/clients/protocols.py +35 -46
- wayfinder_paths/core/clients/sdk_example.py +37 -22
- wayfinder_paths/core/engine/StrategyJob.py +7 -55
- wayfinder_paths/core/services/local_evm_txn.py +6 -6
- wayfinder_paths/core/services/local_token_txn.py +1 -1
- wayfinder_paths/core/strategies/Strategy.py +0 -2
- wayfinder_paths/core/utils/evm_helpers.py +2 -2
- wayfinder_paths/run_strategy.py +8 -19
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +10 -11
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +40 -25
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +3 -3
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +88 -56
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
- wayfinder_paths/templates/strategy/README.md +3 -3
- wayfinder_paths/templates/strategy/test_strategy.py +3 -2
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/METADATA +14 -49
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/RECORD +46 -47
- wayfinder_paths/core/clients/AuthClient.py +0 -83
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/WHEEL +0 -0
|
@@ -157,9 +157,8 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
157
157
|
main_wallet: dict[str, Any] | None = None,
|
|
158
158
|
strategy_wallet: dict[str, Any] | None = None,
|
|
159
159
|
web3_service=None,
|
|
160
|
-
api_key: str | None = None,
|
|
161
160
|
):
|
|
162
|
-
super().__init__(
|
|
161
|
+
super().__init__()
|
|
163
162
|
merged_config: dict[str, Any] = dict(config or {})
|
|
164
163
|
if main_wallet is not None:
|
|
165
164
|
merged_config["main_wallet"] = main_wallet
|
|
@@ -271,7 +270,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
271
270
|
for token_id in self.tracked_token_ids:
|
|
272
271
|
try:
|
|
273
272
|
success, balance_wei = await self.balance_adapter.get_balance(
|
|
274
|
-
|
|
273
|
+
query=token_id,
|
|
275
274
|
wallet_address=strategy_address,
|
|
276
275
|
)
|
|
277
276
|
if success and balance_wei:
|
|
@@ -360,8 +359,8 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
360
359
|
f"Gas token loaded: {gas_token_data.get('symbol', 'Unknown')}"
|
|
361
360
|
)
|
|
362
361
|
# Track gas token (but don't count it as a strategy asset)
|
|
363
|
-
if self.gas_token.get("
|
|
364
|
-
self._track_token(self.gas_token.get("
|
|
362
|
+
if self.gas_token.get("token_id"):
|
|
363
|
+
self._track_token(self.gas_token.get("token_id"))
|
|
365
364
|
else:
|
|
366
365
|
logger.warning("Failed to fetch gas token info, using empty dict")
|
|
367
366
|
self.gas_token = {}
|
|
@@ -446,16 +445,28 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
446
445
|
|
|
447
446
|
llama_report = None
|
|
448
447
|
if pool_ids:
|
|
449
|
-
success,
|
|
448
|
+
success, pool_list_response = await self.pool_adapter.get_pools_by_ids(
|
|
450
449
|
pool_ids=pool_ids
|
|
451
450
|
)
|
|
452
|
-
if success:
|
|
451
|
+
if success and isinstance(pool_list_response, dict):
|
|
452
|
+
pools = pool_list_response.get("pools", [])
|
|
453
|
+
# Search for matching pool by id or constructed identifier
|
|
453
454
|
for identifier in pool_ids:
|
|
454
455
|
if not isinstance(identifier, str):
|
|
455
456
|
continue
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
457
|
+
identifier_lower = identifier.lower()
|
|
458
|
+
for pool in pools:
|
|
459
|
+
pool_id = pool.get("id", "").lower()
|
|
460
|
+
pool_address = pool.get("address", "").lower()
|
|
461
|
+
pool_chain_code = pool.get("chain_code", "").lower()
|
|
462
|
+
constructed_id = f"{pool_chain_code}_{pool_address}"
|
|
463
|
+
if (
|
|
464
|
+
pool_id == identifier_lower
|
|
465
|
+
or constructed_id == identifier_lower
|
|
466
|
+
):
|
|
467
|
+
llama_report = pool
|
|
468
|
+
break
|
|
469
|
+
if llama_report:
|
|
459
470
|
break
|
|
460
471
|
|
|
461
472
|
if self.current_pool_data is None and llama_report:
|
|
@@ -471,7 +482,14 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
471
482
|
elif llama_report and llama_report.get("apy") is not None:
|
|
472
483
|
self.current_combined_apy_pct = llama_report.get("apy", 0) / 100
|
|
473
484
|
elif self.current_pool_data:
|
|
474
|
-
|
|
485
|
+
apy_pct = self.current_pool_data.get("combined_apy_pct")
|
|
486
|
+
if apy_pct is not None:
|
|
487
|
+
self.current_combined_apy_pct = float(apy_pct) / 100
|
|
488
|
+
else:
|
|
489
|
+
apy_val = self.current_pool_data.get("apy", 0)
|
|
490
|
+
self.current_combined_apy_pct = (
|
|
491
|
+
float(apy_val) / 100 if apy_val is not None else 0
|
|
492
|
+
)
|
|
475
493
|
|
|
476
494
|
pool_address = self.current_pool.get("address")
|
|
477
495
|
chain_id = self.current_pool.get("chain", {}).get("id")
|
|
@@ -486,10 +504,10 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
486
504
|
(
|
|
487
505
|
success,
|
|
488
506
|
current_pool_balance_raw,
|
|
489
|
-
) = await self.balance_adapter.
|
|
490
|
-
|
|
507
|
+
) = await self.balance_adapter.get_balance(
|
|
508
|
+
query=pool_address,
|
|
509
|
+
wallet_address=user_address,
|
|
491
510
|
chain_id=chain_id,
|
|
492
|
-
user_address=user_address,
|
|
493
511
|
)
|
|
494
512
|
self.current_pool_balance = current_pool_balance_raw if success else 0
|
|
495
513
|
except Exception as e:
|
|
@@ -535,7 +553,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
535
553
|
|
|
536
554
|
if self.usdc_token_info:
|
|
537
555
|
status, raw_balance = await self.balance_adapter.get_balance(
|
|
538
|
-
|
|
556
|
+
query=self.usdc_token_info.get("token_id"),
|
|
539
557
|
wallet_address=self._get_strategy_wallet_address(),
|
|
540
558
|
)
|
|
541
559
|
if not status or not raw_balance:
|
|
@@ -578,7 +596,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
578
596
|
await self._refresh_tracked_balances()
|
|
579
597
|
|
|
580
598
|
usdc_token_id = self.usdc_token_info.get("token_id")
|
|
581
|
-
gas_token_id = self.gas_token.get("
|
|
599
|
+
gas_token_id = self.gas_token.get("token_id") if self.gas_token else None
|
|
582
600
|
|
|
583
601
|
best_token_id = None
|
|
584
602
|
best_balance_wei = 0
|
|
@@ -609,7 +627,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
609
627
|
strategy_address = self._get_strategy_wallet_address()
|
|
610
628
|
try:
|
|
611
629
|
success, onchain_balance = await self.balance_adapter.get_balance(
|
|
612
|
-
|
|
630
|
+
query=token.get("token_id"),
|
|
613
631
|
wallet_address=strategy_address,
|
|
614
632
|
)
|
|
615
633
|
if success and onchain_balance:
|
|
@@ -635,7 +653,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
635
653
|
token_id = balance.get("token_id")
|
|
636
654
|
if (
|
|
637
655
|
isinstance(token_id, str)
|
|
638
|
-
and token_id.lower() == self.gas_token.get("
|
|
656
|
+
and token_id.lower() == self.gas_token.get("token_id", "").lower()
|
|
639
657
|
):
|
|
640
658
|
return True
|
|
641
659
|
|
|
@@ -680,7 +698,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
680
698
|
"address": token_info.get("address"),
|
|
681
699
|
"chain": token_info.get("chain"),
|
|
682
700
|
}
|
|
683
|
-
gas_token_id = self.gas_token.get("
|
|
701
|
+
gas_token_id = self.gas_token.get("token_id")
|
|
684
702
|
logger.info(
|
|
685
703
|
f"Current pool set to: {token_info.get('symbol')} on {token_info.get('chain', {}).get('name')}"
|
|
686
704
|
)
|
|
@@ -692,7 +710,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
692
710
|
main_usdc_status,
|
|
693
711
|
main_usdc_balance,
|
|
694
712
|
) = await self.balance_adapter.get_balance(
|
|
695
|
-
|
|
713
|
+
query=token_info.get("token_id"),
|
|
696
714
|
wallet_address=self._get_main_wallet_address(),
|
|
697
715
|
)
|
|
698
716
|
if main_usdc_status and main_usdc_balance is not None:
|
|
@@ -737,7 +755,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
737
755
|
_,
|
|
738
756
|
main_gas_raw,
|
|
739
757
|
) = await self.balance_adapter.get_balance(
|
|
740
|
-
|
|
758
|
+
query=gas_token_id,
|
|
741
759
|
wallet_address=self._get_main_wallet_address(),
|
|
742
760
|
)
|
|
743
761
|
main_gas_int = (
|
|
@@ -762,14 +780,14 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
762
780
|
_,
|
|
763
781
|
main_gas_raw,
|
|
764
782
|
) = await self.balance_adapter.get_balance(
|
|
765
|
-
|
|
783
|
+
query=gas_token_id,
|
|
766
784
|
wallet_address=self._get_main_wallet_address(),
|
|
767
785
|
)
|
|
768
786
|
(
|
|
769
787
|
_,
|
|
770
788
|
strategy_gas_raw,
|
|
771
789
|
) = await self.balance_adapter.get_balance(
|
|
772
|
-
|
|
790
|
+
query=gas_token_id,
|
|
773
791
|
wallet_address=self._get_strategy_wallet_address(),
|
|
774
792
|
)
|
|
775
793
|
main_gas_int = (
|
|
@@ -895,10 +913,10 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
895
913
|
(
|
|
896
914
|
_,
|
|
897
915
|
self.current_pool_balance,
|
|
898
|
-
) = await self.balance_adapter.
|
|
899
|
-
|
|
916
|
+
) = await self.balance_adapter.get_balance(
|
|
917
|
+
query=self.current_pool.get("address"),
|
|
918
|
+
wallet_address=self._get_strategy_wallet_address(),
|
|
900
919
|
chain_id=self.current_pool.get("chain").get("id"),
|
|
901
|
-
user_address=self._get_strategy_wallet_address(),
|
|
902
920
|
)
|
|
903
921
|
logger.info(f"Current pool balance: {self.current_pool_balance}")
|
|
904
922
|
except Exception as e:
|
|
@@ -931,8 +949,8 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
931
949
|
)
|
|
932
950
|
if (
|
|
933
951
|
success
|
|
934
|
-
and
|
|
935
|
-
and quotes.get("
|
|
952
|
+
and isinstance(quotes, dict)
|
|
953
|
+
and quotes.get("best_quote")
|
|
936
954
|
):
|
|
937
955
|
logger.info("Successfully obtained swap quote")
|
|
938
956
|
break
|
|
@@ -941,7 +959,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
941
959
|
if attempt == 3: # Last attempt
|
|
942
960
|
logger.error("All quote attempts failed")
|
|
943
961
|
|
|
944
|
-
best_quote = quotes.get("
|
|
962
|
+
best_quote = quotes.get("best_quote") if isinstance(quotes, dict) else None
|
|
945
963
|
if not best_quote:
|
|
946
964
|
return (
|
|
947
965
|
False,
|
|
@@ -998,7 +1016,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
998
1016
|
if self.usdc_token_info.get("token_id") in withdrawn_token_ids:
|
|
999
1017
|
pass
|
|
1000
1018
|
status, raw_balance = await self.balance_adapter.get_balance(
|
|
1001
|
-
|
|
1019
|
+
query=self.usdc_token_info.get("token_id"),
|
|
1002
1020
|
wallet_address=self._get_strategy_wallet_address(),
|
|
1003
1021
|
)
|
|
1004
1022
|
if not status or not raw_balance:
|
|
@@ -1025,9 +1043,9 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1025
1043
|
)
|
|
1026
1044
|
withdrawn_token_ids.add(self.usdc_token_info.get("token_id"))
|
|
1027
1045
|
|
|
1028
|
-
if self.gas_token and self.gas_token.get("
|
|
1046
|
+
if self.gas_token and self.gas_token.get("token_id") not in withdrawn_token_ids:
|
|
1029
1047
|
status, raw_gas = await self.balance_adapter.get_balance(
|
|
1030
|
-
|
|
1048
|
+
query=self.gas_token.get("token_id"),
|
|
1031
1049
|
wallet_address=self._get_strategy_wallet_address(),
|
|
1032
1050
|
)
|
|
1033
1051
|
if status and raw_gas:
|
|
@@ -1039,7 +1057,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1039
1057
|
move_gas_status,
|
|
1040
1058
|
move_gas_message,
|
|
1041
1059
|
) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
|
|
1042
|
-
self.gas_token.get("
|
|
1060
|
+
self.gas_token.get("token_id"),
|
|
1043
1061
|
gas_amount,
|
|
1044
1062
|
strategy_name=self.name,
|
|
1045
1063
|
)
|
|
@@ -1051,7 +1069,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1051
1069
|
float(gas_amount),
|
|
1052
1070
|
)
|
|
1053
1071
|
)
|
|
1054
|
-
withdrawn_token_ids.add(self.gas_token.get("
|
|
1072
|
+
withdrawn_token_ids.add(self.gas_token.get("token_id"))
|
|
1055
1073
|
|
|
1056
1074
|
self.DEPOSIT_USDC = 0
|
|
1057
1075
|
self.current_pool_balance = 0
|
|
@@ -1206,7 +1224,14 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1206
1224
|
else:
|
|
1207
1225
|
self.current_pool_data = None
|
|
1208
1226
|
if self.current_pool_data:
|
|
1209
|
-
|
|
1227
|
+
apy_pct = self.current_pool_data.get("combined_apy_pct")
|
|
1228
|
+
if apy_pct is not None:
|
|
1229
|
+
self.current_combined_apy_pct = float(apy_pct) / 100
|
|
1230
|
+
else:
|
|
1231
|
+
apy_val = self.current_pool_data.get("apy", 0)
|
|
1232
|
+
self.current_combined_apy_pct = (
|
|
1233
|
+
float(apy_val) / 100 if apy_val is not None else 0
|
|
1234
|
+
)
|
|
1210
1235
|
else:
|
|
1211
1236
|
self.current_combined_apy_pct = (
|
|
1212
1237
|
target_pool_data.get("combined_apy_pct", 0) / 100
|
|
@@ -1242,10 +1267,10 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1242
1267
|
(
|
|
1243
1268
|
_,
|
|
1244
1269
|
refreshed_pool_balance,
|
|
1245
|
-
) = await self.balance_adapter.
|
|
1246
|
-
|
|
1270
|
+
) = await self.balance_adapter.get_balance(
|
|
1271
|
+
query=pool.get("address"),
|
|
1272
|
+
wallet_address=strategy_address,
|
|
1247
1273
|
chain_id=pool.get("chain").get("id"),
|
|
1248
|
-
user_address=strategy_address,
|
|
1249
1274
|
)
|
|
1250
1275
|
self.current_pool_balance = int(refreshed_pool_balance)
|
|
1251
1276
|
except Exception:
|
|
@@ -1259,7 +1284,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1259
1284
|
target_token_id = target_token.get("token_id")
|
|
1260
1285
|
target_chain = target_token.get("chain").get("code", "").lower()
|
|
1261
1286
|
target_address = target_token.get("address", "").lower()
|
|
1262
|
-
gas_token_id = self.gas_token.get("
|
|
1287
|
+
gas_token_id = self.gas_token.get("token_id") if self.gas_token else None
|
|
1263
1288
|
|
|
1264
1289
|
# Swap all non-target, non-gas tokens to the target
|
|
1265
1290
|
for token_id, balance_wei in list(self.tracked_balances.items()):
|
|
@@ -1278,7 +1303,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1278
1303
|
# Get fresh balance to ensure accuracy
|
|
1279
1304
|
try:
|
|
1280
1305
|
success, fresh_balance = await self.balance_adapter.get_balance(
|
|
1281
|
-
|
|
1306
|
+
query=token_id,
|
|
1282
1307
|
wallet_address=self._get_strategy_wallet_address(),
|
|
1283
1308
|
)
|
|
1284
1309
|
if not success or not fresh_balance or int(fresh_balance) <= 0:
|
|
@@ -1318,7 +1343,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1318
1343
|
# Refresh target token balance
|
|
1319
1344
|
try:
|
|
1320
1345
|
success, target_balance = await self.balance_adapter.get_balance(
|
|
1321
|
-
|
|
1346
|
+
query=target_token_id,
|
|
1322
1347
|
wallet_address=self._get_strategy_wallet_address(),
|
|
1323
1348
|
)
|
|
1324
1349
|
if success and target_balance:
|
|
@@ -1335,7 +1360,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1335
1360
|
|
|
1336
1361
|
required_gas = int(self.MIN_GAS * (10 ** self.gas_token.get("decimals")))
|
|
1337
1362
|
_, current_gas = await self.balance_adapter.get_balance(
|
|
1338
|
-
|
|
1363
|
+
query=self.gas_token.get("token_id"),
|
|
1339
1364
|
wallet_address=strategy_address,
|
|
1340
1365
|
)
|
|
1341
1366
|
if current_gas >= required_gas:
|
|
@@ -1395,7 +1420,11 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1395
1420
|
if not success:
|
|
1396
1421
|
return 0.0
|
|
1397
1422
|
|
|
1398
|
-
best_quote =
|
|
1423
|
+
best_quote = (
|
|
1424
|
+
exit_quotes.get("best_quote") if isinstance(exit_quotes, dict) else None
|
|
1425
|
+
)
|
|
1426
|
+
if not best_quote:
|
|
1427
|
+
return None
|
|
1399
1428
|
current_pool_usd_value = best_quote.get("output_amount")
|
|
1400
1429
|
|
|
1401
1430
|
return float(
|
|
@@ -1407,7 +1436,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1407
1436
|
# Refresh tracked balances
|
|
1408
1437
|
await self._refresh_tracked_balances()
|
|
1409
1438
|
|
|
1410
|
-
gas_token_id = self.gas_token.get("
|
|
1439
|
+
gas_token_id = self.gas_token.get("token_id") if self.gas_token else None
|
|
1411
1440
|
results = []
|
|
1412
1441
|
|
|
1413
1442
|
for token_id, balance_wei in self.tracked_balances.items():
|
|
@@ -1440,7 +1469,10 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1440
1469
|
return results
|
|
1441
1470
|
|
|
1442
1471
|
async def _find_best_pool(self) -> tuple[bool, dict[str, Any]]:
|
|
1443
|
-
|
|
1472
|
+
chain_id = (self.usdc_token_info or {}).get("chain", {}).get("id")
|
|
1473
|
+
if chain_id is None:
|
|
1474
|
+
chain_id = 8453
|
|
1475
|
+
success, llama_data = await self.pool_adapter.get_pools(chain_id=chain_id)
|
|
1444
1476
|
if not success:
|
|
1445
1477
|
return False, {"message": f"Failed to fetch Llama data: {llama_data}"}
|
|
1446
1478
|
|
|
@@ -1469,11 +1501,11 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1469
1501
|
)
|
|
1470
1502
|
if not target_status and candidate.get("token_id"):
|
|
1471
1503
|
target_status, target_pool = await self.token_adapter.get_token(
|
|
1472
|
-
|
|
1504
|
+
query=candidate.get("token_id")
|
|
1473
1505
|
)
|
|
1474
1506
|
if not target_status and candidate.get("pool_id"):
|
|
1475
1507
|
target_status, target_pool = await self.token_adapter.get_token(
|
|
1476
|
-
|
|
1508
|
+
query=candidate.get("pool_id")
|
|
1477
1509
|
)
|
|
1478
1510
|
if not target_status:
|
|
1479
1511
|
continue
|
|
@@ -1515,7 +1547,8 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1515
1547
|
return None
|
|
1516
1548
|
|
|
1517
1549
|
try:
|
|
1518
|
-
|
|
1550
|
+
apy_pct = pool_data.get("combined_apy_pct") or pool_data.get("apy") or 0
|
|
1551
|
+
combined_apy_pct = float(apy_pct) / 100
|
|
1519
1552
|
success, quotes = await self.brap_adapter.get_swap_quote(
|
|
1520
1553
|
from_token_address=current_token.get("address"),
|
|
1521
1554
|
to_token_address=token.get("address"),
|
|
@@ -1527,10 +1560,9 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1527
1560
|
)
|
|
1528
1561
|
if not success:
|
|
1529
1562
|
return None
|
|
1530
|
-
|
|
1531
|
-
if not isinstance(quotes_data, dict):
|
|
1563
|
+
if not isinstance(quotes, dict):
|
|
1532
1564
|
return None
|
|
1533
|
-
best_quote =
|
|
1565
|
+
best_quote = quotes.get("best_quote")
|
|
1534
1566
|
if not best_quote:
|
|
1535
1567
|
return None
|
|
1536
1568
|
|
|
@@ -1582,7 +1614,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1582
1614
|
async def _status(self) -> StatusDict:
|
|
1583
1615
|
# Get ETH gas balance
|
|
1584
1616
|
gas_success, gas_balance_wei = await self.balance_adapter.get_balance(
|
|
1585
|
-
|
|
1617
|
+
query=self.gas_token.get("token_id"),
|
|
1586
1618
|
wallet_address=self._get_strategy_wallet_address(),
|
|
1587
1619
|
)
|
|
1588
1620
|
gas_balance = (
|
|
@@ -1611,7 +1643,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1611
1643
|
|
|
1612
1644
|
# Calculate total value from tracked non-gas balances
|
|
1613
1645
|
total_value = 0.0
|
|
1614
|
-
gas_token_id = self.gas_token.get("
|
|
1646
|
+
gas_token_id = self.gas_token.get("token_id") if self.gas_token else None
|
|
1615
1647
|
|
|
1616
1648
|
for token_id, balance_wei in self.tracked_balances.items():
|
|
1617
1649
|
if token_id == gas_token_id:
|
|
@@ -1680,7 +1712,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1680
1712
|
|
|
1681
1713
|
usdc_token_id = self.usdc_token_info.get("token_id")
|
|
1682
1714
|
usdc_decimals = self.usdc_token_info.get("decimals")
|
|
1683
|
-
gas_token_id = self.gas_token.get("
|
|
1715
|
+
gas_token_id = self.gas_token.get("token_id") if self.gas_token else None
|
|
1684
1716
|
|
|
1685
1717
|
# Check current USDC balance
|
|
1686
1718
|
available_usdc_wei = self.tracked_balances.get(usdc_token_id, 0)
|
|
@@ -1745,7 +1777,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1745
1777
|
|
|
1746
1778
|
# Refresh USDC balance after swaps
|
|
1747
1779
|
success, usdc_wei = await self.balance_adapter.get_balance(
|
|
1748
|
-
|
|
1780
|
+
query=usdc_token_id,
|
|
1749
1781
|
wallet_address=self._get_strategy_wallet_address(),
|
|
1750
1782
|
)
|
|
1751
1783
|
if success and usdc_wei:
|
|
@@ -1790,7 +1822,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1790
1822
|
|
|
1791
1823
|
# Refresh USDC balance again
|
|
1792
1824
|
success, usdc_wei = await self.balance_adapter.get_balance(
|
|
1793
|
-
|
|
1825
|
+
query=usdc_token_id,
|
|
1794
1826
|
wallet_address=self._get_strategy_wallet_address(),
|
|
1795
1827
|
)
|
|
1796
1828
|
if success and usdc_wei:
|
|
@@ -52,7 +52,10 @@ def strategy():
|
|
|
52
52
|
usdc_balance_mock = AsyncMock(return_value=(True, 60000000))
|
|
53
53
|
gas_balance_mock = AsyncMock(return_value=(True, 2000000000000000))
|
|
54
54
|
|
|
55
|
-
def get_balance_side_effect(
|
|
55
|
+
def get_balance_side_effect(query, wallet_address, **kwargs):
|
|
56
|
+
token_id = (
|
|
57
|
+
query if isinstance(query, str) else (query or {}).get("token_id")
|
|
58
|
+
)
|
|
56
59
|
if token_id == "usd-coin-base" or token_id == "usd-coin":
|
|
57
60
|
return usdc_balance_mock.return_value
|
|
58
61
|
elif token_id == "ethereum-base" or token_id == "ethereum":
|
|
@@ -449,7 +452,8 @@ async def test_sweep_wallet_uses_tracked_tokens(strategy):
|
|
|
449
452
|
assert "token-2" in strategy.tracked_token_ids
|
|
450
453
|
|
|
451
454
|
# Mock balance adapter to return fresh balances
|
|
452
|
-
async def get_balance_mock(
|
|
455
|
+
async def get_balance_mock(query, **kwargs):
|
|
456
|
+
token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
453
457
|
balance = strategy.tracked_balances.get(token_id, 0)
|
|
454
458
|
# Return the balance, ensuring it's an int
|
|
455
459
|
return (True, int(balance) if balance else 0)
|
|
@@ -495,12 +499,11 @@ async def test_get_non_gas_balances_uses_tracked_state(strategy):
|
|
|
495
499
|
strategy._track_token(pool_token_id, 50000000000000000000)
|
|
496
500
|
|
|
497
501
|
# Mock refresh
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
)
|
|
502
|
+
def _get_balance_effect(query, **kwargs):
|
|
503
|
+
token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
504
|
+
return (True, strategy.tracked_balances.get(token_id, 0))
|
|
505
|
+
|
|
506
|
+
strategy.balance_adapter.get_balance = AsyncMock(side_effect=_get_balance_effect)
|
|
504
507
|
|
|
505
508
|
# Get non-gas balances
|
|
506
509
|
balances = await strategy._get_non_gas_balances()
|
|
@@ -519,11 +522,12 @@ async def test_partial_liquidate_uses_tracked_tokens(strategy):
|
|
|
519
522
|
strategy._track_token("test-pool-base", 100000000000000000000) # 100 POOL tokens
|
|
520
523
|
|
|
521
524
|
# Mock balance and token adapters
|
|
525
|
+
def _get_balance_effect_partial(query, **kwargs):
|
|
526
|
+
token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
527
|
+
return (True, strategy.tracked_balances.get(token_id, 0))
|
|
528
|
+
|
|
522
529
|
strategy.balance_adapter.get_balance = AsyncMock(
|
|
523
|
-
side_effect=
|
|
524
|
-
True,
|
|
525
|
-
strategy.tracked_balances.get(token_id, 0),
|
|
526
|
-
)
|
|
530
|
+
side_effect=_get_balance_effect_partial
|
|
527
531
|
)
|
|
528
532
|
|
|
529
533
|
strategy.token_adapter.get_token_price = AsyncMock(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# Strategy Template
|
|
2
2
|
|
|
3
3
|
This template provides the scaffolding for a new strategy. It mirrors the structure in `wayfinder_paths/strategies/...`.
|
|
4
4
|
|
|
@@ -70,7 +70,7 @@ class MyStrategy(Strategy):
|
|
|
70
70
|
return (False, "Nothing to deposit")
|
|
71
71
|
|
|
72
72
|
success, _ = await self.balance_adapter.get_balance(
|
|
73
|
-
|
|
73
|
+
query=self.config.get("token_id"),
|
|
74
74
|
wallet_address=self.config.get("main_wallet", {}).get("address"),
|
|
75
75
|
)
|
|
76
76
|
if not success:
|
|
@@ -87,7 +87,7 @@ class MyStrategy(Strategy):
|
|
|
87
87
|
async def _status(self) -> StatusDict:
|
|
88
88
|
"""Surface state back to run_strategy.py."""
|
|
89
89
|
success, balance = await self.balance_adapter.get_balance(
|
|
90
|
-
|
|
90
|
+
query=self.config.get("token_id"),
|
|
91
91
|
wallet_address=self.config.get("strategy_wallet", {}).get("address"),
|
|
92
92
|
)
|
|
93
93
|
return {
|
|
@@ -66,14 +66,15 @@ def strategy():
|
|
|
66
66
|
# usdc_balance_mock = AsyncMock(return_value=(True, 60000000))
|
|
67
67
|
# gas_balance_mock = AsyncMock(return_value=(True, 2000000000000000))
|
|
68
68
|
#
|
|
69
|
-
# def get_balance_side_effect(
|
|
69
|
+
# def get_balance_side_effect(query, wallet_address, **kwargs):
|
|
70
|
+
# token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
70
71
|
# if token_id == "usd-coin-base" or token_id == "usd-coin":
|
|
71
72
|
# return usdc_balance_mock.return_value
|
|
72
73
|
# elif token_id == "ethereum-base" or token_id == "ethereum":
|
|
73
74
|
# return gas_balance_mock.return_value
|
|
74
75
|
# return (True, 1000000000)
|
|
75
76
|
#
|
|
76
|
-
# s.balance_adapter.
|
|
77
|
+
# s.balance_adapter.get_balance = AsyncMock(
|
|
77
78
|
# side_effect=get_balance_side_effect
|
|
78
79
|
# )
|
|
79
80
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wayfinder-paths
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.14
|
|
4
4
|
Summary: Wayfinder Path: strategies and adapters
|
|
5
5
|
Author: Wayfinder
|
|
6
6
|
Author-email: dev@wayfinder.ai
|
|
@@ -216,63 +216,28 @@ See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for details.
|
|
|
216
216
|
|
|
217
217
|
### Authentication
|
|
218
218
|
|
|
219
|
-
Wayfinder Paths
|
|
219
|
+
Wayfinder Paths uses API key authentication via the `X-API-KEY` header.
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
For backend services and automated systems with higher rate limits:
|
|
224
|
-
|
|
225
|
-
**Option A: Pass to Strategy Constructor**
|
|
226
|
-
|
|
227
|
-
```python
|
|
228
|
-
from wayfinder_paths.strategies.stablecoin_yield_strategy.strategy import StablecoinYieldStrategy
|
|
229
|
-
|
|
230
|
-
strategy = StablecoinYieldStrategy(
|
|
231
|
-
config={...},
|
|
232
|
-
api_key="sk_live_abc123..." # API key is auto-discovered by all clients
|
|
233
|
-
)
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
**Option B: Add to config.json**
|
|
221
|
+
**Add API key to config.json:**
|
|
237
222
|
|
|
238
223
|
```json
|
|
239
224
|
{
|
|
240
|
-
"user": {
|
|
241
|
-
"api_key": "sk_live_abc123..."
|
|
242
|
-
},
|
|
243
225
|
"system": {
|
|
244
|
-
"api_key": "sk_live_abc123..."
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
**Priority Order:** Constructor parameter > `config.json` (user.api_key or system.api_key)
|
|
250
|
-
|
|
251
|
-
**Note:** API keys in `config.json` are loaded directly by `WayfinderClient` via `_load_config_credentials()`, not through the `UserConfig` or `SystemConfig` dataclasses. This allows flexible credential loading.
|
|
252
|
-
|
|
253
|
-
#### 2. Personal Access Authentication (OAuth)
|
|
254
|
-
|
|
255
|
-
For standalone SDK users with username/password:
|
|
256
|
-
|
|
257
|
-
```json
|
|
258
|
-
{
|
|
259
|
-
"user": {
|
|
260
|
-
"username": "your_username",
|
|
261
|
-
"password": "your_password",
|
|
262
|
-
"refresh_token": null // Optional: use refresh token instead
|
|
226
|
+
"api_key": "sk_live_abc123...",
|
|
227
|
+
"api_base_url": "https://wayfinder.ai/api/v1",
|
|
228
|
+
"wallets_path": "wallets.json"
|
|
263
229
|
}
|
|
264
230
|
}
|
|
265
231
|
```
|
|
266
232
|
|
|
267
233
|
**How It Works:**
|
|
268
234
|
|
|
269
|
-
- API
|
|
270
|
-
-
|
|
271
|
-
-
|
|
272
|
-
-
|
|
273
|
-
- API keys in `config.json` are loaded directly by `WayfinderClient._load_config_credentials()` from `user.api_key` or `system.api_key`, not stored in the `UserConfig` or `SystemConfig` dataclasses
|
|
235
|
+
- API key is automatically loaded from `system.api_key` in config.json
|
|
236
|
+
- The API key is sent as the `X-API-KEY` header on all API requests
|
|
237
|
+
- All clients automatically include the API key header
|
|
238
|
+
- No need to pass API keys explicitly to strategies or clients
|
|
274
239
|
|
|
275
|
-
See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for detailed
|
|
240
|
+
See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for detailed configuration documentation.
|
|
276
241
|
|
|
277
242
|
## 🔌 Creating Adapters
|
|
278
243
|
|
|
@@ -299,7 +264,7 @@ class MyAdapter(BaseAdapter):
|
|
|
299
264
|
|
|
300
265
|
async def get_pools(self, pool_ids: list[str]):
|
|
301
266
|
data = await self.pool_client.get_pools_by_ids(
|
|
302
|
-
pool_ids=
|
|
267
|
+
pool_ids=pool_ids
|
|
303
268
|
)
|
|
304
269
|
return (True, data)
|
|
305
270
|
```
|
|
@@ -340,7 +305,7 @@ class MyStrategy(Strategy):
|
|
|
340
305
|
return (False, "Nothing to deposit")
|
|
341
306
|
|
|
342
307
|
success, _ = await self.balance_adapter.get_balance(
|
|
343
|
-
|
|
308
|
+
query=self.config.get("token_id"),
|
|
344
309
|
wallet_address=self.config.get("main_wallet", {}).get("address"),
|
|
345
310
|
)
|
|
346
311
|
if not success:
|
|
@@ -357,7 +322,7 @@ class MyStrategy(Strategy):
|
|
|
357
322
|
async def _status(self) -> StatusDict:
|
|
358
323
|
"""Report balances back to the runner"""
|
|
359
324
|
success, balance = await self.balance_adapter.get_balance(
|
|
360
|
-
|
|
325
|
+
query=self.config.get("token_id"),
|
|
361
326
|
wallet_address=self.config.get("strategy_wallet", {}).get("address"),
|
|
362
327
|
)
|
|
363
328
|
return {
|