@owney/sdk 0.7.11 → 0.7.12-beta.0

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,17 @@
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
+
1
15
  // src/agents/zyfai/zyfai.agent.ts
2
16
  import { SMART_SESSIONS_VALIDATOR, ZyfaiSDK } from "@zyfai/sdk";
3
17
  import { createPublicClient, http, parseAbi } from "viem";
@@ -56,55 +70,6 @@ var SupportedAssets = [
56
70
  }
57
71
  ];
58
72
 
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
73
  // src/lib/utils.ts
109
74
  var isValidChainId = (chainId) => {
110
75
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
@@ -116,6 +81,12 @@ var isValidChainId = (chainId) => {
116
81
  }
117
82
  return chainId;
118
83
  };
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
+ }
119
90
  function hexToDecimal(hex, decimals = 6) {
120
91
  const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
121
92
  const parsed = BigInt(normalized);
@@ -149,6 +120,10 @@ var DECIMALS_BY_SYMBOL = {
149
120
  wbtc: 8,
150
121
  cbbtc: 8
151
122
  };
123
+ function canonicalSymbol(symbol) {
124
+ const upper = symbol.toUpperCase();
125
+ return upper === "ETH" ? "WETH" : upper;
126
+ }
152
127
  function tokenDecimals(symbol, explicit) {
153
128
  if (typeof explicit === "number" && Number.isFinite(explicit))
154
129
  return explicit;
@@ -213,55 +188,89 @@ function mapPendingAllocations(raw) {
213
188
  function mapBalances(raw, _chainId, smartWallet) {
214
189
  const portfolio = raw.portfolio;
215
190
  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
+ }
216
198
  let totalBalance = 0;
217
199
  const tokens = [];
200
+ const positions = [];
218
201
  for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
219
202
  const positionChainId = Number(chainIdKey);
203
+ const chainName2 = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
220
204
  for (const [
221
205
  symbol,
222
206
  { balance, balanceWithFee, decimals }
223
207
  ] of Object.entries(tokensBySymbol ?? {})) {
224
- const amount = hexToDecimal(balanceWithFee ?? balance ?? "0", decimals);
208
+ const amount = hexToDecimal(
209
+ balanceWithFee ?? balance ?? "0",
210
+ tokenDecimals(symbol, decimals)
211
+ );
225
212
  totalBalance += amount;
226
213
  tokens.push({
227
- chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
214
+ chain: chainName2,
228
215
  chainId: positionChainId,
229
216
  asset: symbol,
230
217
  amount: String(amount)
231
218
  });
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
+ });
232
237
  }
233
238
  }
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
- }));
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
+ }
265
274
  const pendingAllocations = mapPendingAllocations(
266
275
  portfolio.staleBalances
267
276
  );
@@ -1344,6 +1353,37 @@ var ZyfaiAgent = class _ZyfaiAgent {
1344
1353
  );
1345
1354
  }
1346
1355
  }
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
+ }
1347
1387
  /**
1348
1388
  * True when `smartWallet` is the backend-managed pool wallet Zyfai assigned
1349
1389
  * to this EOA — the "new wallet" kind that is predeployed on the chains it
@@ -1647,14 +1687,12 @@ var ZyfaiAgent = class _ZyfaiAgent {
1647
1687
  );
1648
1688
  const txHash = await depositCallback(smartWallet, validChainId, amount);
1649
1689
  const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
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
- }
1690
+ await this.logDepositWithRetry(
1691
+ validChainId,
1692
+ txHash,
1693
+ amount,
1694
+ tokenAddress
1695
+ );
1658
1696
  return { txHash, smartWallet, amount };
1659
1697
  }
1660
1698
  await this.ensureWalletDeployed(this.getAddress(), validChainId);
@@ -1935,11 +1973,10 @@ async function withFailureReporting(apiKey, agentType, fn) {
1935
1973
  // src/lib/helpers/withdraw-helper.ts
1936
1974
  import { parseUnits } from "viem";
1937
1975
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
1938
- const target = asset.toUpperCase();
1939
1976
  return agents.map((agent) => {
1940
1977
  const agentBalance = aggregated[agent.id];
1941
1978
  const tokenBalance = agentBalance?.tokens.find(
1942
- (t) => t.chainId === chainId && t.asset.toUpperCase() === target
1979
+ (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
1943
1980
  );
1944
1981
  if (!tokenBalance) return { agent, balance: 0n };
1945
1982
  return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
@@ -2209,84 +2246,6 @@ async function getSponsorRelayerAddress(input) {
2209
2246
  return parsed.data.relayer;
2210
2247
  }
2211
2248
 
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
-
2290
2249
  // src/lib/chain-guard.ts
2291
2250
  var CHAIN_NAMES = {
2292
2251
  1: "Ethereum",
@@ -2605,6 +2564,7 @@ var VIEM_CHAIN2 = {
2605
2564
  42161: arbitrum2,
2606
2565
  1: mainnet2
2607
2566
  };
2567
+ var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
2608
2568
  var SPONSORED_WETH_BY_CHAIN = {
2609
2569
  8453: "0x4200000000000000000000000000000000000006",
2610
2570
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -2633,6 +2593,7 @@ var OwneySDK = class {
2633
2593
  zyfaiRpcUrls;
2634
2594
  routingApiBaseUrl;
2635
2595
  referralSource;
2596
+ surfliquidApiBaseUrl;
2636
2597
  cachedSponsoredCallback = null;
2637
2598
  cachedWethSponsoredCallback = null;
2638
2599
  paymasterServiceUrl;
@@ -2645,6 +2606,7 @@ var OwneySDK = class {
2645
2606
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2646
2607
  this.paymasterServiceUrl = config.paymasterServiceUrl;
2647
2608
  this.referralSource = config.referralSource;
2609
+ this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
2648
2610
  }
2649
2611
  /**
2650
2612
  * Establish connection state for all subsequent SDK calls.
@@ -2874,8 +2836,8 @@ var OwneySDK = class {
2874
2836
  this.initializingAgentsPromise = (async () => {
2875
2837
  const agentKeys = await fetchAgentKeys(this.apiKey);
2876
2838
  this.disabledAgents.clear();
2877
- for (const { key: key2, agent_type, is_enabled } of agentKeys) {
2878
- const agent = this.createAgent(agent_type, key2);
2839
+ for (const { key: key2, agent_type, project_name, is_enabled } of agentKeys) {
2840
+ const agent = await this.createAgent(agent_type, key2, project_name);
2879
2841
  if (!agent) continue;
2880
2842
  this.agents.set(agent_type, agent);
2881
2843
  if (is_enabled === false) {
@@ -2896,10 +2858,22 @@ var OwneySDK = class {
2896
2858
  this.initializingAgentsPromise = null;
2897
2859
  }
2898
2860
  }
2899
- createAgent(agentId, key2) {
2861
+ async createAgent(agentId, key2, projectName) {
2900
2862
  if (agentId === "zyfai") {
2901
2863
  return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2902
2864
  }
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
+ }
2903
2877
  return null;
2904
2878
  }
2905
2879
  // --- Account lifecycle ---
@@ -3054,18 +3028,30 @@ var OwneySDK = class {
3054
3028
  );
3055
3029
  }
3056
3030
  const agentResults = {};
3031
+ const agentErrors = {};
3057
3032
  for (const { agent, amount: agentAmount } of agentAmounts) {
3058
- agentResults[agent.id] = await this.depositWithFallback(
3059
- agent,
3060
- state,
3061
- chainId,
3062
- agentAmount,
3063
- asset,
3064
- effectiveCallback,
3065
- depositCallback
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 }
3066
3052
  );
3067
3053
  }
3068
- return { agentResults };
3054
+ return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
3069
3055
  }
3070
3056
  /**
3071
3057
  * Invokes `agent.deposit` with the resolved sponsored callback, composing
@@ -3181,9 +3167,8 @@ var OwneySDK = class {
3181
3167
  async hasExistingBalance(agent, state, chainId, asset) {
3182
3168
  try {
3183
3169
  const balance = await agent.getBalances(state, chainId);
3184
- const target = asset.toLowerCase();
3185
3170
  const token = balance.tokens.find(
3186
- (t) => t.chainId === chainId && t.asset.toLowerCase() === target
3171
+ (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
3187
3172
  );
3188
3173
  return !!token && Number(token.amount) > 0;
3189
3174
  } catch {
@@ -3222,6 +3207,34 @@ var OwneySDK = class {
3222
3207
  }
3223
3208
  return eligible;
3224
3209
  }
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
+ }
3225
3238
  // --- Fund operations ---
3226
3239
  /**
3227
3240
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
@@ -3257,9 +3270,17 @@ var OwneySDK = class {
3257
3270
  }
3258
3271
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
3259
3272
  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);
3260
3281
  const results2 = {};
3261
3282
  const agentErrors2 = {};
3262
- for (const agent of eligibleAgents) {
3283
+ for (const { agent } of funded) {
3263
3284
  try {
3264
3285
  results2[agent.id] = await agent.withdraw(state, chainId, token);
3265
3286
  } catch (err) {
@@ -3276,9 +3297,10 @@ var OwneySDK = class {
3276
3297
  { asset, agentErrors: agentErrors2 }
3277
3298
  );
3278
3299
  }
3300
+ const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
3279
3301
  return {
3280
3302
  agentResult: results2,
3281
- totalWithdrawn: sumWithdrawnAmount(results2).toString()
3303
+ totalWithdrawn: totalWithdrawn.toString()
3282
3304
  };
3283
3305
  }
3284
3306
  const requested = BigInt(amount);