@owney/sdk 0.7.10-beta.0 → 0.7.11

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,17 +1,3 @@
1
- import {
2
- AgentChainIncompatibleError,
3
- AgentNotFoundError,
4
- ERC20_ALLOWANCE_ABI,
5
- MAX_UINT256,
6
- NotConnectedError,
7
- OwneyError,
8
- PERMIT2_ADDRESS,
9
- buildPermitTransferFromTypedData,
10
- randomPermit2Nonce,
11
- readErc20Balance,
12
- readPermit2Allowance
13
- } from "./chunk-PWIKIQOX.js";
14
-
15
1
  // src/agents/zyfai/zyfai.agent.ts
16
2
  import { SMART_SESSIONS_VALIDATOR, ZyfaiSDK } from "@zyfai/sdk";
17
3
  import { createPublicClient, http, parseAbi } from "viem";
@@ -70,6 +56,55 @@ var SupportedAssets = [
70
56
  }
71
57
  ];
72
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
+
73
108
  // src/lib/utils.ts
74
109
  var isValidChainId = (chainId) => {
75
110
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
@@ -81,12 +116,6 @@ var isValidChainId = (chainId) => {
81
116
  }
82
117
  return chainId;
83
118
  };
84
- function isSameAsset(tokenSymbol, asset) {
85
- const token = tokenSymbol.toLowerCase();
86
- const target = asset.toLowerCase();
87
- if (token === target) return true;
88
- return target === "weth" && token === "eth" || target === "eth" && token === "weth";
89
- }
90
119
  function hexToDecimal(hex, decimals = 6) {
91
120
  const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
92
121
  const parsed = BigInt(normalized);
@@ -120,10 +149,6 @@ var DECIMALS_BY_SYMBOL = {
120
149
  wbtc: 8,
121
150
  cbbtc: 8
122
151
  };
123
- function canonicalSymbol(symbol) {
124
- const upper = symbol.toUpperCase();
125
- return upper === "ETH" ? "WETH" : upper;
126
- }
127
152
  function tokenDecimals(symbol, explicit) {
128
153
  if (typeof explicit === "number" && Number.isFinite(explicit))
129
154
  return explicit;
@@ -188,89 +213,55 @@ function mapPendingAllocations(raw) {
188
213
  function mapBalances(raw, _chainId, smartWallet) {
189
214
  const portfolio = raw.portfolio;
190
215
  const portfolioByChain = portfolio.portfolioByChain ?? {};
191
- const rawPositions = portfolio.positions ?? [];
192
- const detailByKey = /* @__PURE__ */ new Map();
193
- for (const p of rawPositions) {
194
- const cid = resolveChainId(p.chain ?? "");
195
- const symbol = canonicalSymbol(p.token_symbol ?? "");
196
- if (Number.isFinite(cid) && symbol) detailByKey.set(`${cid}:${symbol}`, p);
197
- }
198
216
  let totalBalance = 0;
199
217
  const tokens = [];
200
- const positions = [];
201
218
  for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
202
219
  const positionChainId = Number(chainIdKey);
203
- const chainName2 = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
204
220
  for (const [
205
221
  symbol,
206
222
  { balance, balanceWithFee, decimals }
207
223
  ] of Object.entries(tokensBySymbol ?? {})) {
208
- const amount = hexToDecimal(
209
- balanceWithFee ?? balance ?? "0",
210
- tokenDecimals(symbol, decimals)
211
- );
224
+ const amount = hexToDecimal(balanceWithFee ?? balance ?? "0", decimals);
212
225
  totalBalance += amount;
213
226
  tokens.push({
214
- chain: chainName2,
227
+ chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
215
228
  chainId: positionChainId,
216
229
  asset: symbol,
217
230
  amount: String(amount)
218
231
  });
219
- const detail = detailByKey.get(
220
- `${positionChainId}:${canonicalSymbol(symbol)}`
221
- );
222
- positions.push({
223
- chain: chainName2,
224
- protocol: detail?.protocol_name ?? detail?.protocol_id ?? "",
225
- protocolId: detail?.protocol_id,
226
- pool: detail?.pool,
227
- // Report the token the way the rest of owney spells it ("WETH", as the
228
- // pre-portfolioByChain mapper did), not Zyfai's internal "eth".
229
- asset: canonicalSymbol(symbol),
230
- amount: String(amount),
231
- apy: detail?.pool_apy,
232
- tvl: detail?.pool_tvl,
233
- // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
234
- // structural read keeps it undefined today and auto-populates later.
235
- liquidity: detail?.liquidity
236
- });
237
232
  }
238
233
  }
239
- if (positions.length === 0) {
240
- positions.push(
241
- ...(portfolio.positions ?? []).map((p) => ({
242
- chain: p.chain ?? "",
243
- protocol: p.protocol_name ?? p.protocol_id ?? "",
244
- protocolId: p.protocol_id,
245
- pool: p.pool,
246
- asset: p.token_symbol ?? "",
247
- // Fee-adjusted underlying amounts are decimal base-unit strings; the
248
- // legacy `amount` fallback is hex. Runtime getBalances payloads often
249
- // carry `decimals` (e.g. 18 for WETH) — prefer it when present,
250
- // otherwise fall back to the per-symbol lookup.
251
- amount: String(
252
- p.underlyingAmountWithFee ?? p.underlyingAmount ? normalizeDecimalPlace(
253
- p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0",
254
- tokenDecimals(
255
- p.token_symbol,
256
- p.decimals
257
- )
258
- ) : hexToDecimal(
259
- p.amount || "0",
260
- tokenDecimals(
261
- p.token_symbol,
262
- p.decimals
263
- )
264
- )
265
- ),
266
- apy: p.pool_apy,
267
- tvl: p.pool_tvl,
268
- // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
269
- // structural read keeps it undefined today and auto-populates later.
270
- liquidity: p.liquidity
271
- }))
272
- );
273
- }
234
+ const positions = (portfolio.positions ?? []).map((p) => ({
235
+ chain: p.chain ?? "",
236
+ protocol: p.protocol_name ?? p.protocol_id ?? "",
237
+ protocolId: p.protocol_id,
238
+ pool: p.pool,
239
+ asset: p.token_symbol ?? "",
240
+ // Fee-adjusted underlying amounts are decimal base-unit strings. The
241
+ // legacy `amount` fallback is hex. Runtime
242
+ // getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
243
+ // when present, otherwise fall back to the per-symbol lookup.
244
+ amount: String(
245
+ p.underlyingAmountWithFee ?? p.underlyingAmount ? normalizeDecimalPlace(
246
+ p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0",
247
+ tokenDecimals(
248
+ p.token_symbol,
249
+ p.decimals
250
+ )
251
+ ) : hexToDecimal(
252
+ p.amount || "0",
253
+ tokenDecimals(
254
+ p.token_symbol,
255
+ p.decimals
256
+ )
257
+ )
258
+ ),
259
+ apy: p.pool_apy,
260
+ tvl: p.pool_tvl,
261
+ // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
262
+ // structural read keeps it undefined today and auto-populates later.
263
+ liquidity: p.liquidity
264
+ }));
274
265
  const pendingAllocations = mapPendingAllocations(
275
266
  portfolio.staleBalances
276
267
  );
@@ -861,7 +852,7 @@ var VIEM_CHAIN = { 8453: base, 42161: arbitrum, 1: mainnet };
861
852
  var SPLITTING_ASSETS = ["USDC", "WETH"];
862
853
  var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
863
854
  var EARNINGS_REFRESH_COOLDOWN_MS = 60 * 60 * 1e3;
864
- var ZyfaiAgent = class {
855
+ var ZyfaiAgent = class _ZyfaiAgent {
865
856
  id = "zyfai";
866
857
  supportedChainIds = [8453, 42161, 1];
867
858
  supportedAssets = [
@@ -1353,37 +1344,6 @@ var ZyfaiAgent = class {
1353
1344
  );
1354
1345
  }
1355
1346
  }
1356
- /**
1357
- * Records the deposit with Zyfai, retrying transient failures.
1358
- *
1359
- * This runs AFTER the transfer has already landed on-chain, so it must never
1360
- * fail the deposit — the funds moved. But it is also the ONLY source of the
1361
- * "Top up wallet" entry the history is built from: Zyfai auto-deploys the
1362
- * balance it detects either way, so when this call is lost the user's deposit
1363
- * never appears in Activity (an earlier withdrawal stays the newest row) and
1364
- * nothing ever backfills it. Retry, then log loudly enough to be recoverable.
1365
- */
1366
- async logDepositWithRetry(chainId, txHash, amount, tokenAddress) {
1367
- const ATTEMPTS = 3;
1368
- const RETRY_DELAY_MS = 1e3;
1369
- for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
1370
- try {
1371
- await (tokenAddress ? this.sdk.logDeposit(chainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(chainId, txHash, amount));
1372
- return;
1373
- } catch (logError) {
1374
- if (attempt === ATTEMPTS) {
1375
- console.error(
1376
- "[owney-sdk] Deposit landed on-chain but logDeposit failed \u2014 it will be missing from Zyfai history:",
1377
- { txHash, chainId, amount, tokenAddress, error: logError }
1378
- );
1379
- return;
1380
- }
1381
- await new Promise(
1382
- (resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt)
1383
- );
1384
- }
1385
- }
1386
- }
1387
1347
  /**
1388
1348
  * True when `smartWallet` is the backend-managed pool wallet Zyfai assigned
1389
1349
  * to this EOA — the "new wallet" kind that is predeployed on the chains it
@@ -1687,12 +1647,14 @@ var ZyfaiAgent = class {
1687
1647
  );
1688
1648
  const txHash = await depositCallback(smartWallet, validChainId, amount);
1689
1649
  const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
1690
- await this.logDepositWithRetry(
1691
- validChainId,
1692
- txHash,
1693
- amount,
1694
- tokenAddress
1695
- );
1650
+ try {
1651
+ await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
1652
+ } catch (logError) {
1653
+ console.warn(
1654
+ "[owney-sdk] Deposit landed on-chain but logDeposit failed (non-fatal):",
1655
+ logError
1656
+ );
1657
+ }
1696
1658
  return { txHash, smartWallet, amount };
1697
1659
  }
1698
1660
  await this.ensureWalletDeployed(this.getAddress(), validChainId);
@@ -1760,9 +1722,30 @@ var ZyfaiAgent = class {
1760
1722
  const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
1761
1723
  return mapApyHistory(raw, chainId, tokenSymbol);
1762
1724
  }
1725
+ /**
1726
+ * Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
1727
+ * takes lowercase `assetType` and denominates WETH as "eth" (the same
1728
+ * spelling `resolveWithdrawnAmounts` normalizes on the way in). Anything
1729
+ * unrecognised returns undefined so the request stays unfiltered rather
1730
+ * than silently narrowing to nothing.
1731
+ */
1732
+ static toZyfaiAssetType(tokenSymbol) {
1733
+ switch (tokenSymbol?.toUpperCase()) {
1734
+ case "USDC":
1735
+ return "usdc";
1736
+ case "WETH":
1737
+ case "ETH":
1738
+ return "eth";
1739
+ case "EURC":
1740
+ return "eurc";
1741
+ default:
1742
+ return void 0;
1743
+ }
1744
+ }
1763
1745
  async getHistory(state, chainId, options) {
1764
1746
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
1765
1747
  const limit = options?.limit ?? 10;
1748
+ const assetType = _ZyfaiAgent.toZyfaiAssetType(options?.tokenSymbol);
1766
1749
  const cursor = options?.cursor ? decodeHistoryCursor(options.cursor) : null;
1767
1750
  const toDate = cursor?.toDate ?? options?.toDate ?? (/* @__PURE__ */ new Date()).toISOString();
1768
1751
  let offset = cursor?.rawOffset ?? 0;
@@ -1776,7 +1759,12 @@ var ZyfaiAgent = class {
1776
1759
  limit: backendPageSize,
1777
1760
  offset,
1778
1761
  fromDate: options?.fromDate,
1779
- toDate
1762
+ toDate,
1763
+ // Filtering server-side means the pages coming back are already the
1764
+ // asset the caller asked for, so the loop below fills `limit` from
1765
+ // relevant entries instead of spending its budget on the other
1766
+ // asset's rows and handing back a page that filters to nothing.
1767
+ assetType
1780
1768
  });
1781
1769
  raw.data.forEach((entry, idx) => {
1782
1770
  if (entry.chainId === validChainId) {
@@ -1947,10 +1935,11 @@ async function withFailureReporting(apiKey, agentType, fn) {
1947
1935
  // src/lib/helpers/withdraw-helper.ts
1948
1936
  import { parseUnits } from "viem";
1949
1937
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
1938
+ const target = asset.toUpperCase();
1950
1939
  return agents.map((agent) => {
1951
1940
  const agentBalance = aggregated[agent.id];
1952
1941
  const tokenBalance = agentBalance?.tokens.find(
1953
- (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
1942
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
1954
1943
  );
1955
1944
  if (!tokenBalance) return { agent, balance: 0n };
1956
1945
  return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
@@ -2220,6 +2209,84 @@ async function getSponsorRelayerAddress(input) {
2220
2209
  return parsed.data.relayer;
2221
2210
  }
2222
2211
 
2212
+ // src/lib/permit2.ts
2213
+ import { bytesToHex as bytesToHex2 } from "viem";
2214
+ var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2215
+ var MAX_UINT256 = 2n ** 256n - 1n;
2216
+ var ERC20_ALLOWANCE_ABI = [
2217
+ {
2218
+ type: "function",
2219
+ name: "allowance",
2220
+ stateMutability: "view",
2221
+ inputs: [
2222
+ { name: "owner", type: "address" },
2223
+ { name: "spender", type: "address" }
2224
+ ],
2225
+ outputs: [{ name: "", type: "uint256" }]
2226
+ },
2227
+ {
2228
+ type: "function",
2229
+ name: "approve",
2230
+ stateMutability: "nonpayable",
2231
+ inputs: [
2232
+ { name: "spender", type: "address" },
2233
+ { name: "amount", type: "uint256" }
2234
+ ],
2235
+ outputs: [{ name: "", type: "bool" }]
2236
+ },
2237
+ {
2238
+ type: "function",
2239
+ name: "balanceOf",
2240
+ stateMutability: "view",
2241
+ inputs: [{ name: "account", type: "address" }],
2242
+ outputs: [{ name: "", type: "uint256" }]
2243
+ }
2244
+ ];
2245
+ function buildPermitTransferFromTypedData(input) {
2246
+ return {
2247
+ domain: {
2248
+ name: "Permit2",
2249
+ chainId: input.chainId,
2250
+ verifyingContract: PERMIT2_ADDRESS
2251
+ },
2252
+ types: {
2253
+ PermitTransferFrom: [
2254
+ { name: "permitted", type: "TokenPermissions" },
2255
+ { name: "spender", type: "address" },
2256
+ { name: "nonce", type: "uint256" },
2257
+ { name: "deadline", type: "uint256" }
2258
+ ],
2259
+ TokenPermissions: [
2260
+ { name: "token", type: "address" },
2261
+ { name: "amount", type: "uint256" }
2262
+ ]
2263
+ },
2264
+ primaryType: "PermitTransferFrom",
2265
+ message: input.message
2266
+ };
2267
+ }
2268
+ function randomPermit2Nonce() {
2269
+ const bytes = new Uint8Array(32);
2270
+ globalThis.crypto.getRandomValues(bytes);
2271
+ return BigInt(bytesToHex2(bytes));
2272
+ }
2273
+ async function readPermit2Allowance(publicClient, token, owner) {
2274
+ return publicClient.readContract({
2275
+ address: token,
2276
+ abi: ERC20_ALLOWANCE_ABI,
2277
+ functionName: "allowance",
2278
+ args: [owner, PERMIT2_ADDRESS]
2279
+ });
2280
+ }
2281
+ async function readErc20Balance(publicClient, token, owner) {
2282
+ return publicClient.readContract({
2283
+ address: token,
2284
+ abi: ERC20_ALLOWANCE_ABI,
2285
+ functionName: "balanceOf",
2286
+ args: [owner]
2287
+ });
2288
+ }
2289
+
2223
2290
  // src/lib/chain-guard.ts
2224
2291
  var CHAIN_NAMES = {
2225
2292
  1: "Ethereum",
@@ -2538,7 +2605,6 @@ var VIEM_CHAIN2 = {
2538
2605
  42161: arbitrum2,
2539
2606
  1: mainnet2
2540
2607
  };
2541
- var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
2542
2608
  var SPONSORED_WETH_BY_CHAIN = {
2543
2609
  8453: "0x4200000000000000000000000000000000000006",
2544
2610
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -2567,7 +2633,6 @@ var OwneySDK = class {
2567
2633
  zyfaiRpcUrls;
2568
2634
  routingApiBaseUrl;
2569
2635
  referralSource;
2570
- surfliquidApiBaseUrl;
2571
2636
  cachedSponsoredCallback = null;
2572
2637
  cachedWethSponsoredCallback = null;
2573
2638
  paymasterServiceUrl;
@@ -2580,7 +2645,6 @@ var OwneySDK = class {
2580
2645
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2581
2646
  this.paymasterServiceUrl = config.paymasterServiceUrl;
2582
2647
  this.referralSource = config.referralSource;
2583
- this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
2584
2648
  }
2585
2649
  /**
2586
2650
  * Establish connection state for all subsequent SDK calls.
@@ -2810,8 +2874,8 @@ var OwneySDK = class {
2810
2874
  this.initializingAgentsPromise = (async () => {
2811
2875
  const agentKeys = await fetchAgentKeys(this.apiKey);
2812
2876
  this.disabledAgents.clear();
2813
- for (const { key: key2, agent_type, project_name, is_enabled } of agentKeys) {
2814
- const agent = await this.createAgent(agent_type, key2, project_name);
2877
+ for (const { key: key2, agent_type, is_enabled } of agentKeys) {
2878
+ const agent = this.createAgent(agent_type, key2);
2815
2879
  if (!agent) continue;
2816
2880
  this.agents.set(agent_type, agent);
2817
2881
  if (is_enabled === false) {
@@ -2832,22 +2896,10 @@ var OwneySDK = class {
2832
2896
  this.initializingAgentsPromise = null;
2833
2897
  }
2834
2898
  }
2835
- async createAgent(agentId, key2, projectName) {
2899
+ createAgent(agentId, key2) {
2836
2900
  if (agentId === "zyfai") {
2837
2901
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2838
2902
  }
2839
- if (agentId === "surfliquid") {
2840
- if (!projectName) return null;
2841
- const { SurfLiquidAgent } = await import("./surfliquid.agent-VNGQUSXN.js");
2842
- return new SurfLiquidAgent({
2843
- projectName,
2844
- appId: key2,
2845
- // Same-origin proxy override — SurfLiquid auth is an httpOnly cookie,
2846
- // which WebKit (iOS) discards when it is third-party. See
2847
- // OwneySDKConfig.surfliquidApiBaseUrl.
2848
- apiBaseUrl: this.surfliquidApiBaseUrl
2849
- });
2850
- }
2851
2903
  return null;
2852
2904
  }
2853
2905
  // --- Account lifecycle ---
@@ -3002,30 +3054,18 @@ var OwneySDK = class {
3002
3054
  );
3003
3055
  }
3004
3056
  const agentResults = {};
3005
- const agentErrors = {};
3006
3057
  for (const { agent, amount: agentAmount } of agentAmounts) {
3007
- try {
3008
- agentResults[agent.id] = await this.depositWithFallback(
3009
- agent,
3010
- state,
3011
- chainId,
3012
- agentAmount,
3013
- asset,
3014
- effectiveCallback,
3015
- depositCallback
3016
- );
3017
- } catch (error) {
3018
- agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
3019
- }
3020
- }
3021
- if (Object.keys(agentResults).length === 0) {
3022
- throw new OwneyError(
3023
- "DEPOSIT_ALL_FAILED",
3024
- `All ${agentAmounts.length} agent deposit(s) failed.`,
3025
- { agentErrors }
3058
+ agentResults[agent.id] = await this.depositWithFallback(
3059
+ agent,
3060
+ state,
3061
+ chainId,
3062
+ agentAmount,
3063
+ asset,
3064
+ effectiveCallback,
3065
+ depositCallback
3026
3066
  );
3027
3067
  }
3028
- return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
3068
+ return { agentResults };
3029
3069
  }
3030
3070
  /**
3031
3071
  * Invokes `agent.deposit` with the resolved sponsored callback, composing
@@ -3141,8 +3181,9 @@ var OwneySDK = class {
3141
3181
  async hasExistingBalance(agent, state, chainId, asset) {
3142
3182
  try {
3143
3183
  const balance = await agent.getBalances(state, chainId);
3184
+ const target = asset.toLowerCase();
3144
3185
  const token = balance.tokens.find(
3145
- (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
3186
+ (t) => t.chainId === chainId && t.asset.toLowerCase() === target
3146
3187
  );
3147
3188
  return !!token && Number(token.amount) > 0;
3148
3189
  } catch {
@@ -3181,34 +3222,6 @@ var OwneySDK = class {
3181
3222
  }
3182
3223
  return eligible;
3183
3224
  }
3184
- /**
3185
- * Agent ids the routing API provisioned for this org that support the given
3186
- * chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
3187
- * surfliquid first). Returns `[]` when the org has no compatible agent — never
3188
- * throws on an empty org. Loads agent keys on first call (apiKey only, no
3189
- * wallet), so the UI can resolve which agent to use before the user connects.
3190
- *
3191
- * This is the source of truth for agent availability: an agent appears here
3192
- * iff the routing API returned its key. No per-app feature flags.
3193
- */
3194
- async getEligibleAgentIds(chainId, asset) {
3195
- try {
3196
- await this.ensureAgentsInitialized();
3197
- } catch (error) {
3198
- if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
3199
- return [];
3200
- }
3201
- throw error;
3202
- }
3203
- return [...this.agents.values()].filter((agent) => {
3204
- const chainAssets = agent.supportedAssets.find(
3205
- (sa) => sa.chainId === chainId
3206
- );
3207
- return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
3208
- }).map((agent) => agent.id).sort(
3209
- (a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
3210
- );
3211
- }
3212
3225
  // --- Fund operations ---
3213
3226
  /**
3214
3227
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
@@ -3244,17 +3257,9 @@ var OwneySDK = class {
3244
3257
  }
3245
3258
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
3246
3259
  if (!amount) {
3247
- const aggregated2 = await this.getBalances();
3248
- const funded = projectAgentBalancesForAsset(
3249
- eligibleAgents,
3250
- aggregated2.agentBalances,
3251
- chainId,
3252
- asset,
3253
- assetInfo.decimals
3254
- ).filter((b) => b.balance > 0n);
3255
3260
  const results2 = {};
3256
3261
  const agentErrors2 = {};
3257
- for (const { agent } of funded) {
3262
+ for (const agent of eligibleAgents) {
3258
3263
  try {
3259
3264
  results2[agent.id] = await agent.withdraw(state, chainId, token);
3260
3265
  } catch (err) {
@@ -3271,10 +3276,9 @@ var OwneySDK = class {
3271
3276
  { asset, agentErrors: agentErrors2 }
3272
3277
  );
3273
3278
  }
3274
- const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
3275
3279
  return {
3276
3280
  agentResult: results2,
3277
- totalWithdrawn: totalWithdrawn.toString()
3281
+ totalWithdrawn: sumWithdrawnAmount(results2).toString()
3278
3282
  };
3279
3283
  }
3280
3284
  const requested = BigInt(amount);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.10-beta.0",
3
+ "version": "0.7.11",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -23,9 +23,7 @@
23
23
  "test:watch": "vitest"
24
24
  },
25
25
  "dependencies": {
26
- "@surf_liquid/core-sdk": "^0.4.0",
27
- "@zyfai/sdk": "0.2.48",
28
- "ethers": "^6.17.0",
26
+ "@zyfai/sdk": "0.2.50",
29
27
  "siwe": "^3.0.0",
30
28
  "viem": "^2.48.1"
31
29
  },