@owney/sdk 0.6.7-beta.1 → 0.6.7-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.cjs CHANGED
@@ -3,14 +3,6 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __esm = (fn, res, err) => function __init() {
7
- if (err) throw err[0];
8
- try {
9
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
- } catch (e) {
11
- throw err = [e], e;
12
- }
13
- };
14
6
  var __export = (target, all) => {
15
7
  for (var name in all)
16
8
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -25,478 +17,6 @@ var __copyProps = (to, from, except, desc) => {
25
17
  };
26
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
19
 
28
- // src/errors.ts
29
- var OwneyError, AgentNotFoundError, NotConnectedError, AgentChainIncompatibleError;
30
- var init_errors = __esm({
31
- "src/errors.ts"() {
32
- "use strict";
33
- OwneyError = class extends Error {
34
- code;
35
- details;
36
- agentId;
37
- constructor(code, message, details, agentId) {
38
- const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
39
- super(`${prefix} ${message}`);
40
- this.name = "OwneyError";
41
- this.code = code;
42
- this.details = details;
43
- this.agentId = agentId;
44
- }
45
- };
46
- AgentNotFoundError = class extends OwneyError {
47
- constructor(agentId, available) {
48
- super(
49
- "AGENT_NOT_FOUND",
50
- `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
51
- { agentId, available },
52
- agentId
53
- );
54
- this.name = "AgentNotFoundError";
55
- }
56
- };
57
- NotConnectedError = class extends OwneyError {
58
- constructor() {
59
- super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
60
- this.name = "NotConnectedError";
61
- }
62
- };
63
- AgentChainIncompatibleError = class extends OwneyError {
64
- incompatibleAgents;
65
- connectedChainId;
66
- constructor(incompatibleAgents, connectedChainId) {
67
- const details = incompatibleAgents.map(
68
- ({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
69
- ).join("; ");
70
- super(
71
- "AGENT_CHAIN_INCOMPATIBLE",
72
- `Chain ${connectedChainId} is not supported by the following agents: ${details}`,
73
- { incompatibleAgents, connectedChainId }
74
- );
75
- this.name = "AgentChainIncompatibleError";
76
- this.incompatibleAgents = incompatibleAgents;
77
- this.connectedChainId = connectedChainId;
78
- }
79
- };
80
- }
81
- });
82
-
83
- // src/agents/surfliquid/surfliquid.constants.ts
84
- var SURFLIQUID_CHAIN_ID, SURFLIQUID_CHAIN_NAME, SURFLIQUID_USDC_ADDRESS, SURFLIQUID_USDC_DECIMALS, SURFLIQUID_MIN_DEPOSIT, SURFLIQUID_SUPPORTED_ASSETS, SURFLIQUID_ACTION_MAP;
85
- var init_surfliquid_constants = __esm({
86
- "src/agents/surfliquid/surfliquid.constants.ts"() {
87
- "use strict";
88
- SURFLIQUID_CHAIN_ID = 8453;
89
- SURFLIQUID_CHAIN_NAME = "BASE";
90
- SURFLIQUID_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
91
- SURFLIQUID_USDC_DECIMALS = 6;
92
- SURFLIQUID_MIN_DEPOSIT = "0";
93
- SURFLIQUID_SUPPORTED_ASSETS = [
94
- {
95
- chainId: SURFLIQUID_CHAIN_ID,
96
- chain: SURFLIQUID_CHAIN_NAME,
97
- assets: [{ symbol: "USDC", minDepositAmount: SURFLIQUID_MIN_DEPOSIT }]
98
- }
99
- ];
100
- SURFLIQUID_ACTION_MAP = {
101
- // INITIAL_DEPOSIT is the user's real first deposit, so surface it like any
102
- // other funding ("Top up"). The mini-app intentionally hides the "Deposit"
103
- // action (it marks Zyfai's smart-account-creation event), which would
104
- // otherwise drop a SurfLiquid user's only history row.
105
- INITIAL_DEPOSIT: "Top up",
106
- DEPOSIT: "Top up",
107
- USER_DEPOSIT: "Top up",
108
- WITHDRAWAL: "Withdraw",
109
- USER_WITHDRAWAL: "Withdraw",
110
- REBALANCE: "Rebalance",
111
- REBALANCE_COMPLETED: "Rebalance",
112
- CROSS_CHAIN_REBALANCE: "Rebalance",
113
- MERKL_CLAIM: "Earned"
114
- };
115
- }
116
- });
117
-
118
- // src/agents/surfliquid/surfliquid.mapper.ts
119
- function mapMessage(message) {
120
- const action = SURFLIQUID_ACTION_MAP[message.transactionType];
121
- if (!action) return null;
122
- return {
123
- agent: "surfliquid",
124
- action,
125
- date: message.timestamp,
126
- oldApy: message.apyBefore != null ? String(message.apyBefore) : null,
127
- newApy: message.apyAfter != null ? String(message.apyAfter) : null,
128
- transactions: [
129
- {
130
- txHashes: [message.txHash],
131
- chainId: message.chainId,
132
- tokenSymbol: message.token ?? void 0,
133
- amount: message.amount != null ? String(message.amount) : void 0
134
- }
135
- ],
136
- rebalanceLog: []
137
- };
138
- }
139
- function mapHistory(result, _chainId) {
140
- const data = result.messages.map(mapMessage).filter((entry) => entry !== null);
141
- const hasMore = result.page < result.pages;
142
- return {
143
- data,
144
- hasMore,
145
- nextCursor: hasMore ? String(result.page + 1) : void 0
146
- };
147
- }
148
- function mapBalances2(vault, chainId, morphoStats) {
149
- const assets = (vault.assets ?? []).filter(
150
- (asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId
151
- );
152
- const tokens = assets.map((asset) => ({
153
- chain: SURFLIQUID_CHAIN_NAME,
154
- chainId: SURFLIQUID_CHAIN_ID,
155
- asset: "USDC",
156
- amount: String(asset.currentValueUSD)
157
- }));
158
- const positions = assets.map((asset) => {
159
- const stats = morphoStats?.get(asset.morphoVaultAddress?.toLowerCase() ?? "");
160
- return {
161
- chain: SURFLIQUID_CHAIN_NAME,
162
- protocol: "Morpho",
163
- // The specific MetaMorpho vault (e.g. "Gauntlet USDC Frontier"), read from
164
- // Morpho — renders as "Morpho <vault>" alongside zyfai's vault detail.
165
- pool: stats?.name ?? void 0,
166
- asset: "USDC",
167
- amount: String(asset.currentValueUSD),
168
- apy: asset.currentAPY,
169
- // TVL + liquidity come straight from Morpho (SurfLiquid's API omits them).
170
- tvl: stats?.tvlUsd,
171
- liquidity: stats?.liquidityUsd
172
- };
173
- });
174
- const total = assets.reduce(
175
- (sum, asset) => sum + asset.currentValueUSD,
176
- 0
177
- );
178
- return {
179
- smartWallet: vault.userVaultAddress ?? void 0,
180
- totalBalance: String(total),
181
- totalBalanceAsset: "usdc",
182
- tokens,
183
- positions
184
- };
185
- }
186
- function mapAccountApy(vault, days) {
187
- const breakdown = vault.apyBreakdown;
188
- const windowed = breakdown ? breakdown[APY_WINDOW_KEY[days]] : void 0;
189
- const apy = windowed != null ? windowed : breakdown?.currentAPY ?? 0;
190
- return {
191
- walletAddress: vault.walletAddress ?? "",
192
- weightedApyAfterFee: apy,
193
- apyByChainAndAsset: { [SURFLIQUID_CHAIN_ID]: { USDC: apy } },
194
- // Parity with Sail: SurfLiquid's core-sdk exposes no daily net-APY series.
195
- history: []
196
- };
197
- }
198
- function mapEarnings2(vault, vaultAddress) {
199
- return {
200
- smartWallet: vaultAddress,
201
- lifetimeEarnings: vault.earned?.totalEarningsUSD ?? 0,
202
- tokens: []
203
- };
204
- }
205
- function mapUserProfile2(vault, address) {
206
- return {
207
- address,
208
- smartWallet: vault.userVaultAddress ?? "",
209
- chains: [SURFLIQUID_CHAIN_ID],
210
- // SurfLiquid uses cookie-based session auth — no session-key concept.
211
- hasActiveSessionKey: false,
212
- protocols: ["SurfLiquid"]
213
- };
214
- }
215
- var APY_WINDOW_KEY;
216
- var init_surfliquid_mapper = __esm({
217
- "src/agents/surfliquid/surfliquid.mapper.ts"() {
218
- "use strict";
219
- init_surfliquid_constants();
220
- APY_WINDOW_KEY = {
221
- "7D": "apy7d",
222
- "14D": "apy14d",
223
- "30D": "apy30d"
224
- };
225
- }
226
- });
227
-
228
- // src/agents/surfliquid/surfliquid.wallet-adapter.ts
229
- var import_core_sdk, OwneyWalletAdapter;
230
- var init_surfliquid_wallet_adapter = __esm({
231
- "src/agents/surfliquid/surfliquid.wallet-adapter.ts"() {
232
- "use strict";
233
- import_core_sdk = require("@surf_liquid/core-sdk");
234
- OwneyWalletAdapter = class extends import_core_sdk.BaseWalletAdapter {
235
- constructor(eip1193Provider) {
236
- super();
237
- this.eip1193Provider = eip1193Provider;
238
- }
239
- eip1193Provider;
240
- name = "owney";
241
- get installed() {
242
- return true;
243
- }
244
- resolveProvider() {
245
- return this.eip1193Provider;
246
- }
247
- };
248
- }
249
- });
250
-
251
- // src/agents/surfliquid/surfliquid.morpho.ts
252
- async function fetchMorphoVaultStats(vaultAddress, chainId) {
253
- try {
254
- const res = await fetch(MORPHO_API_URL, {
255
- method: "POST",
256
- headers: { "Content-Type": "application/json" },
257
- body: JSON.stringify({
258
- query: VAULT_STATS_QUERY,
259
- variables: { address: vaultAddress, chainId }
260
- })
261
- });
262
- if (!res.ok) return null;
263
- const json = await res.json();
264
- if (json.errors) return null;
265
- const vault = json.data?.vaultByAddress;
266
- const tvlUsd = vault?.state?.totalAssetsUsd;
267
- const liquidityUsd = vault?.liquidity?.usd;
268
- if (typeof tvlUsd !== "number" || typeof liquidityUsd !== "number") {
269
- return null;
270
- }
271
- const name = typeof vault?.name === "string" ? vault.name : null;
272
- return { name, tvlUsd, liquidityUsd };
273
- } catch (error) {
274
- console.warn("surfliquid: Morpho vault stats fetch failed", {
275
- vaultAddress,
276
- chainId,
277
- error
278
- });
279
- return null;
280
- }
281
- }
282
- async function fetchMorphoStatsByVault(vaultAddresses, chainId) {
283
- const unique = [...new Set(vaultAddresses.map((a) => a.toLowerCase()))];
284
- const entries = await Promise.all(
285
- unique.map(
286
- async (address) => [address, await fetchMorphoVaultStats(address, chainId)]
287
- )
288
- );
289
- const byVault = /* @__PURE__ */ new Map();
290
- for (const [address, stats] of entries) {
291
- if (stats) byVault.set(address, stats);
292
- }
293
- return byVault;
294
- }
295
- var MORPHO_API_URL, VAULT_STATS_QUERY;
296
- var init_surfliquid_morpho = __esm({
297
- "src/agents/surfliquid/surfliquid.morpho.ts"() {
298
- "use strict";
299
- MORPHO_API_URL = "https://api.morpho.org/graphql";
300
- VAULT_STATS_QUERY = `
301
- query VaultStats($address: String!, $chainId: Int!) {
302
- vaultByAddress(address: $address, chainId: $chainId) {
303
- name
304
- state { totalAssetsUsd }
305
- liquidity { usd }
306
- }
307
- }
308
- `;
309
- }
310
- });
311
-
312
- // src/agents/surfliquid/surfliquid.agent.ts
313
- var surfliquid_agent_exports = {};
314
- __export(surfliquid_agent_exports, {
315
- SurfLiquidAgent: () => SurfLiquidAgent
316
- });
317
- var import_core_sdk2, import_viem5, APY_WINDOW_KEY2, SurfLiquidAgent;
318
- var init_surfliquid_agent = __esm({
319
- "src/agents/surfliquid/surfliquid.agent.ts"() {
320
- "use strict";
321
- import_core_sdk2 = require("@surf_liquid/core-sdk");
322
- import_viem5 = require("viem");
323
- init_errors();
324
- init_surfliquid_constants();
325
- init_surfliquid_mapper();
326
- init_surfliquid_wallet_adapter();
327
- init_surfliquid_morpho();
328
- APY_WINDOW_KEY2 = {
329
- "7D": "apy7d",
330
- "14D": "apy14d",
331
- "30D": "apy30d"
332
- };
333
- SurfLiquidAgent = class {
334
- constructor(config) {
335
- this.config = config;
336
- }
337
- config;
338
- id = "surfliquid";
339
- supportedChainIds = [SURFLIQUID_CHAIN_ID];
340
- supportedAssets = SURFLIQUID_SUPPORTED_ASSETS;
341
- surf = null;
342
- connectedAddress = null;
343
- // In-flight connect+auth, shared by concurrent callers to dedupe SIWE.
344
- connectPromise = null;
345
- ensureSurf() {
346
- if (this.surf) return this.surf;
347
- this.surf = import_core_sdk2.SurfClient.create({
348
- projectName: this.config.projectName,
349
- appId: this.config.appId,
350
- environment: "mainnet",
351
- chainId: SURFLIQUID_CHAIN_ID,
352
- apiBaseUrl: this.config.apiBaseUrl,
353
- rpcUrl: this.config.rpcUrl,
354
- // Let the SDK auto-approve USDC to the vault before depositing.
355
- autoApprove: true
356
- });
357
- return this.surf;
358
- }
359
- /**
360
- * Connects + authenticates the SurfClient against owney's provider. Skips the
361
- * SIWE prompt when already authenticated for this wallet (cookie session).
362
- */
363
- async connect(state) {
364
- const surf = this.ensureSurf();
365
- const sameWallet = this.connectedAddress?.toLowerCase() === state.walletAddress.toLowerCase();
366
- if (sameWallet && surf.getAuthState().authenticated) {
367
- return surf;
368
- }
369
- if (this.connectPromise) return this.connectPromise;
370
- this.connectPromise = (async () => {
371
- surf.registerWalletAdapter(
372
- "owney",
373
- new OwneyWalletAdapter(state.provider)
374
- );
375
- await surf.connectWallet("owney");
376
- await surf.authenticate();
377
- this.connectedAddress = state.walletAddress;
378
- return surf;
379
- })();
380
- try {
381
- return await this.connectPromise;
382
- } finally {
383
- this.connectPromise = null;
384
- }
385
- }
386
- /**
387
- * Returns the user's vault address, deploying one on first use. SurfLiquid's
388
- * deposit requires the vault to exist, so this must run before depositing.
389
- * Existing vault → returned as-is; otherwise `deployVault()` is sent
390
- * (user-pays) and the freshly deployed address is returned.
391
- */
392
- async resolveVaultAddress(surf, owner) {
393
- const vault = await surf.getVault(owner).catch(() => null);
394
- if (vault?.exists && vault.userVaultAddress) {
395
- return vault.userVaultAddress;
396
- }
397
- const { vaultAddress } = await surf.deployVault();
398
- return vaultAddress;
399
- }
400
- // --- IAgent: lifecycle ---
401
- async disconnect() {
402
- if (this.surf) {
403
- await this.surf.disconnectWallet();
404
- }
405
- this.connectedAddress = null;
406
- }
407
- async activateAgent(state) {
408
- await this.connect(state);
409
- }
410
- // --- IAgent: funds (user-pays; asset is always USDC, callback ignored) ---
411
- async deposit(state, _chainId, amount, _asset, _depositCallback) {
412
- const surf = await this.connect(state);
413
- const human = (0, import_viem5.formatUnits)(BigInt(amount), SURFLIQUID_USDC_DECIMALS);
414
- const smartWallet = await this.resolveVaultAddress(
415
- surf,
416
- state.walletAddress
417
- );
418
- const tx = await surf.deposit({
419
- asset: SURFLIQUID_USDC_ADDRESS,
420
- amount: human
421
- });
422
- await tx.wait();
423
- return { txHash: tx.hash, smartWallet, amount };
424
- }
425
- async withdraw(state, _chainId, _token, amount) {
426
- const surf = await this.connect(state);
427
- const human = amount != null ? (0, import_viem5.formatUnits)(BigInt(amount), SURFLIQUID_USDC_DECIMALS) : void 0;
428
- try {
429
- const tx = await surf.withdraw({
430
- asset: SURFLIQUID_USDC_ADDRESS,
431
- amount: human
432
- });
433
- await tx.wait();
434
- return {
435
- txHash: tx.hash,
436
- type: amount != null ? "partial" : "full",
437
- amount: amount ?? "0"
438
- };
439
- } catch (error) {
440
- throw new OwneyError(
441
- "WITHDRAW_FAILED",
442
- error instanceof Error ? error.message : "SurfLiquid withdraw failed.",
443
- { chainId: SURFLIQUID_CHAIN_ID, amount },
444
- this.id
445
- );
446
- }
447
- }
448
- // --- IAgent: portfolio reads ---
449
- async getBalances(state, chainId) {
450
- const surf = await this.connect(state);
451
- const vault = await surf.getVault(state.walletAddress);
452
- const morphoVaultAddresses = (vault.assets ?? []).filter((asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId).map((asset) => asset.morphoVaultAddress).filter((address) => Boolean(address));
453
- const morphoStats = await fetchMorphoStatsByVault(
454
- morphoVaultAddresses,
455
- chainId
456
- );
457
- return mapBalances2(vault, chainId, morphoStats);
458
- }
459
- async getEarnings(state) {
460
- const surf = await this.connect(state);
461
- const vault = await surf.getVault(state.walletAddress);
462
- return mapEarnings2(vault, vault.userVaultAddress ?? state.walletAddress);
463
- }
464
- async getAccountApy(state, _chainId, days, _tokenSymbol) {
465
- const surf = await this.connect(state);
466
- const vault = await surf.getVault(state.walletAddress);
467
- return mapAccountApy(vault, days);
468
- }
469
- async getHistory(state, chainId, options) {
470
- const surf = await this.connect(state);
471
- const page = options?.cursor ? Number(options.cursor) : 1;
472
- const limit = options?.limit ?? 10;
473
- const result = await surf.getAgentMessages(
474
- state.walletAddress,
475
- page,
476
- limit,
477
- options?.fromDate,
478
- options?.toDate
479
- );
480
- return mapHistory(result, chainId);
481
- }
482
- async getUserProfile(state) {
483
- const surf = await this.connect(state);
484
- const vault = await surf.getVault(state.walletAddress);
485
- return mapUserProfile2(vault, state.walletAddress);
486
- }
487
- // --- IAgent: discovery (no wallet) ---
488
- async getAgentApy(days, _options) {
489
- const surf = this.ensureSurf();
490
- const assets = await surf.getSupportedAssets(SURFLIQUID_CHAIN_ID);
491
- const usdc = assets.find((asset) => asset.assetSymbol === "USDC");
492
- if (!usdc) return { averageApy: 0 };
493
- const windowed = usdc[APY_WINDOW_KEY2[days]];
494
- return { averageApy: windowed != null ? windowed : usdc.currentAPY };
495
- }
496
- };
497
- }
498
- });
499
-
500
20
  // src/index.ts
501
21
  var index_exports = {};
502
22
  __export(index_exports, {
@@ -568,8 +88,56 @@ var SupportedAssets = [
568
88
  }
569
89
  ];
570
90
 
91
+ // src/errors.ts
92
+ var OwneyError = class extends Error {
93
+ code;
94
+ details;
95
+ agentId;
96
+ constructor(code, message, details, agentId) {
97
+ const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
98
+ super(`${prefix} ${message}`);
99
+ this.name = "OwneyError";
100
+ this.code = code;
101
+ this.details = details;
102
+ this.agentId = agentId;
103
+ }
104
+ };
105
+ var AgentNotFoundError = class extends OwneyError {
106
+ constructor(agentId, available) {
107
+ super(
108
+ "AGENT_NOT_FOUND",
109
+ `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
110
+ { agentId, available },
111
+ agentId
112
+ );
113
+ this.name = "AgentNotFoundError";
114
+ }
115
+ };
116
+ var NotConnectedError = class extends OwneyError {
117
+ constructor() {
118
+ super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
119
+ this.name = "NotConnectedError";
120
+ }
121
+ };
122
+ var AgentChainIncompatibleError = class extends OwneyError {
123
+ incompatibleAgents;
124
+ connectedChainId;
125
+ constructor(incompatibleAgents, connectedChainId) {
126
+ const details = incompatibleAgents.map(
127
+ ({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
128
+ ).join("; ");
129
+ super(
130
+ "AGENT_CHAIN_INCOMPATIBLE",
131
+ `Chain ${connectedChainId} is not supported by the following agents: ${details}`,
132
+ { incompatibleAgents, connectedChainId }
133
+ );
134
+ this.name = "AgentChainIncompatibleError";
135
+ this.incompatibleAgents = incompatibleAgents;
136
+ this.connectedChainId = connectedChainId;
137
+ }
138
+ };
139
+
571
140
  // src/lib/utils.ts
572
- init_errors();
573
141
  var isValidChainId = (chainId) => {
574
142
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
575
143
  throw new OwneyError(
@@ -643,46 +211,46 @@ function resolveChainId(chain) {
643
211
  function mapBalances(raw, _chainId, smartWallet) {
644
212
  const portfolio = raw.portfolio;
645
213
  const portfolioByChain = portfolio.portfolioByChain ?? {};
646
- const rawPositions = portfolio.positions ?? [];
647
- const detailByKey = /* @__PURE__ */ new Map();
648
- for (const p of rawPositions) {
649
- const cid = resolveChainId(p.chain ?? "");
650
- const symbol = (p.token_symbol ?? "").toUpperCase();
651
- if (Number.isFinite(cid) && symbol) detailByKey.set(`${cid}:${symbol}`, p);
652
- }
653
214
  let totalBalance = 0;
654
215
  const tokens = [];
655
- const positions = [];
656
216
  for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
657
217
  const positionChainId = Number(chainIdKey);
658
- const chainName = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
659
218
  for (const [symbol, { balance, decimals }] of Object.entries(
660
219
  tokensBySymbol ?? {}
661
220
  )) {
662
- const amount = hexToDecimal(balance || "0", tokenDecimals(symbol, decimals));
221
+ const amount = hexToDecimal(balance || "0", decimals);
663
222
  totalBalance += amount;
664
223
  tokens.push({
665
- chain: chainName,
224
+ chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
666
225
  chainId: positionChainId,
667
226
  asset: symbol,
668
227
  amount: String(amount)
669
228
  });
670
- const detail = detailByKey.get(`${positionChainId}:${symbol.toUpperCase()}`);
671
- positions.push({
672
- chain: chainName,
673
- protocol: detail?.protocol_name ?? detail?.protocol_id ?? "",
674
- protocolId: detail?.protocol_id,
675
- pool: detail?.pool,
676
- asset: symbol,
677
- amount: String(amount),
678
- apy: detail?.pool_apy,
679
- tvl: detail?.pool_tvl,
680
- // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
681
- // structural read keeps it undefined today and auto-populates later.
682
- liquidity: detail?.liquidity
683
- });
684
229
  }
685
230
  }
231
+ const positions = (portfolio.positions ?? []).map((p) => ({
232
+ chain: p.chain ?? "",
233
+ protocol: p.protocol_name ?? p.protocol_id ?? "",
234
+ protocolId: p.protocol_id,
235
+ pool: p.pool,
236
+ asset: p.token_symbol ?? "",
237
+ // Position amount is hex-encoded base units (like the token balances
238
+ // above). `PositionSlot` doesn't type a `decimals` field, but runtime
239
+ // getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
240
+ // when present, otherwise fall back to the per-symbol lookup so
241
+ // non-6-decimal assets like WETH don't render 1e12 too large.
242
+ amount: String(
243
+ hexToDecimal(
244
+ p.amount || "0",
245
+ tokenDecimals(p.token_symbol, p.decimals)
246
+ )
247
+ ),
248
+ apy: p.pool_apy,
249
+ tvl: p.pool_tvl,
250
+ // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
251
+ // structural read keeps it undefined today and auto-populates later.
252
+ liquidity: p.liquidity
253
+ }));
686
254
  return {
687
255
  smartWallet,
688
256
  totalBalance: String(totalBalance),
@@ -957,9 +525,6 @@ function decodeHistoryCursor(token) {
957
525
  return parsed;
958
526
  }
959
527
 
960
- // src/agents/zyfai/zyfai.agent.ts
961
- init_errors();
962
-
963
528
  // src/agents/zyfai/zyfai.auth-cache.ts
964
529
  var KEY_PREFIX = "owney.zyfai.session";
965
530
  var storage = () => {
@@ -970,7 +535,8 @@ var storage = () => {
970
535
  return null;
971
536
  }
972
537
  };
973
- var buildKey = (address, chainId) => `${KEY_PREFIX}:${address.toLowerCase()}:${chainId}`;
538
+ var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
539
+ var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
974
540
  var isJwtExpired = (token) => {
975
541
  const parts = token.split(".");
976
542
  if (parts.length !== 3) return false;
@@ -987,9 +553,37 @@ var isJwtExpired = (token) => {
987
553
  };
988
554
  var memorySessions = /* @__PURE__ */ new Map();
989
555
  var isFreshSession = (session) => !!session && !!session.token && !isJwtExpired(session.token);
990
- var readSession = (address, chainId) => {
556
+ var readLegacySession = (store, address) => {
557
+ if (!store) return null;
558
+ const prefix = legacyKeyPrefix(address);
559
+ const legacyKeys = [];
560
+ try {
561
+ for (let i = 0; i < store.length; i++) {
562
+ const key = store.key(i);
563
+ if (key?.startsWith(prefix)) legacyKeys.push(key);
564
+ }
565
+ } catch {
566
+ return null;
567
+ }
568
+ let adopted = null;
569
+ for (const key of legacyKeys) {
570
+ if (!adopted) {
571
+ try {
572
+ const parsed = JSON.parse(store.getItem(key) ?? "");
573
+ if (isFreshSession(parsed)) adopted = parsed;
574
+ } catch {
575
+ }
576
+ }
577
+ try {
578
+ store.removeItem(key);
579
+ } catch {
580
+ }
581
+ }
582
+ return adopted;
583
+ };
584
+ var readSession = (address, _chainId) => {
991
585
  if (typeof window === "undefined") return null;
992
- const key = buildKey(address, chainId);
586
+ const key = buildKey(address);
993
587
  const store = storage();
994
588
  let raw = null;
995
589
  try {
@@ -1010,14 +604,19 @@ var readSession = (address, chainId) => {
1010
604
  }
1011
605
  return null;
1012
606
  }
607
+ const legacy = readLegacySession(store, address);
608
+ if (legacy) {
609
+ writeSession(address, _chainId, legacy);
610
+ return legacy;
611
+ }
1013
612
  const cached = memorySessions.get(key);
1014
613
  if (isFreshSession(cached)) return cached;
1015
614
  if (cached) memorySessions.delete(key);
1016
615
  return null;
1017
616
  };
1018
- var writeSession = (address, chainId, session) => {
617
+ var writeSession = (address, _chainId, session) => {
1019
618
  if (typeof window === "undefined") return;
1020
- const key = buildKey(address, chainId);
619
+ const key = buildKey(address);
1021
620
  memorySessions.set(key, session);
1022
621
  const store = storage();
1023
622
  try {
@@ -1025,14 +624,15 @@ var writeSession = (address, chainId, session) => {
1025
624
  } catch {
1026
625
  }
1027
626
  };
1028
- var clearSession = (address, chainId) => {
1029
- const key = buildKey(address, chainId);
627
+ var clearSession = (address, _chainId) => {
628
+ const key = buildKey(address);
1030
629
  memorySessions.delete(key);
1031
630
  const store = storage();
1032
631
  try {
1033
632
  store?.removeItem(key);
1034
633
  } catch {
1035
634
  }
635
+ readLegacySession(store, address);
1036
636
  };
1037
637
 
1038
638
  // src/agents/zyfai/autoSelect.ts
@@ -1109,11 +709,15 @@ var ZyfaiAgent = class {
1109
709
  // successful decode is cached forever; failures are NOT cached so a transient
1110
710
  // RPC error retries on the next fetch.
1111
711
  withdrawAmountCache = /* @__PURE__ */ new Map();
1112
- constructor(apiKey, rpcUrls) {
712
+ constructor(apiKey, rpcUrls, referralSource) {
1113
713
  this.rpcUrls = rpcUrls ?? DEFAULT_ZYFAI_RPC_URLS;
1114
714
  this.sdk = new import_sdk.ZyfaiSDK({
1115
715
  apiKey,
1116
- rpcUrls: this.rpcUrls
716
+ rpcUrls: this.rpcUrls,
717
+ // Forwarded to /auth/login by the SDK's own SIWE handshake, so
718
+ // attribution survives without the AppKit SIWX config (which carried it
719
+ // via `createOwneySIWX({ referralSource })` when apps used the modal).
720
+ referralSource
1117
721
  });
1118
722
  this.installAuthCache();
1119
723
  }
@@ -1531,11 +1135,7 @@ var ZyfaiAgent = class {
1531
1135
  }
1532
1136
  };
1533
1137
 
1534
- // src/client.ts
1535
- init_errors();
1536
-
1537
1138
  // src/lib/routing-api.ts
1538
- init_errors();
1539
1139
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1540
1140
  async function fetchAgentKeys(apiKey) {
1541
1141
  const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
@@ -1566,7 +1166,6 @@ async function fetchAgentKeys(apiKey) {
1566
1166
  }
1567
1167
 
1568
1168
  // src/lib/health-report.ts
1569
- init_errors();
1570
1169
  var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1571
1170
  async function reportAgentFailure(apiKey, agentType, errorCode) {
1572
1171
  try {
@@ -1716,12 +1315,9 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
1716
1315
  }
1717
1316
 
1718
1317
  // src/client.ts
1719
- var import_viem6 = require("viem");
1318
+ var import_viem5 = require("viem");
1720
1319
  var import_chains2 = require("viem/chains");
1721
1320
 
1722
- // src/lib/sponsored-deposit.ts
1723
- init_errors();
1724
-
1725
1321
  // src/lib/transfer-auth.ts
1726
1322
  var import_viem3 = require("viem");
1727
1323
  var ERC20_META_ABI = [
@@ -1759,7 +1355,6 @@ function randomAuthNonce() {
1759
1355
  }
1760
1356
 
1761
1357
  // src/lib/sponsor-client.ts
1762
- init_errors();
1763
1358
  var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1764
1359
  async function postSponsorTransferAuth(input) {
1765
1360
  const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
@@ -1926,9 +1521,6 @@ function makeSponsoredDepositCallback(deps) {
1926
1521
  };
1927
1522
  }
1928
1523
 
1929
- // src/lib/sponsored-weth-deposit.ts
1930
- init_errors();
1931
-
1932
1524
  // src/lib/permit2.ts
1933
1525
  var import_viem4 = require("viem");
1934
1526
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
@@ -2068,7 +1660,6 @@ var VIEM_CHAIN2 = {
2068
1660
  42161: import_chains2.arbitrum,
2069
1661
  1: import_chains2.mainnet
2070
1662
  };
2071
- var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
2072
1663
  var SPONSORED_WETH_BY_CHAIN = {
2073
1664
  8453: "0x4200000000000000000000000000000000000006",
2074
1665
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -2090,6 +1681,7 @@ var OwneySDK = class {
2090
1681
  apiKey;
2091
1682
  zyfaiRpcUrls;
2092
1683
  routingApiBaseUrl;
1684
+ referralSource;
2093
1685
  cachedSponsoredCallback = null;
2094
1686
  cachedWethSponsoredCallback = null;
2095
1687
  initializingAgentsPromise = null;
@@ -2097,6 +1689,7 @@ var OwneySDK = class {
2097
1689
  this.apiKey = config.apiKey;
2098
1690
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
2099
1691
  this.routingApiBaseUrl = config.routingApiBaseUrl;
1692
+ this.referralSource = config.referralSource;
2100
1693
  }
2101
1694
  /**
2102
1695
  * Establish connection state for all subsequent SDK calls.
@@ -2192,14 +1785,14 @@ var OwneySDK = class {
2192
1785
  // Casts work around viem's chain-narrowed Client vs the generic
2193
1786
  // PublicClient/WalletClient param types — structurally identical at
2194
1787
  // runtime, but the two share a name TS treats as unrelated.
2195
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
1788
+ getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
2196
1789
  chain: VIEM_CHAIN2[cid],
2197
- transport: (0, import_viem6.custom)(provider)
1790
+ transport: (0, import_viem5.custom)(provider)
2198
1791
  }),
2199
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
1792
+ getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
2200
1793
  account: owner,
2201
1794
  chain: VIEM_CHAIN2[cid],
2202
- transport: (0, import_viem6.custom)(provider)
1795
+ transport: (0, import_viem5.custom)(provider)
2203
1796
  })
2204
1797
  });
2205
1798
  return this.cachedSponsoredCallback;
@@ -2223,14 +1816,14 @@ var OwneySDK = class {
2223
1816
  // Casts work around viem's chain-narrowed Client vs the generic
2224
1817
  // PublicClient/WalletClient param types — structurally identical at
2225
1818
  // runtime, but the two share a name TS treats as unrelated.
2226
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
1819
+ getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
2227
1820
  chain: VIEM_CHAIN2[cid],
2228
- transport: (0, import_viem6.custom)(provider)
1821
+ transport: (0, import_viem5.custom)(provider)
2229
1822
  }),
2230
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
1823
+ getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
2231
1824
  account: owner,
2232
1825
  chain: VIEM_CHAIN2[cid],
2233
- transport: (0, import_viem6.custom)(provider)
1826
+ transport: (0, import_viem5.custom)(provider)
2234
1827
  })
2235
1828
  });
2236
1829
  return this.cachedWethSponsoredCallback;
@@ -2256,8 +1849,8 @@ var OwneySDK = class {
2256
1849
  this.initializingAgentsPromise = (async () => {
2257
1850
  const agentKeys = await fetchAgentKeys(this.apiKey);
2258
1851
  this.disabledAgents.clear();
2259
- for (const { key, agent_type, project_name, is_enabled } of agentKeys) {
2260
- const agent = await this.createAgent(agent_type, key, project_name);
1852
+ for (const { key, agent_type, is_enabled } of agentKeys) {
1853
+ const agent = this.createAgent(agent_type, key);
2261
1854
  if (!agent) continue;
2262
1855
  this.agents.set(agent_type, agent);
2263
1856
  if (is_enabled === false) {
@@ -2278,14 +1871,9 @@ var OwneySDK = class {
2278
1871
  this.initializingAgentsPromise = null;
2279
1872
  }
2280
1873
  }
2281
- async createAgent(agentId, key, projectName) {
1874
+ createAgent(agentId, key) {
2282
1875
  if (agentId === "zyfai") {
2283
- return new ZyfaiAgent(key, this.zyfaiRpcUrls);
2284
- }
2285
- if (agentId === "surfliquid") {
2286
- if (!projectName) return null;
2287
- const { SurfLiquidAgent: SurfLiquidAgent2 } = await Promise.resolve().then(() => (init_surfliquid_agent(), surfliquid_agent_exports));
2288
- return new SurfLiquidAgent2({ projectName, appId: key });
1876
+ return new ZyfaiAgent(key, this.zyfaiRpcUrls, this.referralSource);
2289
1877
  }
2290
1878
  return null;
2291
1879
  }
@@ -2439,30 +2027,18 @@ var OwneySDK = class {
2439
2027
  );
2440
2028
  }
2441
2029
  const agentResults = {};
2442
- const agentErrors = {};
2443
2030
  for (const { agent, amount: agentAmount } of agentAmounts) {
2444
- try {
2445
- agentResults[agent.id] = await this.depositWithFallback(
2446
- agent,
2447
- state,
2448
- chainId,
2449
- agentAmount,
2450
- asset,
2451
- effectiveCallback,
2452
- depositCallback
2453
- );
2454
- } catch (error) {
2455
- agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
2456
- }
2457
- }
2458
- if (Object.keys(agentResults).length === 0) {
2459
- throw new OwneyError(
2460
- "DEPOSIT_ALL_FAILED",
2461
- `All ${agentAmounts.length} agent deposit(s) failed.`,
2462
- { agentErrors }
2031
+ agentResults[agent.id] = await this.depositWithFallback(
2032
+ agent,
2033
+ state,
2034
+ chainId,
2035
+ agentAmount,
2036
+ asset,
2037
+ effectiveCallback,
2038
+ depositCallback
2463
2039
  );
2464
2040
  }
2465
- return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
2041
+ return { agentResults };
2466
2042
  }
2467
2043
  /**
2468
2044
  * Invokes `agent.deposit` with the resolved sponsored callback, composing
@@ -2619,34 +2195,6 @@ var OwneySDK = class {
2619
2195
  }
2620
2196
  return eligible;
2621
2197
  }
2622
- /**
2623
- * Agent ids the routing API provisioned for this org that support the given
2624
- * chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
2625
- * surfliquid first). Returns `[]` when the org has no compatible agent — never
2626
- * throws on an empty org. Loads agent keys on first call (apiKey only, no
2627
- * wallet), so the UI can resolve which agent to use before the user connects.
2628
- *
2629
- * This is the source of truth for agent availability: an agent appears here
2630
- * iff the routing API returned its key. No per-app feature flags.
2631
- */
2632
- async getEligibleAgentIds(chainId, asset) {
2633
- try {
2634
- await this.ensureAgentsInitialized();
2635
- } catch (error) {
2636
- if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
2637
- return [];
2638
- }
2639
- throw error;
2640
- }
2641
- return [...this.agents.values()].filter((agent) => {
2642
- const chainAssets = agent.supportedAssets.find(
2643
- (sa) => sa.chainId === chainId
2644
- );
2645
- return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
2646
- }).map((agent) => agent.id).sort(
2647
- (a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
2648
- );
2649
- }
2650
2198
  // --- Fund operations ---
2651
2199
  /**
2652
2200
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
@@ -2682,17 +2230,9 @@ var OwneySDK = class {
2682
2230
  }
2683
2231
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
2684
2232
  if (!amount) {
2685
- const aggregated2 = await this.getBalances();
2686
- const funded = projectAgentBalancesForAsset(
2687
- eligibleAgents,
2688
- aggregated2.agentBalances,
2689
- chainId,
2690
- asset,
2691
- assetInfo.decimals
2692
- ).filter((b) => b.balance > 0n);
2693
2233
  const results2 = {};
2694
2234
  const agentErrors2 = {};
2695
- for (const { agent } of funded) {
2235
+ for (const agent of eligibleAgents) {
2696
2236
  try {
2697
2237
  results2[agent.id] = await agent.withdraw(state, chainId, token);
2698
2238
  } catch (err) {
@@ -2709,10 +2249,9 @@ var OwneySDK = class {
2709
2249
  { asset, agentErrors: agentErrors2 }
2710
2250
  );
2711
2251
  }
2712
- const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
2713
2252
  return {
2714
2253
  agentResult: results2,
2715
- totalWithdrawn: totalWithdrawn.toString()
2254
+ totalWithdrawn: sumWithdrawnAmount(results2).toString()
2716
2255
  };
2717
2256
  }
2718
2257
  const requested = BigInt(amount);
@@ -3082,10 +2621,10 @@ var OwneySDK = class {
3082
2621
  );
3083
2622
  }
3084
2623
  const provider = this.requireConnectedProvider();
3085
- const wallet = (0, import_viem6.createWalletClient)({
2624
+ const wallet = (0, import_viem5.createWalletClient)({
3086
2625
  account: state.walletAddress,
3087
2626
  chain: VIEM_CHAIN2[chainId],
3088
- transport: (0, import_viem6.custom)(provider)
2627
+ transport: (0, import_viem5.custom)(provider)
3089
2628
  });
3090
2629
  const hash = await wallet.writeContract({
3091
2630
  address: token,
@@ -3095,9 +2634,9 @@ var OwneySDK = class {
3095
2634
  account: state.walletAddress,
3096
2635
  chain: VIEM_CHAIN2[chainId]
3097
2636
  });
3098
- const publicClient = (0, import_viem6.createPublicClient)({
2637
+ const publicClient = (0, import_viem5.createPublicClient)({
3099
2638
  chain: VIEM_CHAIN2[chainId],
3100
- transport: (0, import_viem6.custom)(provider)
2639
+ transport: (0, import_viem5.custom)(provider)
3101
2640
  });
3102
2641
  const receipt = await publicClient.waitForTransactionReceipt({
3103
2642
  hash,
@@ -3205,11 +2744,8 @@ var OwneySDK = class {
3205
2744
  }
3206
2745
  };
3207
2746
 
3208
- // src/index.ts
3209
- init_errors();
3210
-
3211
2747
  // src/agents/zyfai/zyfai.siwx.ts
3212
- var import_viem7 = require("viem");
2748
+ var import_viem6 = require("viem");
3213
2749
  var import_siwe = require("siwe");
3214
2750
  var import_sdk2 = require("@zyfai/sdk");
3215
2751
 
@@ -3223,11 +2759,39 @@ var storage2 = () => {
3223
2759
  return null;
3224
2760
  }
3225
2761
  };
3226
- var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
2762
+ var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
2763
+ var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
3227
2764
  var memorySiwxSessions = /* @__PURE__ */ new Map();
2765
+ var readLegacySiwxSession = (store, address) => {
2766
+ if (!store) return null;
2767
+ const prefix = legacyKeyPrefix2(address);
2768
+ const legacyKeys = [];
2769
+ try {
2770
+ for (let i = 0; i < store.length; i++) {
2771
+ const key = store.key(i);
2772
+ if (key?.startsWith(prefix)) legacyKeys.push(key);
2773
+ }
2774
+ } catch {
2775
+ return null;
2776
+ }
2777
+ let adopted = null;
2778
+ for (const key of legacyKeys) {
2779
+ if (!adopted) {
2780
+ try {
2781
+ adopted = JSON.parse(store.getItem(key) ?? "");
2782
+ } catch {
2783
+ }
2784
+ }
2785
+ try {
2786
+ store.removeItem(key);
2787
+ } catch {
2788
+ }
2789
+ }
2790
+ return adopted;
2791
+ };
3228
2792
  var readSiwxSession = (address, chainId) => {
3229
2793
  if (typeof window === "undefined") return null;
3230
- const key = buildKey2(address, chainId);
2794
+ const key = buildKey2(address);
3231
2795
  const store = storage2();
3232
2796
  let raw = null;
3233
2797
  try {
@@ -3247,11 +2811,16 @@ var readSiwxSession = (address, chainId) => {
3247
2811
  return null;
3248
2812
  }
3249
2813
  }
2814
+ const legacy = readLegacySiwxSession(store, address);
2815
+ if (legacy) {
2816
+ writeSiwxSession(address, chainId, legacy);
2817
+ return legacy;
2818
+ }
3250
2819
  return memorySiwxSessions.get(key) ?? null;
3251
2820
  };
3252
- var writeSiwxSession = (address, chainId, session) => {
2821
+ var writeSiwxSession = (address, _chainId, session) => {
3253
2822
  if (typeof window === "undefined") return;
3254
- const key = buildKey2(address, chainId);
2823
+ const key = buildKey2(address);
3255
2824
  memorySiwxSessions.set(key, session);
3256
2825
  const store = storage2();
3257
2826
  try {
@@ -3259,14 +2828,15 @@ var writeSiwxSession = (address, chainId, session) => {
3259
2828
  } catch {
3260
2829
  }
3261
2830
  };
3262
- var clearSiwxSession = (address, chainId) => {
3263
- const key = buildKey2(address, chainId);
2831
+ var clearSiwxSession = (address, _chainId) => {
2832
+ const key = buildKey2(address);
3264
2833
  memorySiwxSessions.delete(key);
3265
2834
  const store = storage2();
3266
2835
  try {
3267
2836
  store?.removeItem(key);
3268
2837
  } catch {
3269
2838
  }
2839
+ readLegacySiwxSession(store, address);
3270
2840
  };
3271
2841
 
3272
2842
  // src/agents/zyfai/zyfai.siwx.ts
@@ -3302,7 +2872,7 @@ function buildSIWXConfig(deps) {
3302
2872
  issuedAt,
3303
2873
  toString() {
3304
2874
  return new import_siwe.SiweMessage({
3305
- address: (0, import_viem7.getAddress)(accountAddress),
2875
+ address: (0, import_viem6.getAddress)(accountAddress),
3306
2876
  chainId: numericChainId(chainId),
3307
2877
  domain,
3308
2878
  uri,
@@ -3330,9 +2900,12 @@ function buildSIWXConfig(deps) {
3330
2900
  }
3331
2901
  if (!stored) return [];
3332
2902
  const session = {
3333
- // `stored.data.chainId` is a plain string in the cache (kept AppKit-free);
3334
- // it always holds a valid CaipNetworkId here, so narrow at the boundary.
3335
- data: stored.data,
2903
+ // `stored.data` is kept AppKit-free in the cache, so narrow at the
2904
+ // boundary. Echo the REQUESTED chain rather than the one originally
2905
+ // signed: the Zyfai JWT is account-scoped (no chain claim), so one
2906
+ // signature is valid for every chain, and AppKit would otherwise treat
2907
+ // the session as not covering the active network and re-prompt.
2908
+ data: { ...stored.data, chainId },
3336
2909
  message: stored.message,
3337
2910
  signature: stored.signature
3338
2911
  };