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.

Files changed (47) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +13 -14
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +33 -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 +78 -63
  6. wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
  7. wayfinder_paths/adapters/brap_adapter/test_adapter.py +121 -59
  8. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +16 -14
  9. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
  10. wayfinder_paths/adapters/pool_adapter/README.md +9 -10
  11. wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
  12. wayfinder_paths/adapters/token_adapter/README.md +2 -14
  13. wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
  14. wayfinder_paths/adapters/token_adapter/examples.json +4 -8
  15. wayfinder_paths/adapters/token_adapter/test_adapter.py +5 -3
  16. wayfinder_paths/core/clients/BRAPClient.py +102 -61
  17. wayfinder_paths/core/clients/ClientManager.py +1 -68
  18. wayfinder_paths/core/clients/HyperlendClient.py +125 -64
  19. wayfinder_paths/core/clients/LedgerClient.py +1 -4
  20. wayfinder_paths/core/clients/PoolClient.py +122 -48
  21. wayfinder_paths/core/clients/TokenClient.py +91 -36
  22. wayfinder_paths/core/clients/WalletClient.py +26 -56
  23. wayfinder_paths/core/clients/WayfinderClient.py +28 -160
  24. wayfinder_paths/core/clients/__init__.py +0 -2
  25. wayfinder_paths/core/clients/protocols.py +35 -46
  26. wayfinder_paths/core/clients/sdk_example.py +37 -22
  27. wayfinder_paths/core/engine/StrategyJob.py +7 -55
  28. wayfinder_paths/core/services/local_evm_txn.py +6 -6
  29. wayfinder_paths/core/services/local_token_txn.py +1 -1
  30. wayfinder_paths/core/strategies/Strategy.py +0 -2
  31. wayfinder_paths/core/utils/evm_helpers.py +2 -2
  32. wayfinder_paths/run_strategy.py +8 -19
  33. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +10 -11
  34. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +40 -25
  35. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
  36. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +3 -3
  37. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
  38. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
  39. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +88 -56
  40. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
  41. wayfinder_paths/templates/strategy/README.md +3 -3
  42. wayfinder_paths/templates/strategy/test_strategy.py +3 -2
  43. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/METADATA +14 -49
  44. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/RECORD +46 -47
  45. wayfinder_paths/core/clients/AuthClient.py +0 -83
  46. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/LICENSE +0 -0
  47. {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__(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
@@ -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
- self.current_combined_apy_pct = self.current_pool_data.get("apy", 0)
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.get_pool_balance(
1246
- pool_address=pool.get("address"),
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("id") if self.gas_token else None
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
- token_id=token_id,
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
- token_id=target_token_id,
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
- token_id=self.gas_token.get("id"),
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 = exit_quotes.get("quotes").get("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("id") if self.gas_token else None
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
- success, llama_data = await self.pool_adapter.get_pools()
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
- token_id=candidate.get("token_id")
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
- token_id=candidate.get("pool_id")
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
- combined_apy_pct = pool_data.get("combined_apy_pct") / 100
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
- quotes_data = quotes.get("quotes") if isinstance(quotes, dict) else None
1531
- if not isinstance(quotes_data, dict):
1563
+ if not isinstance(quotes, dict):
1532
1564
  return None
1533
- best_quote = quotes_data.get("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
- token_id=self.gas_token.get("id"),
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("id") if self.gas_token else None
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("id") if self.gas_token else None
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
- token_id=usdc_token_id,
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
- token_id=usdc_token_id,
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(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(
@@ -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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wayfinder-paths
3
- Version: 0.1.13
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 supports two authentication methods:
219
+ Wayfinder Paths uses API key authentication via the `X-API-KEY` header.
220
220
 
221
- #### 1. Service Account Authentication (API Key)
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..." // Alternative: system-level API key
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 keys are automatically discovered by all clients (no need to pass explicitly)
270
- - When an API key is available, it's used for all API requests (including public endpoints) for rate limiting
271
- - If no API key is found, the system falls back to OAuth authentication
272
- - All clients created by adapters automatically inherit the API key discovery mechanism
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 authentication documentation.
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=",".join(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
- token_id=self.config.get("token_id"),
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
- token_id=self.config.get("token_id"),
325
+ query=self.config.get("token_id"),
361
326
  wallet_address=self.config.get("strategy_wallet", {}).get("address"),
362
327
  )
363
328
  return {