wayfinder-paths 0.1.13__py3-none-any.whl → 0.1.15__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.

Files changed (61) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +13 -14
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +73 -32
  3. wayfinder_paths/adapters/balance_adapter/test_adapter.py +123 -0
  4. wayfinder_paths/adapters/brap_adapter/README.md +11 -16
  5. wayfinder_paths/adapters/brap_adapter/adapter.py +144 -78
  6. wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
  7. wayfinder_paths/adapters/brap_adapter/test_adapter.py +127 -65
  8. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +30 -14
  9. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +121 -67
  10. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +6 -6
  11. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +12 -12
  12. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +6 -6
  13. wayfinder_paths/adapters/moonwell_adapter/adapter.py +332 -9
  14. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +13 -13
  15. wayfinder_paths/adapters/pool_adapter/README.md +9 -10
  16. wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
  17. wayfinder_paths/adapters/pool_adapter/test_adapter.py +2 -2
  18. wayfinder_paths/adapters/token_adapter/README.md +2 -14
  19. wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
  20. wayfinder_paths/adapters/token_adapter/examples.json +4 -8
  21. wayfinder_paths/adapters/token_adapter/test_adapter.py +9 -7
  22. wayfinder_paths/core/clients/BRAPClient.py +102 -61
  23. wayfinder_paths/core/clients/ClientManager.py +1 -68
  24. wayfinder_paths/core/clients/HyperlendClient.py +125 -64
  25. wayfinder_paths/core/clients/LedgerClient.py +1 -4
  26. wayfinder_paths/core/clients/PoolClient.py +122 -48
  27. wayfinder_paths/core/clients/TokenClient.py +91 -36
  28. wayfinder_paths/core/clients/WalletClient.py +26 -56
  29. wayfinder_paths/core/clients/WayfinderClient.py +28 -160
  30. wayfinder_paths/core/clients/__init__.py +0 -2
  31. wayfinder_paths/core/clients/protocols.py +35 -46
  32. wayfinder_paths/core/clients/sdk_example.py +37 -22
  33. wayfinder_paths/core/constants/erc20_abi.py +0 -11
  34. wayfinder_paths/core/engine/StrategyJob.py +10 -56
  35. wayfinder_paths/core/services/base.py +1 -0
  36. wayfinder_paths/core/services/local_evm_txn.py +25 -9
  37. wayfinder_paths/core/services/local_token_txn.py +2 -6
  38. wayfinder_paths/core/services/test_local_evm_txn.py +145 -0
  39. wayfinder_paths/core/strategies/Strategy.py +16 -4
  40. wayfinder_paths/core/utils/evm_helpers.py +2 -9
  41. wayfinder_paths/policies/erc20.py +1 -1
  42. wayfinder_paths/run_strategy.py +13 -19
  43. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +77 -11
  44. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +6 -6
  45. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +107 -23
  46. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
  47. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +6 -5
  48. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2246 -1279
  49. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +276 -109
  50. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
  51. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +153 -56
  52. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
  53. wayfinder_paths/templates/adapter/README.md +1 -1
  54. wayfinder_paths/templates/strategy/README.md +3 -3
  55. wayfinder_paths/templates/strategy/test_strategy.py +3 -2
  56. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/METADATA +14 -49
  57. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/RECORD +59 -60
  58. wayfinder_paths/abis/generic/erc20.json +0 -383
  59. wayfinder_paths/core/clients/AuthClient.py +0 -83
  60. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/LICENSE +0 -0
  61. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.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__(api_key=api_key)
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
- token_id=token_id,
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("id"):
364
- self._track_token(self.gas_token.get("id"))
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, llama_reports = await self.pool_adapter.get_pools_by_ids(
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
- report = llama_reports.get(identifier.lower(), None)
457
- if report:
458
- llama_report = report
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
- self.current_combined_apy_pct = self.current_pool_data.get("apy", 0)
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.get_pool_balance(
490
- pool_address=pool_address,
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
- token_id=self.usdc_token_info.get("token_id"),
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("id") if self.gas_token else None
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
- token_id=token.get("token_id"),
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("id", "").lower()
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("id")
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
- token_id=token_info.get("token_id"),
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
- token_id=gas_token_id,
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
- token_id=gas_token_id,
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
- token_id=gas_token_id,
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.get_pool_balance(
899
- pool_address=self.current_pool.get("address"),
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 quotes.get("quotes")
935
- and quotes.get("quotes").get("best_quote")
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("quotes").get("best_quote")
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
- token_id=self.usdc_token_info.get("token_id"),
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("id") not in withdrawn_token_ids:
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
- token_id=self.gas_token.get("id"),
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("id"),
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("id"))
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
@@ -1071,6 +1089,71 @@ class StablecoinYieldStrategy(Strategy):
1071
1089
  f"Successfully withdrew {amount} USDC from strategy: {breakdown_msg}",
1072
1090
  )
1073
1091
 
1092
+ async def exit(self, **kwargs) -> StatusTuple:
1093
+ """Transfer funds from strategy wallet to main wallet."""
1094
+ logger.info("EXIT: Transferring remaining funds to main wallet")
1095
+
1096
+ strategy_address = self._get_strategy_wallet_address()
1097
+ main_address = self._get_main_wallet_address()
1098
+
1099
+ if strategy_address.lower() == main_address.lower():
1100
+ return (True, "Main wallet is strategy wallet, no transfer needed")
1101
+
1102
+ transferred_items = []
1103
+
1104
+ # Transfer USDC to main wallet
1105
+ usdc_ok, usdc_raw = await self.balance_adapter.get_balance(
1106
+ token_id="usd-coin-base",
1107
+ wallet_address=strategy_address,
1108
+ )
1109
+ if usdc_ok and usdc_raw:
1110
+ usdc_balance = float(usdc_raw.get("balance", 0))
1111
+ if usdc_balance > 1.0:
1112
+ logger.info(f"Transferring {usdc_balance:.2f} USDC to main wallet")
1113
+ (
1114
+ success,
1115
+ msg,
1116
+ ) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
1117
+ query="usd-coin-base",
1118
+ amount=usdc_balance,
1119
+ strategy_name=self.name,
1120
+ skip_ledger=False,
1121
+ )
1122
+ if success:
1123
+ transferred_items.append(f"{usdc_balance:.2f} USDC")
1124
+ else:
1125
+ logger.warning(f"USDC transfer failed: {msg}")
1126
+
1127
+ # Transfer ETH (minus reserve for tx fees) to main wallet
1128
+ eth_ok, eth_raw = await self.balance_adapter.get_balance(
1129
+ token_id="ethereum-base",
1130
+ wallet_address=strategy_address,
1131
+ )
1132
+ if eth_ok and eth_raw:
1133
+ eth_balance = float(eth_raw.get("balance", 0))
1134
+ tx_fee_reserve = 0.0002
1135
+ transferable_eth = eth_balance - tx_fee_reserve
1136
+ if transferable_eth > 0.0001:
1137
+ logger.info(f"Transferring {transferable_eth:.6f} ETH to main wallet")
1138
+ (
1139
+ success,
1140
+ msg,
1141
+ ) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
1142
+ query="ethereum-base",
1143
+ amount=transferable_eth,
1144
+ strategy_name=self.name,
1145
+ skip_ledger=False,
1146
+ )
1147
+ if success:
1148
+ transferred_items.append(f"{transferable_eth:.6f} ETH")
1149
+ else:
1150
+ logger.warning(f"ETH transfer failed: {msg}")
1151
+
1152
+ if not transferred_items:
1153
+ return (True, "No funds to transfer to main wallet")
1154
+
1155
+ return (True, f"Transferred to main wallet: {', '.join(transferred_items)}")
1156
+
1074
1157
  async def _get_last_rotation_time(self, wallet_address: str) -> datetime | None:
1075
1158
  success, data = await self.ledger_adapter.get_strategy_latest_transactions(
1076
1159
  wallet_address=self._get_strategy_wallet_address(),
@@ -1206,7 +1289,14 @@ class StablecoinYieldStrategy(Strategy):
1206
1289
  else:
1207
1290
  self.current_pool_data = None
1208
1291
  if self.current_pool_data:
1209
- self.current_combined_apy_pct = self.current_pool_data.get("apy", 0)
1292
+ apy_pct = self.current_pool_data.get("combined_apy_pct")
1293
+ if apy_pct is not None:
1294
+ self.current_combined_apy_pct = float(apy_pct) / 100
1295
+ else:
1296
+ apy_val = self.current_pool_data.get("apy", 0)
1297
+ self.current_combined_apy_pct = (
1298
+ float(apy_val) / 100 if apy_val is not None else 0
1299
+ )
1210
1300
  else:
1211
1301
  self.current_combined_apy_pct = (
1212
1302
  target_pool_data.get("combined_apy_pct", 0) / 100
@@ -1242,10 +1332,10 @@ class StablecoinYieldStrategy(Strategy):
1242
1332
  (
1243
1333
  _,
1244
1334
  refreshed_pool_balance,
1245
- ) = await self.balance_adapter.get_pool_balance(
1246
- pool_address=pool.get("address"),
1335
+ ) = await self.balance_adapter.get_balance(
1336
+ query=pool.get("address"),
1337
+ wallet_address=strategy_address,
1247
1338
  chain_id=pool.get("chain").get("id"),
1248
- user_address=strategy_address,
1249
1339
  )
1250
1340
  self.current_pool_balance = int(refreshed_pool_balance)
1251
1341
  except Exception:
@@ -1259,7 +1349,7 @@ class StablecoinYieldStrategy(Strategy):
1259
1349
  target_token_id = target_token.get("token_id")
1260
1350
  target_chain = target_token.get("chain").get("code", "").lower()
1261
1351
  target_address = target_token.get("address", "").lower()
1262
- gas_token_id = self.gas_token.get("id") if self.gas_token else None
1352
+ gas_token_id = self.gas_token.get("token_id") if self.gas_token else None
1263
1353
 
1264
1354
  # Swap all non-target, non-gas tokens to the target
1265
1355
  for token_id, balance_wei in list(self.tracked_balances.items()):
@@ -1278,7 +1368,7 @@ class StablecoinYieldStrategy(Strategy):
1278
1368
  # Get fresh balance to ensure accuracy
1279
1369
  try:
1280
1370
  success, fresh_balance = await self.balance_adapter.get_balance(
1281
- token_id=token_id,
1371
+ query=token_id,
1282
1372
  wallet_address=self._get_strategy_wallet_address(),
1283
1373
  )
1284
1374
  if not success or not fresh_balance or int(fresh_balance) <= 0:
@@ -1318,7 +1408,7 @@ class StablecoinYieldStrategy(Strategy):
1318
1408
  # Refresh target token balance
1319
1409
  try:
1320
1410
  success, target_balance = await self.balance_adapter.get_balance(
1321
- token_id=target_token_id,
1411
+ query=target_token_id,
1322
1412
  wallet_address=self._get_strategy_wallet_address(),
1323
1413
  )
1324
1414
  if success and target_balance:
@@ -1335,7 +1425,7 @@ class StablecoinYieldStrategy(Strategy):
1335
1425
 
1336
1426
  required_gas = int(self.MIN_GAS * (10 ** self.gas_token.get("decimals")))
1337
1427
  _, current_gas = await self.balance_adapter.get_balance(
1338
- token_id=self.gas_token.get("id"),
1428
+ query=self.gas_token.get("token_id"),
1339
1429
  wallet_address=strategy_address,
1340
1430
  )
1341
1431
  if current_gas >= required_gas:
@@ -1395,7 +1485,11 @@ class StablecoinYieldStrategy(Strategy):
1395
1485
  if not success:
1396
1486
  return 0.0
1397
1487
 
1398
- best_quote = exit_quotes.get("quotes").get("best_quote")
1488
+ best_quote = (
1489
+ exit_quotes.get("best_quote") if isinstance(exit_quotes, dict) else None
1490
+ )
1491
+ if not best_quote:
1492
+ return None
1399
1493
  current_pool_usd_value = best_quote.get("output_amount")
1400
1494
 
1401
1495
  return float(
@@ -1407,7 +1501,7 @@ class StablecoinYieldStrategy(Strategy):
1407
1501
  # Refresh tracked balances
1408
1502
  await self._refresh_tracked_balances()
1409
1503
 
1410
- gas_token_id = self.gas_token.get("id") if self.gas_token else None
1504
+ gas_token_id = self.gas_token.get("token_id") if self.gas_token else None
1411
1505
  results = []
1412
1506
 
1413
1507
  for token_id, balance_wei in self.tracked_balances.items():
@@ -1440,7 +1534,10 @@ class StablecoinYieldStrategy(Strategy):
1440
1534
  return results
1441
1535
 
1442
1536
  async def _find_best_pool(self) -> tuple[bool, dict[str, Any]]:
1443
- success, llama_data = await self.pool_adapter.get_pools()
1537
+ chain_id = (self.usdc_token_info or {}).get("chain", {}).get("id")
1538
+ if chain_id is None:
1539
+ chain_id = 8453
1540
+ success, llama_data = await self.pool_adapter.get_pools(chain_id=chain_id)
1444
1541
  if not success:
1445
1542
  return False, {"message": f"Failed to fetch Llama data: {llama_data}"}
1446
1543
 
@@ -1469,11 +1566,11 @@ class StablecoinYieldStrategy(Strategy):
1469
1566
  )
1470
1567
  if not target_status and candidate.get("token_id"):
1471
1568
  target_status, target_pool = await self.token_adapter.get_token(
1472
- token_id=candidate.get("token_id")
1569
+ query=candidate.get("token_id")
1473
1570
  )
1474
1571
  if not target_status and candidate.get("pool_id"):
1475
1572
  target_status, target_pool = await self.token_adapter.get_token(
1476
- token_id=candidate.get("pool_id")
1573
+ query=candidate.get("pool_id")
1477
1574
  )
1478
1575
  if not target_status:
1479
1576
  continue
@@ -1515,7 +1612,8 @@ class StablecoinYieldStrategy(Strategy):
1515
1612
  return None
1516
1613
 
1517
1614
  try:
1518
- combined_apy_pct = pool_data.get("combined_apy_pct") / 100
1615
+ apy_pct = pool_data.get("combined_apy_pct") or pool_data.get("apy") or 0
1616
+ combined_apy_pct = float(apy_pct) / 100
1519
1617
  success, quotes = await self.brap_adapter.get_swap_quote(
1520
1618
  from_token_address=current_token.get("address"),
1521
1619
  to_token_address=token.get("address"),
@@ -1527,10 +1625,9 @@ class StablecoinYieldStrategy(Strategy):
1527
1625
  )
1528
1626
  if not success:
1529
1627
  return None
1530
- quotes_data = quotes.get("quotes") if isinstance(quotes, dict) else None
1531
- if not isinstance(quotes_data, dict):
1628
+ if not isinstance(quotes, dict):
1532
1629
  return None
1533
- best_quote = quotes_data.get("best_quote")
1630
+ best_quote = quotes.get("best_quote")
1534
1631
  if not best_quote:
1535
1632
  return None
1536
1633
 
@@ -1582,7 +1679,7 @@ class StablecoinYieldStrategy(Strategy):
1582
1679
  async def _status(self) -> StatusDict:
1583
1680
  # Get ETH gas balance
1584
1681
  gas_success, gas_balance_wei = await self.balance_adapter.get_balance(
1585
- token_id=self.gas_token.get("id"),
1682
+ query=self.gas_token.get("token_id"),
1586
1683
  wallet_address=self._get_strategy_wallet_address(),
1587
1684
  )
1588
1685
  gas_balance = (
@@ -1611,7 +1708,7 @@ class StablecoinYieldStrategy(Strategy):
1611
1708
 
1612
1709
  # Calculate total value from tracked non-gas balances
1613
1710
  total_value = 0.0
1614
- gas_token_id = self.gas_token.get("id") if self.gas_token else None
1711
+ gas_token_id = self.gas_token.get("token_id") if self.gas_token else None
1615
1712
 
1616
1713
  for token_id, balance_wei in self.tracked_balances.items():
1617
1714
  if token_id == gas_token_id:
@@ -1680,7 +1777,7 @@ class StablecoinYieldStrategy(Strategy):
1680
1777
 
1681
1778
  usdc_token_id = self.usdc_token_info.get("token_id")
1682
1779
  usdc_decimals = self.usdc_token_info.get("decimals")
1683
- gas_token_id = self.gas_token.get("id") if self.gas_token else None
1780
+ gas_token_id = self.gas_token.get("token_id") if self.gas_token else None
1684
1781
 
1685
1782
  # Check current USDC balance
1686
1783
  available_usdc_wei = self.tracked_balances.get(usdc_token_id, 0)
@@ -1745,7 +1842,7 @@ class StablecoinYieldStrategy(Strategy):
1745
1842
 
1746
1843
  # Refresh USDC balance after swaps
1747
1844
  success, usdc_wei = await self.balance_adapter.get_balance(
1748
- token_id=usdc_token_id,
1845
+ query=usdc_token_id,
1749
1846
  wallet_address=self._get_strategy_wallet_address(),
1750
1847
  )
1751
1848
  if success and usdc_wei:
@@ -1790,7 +1887,7 @@ class StablecoinYieldStrategy(Strategy):
1790
1887
 
1791
1888
  # Refresh USDC balance again
1792
1889
  success, usdc_wei = await self.balance_adapter.get_balance(
1793
- token_id=usdc_token_id,
1890
+ query=usdc_token_id,
1794
1891
  wallet_address=self._get_strategy_wallet_address(),
1795
1892
  )
1796
1893
  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(token_id, wallet_address, **kwargs):
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(token_id, **kwargs):
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
- strategy.balance_adapter.get_balance = AsyncMock(
499
- side_effect=lambda token_id, **kwargs: (
500
- True,
501
- strategy.tracked_balances.get(token_id, 0),
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=lambda token_id, **kwargs: (
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(
@@ -77,7 +77,7 @@ async def test_get_pools():
77
77
  ):
78
78
  adapter = MyAdapter(config={})
79
79
  success, data = await adapter.get_pools(["pool-1"])
80
- assert success is True
80
+ assert success
81
81
  assert "pools" in data
82
82
  ```
83
83
 
@@ -1,4 +1,4 @@
1
- # Strategy Template
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
- token_id=self.config.get("token_id"),
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
- token_id=self.config.get("token_id"),
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(token_id, wallet_address, **kwargs):
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.get_token_balance_for_wallet = AsyncMock(
77
+ # s.balance_adapter.get_balance = AsyncMock(
77
78
  # side_effect=get_balance_side_effect
78
79
  # )
79
80