@owney/sdk 0.6.5-beta.1 → 0.6.5-beta.2

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.
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  // src/agents/zyfai/zyfai.agent.ts
2
2
  import { ZyfaiSDK } from "@zyfai/sdk";
3
3
  import { createPublicClient, http } from "viem";
4
- import { base, arbitrum } from "viem/chains";
4
+ import { base, arbitrum, mainnet } from "viem/chains";
5
5
 
6
6
  // src/types/config.ts
7
7
  var P_SUPPORTED_CHAINS = {
8
- chainIds: [8453, 42161],
9
- chains: ["BASE", "ARBITRUM"]
8
+ chainIds: [8453, 42161, 1],
9
+ chains: ["BASE", "ARBITRUM", "ETHEREUM"]
10
10
  };
11
11
  var SUPPORTED_TOKENS = ["USDC", "USDT", "WETH"];
12
12
  var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
@@ -41,6 +41,12 @@ var SupportedAssets = [
41
41
  symbol: "WETH",
42
42
  chainId: 42161,
43
43
  decimals: 18
44
+ },
45
+ {
46
+ address: `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`,
47
+ symbol: "USDC",
48
+ chainId: 1,
49
+ decimals: 6
44
50
  }
45
51
  ];
46
52
 
@@ -107,8 +113,7 @@ var isValidChainId = (chainId) => {
107
113
  function hexToDecimal(hex, decimals = 6) {
108
114
  const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
109
115
  const parsed = BigInt(normalized);
110
- if (decimals <= 0)
111
- return Number(parsed);
116
+ if (decimals <= 0) return Number(parsed);
112
117
  const divisor = BigInt(10 ** decimals);
113
118
  const whole = parsed / divisor;
114
119
  const remainder = parsed % divisor;
@@ -151,7 +156,8 @@ function mapWithdraw(raw) {
151
156
  }
152
157
  var CHAIN_ID_TO_NAME = {
153
158
  8453: "BASE",
154
- 42161: "ARBITRUM"
159
+ 42161: "ARBITRUM",
160
+ 1: "ETHEREUM"
155
161
  };
156
162
  var NAME_TO_CHAIN_ID = Object.entries(
157
163
  CHAIN_ID_TO_NAME
@@ -161,8 +167,7 @@ var NAME_TO_CHAIN_ID = Object.entries(
161
167
  }, {});
162
168
  function resolveChainId(chain) {
163
169
  const asNum = Number(chain);
164
- if (Number.isFinite(asNum) && asNum > 0)
165
- return asNum;
170
+ if (Number.isFinite(asNum) && asNum > 0) return asNum;
166
171
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
167
172
  }
168
173
  function mapBalances(raw, _chainId, smartWallet) {
@@ -246,41 +251,39 @@ function mapEarnings(raw, smartWallet) {
246
251
  };
247
252
  }
248
253
  function mapWeightedApyByChain(raw) {
249
- if (!raw)
250
- return void 0;
254
+ if (!raw) return void 0;
251
255
  const out = {};
252
256
  for (const [chainKey, tokenApy] of Object.entries(raw)) {
253
257
  const chainId = Number(chainKey);
254
- if (!SUPPORTED_CHAIN_IDS.includes(chainId))
255
- continue;
258
+ if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
256
259
  const perAsset = {};
257
260
  for (const [symbol, apy] of Object.entries(tokenApy)) {
258
261
  const token = symbol;
259
- if (!SUPPORTED_TOKENS.includes(token))
260
- continue;
262
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
261
263
  perAsset[token] = apy;
262
264
  }
263
- if (Object.keys(perAsset).length > 0)
264
- out[chainId] = perAsset;
265
+ if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
265
266
  }
266
267
  return Object.keys(out).length > 0 ? out : void 0;
267
268
  }
268
- function rawPoolApyForChain(entry, chainId) {
269
+ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
269
270
  let weightedSum = 0;
270
271
  let totalBalance = 0;
271
272
  for (const p of entry.positions ?? []) {
272
- if (p.chainId !== chainId)
273
- continue;
273
+ if (p.chainId !== chainId) continue;
274
+ if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
274
275
  const balance = p.balance ?? 0;
275
- if (balance <= 0 || typeof p.apy !== "number")
276
- continue;
276
+ if (balance <= 0 || typeof p.apy !== "number") continue;
277
277
  weightedSum += p.apy * balance;
278
278
  totalBalance += balance;
279
279
  }
280
280
  return totalBalance > 0 ? weightedSum / totalBalance : null;
281
281
  }
282
- function mapApyHistory(raw, chainId) {
283
- const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({ date, apy: rawPoolApyForChain(entry, chainId) })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
282
+ function mapApyHistory(raw, chainId, tokenSymbol) {
283
+ const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
284
+ date,
285
+ apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
286
+ })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
284
287
  return {
285
288
  walletAddress: raw.walletAddress,
286
289
  weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
@@ -303,6 +306,7 @@ function mapEntries(rawEntries, chainId) {
303
306
  "Withdraw from protocol": "Withdraw",
304
307
  Rebalance: "Rebalance"
305
308
  };
309
+ void chainId;
306
310
  return rawEntries.map((entry) => {
307
311
  const rawLog = entry.rebalanceLog;
308
312
  const positions = entry.positions ?? [];
@@ -381,8 +385,7 @@ function mapApyByStrategy(raw) {
381
385
  const supported = SupportedAssets.find(
382
386
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
383
387
  );
384
- if (!supported)
385
- continue;
388
+ if (!supported) continue;
386
389
  const chainKey = supported.chainId;
387
390
  const symbolKey = supported.symbol;
388
391
  const bucket = apyPerAsset[chainKey] ?? {};
@@ -412,10 +415,8 @@ function computeAllocationApy(positions) {
412
415
  totalValue += value;
413
416
  const chainId = resolveChainId(p.chain);
414
417
  const token = p.asset;
415
- if (!SUPPORTED_CHAIN_IDS.includes(chainId))
416
- continue;
417
- if (!SUPPORTED_TOKENS.includes(token))
418
- continue;
418
+ if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
419
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
419
420
  const perAsset = buckets[chainId] ?? {};
420
421
  const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
421
422
  cell.weightedSum += apy * value;
@@ -432,8 +433,7 @@ function computeAllocationApy(positions) {
432
433
  out[token] = cell.weightedSum / cell.value;
433
434
  }
434
435
  }
435
- if (Object.keys(out).length > 0)
436
- apyByChainAndAsset[chainId] = out;
436
+ if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
437
437
  }
438
438
  const totalApy = totalValue > 0 ? String(weightedSum / totalValue) : "0";
439
439
  return { totalApy, apyByChainAndAsset };
@@ -450,19 +450,14 @@ function extractWithdrawnAmount(logs, recipient, token, decimals) {
450
450
  let total = 0n;
451
451
  let matched = false;
452
452
  for (const log of logs) {
453
- if (log.address.toLowerCase() !== wantToken)
454
- continue;
455
- if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC)
456
- continue;
457
- if (topicToAddress(log.topics[2]) !== wantRecipient)
458
- continue;
459
- if (!log.data || log.data === "0x")
460
- continue;
453
+ if (log.address.toLowerCase() !== wantToken) continue;
454
+ if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC) continue;
455
+ if (topicToAddress(log.topics[2]) !== wantRecipient) continue;
456
+ if (!log.data || log.data === "0x") continue;
461
457
  total += BigInt(log.data);
462
458
  matched = true;
463
459
  }
464
- if (!matched)
465
- return void 0;
460
+ if (!matched) return void 0;
466
461
  return String(hexToDecimal(`0x${total.toString(16)}`, decimals));
467
462
  }
468
463
 
@@ -478,8 +473,7 @@ function encodeHistoryCursor(payload) {
478
473
  return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
479
474
  }
480
475
  function decodeHistoryCursor(token) {
481
- if (!token)
482
- throw new InvalidHistoryCursorError("Cursor is empty.");
476
+ if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
483
477
  let parsed;
484
478
  try {
485
479
  const json = Buffer.from(token, "base64").toString("utf8");
@@ -496,8 +490,7 @@ function decodeHistoryCursor(token) {
496
490
  // src/agents/zyfai/zyfai.auth-cache.ts
497
491
  var KEY_PREFIX = "owney.zyfai.session";
498
492
  var storage = () => {
499
- if (typeof window === "undefined")
500
- return null;
493
+ if (typeof window === "undefined") return null;
501
494
  try {
502
495
  return window.localStorage;
503
496
  } catch {
@@ -507,15 +500,13 @@ var storage = () => {
507
500
  var buildKey = (address, chainId) => `${KEY_PREFIX}:${address.toLowerCase()}:${chainId}`;
508
501
  var isJwtExpired = (token) => {
509
502
  const parts = token.split(".");
510
- if (parts.length !== 3)
511
- return false;
503
+ if (parts.length !== 3) return false;
512
504
  try {
513
505
  const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
514
506
  const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
515
507
  const decoded = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
516
508
  const claims = JSON.parse(decoded);
517
- if (typeof claims.exp !== "number")
518
- return false;
509
+ if (typeof claims.exp !== "number") return false;
519
510
  return Date.now() / 1e3 >= claims.exp - 30;
520
511
  } catch {
521
512
  return false;
@@ -523,11 +514,9 @@ var isJwtExpired = (token) => {
523
514
  };
524
515
  var readSession = (address, chainId) => {
525
516
  const store = storage();
526
- if (!store)
527
- return null;
517
+ if (!store) return null;
528
518
  const raw = store.getItem(buildKey(address, chainId));
529
- if (!raw)
530
- return null;
519
+ if (!raw) return null;
531
520
  try {
532
521
  const parsed = JSON.parse(raw);
533
522
  if (!parsed.token || isJwtExpired(parsed.token)) {
@@ -542,8 +531,7 @@ var readSession = (address, chainId) => {
542
531
  };
543
532
  var writeSession = (address, chainId, session) => {
544
533
  const store = storage();
545
- if (!store)
546
- return;
534
+ if (!store) return;
547
535
  try {
548
536
  store.setItem(buildKey(address, chainId), JSON.stringify(session));
549
537
  } catch {
@@ -551,8 +539,7 @@ var writeSession = (address, chainId, session) => {
551
539
  };
552
540
  var clearSession = (address, chainId) => {
553
541
  const store = storage();
554
- if (!store)
555
- return;
542
+ if (!store) return;
556
543
  try {
557
544
  store.removeItem(buildKey(address, chainId));
558
545
  } catch {
@@ -561,8 +548,7 @@ var clearSession = (address, chainId) => {
561
548
 
562
549
  // src/agents/zyfai/autoSelect.ts
563
550
  function buildAutoSelectUpdate(current, asset) {
564
- if (current.autoSelectProtocols === true)
565
- return null;
551
+ if (current.autoSelectProtocols === true) return null;
566
552
  return {
567
553
  asset,
568
554
  autoSelectProtocols: true,
@@ -573,16 +559,18 @@ function buildAutoSelectUpdate(current, asset) {
573
559
  // src/agents/zyfai/zyfai.agent.ts
574
560
  var DEFAULT_ZYFAI_RPC_URLS = {
575
561
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
576
- 42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
562
+ 42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
563
+ 1: "https://eth-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
577
564
  };
578
565
  var WETH_ADDRESS_BY_CHAIN = {
579
566
  8453: "0x4200000000000000000000000000000000000006",
580
- 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
567
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
568
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
581
569
  };
582
- var VIEM_CHAIN = { 8453: base, 42161: arbitrum };
570
+ var VIEM_CHAIN = { 8453: base, 42161: arbitrum, 1: mainnet };
583
571
  var ZyfaiAgent = class {
584
572
  id = "zyfai";
585
- supportedChainIds = [8453, 42161];
573
+ supportedChainIds = [8453, 42161, 1];
586
574
  supportedAssets = [
587
575
  {
588
576
  chainId: 8453,
@@ -599,6 +587,13 @@ var ZyfaiAgent = class {
599
587
  { symbol: "USDC", minDepositAmount: "1500000" },
600
588
  { symbol: "WETH", minDepositAmount: "500000000000000" }
601
589
  ]
590
+ },
591
+ {
592
+ chainId: 1,
593
+ chain: "ETHEREUM",
594
+ // Zyfai enforces a 10,000 USDC minimum portfolio on Ethereum mainnet
595
+ // (mainnet gas only pays off on large positions). 10_000 * 1e6 raw units.
596
+ assets: [{ symbol: "USDC", minDepositAmount: "10000000000" }]
602
597
  }
603
598
  ];
604
599
  sdk;
@@ -622,8 +617,7 @@ var ZyfaiAgent = class {
622
617
  }
623
618
  getPublicClient(chainId) {
624
619
  const cached = this.publicClients.get(chainId);
625
- if (cached)
626
- return cached;
620
+ if (cached) return cached;
627
621
  const client = createPublicClient({
628
622
  chain: VIEM_CHAIN[chainId],
629
623
  transport: http(this.rpcUrls[chainId])
@@ -654,8 +648,7 @@ var ZyfaiAgent = class {
654
648
  const withdrawals = entries.filter(
655
649
  (e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
656
650
  );
657
- if (withdrawals.length === 0)
658
- return;
651
+ if (withdrawals.length === 0) return;
659
652
  const client = this.getPublicClient(chainId);
660
653
  await Promise.all(
661
654
  withdrawals.map(async (entry) => {
@@ -668,8 +661,7 @@ var ZyfaiAgent = class {
668
661
  ) ?? SupportedAssets.find(
669
662
  (a) => a.chainId === chainId && a.symbol === "USDC"
670
663
  );
671
- if (!asset)
672
- return;
664
+ if (!asset) return;
673
665
  const cacheKey = `${txHash}:${asset.address}`;
674
666
  const cached = this.withdrawAmountCache.get(cacheKey);
675
667
  if (cached !== void 0) {
@@ -706,13 +698,11 @@ var ZyfaiAgent = class {
706
698
  * for some apps and the patch only matters in the browser anyway.
707
699
  */
708
700
  installAuthCache() {
709
- if (typeof window === "undefined")
710
- return;
701
+ if (typeof window === "undefined") return;
711
702
  const internals = this.sdk;
712
703
  const original = internals.authenticateUser.bind(internals);
713
704
  internals.authenticateUser = async () => {
714
- if (internals.authenticatedUserId !== null)
715
- return;
705
+ if (internals.authenticatedUserId !== null) return;
716
706
  const address = internals.walletClient?.account?.address;
717
707
  const chainId = internals.currentChainId;
718
708
  if (address && chainId) {
@@ -787,12 +777,10 @@ var ZyfaiAgent = class {
787
777
  return await this.sdk.connectAccount(provider, chainId);
788
778
  } catch (error) {
789
779
  const message = error instanceof Error ? error.message : String(error);
790
- if (!message.toLowerCase().includes("unauthorized"))
791
- throw error;
780
+ if (!message.toLowerCase().includes("unauthorized")) throw error;
792
781
  const internals = this.sdk;
793
782
  const address = internals.walletClient?.account?.address;
794
- if (address)
795
- clearSession(address, chainId);
783
+ if (address) clearSession(address, chainId);
796
784
  return await this.sdk.connectAccount(provider, chainId);
797
785
  }
798
786
  }
@@ -922,10 +910,10 @@ var ZyfaiAgent = class {
922
910
  const raw = await this.sdk.getOnchainEarnings(smartWallet);
923
911
  return mapEarnings(raw, smartWallet);
924
912
  }
925
- async getAccountApy(state, chainId, days) {
913
+ async getAccountApy(state, chainId, days, tokenSymbol) {
926
914
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
927
915
  const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
928
- return mapApyHistory(raw, chainId);
916
+ return mapApyHistory(raw, chainId, tokenSymbol);
929
917
  }
930
918
  async getHistory(state, chainId, options) {
931
919
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
@@ -990,8 +978,7 @@ var ZyfaiAgent = class {
990
978
  },
991
979
  asset
992
980
  );
993
- if (!update)
994
- return false;
981
+ if (!update) return false;
995
982
  await this.sdk.updateUserProfile(update);
996
983
  return true;
997
984
  }
@@ -1080,8 +1067,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
1080
1067
  const tokenBalance = agentBalance?.tokens.find(
1081
1068
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
1082
1069
  );
1083
- if (!tokenBalance)
1084
- return { agent, balance: 0n };
1070
+ if (!tokenBalance) return { agent, balance: 0n };
1085
1071
  return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
1086
1072
  });
1087
1073
  }
@@ -1098,11 +1084,9 @@ function planProportionalShares(balances, requested, totalAvailable) {
1098
1084
  return diff > 0n ? 1 : diff < 0n ? -1 : 0;
1099
1085
  });
1100
1086
  for (const p of byHeadroom) {
1101
- if (remainder === 0n)
1102
- break;
1087
+ if (remainder === 0n) break;
1103
1088
  const headroom = p.balance - p.planned;
1104
- if (headroom <= 0n)
1105
- continue;
1089
+ if (headroom <= 0n) continue;
1106
1090
  const take = headroom < remainder ? headroom : remainder;
1107
1091
  p.planned += take;
1108
1092
  remainder -= take;
@@ -1131,8 +1115,7 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
1131
1115
  (s, c) => s + (c.balance - c.planned),
1132
1116
  0n
1133
1117
  );
1134
- if (totalHeadroom === 0n)
1135
- return;
1118
+ if (totalHeadroom === 0n) return;
1136
1119
  let distributed = 0n;
1137
1120
  for (const c of candidates) {
1138
1121
  const headroom = c.balance - c.planned;
@@ -1143,11 +1126,9 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
1143
1126
  }
1144
1127
  let leftover = amount - distributed;
1145
1128
  for (const c of candidates) {
1146
- if (leftover === 0n)
1147
- break;
1129
+ if (leftover === 0n) break;
1148
1130
  const headroom = c.balance - c.planned;
1149
- if (headroom <= 0n)
1150
- continue;
1131
+ if (headroom <= 0n) continue;
1151
1132
  const take = headroom < leftover ? headroom : leftover;
1152
1133
  c.planned += take;
1153
1134
  leftover -= take;
@@ -1164,16 +1145,13 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
1164
1145
  for (const id of Object.keys(agentApys)) {
1165
1146
  const cells = agentApys[id].apyByChainAndAsset;
1166
1147
  const balance = agentBalances[id] ?? 0;
1167
- if (!cells || balance <= 0)
1168
- continue;
1148
+ if (!cells || balance <= 0) continue;
1169
1149
  for (const [chainKey, perAsset] of Object.entries(cells)) {
1170
- if (!perAsset)
1171
- continue;
1150
+ if (!perAsset) continue;
1172
1151
  const chainId = Number(chainKey);
1173
1152
  for (const [asset, apyValue] of Object.entries(perAsset)) {
1174
1153
  const apy = Number(apyValue ?? 0);
1175
- if (apy === 0)
1176
- continue;
1154
+ if (apy === 0) continue;
1177
1155
  sums[chainId] ??= {};
1178
1156
  weights[chainId] ??= {};
1179
1157
  sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
@@ -1187,8 +1165,7 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
1187
1165
  const perAssetOut = {};
1188
1166
  for (const asset of Object.keys(sums[chainId])) {
1189
1167
  const w = weights[chainId][asset];
1190
- if (w > 0)
1191
- perAssetOut[asset] = sums[chainId][asset] / w;
1168
+ if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
1192
1169
  }
1193
1170
  if (Object.keys(perAssetOut).length > 0) {
1194
1171
  out[chainId] = perAssetOut;
@@ -1203,7 +1180,7 @@ import {
1203
1180
  createWalletClient,
1204
1181
  custom
1205
1182
  } from "viem";
1206
- import { base as base2, arbitrum as arbitrum2 } from "viem/chains";
1183
+ import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
1207
1184
 
1208
1185
  // src/lib/transfer-auth.ts
1209
1186
  import { bytesToHex } from "viem";
@@ -1249,7 +1226,10 @@ async function postSponsorTransferAuth(input) {
1249
1226
  try {
1250
1227
  res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
1251
1228
  method: "POST",
1252
- headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
1229
+ headers: {
1230
+ "content-type": "application/json",
1231
+ "x-owney-api-key": input.apiKey
1232
+ },
1253
1233
  body: JSON.stringify(input.body)
1254
1234
  });
1255
1235
  } catch (networkError) {
@@ -1269,7 +1249,13 @@ async function postSponsorTransferAuth(input) {
1269
1249
  throw new OwneyError(
1270
1250
  "SPONSOR_REQUEST_FAILED",
1271
1251
  `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1272
- { statusCode: res.status, responseBody: text.slice(0, 500) }
1252
+ {
1253
+ statusCode: res.status,
1254
+ responseBody: text.slice(0, 500),
1255
+ // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
1256
+ // before broadcast, so it is safe to fall back to a user-paid deposit.
1257
+ safeToFallback: res.status === 503
1258
+ }
1273
1259
  );
1274
1260
  }
1275
1261
  return parsed.data;
@@ -1280,7 +1266,10 @@ async function postSponsorPermit2Transfer(input) {
1280
1266
  try {
1281
1267
  res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
1282
1268
  method: "POST",
1283
- headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
1269
+ headers: {
1270
+ "content-type": "application/json",
1271
+ "x-owney-api-key": input.apiKey
1272
+ },
1284
1273
  body: JSON.stringify(input.body)
1285
1274
  });
1286
1275
  } catch (networkError) {
@@ -1303,7 +1292,7 @@ async function postSponsorPermit2Transfer(input) {
1303
1292
  {
1304
1293
  statusCode: res.status,
1305
1294
  responseBody: text.slice(0, 500),
1306
- safeToFallback: res.status >= 400 && res.status < 500
1295
+ safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
1307
1296
  }
1308
1297
  );
1309
1298
  }
@@ -1313,9 +1302,12 @@ async function getSponsorRelayerAddress(input) {
1313
1302
  const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1314
1303
  let res;
1315
1304
  try {
1316
- res = await fetch(`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`, {
1317
- headers: { "x-owney-api-key": input.apiKey }
1318
- });
1305
+ res = await fetch(
1306
+ `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
1307
+ {
1308
+ headers: { "x-owney-api-key": input.apiKey }
1309
+ }
1310
+ );
1319
1311
  } catch (networkError) {
1320
1312
  throw new OwneyError(
1321
1313
  "SPONSOR_REQUEST_FAILED",
@@ -1333,7 +1325,11 @@ async function getSponsorRelayerAddress(input) {
1333
1325
  throw new OwneyError(
1334
1326
  "SPONSOR_REQUEST_FAILED",
1335
1327
  `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1336
- { statusCode: res.status, responseBody: text.slice(0, 500), safeToFallback: true }
1328
+ {
1329
+ statusCode: res.status,
1330
+ responseBody: text.slice(0, 500),
1331
+ safeToFallback: true
1332
+ }
1337
1333
  );
1338
1334
  }
1339
1335
  return parsed.data.relayer;
@@ -1520,18 +1516,21 @@ function decodeMultiAgentCursor(token) {
1520
1516
  }
1521
1517
  var SPONSORED_USDC_BY_CHAIN = {
1522
1518
  8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1523
- 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
1519
+ 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
1520
+ 1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
1524
1521
  };
1525
1522
  var VIEM_CHAIN2 = {
1526
1523
  8453: base2,
1527
- 42161: arbitrum2
1524
+ 42161: arbitrum2,
1525
+ 1: mainnet2
1528
1526
  };
1529
1527
  var SPONSORED_WETH_BY_CHAIN = {
1530
1528
  8453: "0x4200000000000000000000000000000000000006",
1531
- 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
1529
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
1530
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
1532
1531
  };
1533
1532
  function shouldFallbackToUserPaid(error, asset, appCallback) {
1534
- return asset === "WETH" && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
1533
+ return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
1535
1534
  }
1536
1535
  var OwneySDK = class {
1537
1536
  agents = /* @__PURE__ */ new Map();
@@ -1545,12 +1544,14 @@ var OwneySDK = class {
1545
1544
  state = null;
1546
1545
  apiKey;
1547
1546
  zyfaiRpcUrls;
1547
+ routingApiBaseUrl;
1548
1548
  cachedSponsoredCallback = null;
1549
1549
  cachedWethSponsoredCallback = null;
1550
1550
  initializingAgentsPromise = null;
1551
1551
  constructor(config) {
1552
1552
  this.apiKey = config.apiKey;
1553
1553
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
1554
+ this.routingApiBaseUrl = config.routingApiBaseUrl;
1554
1555
  }
1555
1556
  /**
1556
1557
  * Establish connection state for all subsequent SDK calls.
@@ -1605,8 +1606,7 @@ var OwneySDK = class {
1605
1606
  return this.state?.chainId ?? null;
1606
1607
  }
1607
1608
  requireState() {
1608
- if (!this.state)
1609
- throw new NotConnectedError();
1609
+ if (!this.state) throw new NotConnectedError();
1610
1610
  return this.state;
1611
1611
  }
1612
1612
  requireChainId() {
@@ -1636,12 +1636,12 @@ var OwneySDK = class {
1636
1636
  * `TransferWithAuthorization`, then POSTs to the sponsor API.
1637
1637
  */
1638
1638
  getDefaultSponsoredCallback() {
1639
- if (this.cachedSponsoredCallback)
1640
- return this.cachedSponsoredCallback;
1639
+ if (this.cachedSponsoredCallback) return this.cachedSponsoredCallback;
1641
1640
  const provider = this.requireConnectedProvider();
1642
1641
  const owner = this.state.walletAddress;
1643
1642
  this.cachedSponsoredCallback = makeSponsoredDepositCallback({
1644
1643
  apiKey: this.apiKey,
1644
+ baseUrl: this.routingApiBaseUrl,
1645
1645
  ownerAddress: owner,
1646
1646
  tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
1647
1647
  // Casts work around viem's chain-narrowed Client vs the generic
@@ -1672,6 +1672,7 @@ var OwneySDK = class {
1672
1672
  const owner = this.state.walletAddress;
1673
1673
  this.cachedWethSponsoredCallback = makeSponsoredWethCallback({
1674
1674
  apiKey: this.apiKey,
1675
+ baseUrl: this.routingApiBaseUrl,
1675
1676
  ownerAddress: owner,
1676
1677
  tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
1677
1678
  // Casts work around viem's chain-narrowed Client vs the generic
@@ -1700,22 +1701,19 @@ var OwneySDK = class {
1700
1701
  const active = /* @__PURE__ */ new Map();
1701
1702
  for (const id of this.activeAgents) {
1702
1703
  const agent = this.agents.get(id);
1703
- if (agent)
1704
- active.set(id, agent);
1704
+ if (agent) active.set(id, agent);
1705
1705
  }
1706
1706
  return active;
1707
1707
  }
1708
1708
  async ensureAgentsInitialized() {
1709
- if (this.agents.size > 0)
1710
- return;
1709
+ if (this.agents.size > 0) return;
1711
1710
  if (!this.initializingAgentsPromise) {
1712
1711
  this.initializingAgentsPromise = (async () => {
1713
1712
  const agentKeys = await fetchAgentKeys(this.apiKey);
1714
1713
  this.disabledAgents.clear();
1715
1714
  for (const { key, agent_type, is_enabled } of agentKeys) {
1716
1715
  const agent = this.createAgent(agent_type, key);
1717
- if (!agent)
1718
- continue;
1716
+ if (!agent) continue;
1719
1717
  this.agents.set(agent_type, agent);
1720
1718
  if (is_enabled === false) {
1721
1719
  this.disabledAgents.add(agent_type);
@@ -1954,10 +1952,9 @@ var OwneySDK = class {
1954
1952
  await this.approvePermit2();
1955
1953
  continue;
1956
1954
  }
1957
- if (!shouldFallbackToUserPaid(error, asset, appCallback))
1958
- throw error;
1955
+ if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
1959
1956
  console.warn(
1960
- "[owney-sdk] WETH sponsorship unavailable, falling back to user-paid deposit:",
1957
+ `[owney-sdk] ${asset} sponsorship unavailable, falling back to user-paid deposit:`,
1961
1958
  error
1962
1959
  );
1963
1960
  return attempt(void 0);
@@ -1972,8 +1969,7 @@ var OwneySDK = class {
1972
1969
  return BigInt(assetEntry?.minDepositAmount ?? "0");
1973
1970
  }
1974
1971
  splitDepositAmount(totalAmount, agents, chainId, asset, exempt = /* @__PURE__ */ new Set()) {
1975
- if (agents.length === 0)
1976
- return [];
1972
+ if (agents.length === 0) return [];
1977
1973
  const perAgent = totalAmount / BigInt(agents.length);
1978
1974
  const remainder = totalAmount % BigInt(agents.length);
1979
1975
  const splits = agents.map((agent, i) => ({
@@ -1997,10 +1993,8 @@ var OwneySDK = class {
1997
1993
  async validateMinDepositAmount(agent, state, chainId, asset, amount) {
1998
1994
  const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
1999
1995
  const parsedAmount = BigInt(amount);
2000
- if (parsedAmount >= minDepositAmount)
2001
- return;
2002
- if (await this.hasExistingBalance(agent, state, chainId, asset))
2003
- return;
1996
+ if (parsedAmount >= minDepositAmount) return;
1997
+ if (await this.hasExistingBalance(agent, state, chainId, asset)) return;
2004
1998
  throw new OwneyError(
2005
1999
  "DEPOSIT_AMOUNT_BELOW_MINIMUM",
2006
2000
  `Amount "${amount}" is below the required minimum deposit "${minDepositAmount.toString()}" for agent "${agent.id}" on chain ${chainId} for asset "${asset}".`,
@@ -2164,8 +2158,7 @@ var OwneySDK = class {
2164
2158
  const agentErrors = {};
2165
2159
  for (let i = 0; i < plans.length; i++) {
2166
2160
  const p = plans[i];
2167
- if (p.planned === 0n)
2168
- continue;
2161
+ if (p.planned === 0n) continue;
2169
2162
  try {
2170
2163
  results[p.agent.id] = await p.agent.withdraw(
2171
2164
  state,
@@ -2299,19 +2292,30 @@ var OwneySDK = class {
2299
2292
  */
2300
2293
  async getAccountApy({
2301
2294
  agentId,
2302
- days
2295
+ days,
2296
+ tokenSymbol
2303
2297
  }) {
2304
2298
  const state = this.requireState();
2305
2299
  const chainId = this.requireChainId();
2306
2300
  if (agentId) {
2307
- return this.getAgent(agentId).getAccountApy(state, chainId, days);
2301
+ return this.getAgent(agentId).getAccountApy(
2302
+ state,
2303
+ chainId,
2304
+ days,
2305
+ tokenSymbol
2306
+ );
2308
2307
  }
2309
2308
  const activeAgents = this.getActiveAgents();
2310
2309
  const entries = [...activeAgents.entries()];
2311
2310
  const [apyResults, balanceResults] = await Promise.all([
2312
2311
  Promise.all(
2313
2312
  entries.map(async ([id, agent]) => {
2314
- const apy = await agent.getAccountApy(state, chainId, days);
2313
+ const apy = await agent.getAccountApy(
2314
+ state,
2315
+ chainId,
2316
+ days,
2317
+ tokenSymbol
2318
+ );
2315
2319
  return [id, apy];
2316
2320
  })
2317
2321
  ),
@@ -2334,15 +2338,13 @@ var OwneySDK = class {
2334
2338
  let relevantBalance = 0;
2335
2339
  for (const id of Object.keys(results)) {
2336
2340
  const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
2337
- if (agentApy === 0)
2338
- continue;
2341
+ if (agentApy === 0) continue;
2339
2342
  relevantBalance += balances[id];
2340
2343
  }
2341
2344
  if (relevantBalance > 0) {
2342
2345
  for (const id of Object.keys(results)) {
2343
2346
  const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
2344
- if (agentApy === 0)
2345
- continue;
2347
+ if (agentApy === 0) continue;
2346
2348
  const weight = balances[id] / relevantBalance;
2347
2349
  totalApy += agentApy * weight;
2348
2350
  }
@@ -2397,8 +2399,7 @@ var OwneySDK = class {
2397
2399
  const nextCursorMap = {};
2398
2400
  let hasMore = false;
2399
2401
  for (const { agentId: id, page } of perAgent) {
2400
- if (!page)
2401
- continue;
2402
+ if (!page) continue;
2402
2403
  merged.push(...page.data);
2403
2404
  if (page.hasMore && page.nextCursor) {
2404
2405
  nextCursorMap[id] = page.nextCursor;
@@ -2407,16 +2408,12 @@ var OwneySDK = class {
2407
2408
  }
2408
2409
  merged.sort((a, b) => {
2409
2410
  const dateDiff = new Date(b.date).getTime() - new Date(a.date).getTime();
2410
- if (dateDiff !== 0)
2411
- return dateDiff;
2412
- if (a.agent !== b.agent)
2413
- return a.agent < b.agent ? -1 : 1;
2411
+ if (dateDiff !== 0) return dateDiff;
2412
+ if (a.agent !== b.agent) return a.agent < b.agent ? -1 : 1;
2414
2413
  const aTx = a.transactions?.[0]?.txHashes?.[0] ?? "";
2415
2414
  const bTx = b.transactions?.[0]?.txHashes?.[0] ?? "";
2416
- if (aTx !== bTx)
2417
- return aTx < bTx ? -1 : 1;
2418
- if (a.action !== b.action)
2419
- return a.action < b.action ? -1 : 1;
2415
+ if (aTx !== bTx) return aTx < bTx ? -1 : 1;
2416
+ if (a.action !== b.action) return a.action < b.action ? -1 : 1;
2420
2417
  return 0;
2421
2418
  });
2422
2419
  return {
@@ -2462,8 +2459,7 @@ var OwneySDK = class {
2462
2459
  const state = this.requireState();
2463
2460
  const chainId = this.requireChainId();
2464
2461
  const agent = this.getAgent(agentId);
2465
- if (typeof agent.ensureAutoSelectProtocols !== "function")
2466
- return false;
2462
+ if (typeof agent.ensureAutoSelectProtocols !== "function") return false;
2467
2463
  return agent.ensureAutoSelectProtocols(state, chainId, asset);
2468
2464
  }
2469
2465
  /**
@@ -2475,8 +2471,10 @@ var OwneySDK = class {
2475
2471
  * @returns the approval transaction hash.
2476
2472
  */
2477
2473
  async approvePermit2(asset = "WETH") {
2474
+ void asset;
2478
2475
  const state = this.requireState();
2479
2476
  const chainId = this.requireChainId();
2477
+ this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
2480
2478
  const token = SPONSORED_WETH_BY_CHAIN[chainId];
2481
2479
  if (!token) {
2482
2480
  throw new OwneyError(
@@ -2616,8 +2614,7 @@ import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
2616
2614
  // src/agents/zyfai/zyfai.siwx-cache.ts
2617
2615
  var KEY_PREFIX2 = "owney.siwx.session";
2618
2616
  var storage2 = () => {
2619
- if (typeof window === "undefined")
2620
- return null;
2617
+ if (typeof window === "undefined") return null;
2621
2618
  try {
2622
2619
  return window.localStorage;
2623
2620
  } catch {
@@ -2627,11 +2624,9 @@ var storage2 = () => {
2627
2624
  var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2628
2625
  var readSiwxSession = (address, chainId) => {
2629
2626
  const store = storage2();
2630
- if (!store)
2631
- return null;
2627
+ if (!store) return null;
2632
2628
  const raw = store.getItem(buildKey2(address, chainId));
2633
- if (!raw)
2634
- return null;
2629
+ if (!raw) return null;
2635
2630
  try {
2636
2631
  return JSON.parse(raw);
2637
2632
  } catch {
@@ -2641,8 +2636,7 @@ var readSiwxSession = (address, chainId) => {
2641
2636
  };
2642
2637
  var writeSiwxSession = (address, chainId, session) => {
2643
2638
  const store = storage2();
2644
- if (!store)
2645
- return;
2639
+ if (!store) return;
2646
2640
  try {
2647
2641
  store.setItem(buildKey2(address, chainId), JSON.stringify(session));
2648
2642
  } catch {
@@ -2650,8 +2644,7 @@ var writeSiwxSession = (address, chainId, session) => {
2650
2644
  };
2651
2645
  var clearSiwxSession = (address, chainId) => {
2652
2646
  const store = storage2();
2653
- if (!store)
2654
- return;
2647
+ if (!store) return;
2655
2648
  try {
2656
2649
  store.removeItem(buildKey2(address, chainId));
2657
2650
  } catch {
@@ -2714,12 +2707,10 @@ function buildSIWXConfig(deps) {
2714
2707
  const jwt = readSession(address, id);
2715
2708
  const stored = readSiwxSession(address, id);
2716
2709
  if (!jwt) {
2717
- if (stored)
2718
- clearSiwxSession(address, id);
2710
+ if (stored) clearSiwxSession(address, id);
2719
2711
  return [];
2720
2712
  }
2721
- if (!stored)
2722
- return [];
2713
+ if (!stored) return [];
2723
2714
  const session = {
2724
2715
  // `stored.data.chainId` is a plain string in the cache (kept AppKit-free);
2725
2716
  // it always holds a valid CaipNetworkId here, so narrow at the boundary.