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

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,18 @@ 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
50
+ },
51
+ {
52
+ address: `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2`,
53
+ symbol: "WETH",
54
+ chainId: 1,
55
+ decimals: 18
44
56
  }
45
57
  ];
46
58
 
@@ -107,8 +119,7 @@ var isValidChainId = (chainId) => {
107
119
  function hexToDecimal(hex, decimals = 6) {
108
120
  const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
109
121
  const parsed = BigInt(normalized);
110
- if (decimals <= 0)
111
- return Number(parsed);
122
+ if (decimals <= 0) return Number(parsed);
112
123
  const divisor = BigInt(10 ** decimals);
113
124
  const whole = parsed / divisor;
114
125
  const remainder = parsed % divisor;
@@ -151,7 +162,8 @@ function mapWithdraw(raw) {
151
162
  }
152
163
  var CHAIN_ID_TO_NAME = {
153
164
  8453: "BASE",
154
- 42161: "ARBITRUM"
165
+ 42161: "ARBITRUM",
166
+ 1: "ETHEREUM"
155
167
  };
156
168
  var NAME_TO_CHAIN_ID = Object.entries(
157
169
  CHAIN_ID_TO_NAME
@@ -161,8 +173,7 @@ var NAME_TO_CHAIN_ID = Object.entries(
161
173
  }, {});
162
174
  function resolveChainId(chain) {
163
175
  const asNum = Number(chain);
164
- if (Number.isFinite(asNum) && asNum > 0)
165
- return asNum;
176
+ if (Number.isFinite(asNum) && asNum > 0) return asNum;
166
177
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
167
178
  }
168
179
  function mapBalances(raw, _chainId, smartWallet) {
@@ -246,41 +257,39 @@ function mapEarnings(raw, smartWallet) {
246
257
  };
247
258
  }
248
259
  function mapWeightedApyByChain(raw) {
249
- if (!raw)
250
- return void 0;
260
+ if (!raw) return void 0;
251
261
  const out = {};
252
262
  for (const [chainKey, tokenApy] of Object.entries(raw)) {
253
263
  const chainId = Number(chainKey);
254
- if (!SUPPORTED_CHAIN_IDS.includes(chainId))
255
- continue;
264
+ if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
256
265
  const perAsset = {};
257
266
  for (const [symbol, apy] of Object.entries(tokenApy)) {
258
267
  const token = symbol;
259
- if (!SUPPORTED_TOKENS.includes(token))
260
- continue;
268
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
261
269
  perAsset[token] = apy;
262
270
  }
263
- if (Object.keys(perAsset).length > 0)
264
- out[chainId] = perAsset;
271
+ if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
265
272
  }
266
273
  return Object.keys(out).length > 0 ? out : void 0;
267
274
  }
268
- function rawPoolApyForChain(entry, chainId) {
275
+ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
269
276
  let weightedSum = 0;
270
277
  let totalBalance = 0;
271
278
  for (const p of entry.positions ?? []) {
272
- if (p.chainId !== chainId)
273
- continue;
279
+ if (p.chainId !== chainId) continue;
280
+ if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
274
281
  const balance = p.balance ?? 0;
275
- if (balance <= 0 || typeof p.apy !== "number")
276
- continue;
282
+ if (balance <= 0 || typeof p.apy !== "number") continue;
277
283
  weightedSum += p.apy * balance;
278
284
  totalBalance += balance;
279
285
  }
280
286
  return totalBalance > 0 ? weightedSum / totalBalance : null;
281
287
  }
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));
288
+ function mapApyHistory(raw, chainId, tokenSymbol) {
289
+ const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
290
+ date,
291
+ apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
292
+ })).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
284
293
  return {
285
294
  walletAddress: raw.walletAddress,
286
295
  weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
@@ -303,6 +312,7 @@ function mapEntries(rawEntries, chainId) {
303
312
  "Withdraw from protocol": "Withdraw",
304
313
  Rebalance: "Rebalance"
305
314
  };
315
+ void chainId;
306
316
  return rawEntries.map((entry) => {
307
317
  const rawLog = entry.rebalanceLog;
308
318
  const positions = entry.positions ?? [];
@@ -381,8 +391,7 @@ function mapApyByStrategy(raw) {
381
391
  const supported = SupportedAssets.find(
382
392
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
383
393
  );
384
- if (!supported)
385
- continue;
394
+ if (!supported) continue;
386
395
  const chainKey = supported.chainId;
387
396
  const symbolKey = supported.symbol;
388
397
  const bucket = apyPerAsset[chainKey] ?? {};
@@ -412,10 +421,8 @@ function computeAllocationApy(positions) {
412
421
  totalValue += value;
413
422
  const chainId = resolveChainId(p.chain);
414
423
  const token = p.asset;
415
- if (!SUPPORTED_CHAIN_IDS.includes(chainId))
416
- continue;
417
- if (!SUPPORTED_TOKENS.includes(token))
418
- continue;
424
+ if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
425
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
419
426
  const perAsset = buckets[chainId] ?? {};
420
427
  const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
421
428
  cell.weightedSum += apy * value;
@@ -432,8 +439,7 @@ function computeAllocationApy(positions) {
432
439
  out[token] = cell.weightedSum / cell.value;
433
440
  }
434
441
  }
435
- if (Object.keys(out).length > 0)
436
- apyByChainAndAsset[chainId] = out;
442
+ if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
437
443
  }
438
444
  const totalApy = totalValue > 0 ? String(weightedSum / totalValue) : "0";
439
445
  return { totalApy, apyByChainAndAsset };
@@ -450,19 +456,14 @@ function extractWithdrawnAmount(logs, recipient, token, decimals) {
450
456
  let total = 0n;
451
457
  let matched = false;
452
458
  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;
459
+ if (log.address.toLowerCase() !== wantToken) continue;
460
+ if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC) continue;
461
+ if (topicToAddress(log.topics[2]) !== wantRecipient) continue;
462
+ if (!log.data || log.data === "0x") continue;
461
463
  total += BigInt(log.data);
462
464
  matched = true;
463
465
  }
464
- if (!matched)
465
- return void 0;
466
+ if (!matched) return void 0;
466
467
  return String(hexToDecimal(`0x${total.toString(16)}`, decimals));
467
468
  }
468
469
 
@@ -478,8 +479,7 @@ function encodeHistoryCursor(payload) {
478
479
  return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
479
480
  }
480
481
  function decodeHistoryCursor(token) {
481
- if (!token)
482
- throw new InvalidHistoryCursorError("Cursor is empty.");
482
+ if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
483
483
  let parsed;
484
484
  try {
485
485
  const json = Buffer.from(token, "base64").toString("utf8");
@@ -496,8 +496,7 @@ function decodeHistoryCursor(token) {
496
496
  // src/agents/zyfai/zyfai.auth-cache.ts
497
497
  var KEY_PREFIX = "owney.zyfai.session";
498
498
  var storage = () => {
499
- if (typeof window === "undefined")
500
- return null;
499
+ if (typeof window === "undefined") return null;
501
500
  try {
502
501
  return window.localStorage;
503
502
  } catch {
@@ -507,15 +506,13 @@ var storage = () => {
507
506
  var buildKey = (address, chainId) => `${KEY_PREFIX}:${address.toLowerCase()}:${chainId}`;
508
507
  var isJwtExpired = (token) => {
509
508
  const parts = token.split(".");
510
- if (parts.length !== 3)
511
- return false;
509
+ if (parts.length !== 3) return false;
512
510
  try {
513
511
  const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
514
512
  const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
515
513
  const decoded = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
516
514
  const claims = JSON.parse(decoded);
517
- if (typeof claims.exp !== "number")
518
- return false;
515
+ if (typeof claims.exp !== "number") return false;
519
516
  return Date.now() / 1e3 >= claims.exp - 30;
520
517
  } catch {
521
518
  return false;
@@ -523,11 +520,9 @@ var isJwtExpired = (token) => {
523
520
  };
524
521
  var readSession = (address, chainId) => {
525
522
  const store = storage();
526
- if (!store)
527
- return null;
523
+ if (!store) return null;
528
524
  const raw = store.getItem(buildKey(address, chainId));
529
- if (!raw)
530
- return null;
525
+ if (!raw) return null;
531
526
  try {
532
527
  const parsed = JSON.parse(raw);
533
528
  if (!parsed.token || isJwtExpired(parsed.token)) {
@@ -542,8 +537,7 @@ var readSession = (address, chainId) => {
542
537
  };
543
538
  var writeSession = (address, chainId, session) => {
544
539
  const store = storage();
545
- if (!store)
546
- return;
540
+ if (!store) return;
547
541
  try {
548
542
  store.setItem(buildKey(address, chainId), JSON.stringify(session));
549
543
  } catch {
@@ -551,8 +545,7 @@ var writeSession = (address, chainId, session) => {
551
545
  };
552
546
  var clearSession = (address, chainId) => {
553
547
  const store = storage();
554
- if (!store)
555
- return;
548
+ if (!store) return;
556
549
  try {
557
550
  store.removeItem(buildKey(address, chainId));
558
551
  } catch {
@@ -561,8 +554,7 @@ var clearSession = (address, chainId) => {
561
554
 
562
555
  // src/agents/zyfai/autoSelect.ts
563
556
  function buildAutoSelectUpdate(current, asset) {
564
- if (current.autoSelectProtocols === true)
565
- return null;
557
+ if (current.autoSelectProtocols === true) return null;
566
558
  return {
567
559
  asset,
568
560
  autoSelectProtocols: true,
@@ -573,16 +565,18 @@ function buildAutoSelectUpdate(current, asset) {
573
565
  // src/agents/zyfai/zyfai.agent.ts
574
566
  var DEFAULT_ZYFAI_RPC_URLS = {
575
567
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
576
- 42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
568
+ 42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
569
+ 1: "https://eth-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
577
570
  };
578
571
  var WETH_ADDRESS_BY_CHAIN = {
579
572
  8453: "0x4200000000000000000000000000000000000006",
580
- 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
573
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
574
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
581
575
  };
582
- var VIEM_CHAIN = { 8453: base, 42161: arbitrum };
576
+ var VIEM_CHAIN = { 8453: base, 42161: arbitrum, 1: mainnet };
583
577
  var ZyfaiAgent = class {
584
578
  id = "zyfai";
585
- supportedChainIds = [8453, 42161];
579
+ supportedChainIds = [8453, 42161, 1];
586
580
  supportedAssets = [
587
581
  {
588
582
  chainId: 8453,
@@ -599,6 +593,19 @@ var ZyfaiAgent = class {
599
593
  { symbol: "USDC", minDepositAmount: "1500000" },
600
594
  { symbol: "WETH", minDepositAmount: "500000000000000" }
601
595
  ]
596
+ },
597
+ {
598
+ chainId: 1,
599
+ chain: "ETHEREUM",
600
+ // Zyfai enforces per-asset minimum portfolios on Ethereum mainnet, keyed
601
+ // by MIN_PORTFOLIO_BALANCE[1] in @zyfai/sdk (mainnet gas only pays off on
602
+ // large positions). USDC: 10,000 (10_000 * 1e6). WETH: 5 (5 * 1e18).
603
+ // NOTE: the @zyfai/sdk README claims WETH has no minimum — that's stale;
604
+ // the shipped code enforces 5 WETH, so match it or deposits reject on-chain.
605
+ assets: [
606
+ { symbol: "USDC", minDepositAmount: "10000000000" },
607
+ { symbol: "WETH", minDepositAmount: "5000000000000000000" }
608
+ ]
602
609
  }
603
610
  ];
604
611
  sdk;
@@ -622,8 +629,7 @@ var ZyfaiAgent = class {
622
629
  }
623
630
  getPublicClient(chainId) {
624
631
  const cached = this.publicClients.get(chainId);
625
- if (cached)
626
- return cached;
632
+ if (cached) return cached;
627
633
  const client = createPublicClient({
628
634
  chain: VIEM_CHAIN[chainId],
629
635
  transport: http(this.rpcUrls[chainId])
@@ -654,8 +660,7 @@ var ZyfaiAgent = class {
654
660
  const withdrawals = entries.filter(
655
661
  (e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
656
662
  );
657
- if (withdrawals.length === 0)
658
- return;
663
+ if (withdrawals.length === 0) return;
659
664
  const client = this.getPublicClient(chainId);
660
665
  await Promise.all(
661
666
  withdrawals.map(async (entry) => {
@@ -668,8 +673,7 @@ var ZyfaiAgent = class {
668
673
  ) ?? SupportedAssets.find(
669
674
  (a) => a.chainId === chainId && a.symbol === "USDC"
670
675
  );
671
- if (!asset)
672
- return;
676
+ if (!asset) return;
673
677
  const cacheKey = `${txHash}:${asset.address}`;
674
678
  const cached = this.withdrawAmountCache.get(cacheKey);
675
679
  if (cached !== void 0) {
@@ -706,13 +710,11 @@ var ZyfaiAgent = class {
706
710
  * for some apps and the patch only matters in the browser anyway.
707
711
  */
708
712
  installAuthCache() {
709
- if (typeof window === "undefined")
710
- return;
713
+ if (typeof window === "undefined") return;
711
714
  const internals = this.sdk;
712
715
  const original = internals.authenticateUser.bind(internals);
713
716
  internals.authenticateUser = async () => {
714
- if (internals.authenticatedUserId !== null)
715
- return;
717
+ if (internals.authenticatedUserId !== null) return;
716
718
  const address = internals.walletClient?.account?.address;
717
719
  const chainId = internals.currentChainId;
718
720
  if (address && chainId) {
@@ -787,12 +789,10 @@ var ZyfaiAgent = class {
787
789
  return await this.sdk.connectAccount(provider, chainId);
788
790
  } catch (error) {
789
791
  const message = error instanceof Error ? error.message : String(error);
790
- if (!message.toLowerCase().includes("unauthorized"))
791
- throw error;
792
+ if (!message.toLowerCase().includes("unauthorized")) throw error;
792
793
  const internals = this.sdk;
793
794
  const address = internals.walletClient?.account?.address;
794
- if (address)
795
- clearSession(address, chainId);
795
+ if (address) clearSession(address, chainId);
796
796
  return await this.sdk.connectAccount(provider, chainId);
797
797
  }
798
798
  }
@@ -922,10 +922,10 @@ var ZyfaiAgent = class {
922
922
  const raw = await this.sdk.getOnchainEarnings(smartWallet);
923
923
  return mapEarnings(raw, smartWallet);
924
924
  }
925
- async getAccountApy(state, chainId, days) {
925
+ async getAccountApy(state, chainId, days, tokenSymbol) {
926
926
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
927
927
  const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
928
- return mapApyHistory(raw, chainId);
928
+ return mapApyHistory(raw, chainId, tokenSymbol);
929
929
  }
930
930
  async getHistory(state, chainId, options) {
931
931
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
@@ -990,8 +990,7 @@ var ZyfaiAgent = class {
990
990
  },
991
991
  asset
992
992
  );
993
- if (!update)
994
- return false;
993
+ if (!update) return false;
995
994
  await this.sdk.updateUserProfile(update);
996
995
  return true;
997
996
  }
@@ -1080,8 +1079,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
1080
1079
  const tokenBalance = agentBalance?.tokens.find(
1081
1080
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
1082
1081
  );
1083
- if (!tokenBalance)
1084
- return { agent, balance: 0n };
1082
+ if (!tokenBalance) return { agent, balance: 0n };
1085
1083
  return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
1086
1084
  });
1087
1085
  }
@@ -1098,11 +1096,9 @@ function planProportionalShares(balances, requested, totalAvailable) {
1098
1096
  return diff > 0n ? 1 : diff < 0n ? -1 : 0;
1099
1097
  });
1100
1098
  for (const p of byHeadroom) {
1101
- if (remainder === 0n)
1102
- break;
1099
+ if (remainder === 0n) break;
1103
1100
  const headroom = p.balance - p.planned;
1104
- if (headroom <= 0n)
1105
- continue;
1101
+ if (headroom <= 0n) continue;
1106
1102
  const take = headroom < remainder ? headroom : remainder;
1107
1103
  p.planned += take;
1108
1104
  remainder -= take;
@@ -1131,8 +1127,7 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
1131
1127
  (s, c) => s + (c.balance - c.planned),
1132
1128
  0n
1133
1129
  );
1134
- if (totalHeadroom === 0n)
1135
- return;
1130
+ if (totalHeadroom === 0n) return;
1136
1131
  let distributed = 0n;
1137
1132
  for (const c of candidates) {
1138
1133
  const headroom = c.balance - c.planned;
@@ -1143,11 +1138,9 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
1143
1138
  }
1144
1139
  let leftover = amount - distributed;
1145
1140
  for (const c of candidates) {
1146
- if (leftover === 0n)
1147
- break;
1141
+ if (leftover === 0n) break;
1148
1142
  const headroom = c.balance - c.planned;
1149
- if (headroom <= 0n)
1150
- continue;
1143
+ if (headroom <= 0n) continue;
1151
1144
  const take = headroom < leftover ? headroom : leftover;
1152
1145
  c.planned += take;
1153
1146
  leftover -= take;
@@ -1164,16 +1157,13 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
1164
1157
  for (const id of Object.keys(agentApys)) {
1165
1158
  const cells = agentApys[id].apyByChainAndAsset;
1166
1159
  const balance = agentBalances[id] ?? 0;
1167
- if (!cells || balance <= 0)
1168
- continue;
1160
+ if (!cells || balance <= 0) continue;
1169
1161
  for (const [chainKey, perAsset] of Object.entries(cells)) {
1170
- if (!perAsset)
1171
- continue;
1162
+ if (!perAsset) continue;
1172
1163
  const chainId = Number(chainKey);
1173
1164
  for (const [asset, apyValue] of Object.entries(perAsset)) {
1174
1165
  const apy = Number(apyValue ?? 0);
1175
- if (apy === 0)
1176
- continue;
1166
+ if (apy === 0) continue;
1177
1167
  sums[chainId] ??= {};
1178
1168
  weights[chainId] ??= {};
1179
1169
  sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
@@ -1187,8 +1177,7 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
1187
1177
  const perAssetOut = {};
1188
1178
  for (const asset of Object.keys(sums[chainId])) {
1189
1179
  const w = weights[chainId][asset];
1190
- if (w > 0)
1191
- perAssetOut[asset] = sums[chainId][asset] / w;
1180
+ if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
1192
1181
  }
1193
1182
  if (Object.keys(perAssetOut).length > 0) {
1194
1183
  out[chainId] = perAssetOut;
@@ -1203,7 +1192,7 @@ import {
1203
1192
  createWalletClient,
1204
1193
  custom
1205
1194
  } from "viem";
1206
- import { base as base2, arbitrum as arbitrum2 } from "viem/chains";
1195
+ import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
1207
1196
 
1208
1197
  // src/lib/transfer-auth.ts
1209
1198
  import { bytesToHex } from "viem";
@@ -1249,7 +1238,10 @@ async function postSponsorTransferAuth(input) {
1249
1238
  try {
1250
1239
  res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
1251
1240
  method: "POST",
1252
- headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
1241
+ headers: {
1242
+ "content-type": "application/json",
1243
+ "x-owney-api-key": input.apiKey
1244
+ },
1253
1245
  body: JSON.stringify(input.body)
1254
1246
  });
1255
1247
  } catch (networkError) {
@@ -1269,7 +1261,13 @@ async function postSponsorTransferAuth(input) {
1269
1261
  throw new OwneyError(
1270
1262
  "SPONSOR_REQUEST_FAILED",
1271
1263
  `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1272
- { statusCode: res.status, responseBody: text.slice(0, 500) }
1264
+ {
1265
+ statusCode: res.status,
1266
+ responseBody: text.slice(0, 500),
1267
+ // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
1268
+ // before broadcast, so it is safe to fall back to a user-paid deposit.
1269
+ safeToFallback: res.status === 503
1270
+ }
1273
1271
  );
1274
1272
  }
1275
1273
  return parsed.data;
@@ -1280,7 +1278,10 @@ async function postSponsorPermit2Transfer(input) {
1280
1278
  try {
1281
1279
  res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
1282
1280
  method: "POST",
1283
- headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
1281
+ headers: {
1282
+ "content-type": "application/json",
1283
+ "x-owney-api-key": input.apiKey
1284
+ },
1284
1285
  body: JSON.stringify(input.body)
1285
1286
  });
1286
1287
  } catch (networkError) {
@@ -1303,7 +1304,7 @@ async function postSponsorPermit2Transfer(input) {
1303
1304
  {
1304
1305
  statusCode: res.status,
1305
1306
  responseBody: text.slice(0, 500),
1306
- safeToFallback: res.status >= 400 && res.status < 500
1307
+ safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
1307
1308
  }
1308
1309
  );
1309
1310
  }
@@ -1313,9 +1314,12 @@ async function getSponsorRelayerAddress(input) {
1313
1314
  const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1314
1315
  let res;
1315
1316
  try {
1316
- res = await fetch(`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`, {
1317
- headers: { "x-owney-api-key": input.apiKey }
1318
- });
1317
+ res = await fetch(
1318
+ `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
1319
+ {
1320
+ headers: { "x-owney-api-key": input.apiKey }
1321
+ }
1322
+ );
1319
1323
  } catch (networkError) {
1320
1324
  throw new OwneyError(
1321
1325
  "SPONSOR_REQUEST_FAILED",
@@ -1333,7 +1337,11 @@ async function getSponsorRelayerAddress(input) {
1333
1337
  throw new OwneyError(
1334
1338
  "SPONSOR_REQUEST_FAILED",
1335
1339
  `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1336
- { statusCode: res.status, responseBody: text.slice(0, 500), safeToFallback: true }
1340
+ {
1341
+ statusCode: res.status,
1342
+ responseBody: text.slice(0, 500),
1343
+ safeToFallback: true
1344
+ }
1337
1345
  );
1338
1346
  }
1339
1347
  return parsed.data.relayer;
@@ -1520,18 +1528,21 @@ function decodeMultiAgentCursor(token) {
1520
1528
  }
1521
1529
  var SPONSORED_USDC_BY_CHAIN = {
1522
1530
  8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1523
- 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
1531
+ 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
1532
+ 1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
1524
1533
  };
1525
1534
  var VIEM_CHAIN2 = {
1526
1535
  8453: base2,
1527
- 42161: arbitrum2
1536
+ 42161: arbitrum2,
1537
+ 1: mainnet2
1528
1538
  };
1529
1539
  var SPONSORED_WETH_BY_CHAIN = {
1530
1540
  8453: "0x4200000000000000000000000000000000000006",
1531
- 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
1541
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
1542
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
1532
1543
  };
1533
1544
  function shouldFallbackToUserPaid(error, asset, appCallback) {
1534
- return asset === "WETH" && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
1545
+ return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
1535
1546
  }
1536
1547
  var OwneySDK = class {
1537
1548
  agents = /* @__PURE__ */ new Map();
@@ -1545,12 +1556,14 @@ var OwneySDK = class {
1545
1556
  state = null;
1546
1557
  apiKey;
1547
1558
  zyfaiRpcUrls;
1559
+ routingApiBaseUrl;
1548
1560
  cachedSponsoredCallback = null;
1549
1561
  cachedWethSponsoredCallback = null;
1550
1562
  initializingAgentsPromise = null;
1551
1563
  constructor(config) {
1552
1564
  this.apiKey = config.apiKey;
1553
1565
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
1566
+ this.routingApiBaseUrl = config.routingApiBaseUrl;
1554
1567
  }
1555
1568
  /**
1556
1569
  * Establish connection state for all subsequent SDK calls.
@@ -1605,8 +1618,7 @@ var OwneySDK = class {
1605
1618
  return this.state?.chainId ?? null;
1606
1619
  }
1607
1620
  requireState() {
1608
- if (!this.state)
1609
- throw new NotConnectedError();
1621
+ if (!this.state) throw new NotConnectedError();
1610
1622
  return this.state;
1611
1623
  }
1612
1624
  requireChainId() {
@@ -1636,12 +1648,12 @@ var OwneySDK = class {
1636
1648
  * `TransferWithAuthorization`, then POSTs to the sponsor API.
1637
1649
  */
1638
1650
  getDefaultSponsoredCallback() {
1639
- if (this.cachedSponsoredCallback)
1640
- return this.cachedSponsoredCallback;
1651
+ if (this.cachedSponsoredCallback) return this.cachedSponsoredCallback;
1641
1652
  const provider = this.requireConnectedProvider();
1642
1653
  const owner = this.state.walletAddress;
1643
1654
  this.cachedSponsoredCallback = makeSponsoredDepositCallback({
1644
1655
  apiKey: this.apiKey,
1656
+ baseUrl: this.routingApiBaseUrl,
1645
1657
  ownerAddress: owner,
1646
1658
  tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
1647
1659
  // Casts work around viem's chain-narrowed Client vs the generic
@@ -1672,6 +1684,7 @@ var OwneySDK = class {
1672
1684
  const owner = this.state.walletAddress;
1673
1685
  this.cachedWethSponsoredCallback = makeSponsoredWethCallback({
1674
1686
  apiKey: this.apiKey,
1687
+ baseUrl: this.routingApiBaseUrl,
1675
1688
  ownerAddress: owner,
1676
1689
  tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
1677
1690
  // Casts work around viem's chain-narrowed Client vs the generic
@@ -1700,22 +1713,19 @@ var OwneySDK = class {
1700
1713
  const active = /* @__PURE__ */ new Map();
1701
1714
  for (const id of this.activeAgents) {
1702
1715
  const agent = this.agents.get(id);
1703
- if (agent)
1704
- active.set(id, agent);
1716
+ if (agent) active.set(id, agent);
1705
1717
  }
1706
1718
  return active;
1707
1719
  }
1708
1720
  async ensureAgentsInitialized() {
1709
- if (this.agents.size > 0)
1710
- return;
1721
+ if (this.agents.size > 0) return;
1711
1722
  if (!this.initializingAgentsPromise) {
1712
1723
  this.initializingAgentsPromise = (async () => {
1713
1724
  const agentKeys = await fetchAgentKeys(this.apiKey);
1714
1725
  this.disabledAgents.clear();
1715
1726
  for (const { key, agent_type, is_enabled } of agentKeys) {
1716
1727
  const agent = this.createAgent(agent_type, key);
1717
- if (!agent)
1718
- continue;
1728
+ if (!agent) continue;
1719
1729
  this.agents.set(agent_type, agent);
1720
1730
  if (is_enabled === false) {
1721
1731
  this.disabledAgents.add(agent_type);
@@ -1954,10 +1964,9 @@ var OwneySDK = class {
1954
1964
  await this.approvePermit2();
1955
1965
  continue;
1956
1966
  }
1957
- if (!shouldFallbackToUserPaid(error, asset, appCallback))
1958
- throw error;
1967
+ if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
1959
1968
  console.warn(
1960
- "[owney-sdk] WETH sponsorship unavailable, falling back to user-paid deposit:",
1969
+ `[owney-sdk] ${asset} sponsorship unavailable, falling back to user-paid deposit:`,
1961
1970
  error
1962
1971
  );
1963
1972
  return attempt(void 0);
@@ -1972,8 +1981,7 @@ var OwneySDK = class {
1972
1981
  return BigInt(assetEntry?.minDepositAmount ?? "0");
1973
1982
  }
1974
1983
  splitDepositAmount(totalAmount, agents, chainId, asset, exempt = /* @__PURE__ */ new Set()) {
1975
- if (agents.length === 0)
1976
- return [];
1984
+ if (agents.length === 0) return [];
1977
1985
  const perAgent = totalAmount / BigInt(agents.length);
1978
1986
  const remainder = totalAmount % BigInt(agents.length);
1979
1987
  const splits = agents.map((agent, i) => ({
@@ -1997,10 +2005,8 @@ var OwneySDK = class {
1997
2005
  async validateMinDepositAmount(agent, state, chainId, asset, amount) {
1998
2006
  const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
1999
2007
  const parsedAmount = BigInt(amount);
2000
- if (parsedAmount >= minDepositAmount)
2001
- return;
2002
- if (await this.hasExistingBalance(agent, state, chainId, asset))
2003
- return;
2008
+ if (parsedAmount >= minDepositAmount) return;
2009
+ if (await this.hasExistingBalance(agent, state, chainId, asset)) return;
2004
2010
  throw new OwneyError(
2005
2011
  "DEPOSIT_AMOUNT_BELOW_MINIMUM",
2006
2012
  `Amount "${amount}" is below the required minimum deposit "${minDepositAmount.toString()}" for agent "${agent.id}" on chain ${chainId} for asset "${asset}".`,
@@ -2164,8 +2170,7 @@ var OwneySDK = class {
2164
2170
  const agentErrors = {};
2165
2171
  for (let i = 0; i < plans.length; i++) {
2166
2172
  const p = plans[i];
2167
- if (p.planned === 0n)
2168
- continue;
2173
+ if (p.planned === 0n) continue;
2169
2174
  try {
2170
2175
  results[p.agent.id] = await p.agent.withdraw(
2171
2176
  state,
@@ -2299,19 +2304,30 @@ var OwneySDK = class {
2299
2304
  */
2300
2305
  async getAccountApy({
2301
2306
  agentId,
2302
- days
2307
+ days,
2308
+ tokenSymbol
2303
2309
  }) {
2304
2310
  const state = this.requireState();
2305
2311
  const chainId = this.requireChainId();
2306
2312
  if (agentId) {
2307
- return this.getAgent(agentId).getAccountApy(state, chainId, days);
2313
+ return this.getAgent(agentId).getAccountApy(
2314
+ state,
2315
+ chainId,
2316
+ days,
2317
+ tokenSymbol
2318
+ );
2308
2319
  }
2309
2320
  const activeAgents = this.getActiveAgents();
2310
2321
  const entries = [...activeAgents.entries()];
2311
2322
  const [apyResults, balanceResults] = await Promise.all([
2312
2323
  Promise.all(
2313
2324
  entries.map(async ([id, agent]) => {
2314
- const apy = await agent.getAccountApy(state, chainId, days);
2325
+ const apy = await agent.getAccountApy(
2326
+ state,
2327
+ chainId,
2328
+ days,
2329
+ tokenSymbol
2330
+ );
2315
2331
  return [id, apy];
2316
2332
  })
2317
2333
  ),
@@ -2334,15 +2350,13 @@ var OwneySDK = class {
2334
2350
  let relevantBalance = 0;
2335
2351
  for (const id of Object.keys(results)) {
2336
2352
  const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
2337
- if (agentApy === 0)
2338
- continue;
2353
+ if (agentApy === 0) continue;
2339
2354
  relevantBalance += balances[id];
2340
2355
  }
2341
2356
  if (relevantBalance > 0) {
2342
2357
  for (const id of Object.keys(results)) {
2343
2358
  const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
2344
- if (agentApy === 0)
2345
- continue;
2359
+ if (agentApy === 0) continue;
2346
2360
  const weight = balances[id] / relevantBalance;
2347
2361
  totalApy += agentApy * weight;
2348
2362
  }
@@ -2397,8 +2411,7 @@ var OwneySDK = class {
2397
2411
  const nextCursorMap = {};
2398
2412
  let hasMore = false;
2399
2413
  for (const { agentId: id, page } of perAgent) {
2400
- if (!page)
2401
- continue;
2414
+ if (!page) continue;
2402
2415
  merged.push(...page.data);
2403
2416
  if (page.hasMore && page.nextCursor) {
2404
2417
  nextCursorMap[id] = page.nextCursor;
@@ -2407,16 +2420,12 @@ var OwneySDK = class {
2407
2420
  }
2408
2421
  merged.sort((a, b) => {
2409
2422
  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;
2423
+ if (dateDiff !== 0) return dateDiff;
2424
+ if (a.agent !== b.agent) return a.agent < b.agent ? -1 : 1;
2414
2425
  const aTx = a.transactions?.[0]?.txHashes?.[0] ?? "";
2415
2426
  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;
2427
+ if (aTx !== bTx) return aTx < bTx ? -1 : 1;
2428
+ if (a.action !== b.action) return a.action < b.action ? -1 : 1;
2420
2429
  return 0;
2421
2430
  });
2422
2431
  return {
@@ -2462,8 +2471,7 @@ var OwneySDK = class {
2462
2471
  const state = this.requireState();
2463
2472
  const chainId = this.requireChainId();
2464
2473
  const agent = this.getAgent(agentId);
2465
- if (typeof agent.ensureAutoSelectProtocols !== "function")
2466
- return false;
2474
+ if (typeof agent.ensureAutoSelectProtocols !== "function") return false;
2467
2475
  return agent.ensureAutoSelectProtocols(state, chainId, asset);
2468
2476
  }
2469
2477
  /**
@@ -2475,8 +2483,10 @@ var OwneySDK = class {
2475
2483
  * @returns the approval transaction hash.
2476
2484
  */
2477
2485
  async approvePermit2(asset = "WETH") {
2486
+ void asset;
2478
2487
  const state = this.requireState();
2479
2488
  const chainId = this.requireChainId();
2489
+ this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
2480
2490
  const token = SPONSORED_WETH_BY_CHAIN[chainId];
2481
2491
  if (!token) {
2482
2492
  throw new OwneyError(
@@ -2616,8 +2626,7 @@ import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
2616
2626
  // src/agents/zyfai/zyfai.siwx-cache.ts
2617
2627
  var KEY_PREFIX2 = "owney.siwx.session";
2618
2628
  var storage2 = () => {
2619
- if (typeof window === "undefined")
2620
- return null;
2629
+ if (typeof window === "undefined") return null;
2621
2630
  try {
2622
2631
  return window.localStorage;
2623
2632
  } catch {
@@ -2627,11 +2636,9 @@ var storage2 = () => {
2627
2636
  var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2628
2637
  var readSiwxSession = (address, chainId) => {
2629
2638
  const store = storage2();
2630
- if (!store)
2631
- return null;
2639
+ if (!store) return null;
2632
2640
  const raw = store.getItem(buildKey2(address, chainId));
2633
- if (!raw)
2634
- return null;
2641
+ if (!raw) return null;
2635
2642
  try {
2636
2643
  return JSON.parse(raw);
2637
2644
  } catch {
@@ -2641,8 +2648,7 @@ var readSiwxSession = (address, chainId) => {
2641
2648
  };
2642
2649
  var writeSiwxSession = (address, chainId, session) => {
2643
2650
  const store = storage2();
2644
- if (!store)
2645
- return;
2651
+ if (!store) return;
2646
2652
  try {
2647
2653
  store.setItem(buildKey2(address, chainId), JSON.stringify(session));
2648
2654
  } catch {
@@ -2650,8 +2656,7 @@ var writeSiwxSession = (address, chainId, session) => {
2650
2656
  };
2651
2657
  var clearSiwxSession = (address, chainId) => {
2652
2658
  const store = storage2();
2653
- if (!store)
2654
- return;
2659
+ if (!store) return;
2655
2660
  try {
2656
2661
  store.removeItem(buildKey2(address, chainId));
2657
2662
  } catch {
@@ -2714,12 +2719,10 @@ function buildSIWXConfig(deps) {
2714
2719
  const jwt = readSession(address, id);
2715
2720
  const stored = readSiwxSession(address, id);
2716
2721
  if (!jwt) {
2717
- if (stored)
2718
- clearSiwxSession(address, id);
2722
+ if (stored) clearSiwxSession(address, id);
2719
2723
  return [];
2720
2724
  }
2721
- if (!stored)
2722
- return [];
2725
+ if (!stored) return [];
2723
2726
  const session = {
2724
2727
  // `stored.data.chainId` is a plain string in the cache (kept AppKit-free);
2725
2728
  // it always holds a valid CaipNetworkId here, so narrow at the boundary.