@owney/sdk 0.6.9-beta.4 → 0.6.9-beta.6

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,3 +1,10 @@
1
+ import {
2
+ AgentChainIncompatibleError,
3
+ AgentNotFoundError,
4
+ NotConnectedError,
5
+ OwneyError
6
+ } from "./chunk-GMNQBWUB.js";
7
+
1
8
  // src/agents/zyfai/zyfai.agent.ts
2
9
  import { ZyfaiSDK } from "@zyfai/sdk";
3
10
  import { createPublicClient, http } from "viem";
@@ -56,55 +63,6 @@ var SupportedAssets = [
56
63
  }
57
64
  ];
58
65
 
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
-
108
66
  // src/lib/utils.ts
109
67
  var isValidChainId = (chainId) => {
110
68
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
@@ -179,46 +137,46 @@ function resolveChainId(chain) {
179
137
  function mapBalances(raw, _chainId, smartWallet) {
180
138
  const portfolio = raw.portfolio;
181
139
  const portfolioByChain = portfolio.portfolioByChain ?? {};
140
+ const rawPositions = portfolio.positions ?? [];
141
+ const detailByKey = /* @__PURE__ */ new Map();
142
+ for (const p of rawPositions) {
143
+ const cid = resolveChainId(p.chain ?? "");
144
+ const symbol = (p.token_symbol ?? "").toUpperCase();
145
+ if (Number.isFinite(cid) && symbol) detailByKey.set(`${cid}:${symbol}`, p);
146
+ }
182
147
  let totalBalance = 0;
183
148
  const tokens = [];
149
+ const positions = [];
184
150
  for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
185
151
  const positionChainId = Number(chainIdKey);
152
+ const chainName = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
186
153
  for (const [symbol, { balance, decimals }] of Object.entries(
187
154
  tokensBySymbol ?? {}
188
155
  )) {
189
- const amount = hexToDecimal(balance || "0", decimals);
156
+ const amount = hexToDecimal(balance || "0", tokenDecimals(symbol, decimals));
190
157
  totalBalance += amount;
191
158
  tokens.push({
192
- chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
159
+ chain: chainName,
193
160
  chainId: positionChainId,
194
161
  asset: symbol,
195
162
  amount: String(amount)
196
163
  });
164
+ const detail = detailByKey.get(`${positionChainId}:${symbol.toUpperCase()}`);
165
+ positions.push({
166
+ chain: chainName,
167
+ protocol: detail?.protocol_name ?? detail?.protocol_id ?? "",
168
+ protocolId: detail?.protocol_id,
169
+ pool: detail?.pool,
170
+ asset: symbol,
171
+ amount: String(amount),
172
+ apy: detail?.pool_apy,
173
+ tvl: detail?.pool_tvl,
174
+ // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
175
+ // structural read keeps it undefined today and auto-populates later.
176
+ liquidity: detail?.liquidity
177
+ });
197
178
  }
198
179
  }
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
- }));
222
180
  return {
223
181
  smartWallet,
224
182
  totalBalance: String(totalBalance),
@@ -529,23 +487,23 @@ var readLegacySession = (store, address) => {
529
487
  const legacyKeys = [];
530
488
  try {
531
489
  for (let i = 0; i < store.length; i++) {
532
- const key = store.key(i);
533
- if (key?.startsWith(prefix)) legacyKeys.push(key);
490
+ const key2 = store.key(i);
491
+ if (key2?.startsWith(prefix)) legacyKeys.push(key2);
534
492
  }
535
493
  } catch {
536
494
  return null;
537
495
  }
538
496
  let adopted = null;
539
- for (const key of legacyKeys) {
497
+ for (const key2 of legacyKeys) {
540
498
  if (!adopted) {
541
499
  try {
542
- const parsed = JSON.parse(store.getItem(key) ?? "");
500
+ const parsed = JSON.parse(store.getItem(key2) ?? "");
543
501
  if (isFreshSession(parsed)) adopted = parsed;
544
502
  } catch {
545
503
  }
546
504
  }
547
505
  try {
548
- store.removeItem(key);
506
+ store.removeItem(key2);
549
507
  } catch {
550
508
  }
551
509
  }
@@ -553,11 +511,11 @@ var readLegacySession = (store, address) => {
553
511
  };
554
512
  var readSession = (address, _chainId) => {
555
513
  if (typeof window === "undefined") return null;
556
- const key = buildKey(address);
514
+ const key2 = buildKey(address);
557
515
  const store = storage();
558
516
  let raw = null;
559
517
  try {
560
- raw = store?.getItem(key) ?? null;
518
+ raw = store?.getItem(key2) ?? null;
561
519
  } catch {
562
520
  raw = null;
563
521
  }
@@ -567,9 +525,9 @@ var readSession = (address, _chainId) => {
567
525
  if (isFreshSession(parsed)) return parsed;
568
526
  } catch {
569
527
  }
570
- memorySessions.delete(key);
528
+ memorySessions.delete(key2);
571
529
  try {
572
- store?.removeItem(key);
530
+ store?.removeItem(key2);
573
531
  } catch {
574
532
  }
575
533
  return null;
@@ -579,27 +537,27 @@ var readSession = (address, _chainId) => {
579
537
  writeSession(address, _chainId, legacy);
580
538
  return legacy;
581
539
  }
582
- const cached = memorySessions.get(key);
540
+ const cached = memorySessions.get(key2);
583
541
  if (isFreshSession(cached)) return cached;
584
- if (cached) memorySessions.delete(key);
542
+ if (cached) memorySessions.delete(key2);
585
543
  return null;
586
544
  };
587
545
  var writeSession = (address, _chainId, session) => {
588
546
  if (typeof window === "undefined") return;
589
- const key = buildKey(address);
590
- memorySessions.set(key, session);
547
+ const key2 = buildKey(address);
548
+ memorySessions.set(key2, session);
591
549
  const store = storage();
592
550
  try {
593
- store?.setItem(key, JSON.stringify(session));
551
+ store?.setItem(key2, JSON.stringify(session));
594
552
  } catch {
595
553
  }
596
554
  };
597
555
  var clearSession = (address, _chainId) => {
598
- const key = buildKey(address);
599
- memorySessions.delete(key);
556
+ const key2 = buildKey(address);
557
+ memorySessions.delete(key2);
600
558
  const store = storage();
601
559
  try {
602
- store?.removeItem(key);
560
+ store?.removeItem(key2);
603
561
  } catch {
604
562
  }
605
563
  readLegacySession(store, address);
@@ -663,6 +621,99 @@ function splittingNeedsUpdate(current, desired) {
663
621
  return current.minSplits !== desired.minSplits;
664
622
  }
665
623
 
624
+ // src/agents/zyfai/pool-policy.ts
625
+ function toZyfaiCustomizations(policy, availableByProtocol, chainsByProtocol) {
626
+ const selectionByKey = /* @__PURE__ */ new Map();
627
+ for (const selection of policy.selections) {
628
+ selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
629
+ }
630
+ const customizations = [];
631
+ for (const [protocolId, chainIds] of Object.entries(chainsByProtocol)) {
632
+ const available = availableByProtocol.get(protocolId);
633
+ if (available === void 0) {
634
+ continue;
635
+ }
636
+ const availableSet = new Set(available);
637
+ for (const chainId of chainIds) {
638
+ const stored = selectionByKey.get(key(protocolId, chainId));
639
+ if (stored === void 0) {
640
+ customizations.push({
641
+ protocolId,
642
+ chainId,
643
+ pools: [],
644
+ autoselect: true
645
+ });
646
+ continue;
647
+ }
648
+ const pools = stored.pools.filter((pool) => availableSet.has(pool));
649
+ customizations.push({
650
+ protocolId,
651
+ chainId,
652
+ pools,
653
+ autoselect: false
654
+ });
655
+ }
656
+ }
657
+ return customizations;
658
+ }
659
+ function deriveProtocols(policy, universe, chainsByProtocol) {
660
+ if (policy.selections.length === 0) {
661
+ return null;
662
+ }
663
+ const selectionByKey = /* @__PURE__ */ new Map();
664
+ for (const selection of policy.selections) {
665
+ selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
666
+ }
667
+ return universe.filter((protocolId) => {
668
+ const chainIds = chainsByProtocol[protocolId];
669
+ if (chainIds === void 0 || chainIds.length === 0) {
670
+ return true;
671
+ }
672
+ const fullyDisabled = chainIds.every((chainId) => {
673
+ const stored = selectionByKey.get(key(protocolId, chainId));
674
+ return stored !== void 0 && stored.pools.length === 0;
675
+ });
676
+ return !fullyDisabled;
677
+ });
678
+ }
679
+ function key(protocolId, chainId) {
680
+ return `${protocolId}:${chainId}`;
681
+ }
682
+ function buildChainsByProtocol(protocols, dashboardChainIds) {
683
+ const allowed = new Set(dashboardChainIds);
684
+ const result = {};
685
+ for (const protocol of protocols) {
686
+ result[protocol.id] = (protocol.chains ?? []).filter(
687
+ (chainId) => allowed.has(chainId)
688
+ );
689
+ }
690
+ return result;
691
+ }
692
+ function flattenAvailablePools(raw) {
693
+ const byChain = raw ?? {};
694
+ const names = [];
695
+ for (const byToken of Object.values(byChain ?? {})) {
696
+ for (const entry of Object.values(byToken ?? {})) {
697
+ for (const pool of entry?.pools ?? []) {
698
+ if (pool?.name) names.push(pool.name);
699
+ }
700
+ }
701
+ }
702
+ return names;
703
+ }
704
+ function protocolListsEqual(a, b) {
705
+ const left = [...a ?? []].sort();
706
+ const right = [...b ?? []].sort();
707
+ if (left.length !== right.length) return false;
708
+ return left.every((value, index) => value === right[index]);
709
+ }
710
+ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect) {
711
+ if (!current) return true;
712
+ if (current.autoSelectProtocols !== desiredAutoSelect) return true;
713
+ if (desiredProtocols === null) return false;
714
+ return !protocolListsEqual(current.protocols, desiredProtocols);
715
+ }
716
+
666
717
  // src/lib/debug.ts
667
718
  var configuredDebug = false;
668
719
  function setOwneyDebug(enabled) {
@@ -693,6 +744,7 @@ var WETH_ADDRESS_BY_CHAIN = {
693
744
  };
694
745
  var VIEM_CHAIN = { 8453: base, 42161: arbitrum, 1: mainnet };
695
746
  var SPLITTING_ASSETS = ["USDC", "WETH"];
747
+ var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
696
748
  function isAlreadyActiveSessionKeyError(error) {
697
749
  const message = error instanceof Error ? error.message : String(error);
698
750
  return /already has an active session key/i.test(message);
@@ -1077,6 +1129,9 @@ var ZyfaiAgent = class {
1077
1129
  await Promise.all(
1078
1130
  SPLITTING_ASSETS.map((asset) => this.applySplitting(asset, policy))
1079
1131
  );
1132
+ if (policy.poolPolicy) {
1133
+ await this.applyPoolPolicy(policy.poolPolicy);
1134
+ }
1080
1135
  }
1081
1136
  async applySplitting(asset, policy) {
1082
1137
  try {
@@ -1113,6 +1168,117 @@ var ZyfaiAgent = class {
1113
1168
  );
1114
1169
  }
1115
1170
  }
1171
+ /**
1172
+ * Apply the org's protocol/pool policy to this user's Zyfai profile.
1173
+ *
1174
+ * Two Zyfai writes, in a strict order:
1175
+ * 1. `updateUserProfile({ protocols, autoSelectProtocols })` per asset —
1176
+ * the explicit allow-list. Chain-agnostic, mirrors `applySplitting`.
1177
+ * 2. `customizeBatch(...)` once — per (protocol, chain), asset-agnostic
1178
+ * pool narrowing.
1179
+ *
1180
+ * Order matters: constraining a protocol's pools (step 2) while that
1181
+ * protocol is not yet in the allow-list (step 1 not yet applied) lands the
1182
+ * constraint on something the rebalance engine isn't considering.
1183
+ *
1184
+ * Wrapped so a failure anywhere is non-fatal — this runs as a side effect of
1185
+ * connecting, so a Zyfai hiccup must leave the user connected with their
1186
+ * profile unchanged, never fail activation.
1187
+ */
1188
+ async applyPoolPolicy(policy) {
1189
+ try {
1190
+ const { protocols: universe } = await this.sdk.getAvailableProtocols(
1191
+ this.supportedChainIds[0]
1192
+ );
1193
+ const universeIds = universe.map((p) => p.id);
1194
+ const chainsByProtocol = buildChainsByProtocol(
1195
+ universe,
1196
+ POOL_POLICY_CHAIN_IDS
1197
+ );
1198
+ debugLog("zyfai:pools", "protocol universe", {
1199
+ universeIds,
1200
+ chainsByProtocol
1201
+ });
1202
+ let strategy;
1203
+ const desiredProtocols = deriveProtocols(
1204
+ policy,
1205
+ universeIds,
1206
+ chainsByProtocol
1207
+ );
1208
+ const desiredAutoSelect = policy.autoApproveProtocols;
1209
+ for (const asset of SPLITTING_ASSETS) {
1210
+ const current = await this.sdk.getUserDetails(asset);
1211
+ if (strategy === void 0 && (current.strategy === "conservative" || current.strategy === "aggressive")) {
1212
+ strategy = current.strategy;
1213
+ }
1214
+ const needsUpdate = protocolsPolicyNeedsUpdate(
1215
+ current,
1216
+ desiredProtocols,
1217
+ desiredAutoSelect
1218
+ );
1219
+ debugLog("zyfai:pools", `${asset}: comparing protocols`, {
1220
+ current: {
1221
+ protocols: current.protocols,
1222
+ autoSelectProtocols: current.autoSelectProtocols
1223
+ },
1224
+ desired: {
1225
+ protocols: desiredProtocols,
1226
+ autoSelectProtocols: desiredAutoSelect
1227
+ },
1228
+ decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
1229
+ });
1230
+ if (!needsUpdate) continue;
1231
+ const update = {
1232
+ asset,
1233
+ autoSelectProtocols: desiredAutoSelect
1234
+ };
1235
+ if (desiredProtocols !== null) update.protocols = desiredProtocols;
1236
+ await this.sdk.updateUserProfile(update);
1237
+ debugLog("zyfai:pools", `${asset}: wrote protocols update`, update);
1238
+ }
1239
+ const poolResults = await Promise.all(
1240
+ universe.map(async (protocol) => {
1241
+ try {
1242
+ const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1243
+ return [protocol.id, flattenAvailablePools(raw)];
1244
+ } catch (error) {
1245
+ console.warn(
1246
+ `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
1247
+ error instanceof Error ? error.message : String(error)
1248
+ );
1249
+ return null;
1250
+ }
1251
+ })
1252
+ );
1253
+ const availableByProtocol = /* @__PURE__ */ new Map();
1254
+ for (const result of poolResults) {
1255
+ if (result) availableByProtocol.set(result[0], result[1]);
1256
+ }
1257
+ const customizations = toZyfaiCustomizations(
1258
+ policy,
1259
+ availableByProtocol,
1260
+ chainsByProtocol
1261
+ );
1262
+ debugLog("zyfai:pools", "derived customizeBatch payload", {
1263
+ strategy,
1264
+ availableProtocolCount: availableByProtocol.size,
1265
+ customizations
1266
+ });
1267
+ if (customizations.length === 0) {
1268
+ debugLog("zyfai:pools", "skipping customizeBatch \u2014 nothing to send");
1269
+ return;
1270
+ }
1271
+ await this.sdk.customizeBatch(customizations);
1272
+ debugLog("zyfai:pools", "wrote customizeBatch", {
1273
+ count: customizations.length
1274
+ });
1275
+ } catch (error) {
1276
+ console.warn(
1277
+ "[zyfai] applyPoolPolicy failed (non-fatal):",
1278
+ error instanceof Error ? error.message : String(error)
1279
+ );
1280
+ }
1281
+ }
1116
1282
  async deposit(state, chainId, amount, asset, depositCallback) {
1117
1283
  try {
1118
1284
  await this.connectFull(state, chainId);
@@ -1908,6 +2074,7 @@ var VIEM_CHAIN2 = {
1908
2074
  42161: arbitrum2,
1909
2075
  1: mainnet2
1910
2076
  };
2077
+ var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
1911
2078
  var SPONSORED_WETH_BY_CHAIN = {
1912
2079
  8453: "0x4200000000000000000000000000000000000006",
1913
2080
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -1936,6 +2103,7 @@ var OwneySDK = class {
1936
2103
  zyfaiRpcUrls;
1937
2104
  routingApiBaseUrl;
1938
2105
  referralSource;
2106
+ surfliquidApiBaseUrl;
1939
2107
  cachedSponsoredCallback = null;
1940
2108
  cachedWethSponsoredCallback = null;
1941
2109
  initializingAgentsPromise = null;
@@ -1945,6 +2113,7 @@ var OwneySDK = class {
1945
2113
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
1946
2114
  this.routingApiBaseUrl = config.routingApiBaseUrl;
1947
2115
  this.referralSource = config.referralSource;
2116
+ this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
1948
2117
  }
1949
2118
  /**
1950
2119
  * Establish connection state for all subsequent SDK calls.
@@ -2152,8 +2321,8 @@ var OwneySDK = class {
2152
2321
  this.initializingAgentsPromise = (async () => {
2153
2322
  const agentKeys = await fetchAgentKeys(this.apiKey);
2154
2323
  this.disabledAgents.clear();
2155
- for (const { key, agent_type, is_enabled } of agentKeys) {
2156
- const agent = this.createAgent(agent_type, key);
2324
+ for (const { key: key2, agent_type, project_name, is_enabled } of agentKeys) {
2325
+ const agent = await this.createAgent(agent_type, key2, project_name);
2157
2326
  if (!agent) continue;
2158
2327
  this.agents.set(agent_type, agent);
2159
2328
  if (is_enabled === false) {
@@ -2174,9 +2343,21 @@ var OwneySDK = class {
2174
2343
  this.initializingAgentsPromise = null;
2175
2344
  }
2176
2345
  }
2177
- createAgent(agentId, key) {
2346
+ async createAgent(agentId, key2, projectName) {
2178
2347
  if (agentId === "zyfai") {
2179
- return new ZyfaiAgent(key, this.zyfaiRpcUrls, this.referralSource);
2348
+ return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2349
+ }
2350
+ if (agentId === "surfliquid") {
2351
+ if (!projectName) return null;
2352
+ const { SurfLiquidAgent } = await import("./surfliquid.agent-OMOYXWLP.js");
2353
+ return new SurfLiquidAgent({
2354
+ projectName,
2355
+ appId: key2,
2356
+ // Same-origin proxy override — SurfLiquid auth is an httpOnly cookie,
2357
+ // which WebKit (iOS) discards when it is third-party. See
2358
+ // OwneySDKConfig.surfliquidApiBaseUrl.
2359
+ apiBaseUrl: this.surfliquidApiBaseUrl
2360
+ });
2180
2361
  }
2181
2362
  return null;
2182
2363
  }
@@ -2332,18 +2513,30 @@ var OwneySDK = class {
2332
2513
  );
2333
2514
  }
2334
2515
  const agentResults = {};
2516
+ const agentErrors = {};
2335
2517
  for (const { agent, amount: agentAmount } of agentAmounts) {
2336
- agentResults[agent.id] = await this.depositWithFallback(
2337
- agent,
2338
- state,
2339
- chainId,
2340
- agentAmount,
2341
- asset,
2342
- effectiveCallback,
2343
- depositCallback
2518
+ try {
2519
+ agentResults[agent.id] = await this.depositWithFallback(
2520
+ agent,
2521
+ state,
2522
+ chainId,
2523
+ agentAmount,
2524
+ asset,
2525
+ effectiveCallback,
2526
+ depositCallback
2527
+ );
2528
+ } catch (error) {
2529
+ agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
2530
+ }
2531
+ }
2532
+ if (Object.keys(agentResults).length === 0) {
2533
+ throw new OwneyError(
2534
+ "DEPOSIT_ALL_FAILED",
2535
+ `All ${agentAmounts.length} agent deposit(s) failed.`,
2536
+ { agentErrors }
2344
2537
  );
2345
2538
  }
2346
- return { agentResults };
2539
+ return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
2347
2540
  }
2348
2541
  /**
2349
2542
  * Invokes `agent.deposit` with the resolved sponsored callback, composing
@@ -2500,6 +2693,34 @@ var OwneySDK = class {
2500
2693
  }
2501
2694
  return eligible;
2502
2695
  }
2696
+ /**
2697
+ * Agent ids the routing API provisioned for this org that support the given
2698
+ * chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
2699
+ * surfliquid first). Returns `[]` when the org has no compatible agent — never
2700
+ * throws on an empty org. Loads agent keys on first call (apiKey only, no
2701
+ * wallet), so the UI can resolve which agent to use before the user connects.
2702
+ *
2703
+ * This is the source of truth for agent availability: an agent appears here
2704
+ * iff the routing API returned its key. No per-app feature flags.
2705
+ */
2706
+ async getEligibleAgentIds(chainId, asset) {
2707
+ try {
2708
+ await this.ensureAgentsInitialized();
2709
+ } catch (error) {
2710
+ if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
2711
+ return [];
2712
+ }
2713
+ throw error;
2714
+ }
2715
+ return [...this.agents.values()].filter((agent) => {
2716
+ const chainAssets = agent.supportedAssets.find(
2717
+ (sa) => sa.chainId === chainId
2718
+ );
2719
+ return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
2720
+ }).map((agent) => agent.id).sort(
2721
+ (a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
2722
+ );
2723
+ }
2503
2724
  // --- Fund operations ---
2504
2725
  /**
2505
2726
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
@@ -2535,9 +2756,17 @@ var OwneySDK = class {
2535
2756
  }
2536
2757
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
2537
2758
  if (!amount) {
2759
+ const aggregated2 = await this.getBalances();
2760
+ const funded = projectAgentBalancesForAsset(
2761
+ eligibleAgents,
2762
+ aggregated2.agentBalances,
2763
+ chainId,
2764
+ asset,
2765
+ assetInfo.decimals
2766
+ ).filter((b) => b.balance > 0n);
2538
2767
  const results2 = {};
2539
2768
  const agentErrors2 = {};
2540
- for (const agent of eligibleAgents) {
2769
+ for (const { agent } of funded) {
2541
2770
  try {
2542
2771
  results2[agent.id] = await agent.withdraw(state, chainId, token);
2543
2772
  } catch (err) {
@@ -2554,9 +2783,10 @@ var OwneySDK = class {
2554
2783
  { asset, agentErrors: agentErrors2 }
2555
2784
  );
2556
2785
  }
2786
+ const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
2557
2787
  return {
2558
2788
  agentResult: results2,
2559
- totalWithdrawn: sumWithdrawnAmount(results2).toString()
2789
+ totalWithdrawn: totalWithdrawn.toString()
2560
2790
  };
2561
2791
  }
2562
2792
  const requested = BigInt(amount);
@@ -3073,22 +3303,22 @@ var readLegacySiwxSession = (store, address) => {
3073
3303
  const legacyKeys = [];
3074
3304
  try {
3075
3305
  for (let i = 0; i < store.length; i++) {
3076
- const key = store.key(i);
3077
- if (key?.startsWith(prefix)) legacyKeys.push(key);
3306
+ const key2 = store.key(i);
3307
+ if (key2?.startsWith(prefix)) legacyKeys.push(key2);
3078
3308
  }
3079
3309
  } catch {
3080
3310
  return null;
3081
3311
  }
3082
3312
  let adopted = null;
3083
- for (const key of legacyKeys) {
3313
+ for (const key2 of legacyKeys) {
3084
3314
  if (!adopted) {
3085
3315
  try {
3086
- adopted = JSON.parse(store.getItem(key) ?? "");
3316
+ adopted = JSON.parse(store.getItem(key2) ?? "");
3087
3317
  } catch {
3088
3318
  }
3089
3319
  }
3090
3320
  try {
3091
- store.removeItem(key);
3321
+ store.removeItem(key2);
3092
3322
  } catch {
3093
3323
  }
3094
3324
  }
@@ -3096,11 +3326,11 @@ var readLegacySiwxSession = (store, address) => {
3096
3326
  };
3097
3327
  var readSiwxSession = (address, chainId) => {
3098
3328
  if (typeof window === "undefined") return null;
3099
- const key = buildKey2(address);
3329
+ const key2 = buildKey2(address);
3100
3330
  const store = storage2();
3101
3331
  let raw = null;
3102
3332
  try {
3103
- raw = store?.getItem(key) ?? null;
3333
+ raw = store?.getItem(key2) ?? null;
3104
3334
  } catch {
3105
3335
  raw = null;
3106
3336
  }
@@ -3108,9 +3338,9 @@ var readSiwxSession = (address, chainId) => {
3108
3338
  try {
3109
3339
  return JSON.parse(raw);
3110
3340
  } catch {
3111
- memorySiwxSessions.delete(key);
3341
+ memorySiwxSessions.delete(key2);
3112
3342
  try {
3113
- store?.removeItem(key);
3343
+ store?.removeItem(key2);
3114
3344
  } catch {
3115
3345
  }
3116
3346
  return null;
@@ -3121,24 +3351,24 @@ var readSiwxSession = (address, chainId) => {
3121
3351
  writeSiwxSession(address, chainId, legacy);
3122
3352
  return legacy;
3123
3353
  }
3124
- return memorySiwxSessions.get(key) ?? null;
3354
+ return memorySiwxSessions.get(key2) ?? null;
3125
3355
  };
3126
3356
  var writeSiwxSession = (address, _chainId, session) => {
3127
3357
  if (typeof window === "undefined") return;
3128
- const key = buildKey2(address);
3129
- memorySiwxSessions.set(key, session);
3358
+ const key2 = buildKey2(address);
3359
+ memorySiwxSessions.set(key2, session);
3130
3360
  const store = storage2();
3131
3361
  try {
3132
- store?.setItem(key, JSON.stringify(session));
3362
+ store?.setItem(key2, JSON.stringify(session));
3133
3363
  } catch {
3134
3364
  }
3135
3365
  };
3136
3366
  var clearSiwxSession = (address, _chainId) => {
3137
- const key = buildKey2(address);
3138
- memorySiwxSessions.delete(key);
3367
+ const key2 = buildKey2(address);
3368
+ memorySiwxSessions.delete(key2);
3139
3369
  const store = storage2();
3140
3370
  try {
3141
- store?.removeItem(key);
3371
+ store?.removeItem(key2);
3142
3372
  } catch {
3143
3373
  }
3144
3374
  readLegacySiwxSession(store, address);