@owney/sdk 0.6.6-beta.1 → 0.6.6-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,21 +1,14 @@
1
- import {
2
- AgentChainIncompatibleError,
3
- AgentNotFoundError,
4
- NotConnectedError,
5
- OwneyError
6
- } from "./chunk-GMNQBWUB.js";
7
-
8
1
  // src/agents/zyfai/zyfai.agent.ts
9
2
  import { ZyfaiSDK } from "@zyfai/sdk";
10
3
  import { createPublicClient, http } from "viem";
11
- import { base, arbitrum } from "viem/chains";
4
+ import { base, arbitrum, mainnet } from "viem/chains";
12
5
 
13
6
  // src/types/config.ts
14
7
  var P_SUPPORTED_CHAINS = {
15
- chainIds: [8453, 42161],
16
- chains: ["BASE", "ARBITRUM"]
8
+ chainIds: [8453, 42161, 1],
9
+ chains: ["BASE", "ARBITRUM", "ETHEREUM"]
17
10
  };
18
- var SUPPORTED_TOKENS = ["USDC", "USDT"];
11
+ var SUPPORTED_TOKENS = ["USDC", "USDT", "WETH"];
19
12
  var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
20
13
  var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
21
14
  var SupportedAssets = [
@@ -36,9 +29,82 @@ var SupportedAssets = [
36
29
  symbol: "USDT",
37
30
  chainId: 42161,
38
31
  decimals: 6
32
+ },
33
+ {
34
+ address: `0x4200000000000000000000000000000000000006`,
35
+ symbol: "WETH",
36
+ chainId: 8453,
37
+ decimals: 18
38
+ },
39
+ {
40
+ address: `0x82aF49447D8a07e3bd95BD0d56f35241523fBab1`,
41
+ symbol: "WETH",
42
+ chainId: 42161,
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
39
56
  }
40
57
  ];
41
58
 
59
+ // src/errors.ts
60
+ var OwneyError = class extends Error {
61
+ code;
62
+ details;
63
+ agentId;
64
+ constructor(code, message, details, agentId) {
65
+ const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
66
+ super(`${prefix} ${message}`);
67
+ this.name = "OwneyError";
68
+ this.code = code;
69
+ this.details = details;
70
+ this.agentId = agentId;
71
+ }
72
+ };
73
+ var AgentNotFoundError = class extends OwneyError {
74
+ constructor(agentId, available) {
75
+ super(
76
+ "AGENT_NOT_FOUND",
77
+ `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
78
+ { agentId, available },
79
+ agentId
80
+ );
81
+ this.name = "AgentNotFoundError";
82
+ }
83
+ };
84
+ var NotConnectedError = class extends OwneyError {
85
+ constructor() {
86
+ super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
87
+ this.name = "NotConnectedError";
88
+ }
89
+ };
90
+ var AgentChainIncompatibleError = class extends OwneyError {
91
+ incompatibleAgents;
92
+ connectedChainId;
93
+ constructor(incompatibleAgents, connectedChainId) {
94
+ const details = incompatibleAgents.map(
95
+ ({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
96
+ ).join("; ");
97
+ super(
98
+ "AGENT_CHAIN_INCOMPATIBLE",
99
+ `Chain ${connectedChainId} is not supported by the following agents: ${details}`,
100
+ { incompatibleAgents, connectedChainId }
101
+ );
102
+ this.name = "AgentChainIncompatibleError";
103
+ this.incompatibleAgents = incompatibleAgents;
104
+ this.connectedChainId = connectedChainId;
105
+ }
106
+ };
107
+
42
108
  // src/lib/utils.ts
43
109
  var isValidChainId = (chainId) => {
44
110
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
@@ -53,8 +119,7 @@ var isValidChainId = (chainId) => {
53
119
  function hexToDecimal(hex, decimals = 6) {
54
120
  const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
55
121
  const parsed = BigInt(normalized);
56
- if (decimals <= 0)
57
- return Number(parsed);
122
+ if (decimals <= 0) return Number(parsed);
58
123
  const divisor = BigInt(10 ** decimals);
59
124
  const whole = parsed / divisor;
60
125
  const remainder = parsed % divisor;
@@ -67,6 +132,20 @@ var DayFilterMapping = {
67
132
  };
68
133
 
69
134
  // src/agents/zyfai/zyfai.mapper.ts
135
+ var DECIMALS_BY_SYMBOL = {
136
+ usdc: 6,
137
+ usdt: 6,
138
+ weth: 18,
139
+ eth: 18,
140
+ cbeth: 18,
141
+ wbtc: 8,
142
+ cbbtc: 8
143
+ };
144
+ function tokenDecimals(symbol, explicit) {
145
+ if (typeof explicit === "number" && Number.isFinite(explicit))
146
+ return explicit;
147
+ return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
148
+ }
70
149
  function mapDeposit(raw) {
71
150
  return {
72
151
  txHash: raw.txHash,
@@ -83,7 +162,8 @@ function mapWithdraw(raw) {
83
162
  }
84
163
  var CHAIN_ID_TO_NAME = {
85
164
  8453: "BASE",
86
- 42161: "ARBITRUM"
165
+ 42161: "ARBITRUM",
166
+ 1: "ETHEREUM"
87
167
  };
88
168
  var NAME_TO_CHAIN_ID = Object.entries(
89
169
  CHAIN_ID_TO_NAME
@@ -93,54 +173,52 @@ var NAME_TO_CHAIN_ID = Object.entries(
93
173
  }, {});
94
174
  function resolveChainId(chain) {
95
175
  const asNum = Number(chain);
96
- if (Number.isFinite(asNum) && asNum > 0)
97
- return asNum;
176
+ if (Number.isFinite(asNum) && asNum > 0) return asNum;
98
177
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
99
178
  }
100
179
  function mapBalances(raw, _chainId, smartWallet) {
101
180
  const portfolio = raw.portfolio;
102
181
  const portfolioByChain = portfolio.portfolioByChain ?? {};
103
- const rawPositions = portfolio.positions ?? [];
104
- const detailByKey = /* @__PURE__ */ new Map();
105
- for (const p of rawPositions) {
106
- const cid = resolveChainId(p.chain ?? "");
107
- const symbol = (p.token_symbol ?? "").toUpperCase();
108
- if (Number.isFinite(cid) && symbol)
109
- detailByKey.set(`${cid}:${symbol}`, p);
110
- }
111
182
  let totalBalance = 0;
112
183
  const tokens = [];
113
- const positions = [];
114
184
  for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
115
185
  const positionChainId = Number(chainIdKey);
116
- const chainName = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
117
186
  for (const [symbol, { balance, decimals }] of Object.entries(
118
187
  tokensBySymbol ?? {}
119
188
  )) {
120
189
  const amount = hexToDecimal(balance || "0", decimals);
121
190
  totalBalance += amount;
122
191
  tokens.push({
123
- chain: chainName,
192
+ chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
124
193
  chainId: positionChainId,
125
194
  asset: symbol,
126
195
  amount: String(amount)
127
196
  });
128
- const detail = detailByKey.get(`${positionChainId}:${symbol.toUpperCase()}`);
129
- positions.push({
130
- chain: chainName,
131
- protocol: detail?.protocol_name ?? detail?.protocol_id ?? "",
132
- protocolId: detail?.protocol_id,
133
- pool: detail?.pool,
134
- asset: symbol,
135
- amount: String(amount),
136
- apy: detail?.pool_apy,
137
- tvl: detail?.pool_tvl,
138
- // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
139
- // structural read keeps it undefined today and auto-populates later.
140
- liquidity: detail?.liquidity
141
- });
142
197
  }
143
198
  }
199
+ const positions = (portfolio.positions ?? []).map((p) => ({
200
+ chain: p.chain ?? "",
201
+ protocol: p.protocol_name ?? p.protocol_id ?? "",
202
+ protocolId: p.protocol_id,
203
+ pool: p.pool,
204
+ asset: p.token_symbol ?? "",
205
+ // Position amount is hex-encoded base units (like the token balances
206
+ // above). `PositionSlot` doesn't type a `decimals` field, but runtime
207
+ // getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
208
+ // when present, otherwise fall back to the per-symbol lookup so
209
+ // non-6-decimal assets like WETH don't render 1e12 too large.
210
+ amount: String(
211
+ hexToDecimal(
212
+ p.amount || "0",
213
+ tokenDecimals(p.token_symbol, p.decimals)
214
+ )
215
+ ),
216
+ apy: p.pool_apy,
217
+ tvl: p.pool_tvl,
218
+ // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
219
+ // structural read keeps it undefined today and auto-populates later.
220
+ liquidity: p.liquidity
221
+ }));
144
222
  return {
145
223
  smartWallet,
146
224
  totalBalance: String(totalBalance),
@@ -179,41 +257,39 @@ function mapEarnings(raw, smartWallet) {
179
257
  };
180
258
  }
181
259
  function mapWeightedApyByChain(raw) {
182
- if (!raw)
183
- return void 0;
260
+ if (!raw) return void 0;
184
261
  const out = {};
185
262
  for (const [chainKey, tokenApy] of Object.entries(raw)) {
186
263
  const chainId = Number(chainKey);
187
- if (!SUPPORTED_CHAIN_IDS.includes(chainId))
188
- continue;
264
+ if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
189
265
  const perAsset = {};
190
266
  for (const [symbol, apy] of Object.entries(tokenApy)) {
191
267
  const token = symbol;
192
- if (!SUPPORTED_TOKENS.includes(token))
193
- continue;
268
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
194
269
  perAsset[token] = apy;
195
270
  }
196
- if (Object.keys(perAsset).length > 0)
197
- out[chainId] = perAsset;
271
+ if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
198
272
  }
199
273
  return Object.keys(out).length > 0 ? out : void 0;
200
274
  }
201
- function rawPoolApyForChain(entry, chainId) {
275
+ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
202
276
  let weightedSum = 0;
203
277
  let totalBalance = 0;
204
278
  for (const p of entry.positions ?? []) {
205
- if (p.chainId !== chainId)
206
- continue;
279
+ if (p.chainId !== chainId) continue;
280
+ if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
207
281
  const balance = p.balance ?? 0;
208
- if (balance <= 0 || typeof p.apy !== "number")
209
- continue;
282
+ if (balance <= 0 || typeof p.apy !== "number") continue;
210
283
  weightedSum += p.apy * balance;
211
284
  totalBalance += balance;
212
285
  }
213
286
  return totalBalance > 0 ? weightedSum / totalBalance : null;
214
287
  }
215
- function mapApyHistory(raw, chainId) {
216
- 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));
217
293
  return {
218
294
  walletAddress: raw.walletAddress,
219
295
  weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
@@ -236,6 +312,7 @@ function mapEntries(rawEntries, chainId) {
236
312
  "Withdraw from protocol": "Withdraw",
237
313
  Rebalance: "Rebalance"
238
314
  };
315
+ void chainId;
239
316
  return rawEntries.map((entry) => {
240
317
  const rawLog = entry.rebalanceLog;
241
318
  const positions = entry.positions ?? [];
@@ -257,7 +334,15 @@ function mapEntries(rawEntries, chainId) {
257
334
  fromPool: from.pool,
258
335
  toPool: to.pool,
259
336
  tokenSymbol: matched?.token_symbol ?? "",
260
- amount: matched ? String(hexToDecimal(matched.amount || "0x")) : "",
337
+ amount: matched ? String(
338
+ hexToDecimal(
339
+ matched.amount || "0x",
340
+ tokenDecimals(
341
+ matched.token_symbol,
342
+ matched.decimals
343
+ )
344
+ )
345
+ ) : "",
261
346
  status: "success"
262
347
  }
263
348
  ];
@@ -276,7 +361,12 @@ function mapEntries(rawEntries, chainId) {
276
361
  txHashes: entry.transactionHash ? [entry.transactionHash] : [],
277
362
  chainId: entry.chainId,
278
363
  tokenSymbol: entry?.positions?.[0]?.token_symbol,
279
- amount: hasAmount ? String(hexToDecimal(rawAmount)) : void 0
364
+ amount: hasAmount ? String(
365
+ hexToDecimal(
366
+ rawAmount,
367
+ tokenDecimals(entry?.positions?.[0]?.token_symbol)
368
+ )
369
+ ) : void 0
280
370
  }
281
371
  ],
282
372
  rebalanceLog
@@ -301,8 +391,7 @@ function mapApyByStrategy(raw) {
301
391
  const supported = SupportedAssets.find(
302
392
  (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
303
393
  );
304
- if (!supported)
305
- continue;
394
+ if (!supported) continue;
306
395
  const chainKey = supported.chainId;
307
396
  const symbolKey = supported.symbol;
308
397
  const bucket = apyPerAsset[chainKey] ?? {};
@@ -332,10 +421,8 @@ function computeAllocationApy(positions) {
332
421
  totalValue += value;
333
422
  const chainId = resolveChainId(p.chain);
334
423
  const token = p.asset;
335
- if (!SUPPORTED_CHAIN_IDS.includes(chainId))
336
- continue;
337
- if (!SUPPORTED_TOKENS.includes(token))
338
- continue;
424
+ if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
425
+ if (!SUPPORTED_TOKENS.includes(token)) continue;
339
426
  const perAsset = buckets[chainId] ?? {};
340
427
  const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
341
428
  cell.weightedSum += apy * value;
@@ -352,8 +439,7 @@ function computeAllocationApy(positions) {
352
439
  out[token] = cell.weightedSum / cell.value;
353
440
  }
354
441
  }
355
- if (Object.keys(out).length > 0)
356
- apyByChainAndAsset[chainId] = out;
442
+ if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
357
443
  }
358
444
  const totalApy = totalValue > 0 ? String(weightedSum / totalValue) : "0";
359
445
  return { totalApy, apyByChainAndAsset };
@@ -370,19 +456,14 @@ function extractWithdrawnAmount(logs, recipient, token, decimals) {
370
456
  let total = 0n;
371
457
  let matched = false;
372
458
  for (const log of logs) {
373
- if (log.address.toLowerCase() !== wantToken)
374
- continue;
375
- if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC)
376
- continue;
377
- if (topicToAddress(log.topics[2]) !== wantRecipient)
378
- continue;
379
- if (!log.data || log.data === "0x")
380
- 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;
381
463
  total += BigInt(log.data);
382
464
  matched = true;
383
465
  }
384
- if (!matched)
385
- return void 0;
466
+ if (!matched) return void 0;
386
467
  return String(hexToDecimal(`0x${total.toString(16)}`, decimals));
387
468
  }
388
469
 
@@ -398,8 +479,7 @@ function encodeHistoryCursor(payload) {
398
479
  return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
399
480
  }
400
481
  function decodeHistoryCursor(token) {
401
- if (!token)
402
- throw new InvalidHistoryCursorError("Cursor is empty.");
482
+ if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
403
483
  let parsed;
404
484
  try {
405
485
  const json = Buffer.from(token, "base64").toString("utf8");
@@ -416,8 +496,7 @@ function decodeHistoryCursor(token) {
416
496
  // src/agents/zyfai/zyfai.auth-cache.ts
417
497
  var KEY_PREFIX = "owney.zyfai.session";
418
498
  var storage = () => {
419
- if (typeof window === "undefined")
420
- return null;
499
+ if (typeof window === "undefined") return null;
421
500
  try {
422
501
  return window.localStorage;
423
502
  } catch {
@@ -427,15 +506,13 @@ var storage = () => {
427
506
  var buildKey = (address, chainId) => `${KEY_PREFIX}:${address.toLowerCase()}:${chainId}`;
428
507
  var isJwtExpired = (token) => {
429
508
  const parts = token.split(".");
430
- if (parts.length !== 3)
431
- return false;
509
+ if (parts.length !== 3) return false;
432
510
  try {
433
511
  const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
434
512
  const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
435
513
  const decoded = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
436
514
  const claims = JSON.parse(decoded);
437
- if (typeof claims.exp !== "number")
438
- return false;
515
+ if (typeof claims.exp !== "number") return false;
439
516
  return Date.now() / 1e3 >= claims.exp - 30;
440
517
  } catch {
441
518
  return false;
@@ -443,11 +520,9 @@ var isJwtExpired = (token) => {
443
520
  };
444
521
  var readSession = (address, chainId) => {
445
522
  const store = storage();
446
- if (!store)
447
- return null;
523
+ if (!store) return null;
448
524
  const raw = store.getItem(buildKey(address, chainId));
449
- if (!raw)
450
- return null;
525
+ if (!raw) return null;
451
526
  try {
452
527
  const parsed = JSON.parse(raw);
453
528
  if (!parsed.token || isJwtExpired(parsed.token)) {
@@ -462,8 +537,7 @@ var readSession = (address, chainId) => {
462
537
  };
463
538
  var writeSession = (address, chainId, session) => {
464
539
  const store = storage();
465
- if (!store)
466
- return;
540
+ if (!store) return;
467
541
  try {
468
542
  store.setItem(buildKey(address, chainId), JSON.stringify(session));
469
543
  } catch {
@@ -471,33 +545,67 @@ var writeSession = (address, chainId, session) => {
471
545
  };
472
546
  var clearSession = (address, chainId) => {
473
547
  const store = storage();
474
- if (!store)
475
- return;
548
+ if (!store) return;
476
549
  try {
477
550
  store.removeItem(buildKey(address, chainId));
478
551
  } catch {
479
552
  }
480
553
  };
481
554
 
555
+ // src/agents/zyfai/autoSelect.ts
556
+ function buildAutoSelectUpdate(current, asset) {
557
+ if (current.autoSelectProtocols === true) return null;
558
+ return {
559
+ asset,
560
+ autoSelectProtocols: true,
561
+ ...current.chains ? { chains: current.chains } : {}
562
+ };
563
+ }
564
+
482
565
  // src/agents/zyfai/zyfai.agent.ts
483
566
  var DEFAULT_ZYFAI_RPC_URLS = {
484
567
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
485
- 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"
570
+ };
571
+ var WETH_ADDRESS_BY_CHAIN = {
572
+ 8453: "0x4200000000000000000000000000000000000006",
573
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
574
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
486
575
  };
487
- var VIEM_CHAIN = { 8453: base, 42161: arbitrum };
576
+ var VIEM_CHAIN = { 8453: base, 42161: arbitrum, 1: mainnet };
488
577
  var ZyfaiAgent = class {
489
578
  id = "zyfai";
490
- supportedChainIds = [8453, 42161];
579
+ supportedChainIds = [8453, 42161, 1];
491
580
  supportedAssets = [
492
581
  {
493
582
  chainId: 8453,
494
583
  chain: "BASE",
495
- assets: [{ symbol: "USDC", minDepositAmount: "1500000" }]
584
+ assets: [
585
+ { symbol: "USDC", minDepositAmount: "1500000" },
586
+ { symbol: "WETH", minDepositAmount: "500000000000000" }
587
+ ]
496
588
  },
497
589
  {
498
590
  chainId: 42161,
499
591
  chain: "ARBITRUM",
500
- assets: [{ symbol: "USDC", minDepositAmount: "1500000" }]
592
+ assets: [
593
+ { symbol: "USDC", minDepositAmount: "1500000" },
594
+ { symbol: "WETH", minDepositAmount: "500000000000000" }
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. Lowered in @zyfai/sdk@0.2.40.
602
+ // USDC: 100 (100 * 1e6). WETH: 0.005 (5 * 1e15).
603
+ // NOTE: the @zyfai/sdk README claims WETH has no minimum — that's stale;
604
+ // the shipped code enforces 0.005 WETH, so match it or deposits reject on-chain.
605
+ assets: [
606
+ { symbol: "USDC", minDepositAmount: "100000000" },
607
+ { symbol: "WETH", minDepositAmount: "5000000000000000" }
608
+ ]
501
609
  }
502
610
  ];
503
611
  sdk;
@@ -521,8 +629,7 @@ var ZyfaiAgent = class {
521
629
  }
522
630
  getPublicClient(chainId) {
523
631
  const cached = this.publicClients.get(chainId);
524
- if (cached)
525
- return cached;
632
+ if (cached) return cached;
526
633
  const client = createPublicClient({
527
634
  chain: VIEM_CHAIN[chainId],
528
635
  transport: http(this.rpcUrls[chainId])
@@ -532,33 +639,43 @@ var ZyfaiAgent = class {
532
639
  }
533
640
  /**
534
641
  * Fills in the true withdrawn amount for `Withdraw` entries by decoding the
535
- * USDC `Transfer` that lands in the user's wallet on-chain. The Zyfai history
642
+ * on-chain `Transfer` that lands in the user's wallet. The Zyfai history
536
643
  * payload only reports the *remaining* position for a partial withdraw (see
537
644
  * the note in `mapEntries`), so without this the UI shows the leftover
538
645
  * balance instead of the amount withdrawn.
539
646
  *
647
+ * The withdrawn asset varies per entry (USDC or WETH), so it's resolved
648
+ * from `tx.tokenSymbol` (set by `mapEntries` from the entry's own
649
+ * position) rather than assumed to always be USDC. Zyfai sometimes
650
+ * denominates WETH as lowercase "eth" — that's normalized to WETH.
651
+ * Entries with no symbol (older payload shapes) fall back to USDC to
652
+ * preserve prior behavior.
653
+ *
540
654
  * Mutates `entries` in place. Runs all lookups concurrently and is tolerant
541
655
  * of RPC failures: an entry whose amount can't be resolved keeps
542
656
  * `amount: undefined`, which consumers render as a hidden/retryable row
543
657
  * rather than a wrong number.
544
658
  */
545
659
  async resolveWithdrawnAmounts(entries, chainId, recipient) {
546
- const asset = SupportedAssets.find(
547
- (a) => a.chainId === chainId && a.symbol === "USDC"
548
- );
549
- if (!asset)
550
- return;
551
660
  const withdrawals = entries.filter(
552
661
  (e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
553
662
  );
554
- if (withdrawals.length === 0)
555
- return;
663
+ if (withdrawals.length === 0) return;
556
664
  const client = this.getPublicClient(chainId);
557
665
  await Promise.all(
558
666
  withdrawals.map(async (entry) => {
559
667
  const tx = entry.transactions[0];
560
668
  const txHash = tx.txHashes[0];
561
- const cached = this.withdrawAmountCache.get(txHash);
669
+ const rawSymbol = tx.tokenSymbol;
670
+ const normalizedSymbol = rawSymbol ? rawSymbol.toLowerCase() === "eth" ? "WETH" : rawSymbol.toUpperCase() : "USDC";
671
+ const asset = SupportedAssets.find(
672
+ (a) => a.chainId === chainId && a.symbol === normalizedSymbol
673
+ ) ?? SupportedAssets.find(
674
+ (a) => a.chainId === chainId && a.symbol === "USDC"
675
+ );
676
+ if (!asset) return;
677
+ const cacheKey = `${txHash}:${asset.address}`;
678
+ const cached = this.withdrawAmountCache.get(cacheKey);
562
679
  if (cached !== void 0) {
563
680
  tx.amount = cached;
564
681
  return;
@@ -574,7 +691,7 @@ var ZyfaiAgent = class {
574
691
  asset.decimals
575
692
  );
576
693
  if (amount !== void 0) {
577
- this.withdrawAmountCache.set(txHash, amount);
694
+ this.withdrawAmountCache.set(cacheKey, amount);
578
695
  tx.amount = amount;
579
696
  }
580
697
  } catch {
@@ -593,13 +710,11 @@ var ZyfaiAgent = class {
593
710
  * for some apps and the patch only matters in the browser anyway.
594
711
  */
595
712
  installAuthCache() {
596
- if (typeof window === "undefined")
597
- return;
713
+ if (typeof window === "undefined") return;
598
714
  const internals = this.sdk;
599
715
  const original = internals.authenticateUser.bind(internals);
600
716
  internals.authenticateUser = async () => {
601
- if (internals.authenticatedUserId !== null)
602
- return;
717
+ if (internals.authenticatedUserId !== null) return;
603
718
  const address = internals.walletClient?.account?.address;
604
719
  const chainId = internals.currentChainId;
605
720
  if (address && chainId) {
@@ -674,12 +789,10 @@ var ZyfaiAgent = class {
674
789
  return await this.sdk.connectAccount(provider, chainId);
675
790
  } catch (error) {
676
791
  const message = error instanceof Error ? error.message : String(error);
677
- if (!message.toLowerCase().includes("unauthorized"))
678
- throw error;
792
+ if (!message.toLowerCase().includes("unauthorized")) throw error;
679
793
  const internals = this.sdk;
680
794
  const address = internals.walletClient?.account?.address;
681
- if (address)
682
- clearSession(address, chainId);
795
+ if (address) clearSession(address, chainId);
683
796
  return await this.sdk.connectAccount(provider, chainId);
684
797
  }
685
798
  }
@@ -692,6 +805,22 @@ var ZyfaiAgent = class {
692
805
  this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
693
806
  if (!wallet.isDeployed) {
694
807
  await this.sdk.deploySafe(address, validChainId, "aggressive", true);
808
+ try {
809
+ if (validChainId === 42161) {
810
+ await this.sdk.updateUserProfile({
811
+ asset: "WETH",
812
+ chains: [validChainId],
813
+ protocols: ["c24a33fa-4a87-4376-b756-93bd3957d3ea"],
814
+ // AAVE protocol ID
815
+ autoSelectProtocols: false
816
+ });
817
+ }
818
+ } catch (err) {
819
+ console.warn(
820
+ "Failed to configure AAVE protocol after Safe deployment:",
821
+ err instanceof Error ? err.message : String(err)
822
+ );
823
+ }
695
824
  }
696
825
  const user = await this.sdk.getUserDetails();
697
826
  if (!user.hasActiveSessionKey) {
@@ -733,7 +862,7 @@ var ZyfaiAgent = class {
733
862
  throw error;
734
863
  }
735
864
  }
736
- async deposit(state, chainId, amount, depositCallback) {
865
+ async deposit(state, chainId, amount, asset, depositCallback) {
737
866
  try {
738
867
  await this.connectFull(state, chainId);
739
868
  const validChainId = isValidChainId(chainId);
@@ -747,13 +876,15 @@ var ZyfaiAgent = class {
747
876
  validChainId,
748
877
  amount
749
878
  );
750
- await this.sdk.logDeposit(validChainId, txHash, amount);
879
+ const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
880
+ await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
751
881
  return { txHash, smartWallet: wallet.address, amount };
752
882
  }
753
883
  const raw = await this.sdk.depositFunds(
754
884
  this.getAddress(),
755
885
  validChainId,
756
- amount
886
+ amount,
887
+ asset
757
888
  );
758
889
  return mapDeposit(raw);
759
890
  } catch (error) {
@@ -791,10 +922,10 @@ var ZyfaiAgent = class {
791
922
  const raw = await this.sdk.getOnchainEarnings(smartWallet);
792
923
  return mapEarnings(raw, smartWallet);
793
924
  }
794
- async getAccountApy(state, chainId, days) {
925
+ async getAccountApy(state, chainId, days, tokenSymbol) {
795
926
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
796
927
  const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
797
- return mapApyHistory(raw, chainId);
928
+ return mapApyHistory(raw, chainId, tokenSymbol);
798
929
  }
799
930
  async getHistory(state, chainId, options) {
800
931
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
@@ -849,6 +980,20 @@ var ZyfaiAgent = class {
849
980
  }
850
981
  return mapUserProfile(raw, this.connectedAddress);
851
982
  }
983
+ async ensureAutoSelectProtocols(state, chainId, asset) {
984
+ await this.connectAuth(state, chainId);
985
+ const current = await this.sdk.getUserDetails(asset);
986
+ const update = buildAutoSelectUpdate(
987
+ {
988
+ autoSelectProtocols: current.autoSelectProtocols,
989
+ chains: current.chains
990
+ },
991
+ asset
992
+ );
993
+ if (!update) return false;
994
+ await this.sdk.updateUserProfile(update);
995
+ return true;
996
+ }
852
997
  // --- IAgent: Discovery (no wallet required) ---
853
998
  async getAgentApy(days, options) {
854
999
  const raw = await this.sdk.getAPYPerStrategy(
@@ -934,8 +1079,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
934
1079
  const tokenBalance = agentBalance?.tokens.find(
935
1080
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
936
1081
  );
937
- if (!tokenBalance)
938
- return { agent, balance: 0n };
1082
+ if (!tokenBalance) return { agent, balance: 0n };
939
1083
  return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
940
1084
  });
941
1085
  }
@@ -952,11 +1096,9 @@ function planProportionalShares(balances, requested, totalAvailable) {
952
1096
  return diff > 0n ? 1 : diff < 0n ? -1 : 0;
953
1097
  });
954
1098
  for (const p of byHeadroom) {
955
- if (remainder === 0n)
956
- break;
1099
+ if (remainder === 0n) break;
957
1100
  const headroom = p.balance - p.planned;
958
- if (headroom <= 0n)
959
- continue;
1101
+ if (headroom <= 0n) continue;
960
1102
  const take = headroom < remainder ? headroom : remainder;
961
1103
  p.planned += take;
962
1104
  remainder -= take;
@@ -985,8 +1127,7 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
985
1127
  (s, c) => s + (c.balance - c.planned),
986
1128
  0n
987
1129
  );
988
- if (totalHeadroom === 0n)
989
- return;
1130
+ if (totalHeadroom === 0n) return;
990
1131
  let distributed = 0n;
991
1132
  for (const c of candidates) {
992
1133
  const headroom = c.balance - c.planned;
@@ -997,11 +1138,9 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
997
1138
  }
998
1139
  let leftover = amount - distributed;
999
1140
  for (const c of candidates) {
1000
- if (leftover === 0n)
1001
- break;
1141
+ if (leftover === 0n) break;
1002
1142
  const headroom = c.balance - c.planned;
1003
- if (headroom <= 0n)
1004
- continue;
1143
+ if (headroom <= 0n) continue;
1005
1144
  const take = headroom < leftover ? headroom : leftover;
1006
1145
  c.planned += take;
1007
1146
  leftover -= take;
@@ -1018,16 +1157,13 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
1018
1157
  for (const id of Object.keys(agentApys)) {
1019
1158
  const cells = agentApys[id].apyByChainAndAsset;
1020
1159
  const balance = agentBalances[id] ?? 0;
1021
- if (!cells || balance <= 0)
1022
- continue;
1160
+ if (!cells || balance <= 0) continue;
1023
1161
  for (const [chainKey, perAsset] of Object.entries(cells)) {
1024
- if (!perAsset)
1025
- continue;
1162
+ if (!perAsset) continue;
1026
1163
  const chainId = Number(chainKey);
1027
1164
  for (const [asset, apyValue] of Object.entries(perAsset)) {
1028
1165
  const apy = Number(apyValue ?? 0);
1029
- if (apy === 0)
1030
- continue;
1166
+ if (apy === 0) continue;
1031
1167
  sums[chainId] ??= {};
1032
1168
  weights[chainId] ??= {};
1033
1169
  sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
@@ -1041,8 +1177,7 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
1041
1177
  const perAssetOut = {};
1042
1178
  for (const asset of Object.keys(sums[chainId])) {
1043
1179
  const w = weights[chainId][asset];
1044
- if (w > 0)
1045
- perAssetOut[asset] = sums[chainId][asset] / w;
1180
+ if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
1046
1181
  }
1047
1182
  if (Object.keys(perAssetOut).length > 0) {
1048
1183
  out[chainId] = perAssetOut;
@@ -1057,7 +1192,7 @@ import {
1057
1192
  createWalletClient,
1058
1193
  custom
1059
1194
  } from "viem";
1060
- import { base as base2, arbitrum as arbitrum2 } from "viem/chains";
1195
+ import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
1061
1196
 
1062
1197
  // src/lib/transfer-auth.ts
1063
1198
  import { bytesToHex } from "viem";
@@ -1103,7 +1238,10 @@ async function postSponsorTransferAuth(input) {
1103
1238
  try {
1104
1239
  res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
1105
1240
  method: "POST",
1106
- 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
+ },
1107
1245
  body: JSON.stringify(input.body)
1108
1246
  });
1109
1247
  } catch (networkError) {
@@ -1123,11 +1261,91 @@ async function postSponsorTransferAuth(input) {
1123
1261
  throw new OwneyError(
1124
1262
  "SPONSOR_REQUEST_FAILED",
1125
1263
  `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1126
- { 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
+ }
1127
1271
  );
1128
1272
  }
1129
1273
  return parsed.data;
1130
1274
  }
1275
+ async function postSponsorPermit2Transfer(input) {
1276
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1277
+ let res;
1278
+ try {
1279
+ res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
1280
+ method: "POST",
1281
+ headers: {
1282
+ "content-type": "application/json",
1283
+ "x-owney-api-key": input.apiKey
1284
+ },
1285
+ body: JSON.stringify(input.body)
1286
+ });
1287
+ } catch (networkError) {
1288
+ throw new OwneyError(
1289
+ "SPONSOR_REQUEST_FAILED",
1290
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1291
+ { cause: String(networkError), safeToFallback: false }
1292
+ );
1293
+ }
1294
+ const text = await res.text();
1295
+ let parsed = null;
1296
+ try {
1297
+ parsed = JSON.parse(text);
1298
+ } catch {
1299
+ }
1300
+ if (!res.ok || !parsed?.success || !parsed.data) {
1301
+ throw new OwneyError(
1302
+ "SPONSOR_REQUEST_FAILED",
1303
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1304
+ {
1305
+ statusCode: res.status,
1306
+ responseBody: text.slice(0, 500),
1307
+ safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
1308
+ }
1309
+ );
1310
+ }
1311
+ return parsed.data;
1312
+ }
1313
+ async function getSponsorRelayerAddress(input) {
1314
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1315
+ let res;
1316
+ try {
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
+ );
1323
+ } catch (networkError) {
1324
+ throw new OwneyError(
1325
+ "SPONSOR_REQUEST_FAILED",
1326
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1327
+ { cause: String(networkError), safeToFallback: true }
1328
+ );
1329
+ }
1330
+ const text = await res.text();
1331
+ let parsed = null;
1332
+ try {
1333
+ parsed = JSON.parse(text);
1334
+ } catch {
1335
+ }
1336
+ if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
1337
+ throw new OwneyError(
1338
+ "SPONSOR_REQUEST_FAILED",
1339
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1340
+ {
1341
+ statusCode: res.status,
1342
+ responseBody: text.slice(0, 500),
1343
+ safeToFallback: true
1344
+ }
1345
+ );
1346
+ }
1347
+ return parsed.data.relayer;
1348
+ }
1131
1349
 
1132
1350
  // src/lib/sponsored-deposit.ts
1133
1351
  var AUTH_WINDOW_SECONDS = 15 * 60;
@@ -1179,6 +1397,114 @@ function makeSponsoredDepositCallback(deps) {
1179
1397
  };
1180
1398
  }
1181
1399
 
1400
+ // src/lib/permit2.ts
1401
+ import { bytesToHex as bytesToHex2 } from "viem";
1402
+ var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
1403
+ var MAX_UINT256 = 2n ** 256n - 1n;
1404
+ var ERC20_ALLOWANCE_ABI = [
1405
+ {
1406
+ type: "function",
1407
+ name: "allowance",
1408
+ stateMutability: "view",
1409
+ inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }],
1410
+ outputs: [{ name: "", type: "uint256" }]
1411
+ },
1412
+ {
1413
+ type: "function",
1414
+ name: "approve",
1415
+ stateMutability: "nonpayable",
1416
+ inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
1417
+ outputs: [{ name: "", type: "bool" }]
1418
+ }
1419
+ ];
1420
+ function buildPermitTransferFromTypedData(input) {
1421
+ return {
1422
+ domain: { name: "Permit2", chainId: input.chainId, verifyingContract: PERMIT2_ADDRESS },
1423
+ types: {
1424
+ PermitTransferFrom: [
1425
+ { name: "permitted", type: "TokenPermissions" },
1426
+ { name: "spender", type: "address" },
1427
+ { name: "nonce", type: "uint256" },
1428
+ { name: "deadline", type: "uint256" }
1429
+ ],
1430
+ TokenPermissions: [
1431
+ { name: "token", type: "address" },
1432
+ { name: "amount", type: "uint256" }
1433
+ ]
1434
+ },
1435
+ primaryType: "PermitTransferFrom",
1436
+ message: input.message
1437
+ };
1438
+ }
1439
+ function randomPermit2Nonce() {
1440
+ const bytes = new Uint8Array(32);
1441
+ globalThis.crypto.getRandomValues(bytes);
1442
+ return BigInt(bytesToHex2(bytes));
1443
+ }
1444
+ async function readPermit2Allowance(publicClient, token, owner) {
1445
+ return publicClient.readContract({
1446
+ address: token,
1447
+ abi: ERC20_ALLOWANCE_ABI,
1448
+ functionName: "allowance",
1449
+ args: [owner, PERMIT2_ADDRESS]
1450
+ });
1451
+ }
1452
+
1453
+ // src/lib/sponsored-weth-deposit.ts
1454
+ var PERMIT_WINDOW_SECONDS = 15 * 60;
1455
+ function makeSponsoredWethCallback(deps) {
1456
+ const get = deps.httpGet ?? getSponsorRelayerAddress;
1457
+ const post = deps.httpPost ?? postSponsorPermit2Transfer;
1458
+ return async (smartWallet, chainId, amount) => {
1459
+ const cid = chainId;
1460
+ const token = deps.tokenAddressByChain[cid];
1461
+ if (!token) {
1462
+ throw new OwneyError("CHAIN_UNSUPPORTED", `No sponsored WETH configured for chain ${chainId}`);
1463
+ }
1464
+ const amountWei = BigInt(amount);
1465
+ const pub = deps.getPublicClient(cid);
1466
+ const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
1467
+ if (allowance < amountWei) {
1468
+ throw new OwneyError(
1469
+ "PERMIT2_APPROVAL_REQUIRED",
1470
+ "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
1471
+ { token, chainId: cid, allowance: allowance.toString(), amount }
1472
+ );
1473
+ }
1474
+ const relayer = await get({ baseUrl: deps.baseUrl, apiKey: deps.apiKey, chainId: cid });
1475
+ const nonce = randomPermit2Nonce();
1476
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS);
1477
+ const typedData = buildPermitTransferFromTypedData({
1478
+ chainId: cid,
1479
+ message: {
1480
+ permitted: { token, amount: amountWei },
1481
+ spender: relayer,
1482
+ nonce,
1483
+ deadline
1484
+ }
1485
+ });
1486
+ const wallet = deps.getWalletClient(cid);
1487
+ const signature = await wallet.signTypedData(
1488
+ { account: deps.ownerAddress, ...typedData }
1489
+ );
1490
+ const result = await post({
1491
+ baseUrl: deps.baseUrl,
1492
+ apiKey: deps.apiKey,
1493
+ body: {
1494
+ chainId: cid,
1495
+ token,
1496
+ from: deps.ownerAddress,
1497
+ to: smartWallet,
1498
+ amount,
1499
+ nonce: nonce.toString(),
1500
+ deadline: deadline.toString(),
1501
+ signature
1502
+ }
1503
+ });
1504
+ return result.txHash;
1505
+ };
1506
+ }
1507
+
1182
1508
  // src/client.ts
1183
1509
  function encodeMultiAgentCursor(map) {
1184
1510
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
@@ -1202,13 +1528,22 @@ function decodeMultiAgentCursor(token) {
1202
1528
  }
1203
1529
  var SPONSORED_USDC_BY_CHAIN = {
1204
1530
  8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1205
- 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
1531
+ 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
1532
+ 1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
1206
1533
  };
1207
1534
  var VIEM_CHAIN2 = {
1208
1535
  8453: base2,
1209
- 42161: arbitrum2
1536
+ 42161: arbitrum2,
1537
+ 1: mainnet2
1538
+ };
1539
+ var SPONSORED_WETH_BY_CHAIN = {
1540
+ 8453: "0x4200000000000000000000000000000000000006",
1541
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
1542
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
1210
1543
  };
1211
- var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
1544
+ function shouldFallbackToUserPaid(error, asset, appCallback) {
1545
+ return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
1546
+ }
1212
1547
  var OwneySDK = class {
1213
1548
  agents = /* @__PURE__ */ new Map();
1214
1549
  activeAgents = /* @__PURE__ */ new Set();
@@ -1221,11 +1556,14 @@ var OwneySDK = class {
1221
1556
  state = null;
1222
1557
  apiKey;
1223
1558
  zyfaiRpcUrls;
1559
+ routingApiBaseUrl;
1224
1560
  cachedSponsoredCallback = null;
1561
+ cachedWethSponsoredCallback = null;
1225
1562
  initializingAgentsPromise = null;
1226
1563
  constructor(config) {
1227
1564
  this.apiKey = config.apiKey;
1228
1565
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
1566
+ this.routingApiBaseUrl = config.routingApiBaseUrl;
1229
1567
  }
1230
1568
  /**
1231
1569
  * Establish connection state for all subsequent SDK calls.
@@ -1246,6 +1584,7 @@ var OwneySDK = class {
1246
1584
  }
1247
1585
  this.state = { provider, walletAddress, chainId: null };
1248
1586
  this.cachedSponsoredCallback = null;
1587
+ this.cachedWethSponsoredCallback = null;
1249
1588
  }
1250
1589
  /**
1251
1590
  * Disconnect from all agents and clear connection state.
@@ -1259,6 +1598,7 @@ var OwneySDK = class {
1259
1598
  this.disabledAgents.clear();
1260
1599
  this.state = null;
1261
1600
  this.cachedSponsoredCallback = null;
1601
+ this.cachedWethSponsoredCallback = null;
1262
1602
  }
1263
1603
  isAgentDisabled(agentId) {
1264
1604
  return this.disabledAgents.has(agentId);
@@ -1278,8 +1618,7 @@ var OwneySDK = class {
1278
1618
  return this.state?.chainId ?? null;
1279
1619
  }
1280
1620
  requireState() {
1281
- if (!this.state)
1282
- throw new NotConnectedError();
1621
+ if (!this.state) throw new NotConnectedError();
1283
1622
  return this.state;
1284
1623
  }
1285
1624
  requireChainId() {
@@ -1309,12 +1648,12 @@ var OwneySDK = class {
1309
1648
  * `TransferWithAuthorization`, then POSTs to the sponsor API.
1310
1649
  */
1311
1650
  getDefaultSponsoredCallback() {
1312
- if (this.cachedSponsoredCallback)
1313
- return this.cachedSponsoredCallback;
1651
+ if (this.cachedSponsoredCallback) return this.cachedSponsoredCallback;
1314
1652
  const provider = this.requireConnectedProvider();
1315
1653
  const owner = this.state.walletAddress;
1316
1654
  this.cachedSponsoredCallback = makeSponsoredDepositCallback({
1317
1655
  apiKey: this.apiKey,
1656
+ baseUrl: this.routingApiBaseUrl,
1318
1657
  ownerAddress: owner,
1319
1658
  tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
1320
1659
  // Casts work around viem's chain-narrowed Client vs the generic
@@ -1332,6 +1671,37 @@ var OwneySDK = class {
1332
1671
  });
1333
1672
  return this.cachedSponsoredCallback;
1334
1673
  }
1674
+ /**
1675
+ * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
1676
+ * callback used when the caller omits `depositCallback` for a WETH
1677
+ * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
1678
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
1679
+ */
1680
+ getDefaultWethSponsoredCallback() {
1681
+ if (this.cachedWethSponsoredCallback)
1682
+ return this.cachedWethSponsoredCallback;
1683
+ const provider = this.requireConnectedProvider();
1684
+ const owner = this.state.walletAddress;
1685
+ this.cachedWethSponsoredCallback = makeSponsoredWethCallback({
1686
+ apiKey: this.apiKey,
1687
+ baseUrl: this.routingApiBaseUrl,
1688
+ ownerAddress: owner,
1689
+ tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
1690
+ // Casts work around viem's chain-narrowed Client vs the generic
1691
+ // PublicClient/WalletClient param types — structurally identical at
1692
+ // runtime, but the two share a name TS treats as unrelated.
1693
+ getPublicClient: (cid) => createPublicClient2({
1694
+ chain: VIEM_CHAIN2[cid],
1695
+ transport: custom(provider)
1696
+ }),
1697
+ getWalletClient: (cid) => createWalletClient({
1698
+ account: owner,
1699
+ chain: VIEM_CHAIN2[cid],
1700
+ transport: custom(provider)
1701
+ })
1702
+ });
1703
+ return this.cachedWethSponsoredCallback;
1704
+ }
1335
1705
  getAgent(agentId) {
1336
1706
  const agent = this.agents.get(agentId);
1337
1707
  if (!agent) {
@@ -1343,22 +1713,19 @@ var OwneySDK = class {
1343
1713
  const active = /* @__PURE__ */ new Map();
1344
1714
  for (const id of this.activeAgents) {
1345
1715
  const agent = this.agents.get(id);
1346
- if (agent)
1347
- active.set(id, agent);
1716
+ if (agent) active.set(id, agent);
1348
1717
  }
1349
1718
  return active;
1350
1719
  }
1351
1720
  async ensureAgentsInitialized() {
1352
- if (this.agents.size > 0)
1353
- return;
1721
+ if (this.agents.size > 0) return;
1354
1722
  if (!this.initializingAgentsPromise) {
1355
1723
  this.initializingAgentsPromise = (async () => {
1356
1724
  const agentKeys = await fetchAgentKeys(this.apiKey);
1357
1725
  this.disabledAgents.clear();
1358
- for (const { key, agent_type, project_name, is_enabled } of agentKeys) {
1359
- const agent = await this.createAgent(agent_type, key, project_name);
1360
- if (!agent)
1361
- continue;
1726
+ for (const { key, agent_type, is_enabled } of agentKeys) {
1727
+ const agent = this.createAgent(agent_type, key);
1728
+ if (!agent) continue;
1362
1729
  this.agents.set(agent_type, agent);
1363
1730
  if (is_enabled === false) {
1364
1731
  this.disabledAgents.add(agent_type);
@@ -1378,16 +1745,10 @@ var OwneySDK = class {
1378
1745
  this.initializingAgentsPromise = null;
1379
1746
  }
1380
1747
  }
1381
- async createAgent(agentId, key, projectName) {
1748
+ createAgent(agentId, key) {
1382
1749
  if (agentId === "zyfai") {
1383
1750
  return new ZyfaiAgent(key, this.zyfaiRpcUrls);
1384
1751
  }
1385
- if (agentId === "surfliquid") {
1386
- if (!projectName)
1387
- return null;
1388
- const { SurfLiquidAgent } = await import("./surfliquid.agent-EPFISJPR.js");
1389
- return new SurfLiquidAgent({ projectName, appId: key });
1390
- }
1391
1752
  return null;
1392
1753
  }
1393
1754
  // --- Account lifecycle ---
@@ -1491,7 +1852,7 @@ var OwneySDK = class {
1491
1852
  }
1492
1853
  const state = this.requireState();
1493
1854
  const chainId = this.requireChainId();
1494
- const effectiveCallback = depositCallback ?? this.getDefaultSponsoredCallback();
1855
+ const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback() : this.getDefaultSponsoredCallback());
1495
1856
  if (agentId) {
1496
1857
  if (this.isAgentDisabled(agentId)) {
1497
1858
  throw new OwneyError(
@@ -1503,10 +1864,14 @@ var OwneySDK = class {
1503
1864
  const agent = this.getAgent(agentId);
1504
1865
  this.validateAssetSupport(agent, chainId, asset);
1505
1866
  await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
1506
- return withFailureReporting(
1507
- this.apiKey,
1508
- agent.id,
1509
- () => agent.deposit(state, chainId, amount, effectiveCallback)
1867
+ return this.depositWithFallback(
1868
+ agent,
1869
+ state,
1870
+ chainId,
1871
+ amount,
1872
+ asset,
1873
+ effectiveCallback,
1874
+ depositCallback
1510
1875
  );
1511
1876
  }
1512
1877
  const totalAmount = BigInt(amount);
@@ -1536,26 +1901,77 @@ var OwneySDK = class {
1536
1901
  );
1537
1902
  }
1538
1903
  const agentResults = {};
1539
- const agentErrors = {};
1540
1904
  for (const { agent, amount: agentAmount } of agentAmounts) {
1905
+ agentResults[agent.id] = await this.depositWithFallback(
1906
+ agent,
1907
+ state,
1908
+ chainId,
1909
+ agentAmount,
1910
+ asset,
1911
+ effectiveCallback,
1912
+ depositCallback
1913
+ );
1914
+ }
1915
+ return { agentResults };
1916
+ }
1917
+ /**
1918
+ * Invokes `agent.deposit` with the resolved sponsored callback, composing
1919
+ * two independent auto-recovery mechanisms:
1920
+ *
1921
+ * 1. Missing Permit2 allowance: when the app did not supply its own
1922
+ * callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
1923
+ * WETH deposit, this is the wallet's first gasless WETH deposit. We send
1924
+ * the one-time (user-paid) Permit2 approval via `approvePermit2()` and
1925
+ * retry the SAME sponsored attempt once. Bounded to one approval attempt
1926
+ * per call so a wallet/agent that keeps reporting the allowance as
1927
+ * missing can't loop forever. If `approvePermit2()` itself throws (e.g.
1928
+ * the user rejects the wallet prompt), that error propagates as-is —
1929
+ * the user said no to a transaction, so we must not turn around and ask
1930
+ * them to pay for a different one via the fallback below.
1931
+ * 2. `shouldFallbackToUserPaid`: the SDK's own WETH sponsor path failed at
1932
+ * the infrastructure layer in a provably safe-to-retry way, so we warn
1933
+ * and retry once with `undefined`, falling through to a user-paid
1934
+ * native deposit. This still applies after a successful Permit2
1935
+ * approval retry (the retried sponsored attempt can itself hit a safe
1936
+ * sponsor failure).
1937
+ *
1938
+ * Any other error — an app-supplied callback's own failure in particular —
1939
+ * propagates unchanged. Scoped per agent invocation so one agent's failure
1940
+ * doesn't force a retry of sibling agents in a multi-agent split.
1941
+ */
1942
+ async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback) {
1943
+ const attempt = (callback) => withFailureReporting(
1944
+ this.apiKey,
1945
+ agent.id,
1946
+ () => agent.deposit(
1947
+ state,
1948
+ chainId,
1949
+ amount,
1950
+ asset,
1951
+ callback
1952
+ )
1953
+ );
1954
+ let approvalAttempted = false;
1955
+ for (; ; ) {
1541
1956
  try {
1542
- agentResults[agent.id] = await withFailureReporting(
1543
- this.apiKey,
1544
- agent.id,
1545
- () => agent.deposit(state, chainId, agentAmount, effectiveCallback)
1546
- );
1957
+ return await attempt(effectiveCallback);
1547
1958
  } catch (error) {
1548
- agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
1959
+ if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
1960
+ approvalAttempted = true;
1961
+ console.warn(
1962
+ "[owney-sdk] First WETH deposit: sending one-time Permit2 approval..."
1963
+ );
1964
+ await this.approvePermit2();
1965
+ continue;
1966
+ }
1967
+ if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
1968
+ console.warn(
1969
+ `[owney-sdk] ${asset} sponsorship unavailable, falling back to user-paid deposit:`,
1970
+ error
1971
+ );
1972
+ return attempt(void 0);
1549
1973
  }
1550
1974
  }
1551
- if (Object.keys(agentResults).length === 0) {
1552
- throw new OwneyError(
1553
- "DEPOSIT_ALL_FAILED",
1554
- `All ${agentAmounts.length} agent deposit(s) failed.`,
1555
- { agentErrors }
1556
- );
1557
- }
1558
- return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
1559
1975
  }
1560
1976
  getMinDepositAmount(agent, chainId, asset) {
1561
1977
  const chainAssets = agent.supportedAssets.find(
@@ -1565,8 +1981,7 @@ var OwneySDK = class {
1565
1981
  return BigInt(assetEntry?.minDepositAmount ?? "0");
1566
1982
  }
1567
1983
  splitDepositAmount(totalAmount, agents, chainId, asset, exempt = /* @__PURE__ */ new Set()) {
1568
- if (agents.length === 0)
1569
- return [];
1984
+ if (agents.length === 0) return [];
1570
1985
  const perAgent = totalAmount / BigInt(agents.length);
1571
1986
  const remainder = totalAmount % BigInt(agents.length);
1572
1987
  const splits = agents.map((agent, i) => ({
@@ -1590,10 +2005,8 @@ var OwneySDK = class {
1590
2005
  async validateMinDepositAmount(agent, state, chainId, asset, amount) {
1591
2006
  const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
1592
2007
  const parsedAmount = BigInt(amount);
1593
- if (parsedAmount >= minDepositAmount)
1594
- return;
1595
- if (await this.hasExistingBalance(agent, state, chainId, asset))
1596
- return;
2008
+ if (parsedAmount >= minDepositAmount) return;
2009
+ if (await this.hasExistingBalance(agent, state, chainId, asset)) return;
1597
2010
  throw new OwneyError(
1598
2011
  "DEPOSIT_AMOUNT_BELOW_MINIMUM",
1599
2012
  `Amount "${amount}" is below the required minimum deposit "${minDepositAmount.toString()}" for agent "${agent.id}" on chain ${chainId} for asset "${asset}".`,
@@ -1656,34 +2069,6 @@ var OwneySDK = class {
1656
2069
  }
1657
2070
  return eligible;
1658
2071
  }
1659
- /**
1660
- * Agent ids the routing API provisioned for this org that support the given
1661
- * chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
1662
- * surfliquid first). Returns `[]` when the org has no compatible agent — never
1663
- * throws on an empty org. Loads agent keys on first call (apiKey only, no
1664
- * wallet), so the UI can resolve which agent to use before the user connects.
1665
- *
1666
- * This is the source of truth for agent availability: an agent appears here
1667
- * iff the routing API returned its key. No per-app feature flags.
1668
- */
1669
- async getEligibleAgentIds(chainId, asset) {
1670
- try {
1671
- await this.ensureAgentsInitialized();
1672
- } catch (error) {
1673
- if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
1674
- return [];
1675
- }
1676
- throw error;
1677
- }
1678
- return [...this.agents.values()].filter((agent) => {
1679
- const chainAssets = agent.supportedAssets.find(
1680
- (sa) => sa.chainId === chainId
1681
- );
1682
- return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
1683
- }).map((agent) => agent.id).sort(
1684
- (a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
1685
- );
1686
- }
1687
2072
  // --- Fund operations ---
1688
2073
  /**
1689
2074
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
@@ -1719,17 +2104,9 @@ var OwneySDK = class {
1719
2104
  }
1720
2105
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1721
2106
  if (!amount) {
1722
- const aggregated2 = await this.getBalances();
1723
- const funded = projectAgentBalancesForAsset(
1724
- eligibleAgents,
1725
- aggregated2.agentBalances,
1726
- chainId,
1727
- asset,
1728
- assetInfo.decimals
1729
- ).filter((b) => b.balance > 0n);
1730
2107
  const results2 = {};
1731
2108
  const agentErrors2 = {};
1732
- for (const { agent } of funded) {
2109
+ for (const agent of eligibleAgents) {
1733
2110
  try {
1734
2111
  results2[agent.id] = await agent.withdraw(state, chainId, token);
1735
2112
  } catch (err) {
@@ -1746,10 +2123,9 @@ var OwneySDK = class {
1746
2123
  { asset, agentErrors: agentErrors2 }
1747
2124
  );
1748
2125
  }
1749
- const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
1750
2126
  return {
1751
2127
  agentResult: results2,
1752
- totalWithdrawn: totalWithdrawn.toString()
2128
+ totalWithdrawn: sumWithdrawnAmount(results2).toString()
1753
2129
  };
1754
2130
  }
1755
2131
  const requested = BigInt(amount);
@@ -1794,8 +2170,7 @@ var OwneySDK = class {
1794
2170
  const agentErrors = {};
1795
2171
  for (let i = 0; i < plans.length; i++) {
1796
2172
  const p = plans[i];
1797
- if (p.planned === 0n)
1798
- continue;
2173
+ if (p.planned === 0n) continue;
1799
2174
  try {
1800
2175
  results[p.agent.id] = await p.agent.withdraw(
1801
2176
  state,
@@ -1929,19 +2304,30 @@ var OwneySDK = class {
1929
2304
  */
1930
2305
  async getAccountApy({
1931
2306
  agentId,
1932
- days
2307
+ days,
2308
+ tokenSymbol
1933
2309
  }) {
1934
2310
  const state = this.requireState();
1935
2311
  const chainId = this.requireChainId();
1936
2312
  if (agentId) {
1937
- return this.getAgent(agentId).getAccountApy(state, chainId, days);
2313
+ return this.getAgent(agentId).getAccountApy(
2314
+ state,
2315
+ chainId,
2316
+ days,
2317
+ tokenSymbol
2318
+ );
1938
2319
  }
1939
2320
  const activeAgents = this.getActiveAgents();
1940
2321
  const entries = [...activeAgents.entries()];
1941
2322
  const [apyResults, balanceResults] = await Promise.all([
1942
2323
  Promise.all(
1943
2324
  entries.map(async ([id, agent]) => {
1944
- const apy = await agent.getAccountApy(state, chainId, days);
2325
+ const apy = await agent.getAccountApy(
2326
+ state,
2327
+ chainId,
2328
+ days,
2329
+ tokenSymbol
2330
+ );
1945
2331
  return [id, apy];
1946
2332
  })
1947
2333
  ),
@@ -1964,15 +2350,13 @@ var OwneySDK = class {
1964
2350
  let relevantBalance = 0;
1965
2351
  for (const id of Object.keys(results)) {
1966
2352
  const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
1967
- if (agentApy === 0)
1968
- continue;
2353
+ if (agentApy === 0) continue;
1969
2354
  relevantBalance += balances[id];
1970
2355
  }
1971
2356
  if (relevantBalance > 0) {
1972
2357
  for (const id of Object.keys(results)) {
1973
2358
  const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
1974
- if (agentApy === 0)
1975
- continue;
2359
+ if (agentApy === 0) continue;
1976
2360
  const weight = balances[id] / relevantBalance;
1977
2361
  totalApy += agentApy * weight;
1978
2362
  }
@@ -2027,8 +2411,7 @@ var OwneySDK = class {
2027
2411
  const nextCursorMap = {};
2028
2412
  let hasMore = false;
2029
2413
  for (const { agentId: id, page } of perAgent) {
2030
- if (!page)
2031
- continue;
2414
+ if (!page) continue;
2032
2415
  merged.push(...page.data);
2033
2416
  if (page.hasMore && page.nextCursor) {
2034
2417
  nextCursorMap[id] = page.nextCursor;
@@ -2037,16 +2420,12 @@ var OwneySDK = class {
2037
2420
  }
2038
2421
  merged.sort((a, b) => {
2039
2422
  const dateDiff = new Date(b.date).getTime() - new Date(a.date).getTime();
2040
- if (dateDiff !== 0)
2041
- return dateDiff;
2042
- if (a.agent !== b.agent)
2043
- 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;
2044
2425
  const aTx = a.transactions?.[0]?.txHashes?.[0] ?? "";
2045
2426
  const bTx = b.transactions?.[0]?.txHashes?.[0] ?? "";
2046
- if (aTx !== bTx)
2047
- return aTx < bTx ? -1 : 1;
2048
- if (a.action !== b.action)
2049
- 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;
2050
2429
  return 0;
2051
2430
  });
2052
2431
  return {
@@ -2081,6 +2460,67 @@ var OwneySDK = class {
2081
2460
  agentUserProfile: results
2082
2461
  };
2083
2462
  }
2463
+ /**
2464
+ * Ensure the given asset uses backend protocol auto-selection for the active
2465
+ * chain's account. No-op (returns false) for agents that don't support it.
2466
+ * @param asset - "USDC" or "WETH".
2467
+ * @param agentId - Agent to target. Defaults to "zyfai".
2468
+ * @returns whether a profile write occurred.
2469
+ */
2470
+ async ensureAutoSelectProtocols(asset, agentId = "zyfai") {
2471
+ const state = this.requireState();
2472
+ const chainId = this.requireChainId();
2473
+ const agent = this.getAgent(agentId);
2474
+ if (typeof agent.ensureAutoSelectProtocols !== "function") return false;
2475
+ return agent.ensureAutoSelectProtocols(state, chainId, asset);
2476
+ }
2477
+ /**
2478
+ * One-time, user-paid approval of Permit2 on the sponsored WETH token for
2479
+ * the active chain. Required once per wallet per chain before gasless WETH
2480
+ * deposits; afterwards deposit() is signature-only. Resolves only after the
2481
+ * approval transaction is mined (1 confirmation), so a subsequent deposit()
2482
+ * will see the new allowance; throws if the transaction reverted.
2483
+ * @returns the approval transaction hash.
2484
+ */
2485
+ async approvePermit2(asset = "WETH") {
2486
+ void asset;
2487
+ const state = this.requireState();
2488
+ const chainId = this.requireChainId();
2489
+ this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
2490
+ const token = SPONSORED_WETH_BY_CHAIN[chainId];
2491
+ if (!token) {
2492
+ throw new OwneyError(
2493
+ "CHAIN_UNSUPPORTED",
2494
+ `No sponsored WETH on chain ${chainId}`
2495
+ );
2496
+ }
2497
+ const provider = this.requireConnectedProvider();
2498
+ const wallet = createWalletClient({
2499
+ account: state.walletAddress,
2500
+ chain: VIEM_CHAIN2[chainId],
2501
+ transport: custom(provider)
2502
+ });
2503
+ const hash = await wallet.writeContract({
2504
+ address: token,
2505
+ abi: ERC20_ALLOWANCE_ABI,
2506
+ functionName: "approve",
2507
+ args: [PERMIT2_ADDRESS, MAX_UINT256],
2508
+ account: state.walletAddress,
2509
+ chain: VIEM_CHAIN2[chainId]
2510
+ });
2511
+ const publicClient = createPublicClient2({
2512
+ chain: VIEM_CHAIN2[chainId],
2513
+ transport: custom(provider)
2514
+ });
2515
+ const receipt = await publicClient.waitForTransactionReceipt({
2516
+ hash,
2517
+ confirmations: 1
2518
+ });
2519
+ if (receipt.status !== "success") {
2520
+ throw new Error(`Permit2 approval reverted (tx ${hash})`);
2521
+ }
2522
+ return hash;
2523
+ }
2084
2524
  // --- Discovery (no wallet required) ---
2085
2525
  /**
2086
2526
  * Get the agent's average APY performance over a time period. Does not require a wallet connection.
@@ -2186,8 +2626,7 @@ import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
2186
2626
  // src/agents/zyfai/zyfai.siwx-cache.ts
2187
2627
  var KEY_PREFIX2 = "owney.siwx.session";
2188
2628
  var storage2 = () => {
2189
- if (typeof window === "undefined")
2190
- return null;
2629
+ if (typeof window === "undefined") return null;
2191
2630
  try {
2192
2631
  return window.localStorage;
2193
2632
  } catch {
@@ -2197,11 +2636,9 @@ var storage2 = () => {
2197
2636
  var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2198
2637
  var readSiwxSession = (address, chainId) => {
2199
2638
  const store = storage2();
2200
- if (!store)
2201
- return null;
2639
+ if (!store) return null;
2202
2640
  const raw = store.getItem(buildKey2(address, chainId));
2203
- if (!raw)
2204
- return null;
2641
+ if (!raw) return null;
2205
2642
  try {
2206
2643
  return JSON.parse(raw);
2207
2644
  } catch {
@@ -2211,8 +2648,7 @@ var readSiwxSession = (address, chainId) => {
2211
2648
  };
2212
2649
  var writeSiwxSession = (address, chainId, session) => {
2213
2650
  const store = storage2();
2214
- if (!store)
2215
- return;
2651
+ if (!store) return;
2216
2652
  try {
2217
2653
  store.setItem(buildKey2(address, chainId), JSON.stringify(session));
2218
2654
  } catch {
@@ -2220,8 +2656,7 @@ var writeSiwxSession = (address, chainId, session) => {
2220
2656
  };
2221
2657
  var clearSiwxSession = (address, chainId) => {
2222
2658
  const store = storage2();
2223
- if (!store)
2224
- return;
2659
+ if (!store) return;
2225
2660
  try {
2226
2661
  store.removeItem(buildKey2(address, chainId));
2227
2662
  } catch {
@@ -2284,12 +2719,10 @@ function buildSIWXConfig(deps) {
2284
2719
  const jwt = readSession(address, id);
2285
2720
  const stored = readSiwxSession(address, id);
2286
2721
  if (!jwt) {
2287
- if (stored)
2288
- clearSiwxSession(address, id);
2722
+ if (stored) clearSiwxSession(address, id);
2289
2723
  return [];
2290
2724
  }
2291
- if (!stored)
2292
- return [];
2725
+ if (!stored) return [];
2293
2726
  const session = {
2294
2727
  // `stored.data.chainId` is a plain string in the cache (kept AppKit-free);
2295
2728
  // it always holds a valid CaipNetworkId here, so narrow at the boundary.