@owney/sdk 0.7.12-beta.0 → 0.7.12-beta.2

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
  );
@@ -1353,37 +1344,6 @@ var ZyfaiAgent = class _ZyfaiAgent {
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 _ZyfaiAgent {
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);
@@ -1973,10 +1935,11 @@ async function withFailureReporting(apiKey, agentType, fn) {
1973
1935
  // src/lib/helpers/withdraw-helper.ts
1974
1936
  import { parseUnits } from "viem";
1975
1937
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
1938
+ const target = asset.toUpperCase();
1976
1939
  return agents.map((agent) => {
1977
1940
  const agentBalance = aggregated[agent.id];
1978
1941
  const tokenBalance = agentBalance?.tokens.find(
1979
- (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
1942
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
1980
1943
  );
1981
1944
  if (!tokenBalance) return { agent, balance: 0n };
1982
1945
  return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
@@ -2246,6 +2209,84 @@ async function getSponsorRelayerAddress(input) {
2246
2209
  return parsed.data.relayer;
2247
2210
  }
2248
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
+
2249
2290
  // src/lib/chain-guard.ts
2250
2291
  var CHAIN_NAMES = {
2251
2292
  1: "Ethereum",
@@ -2564,7 +2605,6 @@ var VIEM_CHAIN2 = {
2564
2605
  42161: arbitrum2,
2565
2606
  1: mainnet2
2566
2607
  };
2567
- var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
2568
2608
  var SPONSORED_WETH_BY_CHAIN = {
2569
2609
  8453: "0x4200000000000000000000000000000000000006",
2570
2610
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -2593,7 +2633,6 @@ var OwneySDK = class {
2593
2633
  zyfaiRpcUrls;
2594
2634
  routingApiBaseUrl;
2595
2635
  referralSource;
2596
- surfliquidApiBaseUrl;
2597
2636
  cachedSponsoredCallback = null;
2598
2637
  cachedWethSponsoredCallback = null;
2599
2638
  paymasterServiceUrl;
@@ -2606,7 +2645,6 @@ var OwneySDK = class {
2606
2645
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2607
2646
  this.paymasterServiceUrl = config.paymasterServiceUrl;
2608
2647
  this.referralSource = config.referralSource;
2609
- this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
2610
2648
  }
2611
2649
  /**
2612
2650
  * Establish connection state for all subsequent SDK calls.
@@ -2836,8 +2874,8 @@ var OwneySDK = class {
2836
2874
  this.initializingAgentsPromise = (async () => {
2837
2875
  const agentKeys = await fetchAgentKeys(this.apiKey);
2838
2876
  this.disabledAgents.clear();
2839
- for (const { key: key2, agent_type, project_name, is_enabled } of agentKeys) {
2840
- 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);
2841
2879
  if (!agent) continue;
2842
2880
  this.agents.set(agent_type, agent);
2843
2881
  if (is_enabled === false) {
@@ -2858,22 +2896,10 @@ var OwneySDK = class {
2858
2896
  this.initializingAgentsPromise = null;
2859
2897
  }
2860
2898
  }
2861
- async createAgent(agentId, key2, projectName) {
2899
+ createAgent(agentId, key2) {
2862
2900
  if (agentId === "zyfai") {
2863
2901
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2864
2902
  }
2865
- if (agentId === "surfliquid") {
2866
- if (!projectName) return null;
2867
- const { SurfLiquidAgent } = await import("./surfliquid.agent-VNGQUSXN.js");
2868
- return new SurfLiquidAgent({
2869
- projectName,
2870
- appId: key2,
2871
- // Same-origin proxy override — SurfLiquid auth is an httpOnly cookie,
2872
- // which WebKit (iOS) discards when it is third-party. See
2873
- // OwneySDKConfig.surfliquidApiBaseUrl.
2874
- apiBaseUrl: this.surfliquidApiBaseUrl
2875
- });
2876
- }
2877
2903
  return null;
2878
2904
  }
2879
2905
  // --- Account lifecycle ---
@@ -2923,12 +2949,7 @@ var OwneySDK = class {
2923
2949
  this.activeAgents.add(id);
2924
2950
  }
2925
2951
  state.chainId = chainId;
2926
- Promise.all(
2927
- agents.map(async (agent) => {
2928
- await agent.activateAgent(state, chainId);
2929
- await this.applyOrgPolicyTo(agent, state, chainId);
2930
- })
2931
- ).catch((error) => {
2952
+ this.activateAgentsInTurn(agents, state, chainId).catch((error) => {
2932
2953
  console.error("activateAgent background init failed:", error);
2933
2954
  });
2934
2955
  return;
@@ -2951,14 +2972,48 @@ var OwneySDK = class {
2951
2972
  const enabledCompatible = compatible.filter(
2952
2973
  (agent) => !this.isAgentDisabled(agent.id)
2953
2974
  );
2954
- Promise.all(
2955
- enabledCompatible.map(async (agent) => {
2975
+ this.activateAgentsInTurn(enabledCompatible, state, chainId).catch(
2976
+ (error) => {
2977
+ console.error("activateAgent background init failed:", error);
2978
+ }
2979
+ );
2980
+ }
2981
+ /**
2982
+ * Activate agents ONE AT A TIME, each followed by its org policy.
2983
+ *
2984
+ * Every agent opens its activation with its own wallet signature, each
2985
+ * exchanged for that agent's own backend session — Zyfai's SIWX for a JWT,
2986
+ * Sail's SIWE. Run in parallel, that puts two `personal_sign` requests on one
2987
+ * wallet connection in the same instant. Over WalletConnect both are
2988
+ * delivered to the wallet, which renders one signing request at a time and
2989
+ * queues or silently drops the other: neither promise settles, so activation
2990
+ * hangs until the caller's timeout with no prompt the user can act on. The
2991
+ * single-agent era never hit this — there was no second prompt to collide
2992
+ * with — so it appeared the moment a second agent became eligible.
2993
+ *
2994
+ * Serializing costs no real wall-clock: the user can only approve one prompt
2995
+ * at a time anyway.
2996
+ *
2997
+ * Every agent is attempted even if an earlier one fails, so one declined
2998
+ * signature can't deny the remaining agents their turn. The first failure is
2999
+ * rethrown (matching the previous `Promise.all` rejection) once all agents
3000
+ * have had a chance to activate.
3001
+ */
3002
+ async activateAgentsInTurn(agents, state, chainId) {
3003
+ let firstError = null;
3004
+ for (const agent of agents) {
3005
+ try {
2956
3006
  await agent.activateAgent(state, chainId);
2957
3007
  await this.applyOrgPolicyTo(agent, state, chainId);
2958
- })
2959
- ).catch((error) => {
2960
- console.error("activateAgent background init failed:", error);
2961
- });
3008
+ } catch (error) {
3009
+ if (firstError === null) {
3010
+ firstError = error;
3011
+ } else {
3012
+ console.error(`activateAgent(${agent.id}) failed:`, error);
3013
+ }
3014
+ }
3015
+ }
3016
+ if (firstError !== null) throw firstError;
2962
3017
  }
2963
3018
  /**
2964
3019
  * Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
@@ -3028,30 +3083,18 @@ var OwneySDK = class {
3028
3083
  );
3029
3084
  }
3030
3085
  const agentResults = {};
3031
- const agentErrors = {};
3032
3086
  for (const { agent, amount: agentAmount } of agentAmounts) {
3033
- try {
3034
- agentResults[agent.id] = await this.depositWithFallback(
3035
- agent,
3036
- state,
3037
- chainId,
3038
- agentAmount,
3039
- asset,
3040
- effectiveCallback,
3041
- depositCallback
3042
- );
3043
- } catch (error) {
3044
- agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
3045
- }
3046
- }
3047
- if (Object.keys(agentResults).length === 0) {
3048
- throw new OwneyError(
3049
- "DEPOSIT_ALL_FAILED",
3050
- `All ${agentAmounts.length} agent deposit(s) failed.`,
3051
- { agentErrors }
3087
+ agentResults[agent.id] = await this.depositWithFallback(
3088
+ agent,
3089
+ state,
3090
+ chainId,
3091
+ agentAmount,
3092
+ asset,
3093
+ effectiveCallback,
3094
+ depositCallback
3052
3095
  );
3053
3096
  }
3054
- return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
3097
+ return { agentResults };
3055
3098
  }
3056
3099
  /**
3057
3100
  * Invokes `agent.deposit` with the resolved sponsored callback, composing
@@ -3167,8 +3210,9 @@ var OwneySDK = class {
3167
3210
  async hasExistingBalance(agent, state, chainId, asset) {
3168
3211
  try {
3169
3212
  const balance = await agent.getBalances(state, chainId);
3213
+ const target = asset.toLowerCase();
3170
3214
  const token = balance.tokens.find(
3171
- (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
3215
+ (t) => t.chainId === chainId && t.asset.toLowerCase() === target
3172
3216
  );
3173
3217
  return !!token && Number(token.amount) > 0;
3174
3218
  } catch {
@@ -3207,34 +3251,6 @@ var OwneySDK = class {
3207
3251
  }
3208
3252
  return eligible;
3209
3253
  }
3210
- /**
3211
- * Agent ids the routing API provisioned for this org that support the given
3212
- * chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
3213
- * surfliquid first). Returns `[]` when the org has no compatible agent — never
3214
- * throws on an empty org. Loads agent keys on first call (apiKey only, no
3215
- * wallet), so the UI can resolve which agent to use before the user connects.
3216
- *
3217
- * This is the source of truth for agent availability: an agent appears here
3218
- * iff the routing API returned its key. No per-app feature flags.
3219
- */
3220
- async getEligibleAgentIds(chainId, asset) {
3221
- try {
3222
- await this.ensureAgentsInitialized();
3223
- } catch (error) {
3224
- if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
3225
- return [];
3226
- }
3227
- throw error;
3228
- }
3229
- return [...this.agents.values()].filter((agent) => {
3230
- const chainAssets = agent.supportedAssets.find(
3231
- (sa) => sa.chainId === chainId
3232
- );
3233
- return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
3234
- }).map((agent) => agent.id).sort(
3235
- (a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
3236
- );
3237
- }
3238
3254
  // --- Fund operations ---
3239
3255
  /**
3240
3256
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
@@ -3270,17 +3286,9 @@ var OwneySDK = class {
3270
3286
  }
3271
3287
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
3272
3288
  if (!amount) {
3273
- const aggregated2 = await this.getBalances();
3274
- const funded = projectAgentBalancesForAsset(
3275
- eligibleAgents,
3276
- aggregated2.agentBalances,
3277
- chainId,
3278
- asset,
3279
- assetInfo.decimals
3280
- ).filter((b) => b.balance > 0n);
3281
3289
  const results2 = {};
3282
3290
  const agentErrors2 = {};
3283
- for (const { agent } of funded) {
3291
+ for (const agent of eligibleAgents) {
3284
3292
  try {
3285
3293
  results2[agent.id] = await agent.withdraw(state, chainId, token);
3286
3294
  } catch (err) {
@@ -3297,10 +3305,9 @@ var OwneySDK = class {
3297
3305
  { asset, agentErrors: agentErrors2 }
3298
3306
  );
3299
3307
  }
3300
- const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
3301
3308
  return {
3302
3309
  agentResult: results2,
3303
- totalWithdrawn: totalWithdrawn.toString()
3310
+ totalWithdrawn: sumWithdrawnAmount(results2).toString()
3304
3311
  };
3305
3312
  }
3306
3313
  const requested = BigInt(amount);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.12-beta.0",
3
+ "version": "0.7.12-beta.2",
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
26
  "@zyfai/sdk": "0.2.50",
28
- "ethers": "^6.17.0",
29
27
  "siwe": "^3.0.0",
30
28
  "viem": "^2.48.1"
31
29
  },