@owney/sdk 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ __export(src_exports, {
23
23
  AgentChainIncompatibleError: () => AgentChainIncompatibleError,
24
24
  AgentNotFoundError: () => AgentNotFoundError,
25
25
  NotConnectedError: () => NotConnectedError,
26
+ OwneyError: () => OwneyError,
26
27
  OwneySDK: () => OwneySDK
27
28
  });
28
29
  module.exports = __toCommonJS(src_exports);
@@ -55,11 +56,58 @@ var SupportedAssets = [
55
56
  }
56
57
  ];
57
58
 
59
+ // src/errors.ts
60
+ var OwneyError = class extends Error {
61
+ code;
62
+ details;
63
+ constructor(code, message, details) {
64
+ super(message);
65
+ this.name = "OwneyError";
66
+ this.code = code;
67
+ this.details = details;
68
+ }
69
+ };
70
+ var AgentNotFoundError = class extends OwneyError {
71
+ constructor(agentId, available) {
72
+ super(
73
+ "AGENT_NOT_FOUND",
74
+ `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
75
+ { agentId, available }
76
+ );
77
+ this.name = "AgentNotFoundError";
78
+ }
79
+ };
80
+ var NotConnectedError = class extends OwneyError {
81
+ constructor() {
82
+ super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
83
+ this.name = "NotConnectedError";
84
+ }
85
+ };
86
+ var AgentChainIncompatibleError = class extends OwneyError {
87
+ incompatibleAgents;
88
+ connectedChainId;
89
+ constructor(incompatibleAgents, connectedChainId) {
90
+ const details = incompatibleAgents.map(
91
+ ({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
92
+ ).join("; ");
93
+ super(
94
+ "AGENT_CHAIN_INCOMPATIBLE",
95
+ `Chain ${connectedChainId} is not supported by the following agents: ${details}`,
96
+ { incompatibleAgents, connectedChainId }
97
+ );
98
+ this.name = "AgentChainIncompatibleError";
99
+ this.incompatibleAgents = incompatibleAgents;
100
+ this.connectedChainId = connectedChainId;
101
+ }
102
+ };
103
+
58
104
  // src/lib/utils.ts
59
105
  var isValidChainId = (chainId) => {
60
106
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
61
- throw new Error(
62
- `Unsupported chain ID: ${chainId}. Supported chains: ${SUPPORTED_CHAIN_IDS.join(", ")}`
107
+ throw new OwneyError(
108
+ "CHAIN_UNSUPPORTED",
109
+ `Unsupported chain ID: ${chainId}. Supported chains: ${SUPPORTED_CHAIN_IDS.join(", ")}`,
110
+ { chainId, supportedChainIds: [...SUPPORTED_CHAIN_IDS] }
63
111
  );
64
112
  }
65
113
  return chainId;
@@ -74,6 +122,15 @@ function hexToDecimal(hex, decimals = 6) {
74
122
  const remainder = parsed % divisor;
75
123
  return Number(whole) + Number(remainder) / Number(divisor);
76
124
  }
125
+ function normalizeDecimalPlace(value, decimals = 6) {
126
+ const parsed = BigInt(value);
127
+ if (decimals <= 0)
128
+ return Number(parsed);
129
+ const divisor = BigInt(10 ** decimals);
130
+ const whole = parsed / divisor;
131
+ const remainder = parsed % divisor;
132
+ return Number(whole) + Number(remainder) / Number(divisor);
133
+ }
77
134
  var DayFilterMapping = {
78
135
  "30D": 30,
79
136
  "14D": 14,
@@ -103,7 +160,7 @@ var CHAIN_NAME_TO_ID = {
103
160
  BASE: 8453,
104
161
  ARBITRUM: 42161
105
162
  };
106
- function mapBalances(raw, chainId) {
163
+ function mapBalances(raw, chainId, smartWallet) {
107
164
  const portfolio = raw.portfolio;
108
165
  const tokenMap = /* @__PURE__ */ new Map();
109
166
  for (const p of portfolio.positions || []) {
@@ -114,18 +171,7 @@ function mapBalances(raw, chainId) {
114
171
  tokenMap.set(key, {
115
172
  symbol,
116
173
  chainId: positionChainId,
117
- amount: (current?.amount ?? 0) + hexToDecimal(p.amount || "0")
118
- });
119
- }
120
- for (const s of portfolio.staleBalances || []) {
121
- const symbol = s.tokenSymbol || "USDC";
122
- const staleChainId = s.chainId ?? chainId;
123
- const key = `${symbol}:${staleChainId}`;
124
- const current = tokenMap.get(key);
125
- tokenMap.set(key, {
126
- symbol,
127
- chainId: staleChainId,
128
- amount: (current?.amount ?? 0) + hexToDecimal(s.balance || "0")
174
+ amount: (current?.amount ?? 0) + normalizeDecimalPlace(p.underlyingAmount || "0")
129
175
  });
130
176
  }
131
177
  let totalBalance = 0;
@@ -140,7 +186,7 @@ function mapBalances(raw, chainId) {
140
186
  });
141
187
  }
142
188
  return {
143
- smartWallet: portfolio.smartWallet,
189
+ smartWallet,
144
190
  totalBalance: String(totalBalance),
145
191
  tokens
146
192
  };
@@ -292,7 +338,10 @@ var ZyfaiAgent = class {
292
338
  // --- Private helpers ---
293
339
  getAddress() {
294
340
  if (!this.connectedAddress) {
295
- throw new Error("Wallet not connected. Call sdk.connect() first.");
341
+ throw new OwneyError(
342
+ "NOT_CONNECTED",
343
+ "Wallet not connected. Call sdk.connect() first."
344
+ );
296
345
  }
297
346
  return this.connectedAddress;
298
347
  }
@@ -444,15 +493,14 @@ var ZyfaiAgent = class {
444
493
  // --- IAgent: Portfolio reads ---
445
494
  async getBalances(state, chainId) {
446
495
  const startedAt = Date.now();
447
- await this.connectAuth(state, chainId);
448
- const validChainId = isValidChainId(chainId);
449
- const positionsStart = Date.now();
450
- const raw = await this.sdk.getPositions(this.getAddress(), validChainId);
451
- logTiming("getBalances getPositions", positionsStart, {
496
+ const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
497
+ const portfolioStart = Date.now();
498
+ const raw = await this.sdk.getPortfolio(this.getAddress());
499
+ logTiming("getBalances getPortfolio", portfolioStart, {
452
500
  chainId: validChainId
453
501
  });
454
502
  logTiming("getBalances success", startedAt, { chainId: validChainId });
455
- return mapBalances(raw, validChainId);
503
+ return mapBalances(raw, validChainId, smartWallet);
456
504
  }
457
505
  async getEarnings(state, chainId) {
458
506
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
@@ -764,7 +812,11 @@ var SailAgent = class {
764
812
  });
765
813
  } catch (error) {
766
814
  if (error instanceof Error && error.name === "AbortError" && timeoutMs) {
767
- throw new Error(`Sail API timeout ${timeoutMs}ms: ${method} ${path}`);
815
+ throw new OwneyError(
816
+ "API_SAIL_TIMEOUT",
817
+ `Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
818
+ { timeoutMs, method, path }
819
+ );
768
820
  }
769
821
  throw error;
770
822
  } finally {
@@ -773,7 +825,11 @@ var SailAgent = class {
773
825
  }
774
826
  if (!res.ok) {
775
827
  const text = await res.text().catch(() => "");
776
- throw new Error(`Sail API error ${res.status}: ${text}`);
828
+ throw new OwneyError(
829
+ "API_SAIL_ERROR",
830
+ `Sail API error ${res.status}: ${text}`,
831
+ { statusCode: res.status, method, path, responseBody: text }
832
+ );
777
833
  }
778
834
  return res.json();
779
835
  }
@@ -814,7 +870,10 @@ var SailAgent = class {
814
870
  return;
815
871
  }
816
872
  if (!state.walletAddress) {
817
- throw new Error("Wallet address is required");
873
+ throw new OwneyError(
874
+ "WALLET_ADDRESS_REQUIRED",
875
+ "Wallet address is required. Call sdk.connect(provider) first."
876
+ );
818
877
  }
819
878
  this.connectPromise = (async () => {
820
879
  const { sessionId, signature, message } = await this.signSiwe(
@@ -850,7 +909,10 @@ var SailAgent = class {
850
909
  await this.connectFull(state, chainId);
851
910
  const validChainId = isValidChainId(chainId);
852
911
  if (!this.smartWalletAddress) {
853
- throw new Error("Wallet not connected. Call sdk.connect() first.");
912
+ throw new OwneyError(
913
+ "NOT_CONNECTED",
914
+ "Wallet not connected. Call sdk.connect() first."
915
+ );
854
916
  }
855
917
  logTiming2("resolveSmartWallet ready", startedAt, { chainId: validChainId });
856
918
  return {
@@ -877,7 +939,8 @@ var SailAgent = class {
877
939
  }
878
940
  async deposit(state, chainId, amount, depositCallback) {
879
941
  if (!depositCallback) {
880
- throw new Error(
942
+ throw new OwneyError(
943
+ "DEPOSIT_CALLBACK_REQUIRED",
881
944
  "depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
882
945
  );
883
946
  }
@@ -886,12 +949,16 @@ var SailAgent = class {
886
949
  const depositInfo = await this.getDepositInfo();
887
950
  const resolvedTokenAddress = depositInfo.permittedTokens[0]?.address;
888
951
  if (!resolvedTokenAddress) {
889
- throw new Error("No permitted tokens available for deposit.");
952
+ throw new OwneyError(
953
+ "DEPOSIT_NO_PERMITTED_TOKENS",
954
+ "No permitted tokens available for deposit."
955
+ );
890
956
  }
891
957
  await this.executePreHooks(resolvedTokenAddress, amount, chainId);
892
958
  const txHash = await depositCallback(smartWallet, chainId, amount);
893
959
  if (!txHash) {
894
- throw new Error(
960
+ throw new OwneyError(
961
+ "DEPOSIT_CALLBACK_INVALID",
895
962
  "depositCallback must return a transaction hash (txHash) for Sail deposits."
896
963
  );
897
964
  }
@@ -945,7 +1012,10 @@ var SailAgent = class {
945
1012
  const withdrawInfo = await this.getWithdrawInfo();
946
1013
  const tokenAddress = withdrawInfo.permittedTokens[0]?.address;
947
1014
  if (!tokenAddress) {
948
- throw new Error("No permitted tokens available for withdrawal.");
1015
+ throw new OwneyError(
1016
+ "WITHDRAW_NO_PERMITTED_TOKENS",
1017
+ "No permitted tokens available for withdrawal."
1018
+ );
949
1019
  }
950
1020
  const resolvedAmount = amount ?? withdrawInfo.currentBalance;
951
1021
  const { signature: adminWalletSignature, message: adminWalletPayload } = await this.signSiwe(state, chainId);
@@ -978,7 +1048,7 @@ var SailAgent = class {
978
1048
  logTiming2("getBalances success", startedAt, { chainId });
979
1049
  return mapPortfolioBalances(raw, smartWallet);
980
1050
  } catch (error) {
981
- if (error instanceof Error && error.message.startsWith("Sail API timeout")) {
1051
+ if (error instanceof OwneyError && error.code === "API_SAIL_TIMEOUT") {
982
1052
  console.warn(
983
1053
  "Sail portfolio request timed out; using empty snapshot.",
984
1054
  {
@@ -1062,8 +1132,10 @@ var SailAgent = class {
1062
1132
  }
1063
1133
  const dayCount = DayFilterMapping[days];
1064
1134
  if (dayCount === void 0) {
1065
- throw new Error(
1066
- `Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`
1135
+ throw new OwneyError(
1136
+ "VALIDATION_INVALID_DAYS",
1137
+ `Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
1138
+ { days }
1067
1139
  );
1068
1140
  }
1069
1141
  const now = /* @__PURE__ */ new Date();
@@ -1140,37 +1212,6 @@ var SailAgent = class {
1140
1212
  }
1141
1213
  };
1142
1214
 
1143
- // src/errors.ts
1144
- var AgentNotFoundError = class extends Error {
1145
- constructor(agentId, available) {
1146
- super(
1147
- `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`
1148
- );
1149
- this.name = "AgentNotFoundError";
1150
- }
1151
- };
1152
- var NotConnectedError = class extends Error {
1153
- constructor() {
1154
- super("Not connected. Call sdk.connect(provider) first.");
1155
- this.name = "NotConnectedError";
1156
- }
1157
- };
1158
- var AgentChainIncompatibleError = class extends Error {
1159
- incompatibleAgents;
1160
- connectedChainId;
1161
- constructor(incompatibleAgents, connectedChainId) {
1162
- const details = incompatibleAgents.map(
1163
- ({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
1164
- ).join("; ");
1165
- super(
1166
- `Chain ${connectedChainId} is not supported by the following agents: ${details}`
1167
- );
1168
- this.name = "AgentChainIncompatibleError";
1169
- this.incompatibleAgents = incompatibleAgents;
1170
- this.connectedChainId = connectedChainId;
1171
- }
1172
- };
1173
-
1174
1215
  // src/lib/routing-api.ts
1175
1216
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1176
1217
  async function fetchAgentKeys(apiKey) {
@@ -1184,11 +1225,19 @@ async function fetchAgentKeys(apiKey) {
1184
1225
  });
1185
1226
  if (!res.ok) {
1186
1227
  const text = await res.text().catch(() => "");
1187
- throw new Error(`Routing API error ${res.status}: ${text}`);
1228
+ throw new OwneyError(
1229
+ "API_ROUTING_ERROR",
1230
+ `Routing API error ${res.status}: ${text}`,
1231
+ { statusCode: res.status, responseBody: text }
1232
+ );
1188
1233
  }
1189
1234
  const json = await res.json();
1190
1235
  if (!json.success) {
1191
- throw new Error(`Routing API request failed: ${json.message}`);
1236
+ throw new OwneyError(
1237
+ "API_ROUTING_FAILED",
1238
+ `Routing API request failed: ${json.message}`,
1239
+ { message: json.message }
1240
+ );
1192
1241
  }
1193
1242
  return json.data;
1194
1243
  }
@@ -1228,7 +1277,8 @@ var OwneySDK = class {
1228
1277
  });
1229
1278
  const walletAddress = accounts[0];
1230
1279
  if (!walletAddress) {
1231
- throw new Error(
1280
+ throw new OwneyError(
1281
+ "WALLET_NO_ACCOUNTS",
1232
1282
  "No accounts found. Ensure the wallet is unlocked and connected."
1233
1283
  );
1234
1284
  }
@@ -1267,7 +1317,8 @@ var OwneySDK = class {
1267
1317
  requireChainId() {
1268
1318
  const chainId = this.state?.chainId ?? null;
1269
1319
  if (chainId === null) {
1270
- throw new Error(
1320
+ throw new OwneyError(
1321
+ "NO_ACTIVE_CHAIN",
1271
1322
  "No active chain. Call sdk.activateAgent(chainId) first."
1272
1323
  );
1273
1324
  }
@@ -1303,7 +1354,8 @@ var OwneySDK = class {
1303
1354
  this.agents.set(agent_type, agent);
1304
1355
  }
1305
1356
  if (this.agents.size === 0) {
1306
- throw new Error(
1357
+ throw new OwneyError(
1358
+ "API_NO_AGENTS",
1307
1359
  "No supported agents returned by the routing API. Check your API key and routing configuration."
1308
1360
  );
1309
1361
  }
@@ -1332,7 +1384,8 @@ var OwneySDK = class {
1332
1384
  await this.ensureAgentsInitialized();
1333
1385
  if (agentId !== void 0) {
1334
1386
  if (agentId.length === 0) {
1335
- throw new Error(
1387
+ throw new OwneyError(
1388
+ "AGENT_EMPTY_LIST",
1336
1389
  "agentId array must not be empty. Omit the argument to activate all compatible agents."
1337
1390
  );
1338
1391
  }
@@ -1374,7 +1427,11 @@ var OwneySDK = class {
1374
1427
  (agent) => agent.supportedChainIds.includes(chainId)
1375
1428
  );
1376
1429
  if (compatible.length === 0) {
1377
- throw new Error(`No agents support chain ${chainId}.`);
1430
+ throw new OwneyError(
1431
+ "CHAIN_NO_COMPATIBLE_AGENTS",
1432
+ `No agents support chain ${chainId}.`,
1433
+ { chainId }
1434
+ );
1378
1435
  }
1379
1436
  this.activeAgents.clear();
1380
1437
  for (const agent of compatible) {
@@ -1422,8 +1479,10 @@ var OwneySDK = class {
1422
1479
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1423
1480
  const totalAmount = BigInt(amount);
1424
1481
  if (totalAmount < BigInt(eligibleAgents.length)) {
1425
- throw new Error(
1426
- `Amount "${amount}" is too small to split across ${eligibleAgents.length} agents.`
1482
+ throw new OwneyError(
1483
+ "DEPOSIT_AMOUNT_TOO_SMALL",
1484
+ `Amount "${amount}" is too small to split across ${eligibleAgents.length} agents.`,
1485
+ { amount, agentCount: eligibleAgents.length }
1427
1486
  );
1428
1487
  }
1429
1488
  const perAgent = totalAmount / BigInt(eligibleAgents.length);
@@ -1446,8 +1505,10 @@ var OwneySDK = class {
1446
1505
  (sa) => sa.chainId === chainId
1447
1506
  );
1448
1507
  if (!chainAssets || !chainAssets.assets.includes(asset)) {
1449
- throw new Error(
1450
- `Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`
1508
+ throw new OwneyError(
1509
+ "ASSET_UNSUPPORTED",
1510
+ `Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
1511
+ { agentId: agent.id, asset, chainId }
1451
1512
  );
1452
1513
  }
1453
1514
  }
@@ -1459,8 +1520,10 @@ var OwneySDK = class {
1459
1520
  return chainAssets && chainAssets.assets.includes(asset);
1460
1521
  });
1461
1522
  if (eligible.length === 0) {
1462
- throw new Error(
1463
- `No agents support asset "${asset}" on chain ${chainId}.`
1523
+ throw new OwneyError(
1524
+ "ASSET_NO_COMPATIBLE_AGENTS",
1525
+ `No agents support asset "${asset}" on chain ${chainId}.`,
1526
+ { asset, chainId }
1464
1527
  );
1465
1528
  }
1466
1529
  return eligible;
@@ -1485,10 +1548,25 @@ var OwneySDK = class {
1485
1548
  return agent.withdraw(state, chainId, amount);
1486
1549
  }
1487
1550
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1551
+ if (!amount) {
1552
+ const results2 = {};
1553
+ for (const agent of eligibleAgents) {
1554
+ results2[agent.id] = await agent.withdraw(state, chainId);
1555
+ }
1556
+ return { agentResult: results2 };
1557
+ }
1488
1558
  const results = {};
1559
+ let lastError;
1489
1560
  for (const agent of eligibleAgents) {
1490
- results[agent.id] = await agent.withdraw(state, chainId, amount);
1561
+ try {
1562
+ results[agent.id] = await agent.withdraw(state, chainId, amount);
1563
+ return { agentResult: results };
1564
+ } catch (err) {
1565
+ lastError = err;
1566
+ }
1491
1567
  }
1568
+ if (lastError)
1569
+ throw lastError;
1492
1570
  return {
1493
1571
  agentResult: results
1494
1572
  };
@@ -1534,7 +1612,10 @@ var OwneySDK = class {
1534
1612
  console.error("getBalances agent failed:", settledResult.reason);
1535
1613
  }
1536
1614
  if (successCount === 0) {
1537
- throw new Error("Failed to fetch balances for all active agents.");
1615
+ throw new OwneyError(
1616
+ "BALANCE_ALL_FAILED",
1617
+ "Failed to fetch balances for all active agents."
1618
+ );
1538
1619
  }
1539
1620
  logTiming3("getBalances aggregate", startedAt, {
1540
1621
  chainId,
@@ -1776,7 +1857,10 @@ var OwneySDK = class {
1776
1857
  }
1777
1858
  }
1778
1859
  if (successCount === 0) {
1779
- throw new Error("Failed to fetch allocation: all agents failed.");
1860
+ throw new OwneyError(
1861
+ "ALLOCATION_ALL_FAILED",
1862
+ "Failed to fetch allocation: all agents failed."
1863
+ );
1780
1864
  }
1781
1865
  const allocation = {};
1782
1866
  if (totalBalance === 0) {
@@ -1805,5 +1889,6 @@ var OwneySDK = class {
1805
1889
  AgentChainIncompatibleError,
1806
1890
  AgentNotFoundError,
1807
1891
  NotConnectedError,
1892
+ OwneyError,
1808
1893
  OwneySDK
1809
1894
  });
package/dist/index.d.cts CHANGED
@@ -334,13 +334,19 @@ declare class OwneySDK {
334
334
  getAgentAllocation(): Promise<OwneyAgentAllocation>;
335
335
  }
336
336
 
337
- declare class AgentNotFoundError extends Error {
337
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_TOO_SMALL" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
338
+ declare class OwneyError extends Error {
339
+ readonly code: OwneyErrorCode;
340
+ readonly details?: Record<string, unknown>;
341
+ constructor(code: OwneyErrorCode, message: string, details?: Record<string, unknown>);
342
+ }
343
+ declare class AgentNotFoundError extends OwneyError {
338
344
  constructor(agentId: string, available: string[]);
339
345
  }
340
- declare class NotConnectedError extends Error {
346
+ declare class NotConnectedError extends OwneyError {
341
347
  constructor();
342
348
  }
343
- declare class AgentChainIncompatibleError extends Error {
349
+ declare class AgentChainIncompatibleError extends OwneyError {
344
350
  readonly incompatibleAgents: Array<{
345
351
  agentId: string;
346
352
  supportedChainIds: readonly number[];
@@ -352,4 +358,4 @@ declare class AgentChainIncompatibleError extends Error {
352
358
  }>, connectedChainId: number);
353
359
  }
354
360
 
355
- export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentId, AgentNotFoundError, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyMultiDepositResult, OwneyRouteResponse, OwneySDK, OwneySDKConfig, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };
361
+ export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentId, AgentNotFoundError, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyError, OwneyErrorCode, OwneyMultiDepositResult, OwneyRouteResponse, OwneySDK, OwneySDKConfig, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };
package/dist/index.d.ts CHANGED
@@ -334,13 +334,19 @@ declare class OwneySDK {
334
334
  getAgentAllocation(): Promise<OwneyAgentAllocation>;
335
335
  }
336
336
 
337
- declare class AgentNotFoundError extends Error {
337
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_TOO_SMALL" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
338
+ declare class OwneyError extends Error {
339
+ readonly code: OwneyErrorCode;
340
+ readonly details?: Record<string, unknown>;
341
+ constructor(code: OwneyErrorCode, message: string, details?: Record<string, unknown>);
342
+ }
343
+ declare class AgentNotFoundError extends OwneyError {
338
344
  constructor(agentId: string, available: string[]);
339
345
  }
340
- declare class NotConnectedError extends Error {
346
+ declare class NotConnectedError extends OwneyError {
341
347
  constructor();
342
348
  }
343
- declare class AgentChainIncompatibleError extends Error {
349
+ declare class AgentChainIncompatibleError extends OwneyError {
344
350
  readonly incompatibleAgents: Array<{
345
351
  agentId: string;
346
352
  supportedChainIds: readonly number[];
@@ -352,4 +358,4 @@ declare class AgentChainIncompatibleError extends Error {
352
358
  }>, connectedChainId: number);
353
359
  }
354
360
 
355
- export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentId, AgentNotFoundError, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyMultiDepositResult, OwneyRouteResponse, OwneySDK, OwneySDKConfig, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };
361
+ export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentId, AgentNotFoundError, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyError, OwneyErrorCode, OwneyMultiDepositResult, OwneyRouteResponse, OwneySDK, OwneySDKConfig, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };
package/dist/index.js CHANGED
@@ -26,11 +26,58 @@ var SupportedAssets = [
26
26
  }
27
27
  ];
28
28
 
29
+ // src/errors.ts
30
+ var OwneyError = class extends Error {
31
+ code;
32
+ details;
33
+ constructor(code, message, details) {
34
+ super(message);
35
+ this.name = "OwneyError";
36
+ this.code = code;
37
+ this.details = details;
38
+ }
39
+ };
40
+ var AgentNotFoundError = class extends OwneyError {
41
+ constructor(agentId, available) {
42
+ super(
43
+ "AGENT_NOT_FOUND",
44
+ `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
45
+ { agentId, available }
46
+ );
47
+ this.name = "AgentNotFoundError";
48
+ }
49
+ };
50
+ var NotConnectedError = class extends OwneyError {
51
+ constructor() {
52
+ super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
53
+ this.name = "NotConnectedError";
54
+ }
55
+ };
56
+ var AgentChainIncompatibleError = class extends OwneyError {
57
+ incompatibleAgents;
58
+ connectedChainId;
59
+ constructor(incompatibleAgents, connectedChainId) {
60
+ const details = incompatibleAgents.map(
61
+ ({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
62
+ ).join("; ");
63
+ super(
64
+ "AGENT_CHAIN_INCOMPATIBLE",
65
+ `Chain ${connectedChainId} is not supported by the following agents: ${details}`,
66
+ { incompatibleAgents, connectedChainId }
67
+ );
68
+ this.name = "AgentChainIncompatibleError";
69
+ this.incompatibleAgents = incompatibleAgents;
70
+ this.connectedChainId = connectedChainId;
71
+ }
72
+ };
73
+
29
74
  // src/lib/utils.ts
30
75
  var isValidChainId = (chainId) => {
31
76
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
32
- throw new Error(
33
- `Unsupported chain ID: ${chainId}. Supported chains: ${SUPPORTED_CHAIN_IDS.join(", ")}`
77
+ throw new OwneyError(
78
+ "CHAIN_UNSUPPORTED",
79
+ `Unsupported chain ID: ${chainId}. Supported chains: ${SUPPORTED_CHAIN_IDS.join(", ")}`,
80
+ { chainId, supportedChainIds: [...SUPPORTED_CHAIN_IDS] }
34
81
  );
35
82
  }
36
83
  return chainId;
@@ -45,6 +92,15 @@ function hexToDecimal(hex, decimals = 6) {
45
92
  const remainder = parsed % divisor;
46
93
  return Number(whole) + Number(remainder) / Number(divisor);
47
94
  }
95
+ function normalizeDecimalPlace(value, decimals = 6) {
96
+ const parsed = BigInt(value);
97
+ if (decimals <= 0)
98
+ return Number(parsed);
99
+ const divisor = BigInt(10 ** decimals);
100
+ const whole = parsed / divisor;
101
+ const remainder = parsed % divisor;
102
+ return Number(whole) + Number(remainder) / Number(divisor);
103
+ }
48
104
  var DayFilterMapping = {
49
105
  "30D": 30,
50
106
  "14D": 14,
@@ -74,7 +130,7 @@ var CHAIN_NAME_TO_ID = {
74
130
  BASE: 8453,
75
131
  ARBITRUM: 42161
76
132
  };
77
- function mapBalances(raw, chainId) {
133
+ function mapBalances(raw, chainId, smartWallet) {
78
134
  const portfolio = raw.portfolio;
79
135
  const tokenMap = /* @__PURE__ */ new Map();
80
136
  for (const p of portfolio.positions || []) {
@@ -85,18 +141,7 @@ function mapBalances(raw, chainId) {
85
141
  tokenMap.set(key, {
86
142
  symbol,
87
143
  chainId: positionChainId,
88
- amount: (current?.amount ?? 0) + hexToDecimal(p.amount || "0")
89
- });
90
- }
91
- for (const s of portfolio.staleBalances || []) {
92
- const symbol = s.tokenSymbol || "USDC";
93
- const staleChainId = s.chainId ?? chainId;
94
- const key = `${symbol}:${staleChainId}`;
95
- const current = tokenMap.get(key);
96
- tokenMap.set(key, {
97
- symbol,
98
- chainId: staleChainId,
99
- amount: (current?.amount ?? 0) + hexToDecimal(s.balance || "0")
144
+ amount: (current?.amount ?? 0) + normalizeDecimalPlace(p.underlyingAmount || "0")
100
145
  });
101
146
  }
102
147
  let totalBalance = 0;
@@ -111,7 +156,7 @@ function mapBalances(raw, chainId) {
111
156
  });
112
157
  }
113
158
  return {
114
- smartWallet: portfolio.smartWallet,
159
+ smartWallet,
115
160
  totalBalance: String(totalBalance),
116
161
  tokens
117
162
  };
@@ -263,7 +308,10 @@ var ZyfaiAgent = class {
263
308
  // --- Private helpers ---
264
309
  getAddress() {
265
310
  if (!this.connectedAddress) {
266
- throw new Error("Wallet not connected. Call sdk.connect() first.");
311
+ throw new OwneyError(
312
+ "NOT_CONNECTED",
313
+ "Wallet not connected. Call sdk.connect() first."
314
+ );
267
315
  }
268
316
  return this.connectedAddress;
269
317
  }
@@ -415,15 +463,14 @@ var ZyfaiAgent = class {
415
463
  // --- IAgent: Portfolio reads ---
416
464
  async getBalances(state, chainId) {
417
465
  const startedAt = Date.now();
418
- await this.connectAuth(state, chainId);
419
- const validChainId = isValidChainId(chainId);
420
- const positionsStart = Date.now();
421
- const raw = await this.sdk.getPositions(this.getAddress(), validChainId);
422
- logTiming("getBalances getPositions", positionsStart, {
466
+ const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
467
+ const portfolioStart = Date.now();
468
+ const raw = await this.sdk.getPortfolio(this.getAddress());
469
+ logTiming("getBalances getPortfolio", portfolioStart, {
423
470
  chainId: validChainId
424
471
  });
425
472
  logTiming("getBalances success", startedAt, { chainId: validChainId });
426
- return mapBalances(raw, validChainId);
473
+ return mapBalances(raw, validChainId, smartWallet);
427
474
  }
428
475
  async getEarnings(state, chainId) {
429
476
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
@@ -735,7 +782,11 @@ var SailAgent = class {
735
782
  });
736
783
  } catch (error) {
737
784
  if (error instanceof Error && error.name === "AbortError" && timeoutMs) {
738
- throw new Error(`Sail API timeout ${timeoutMs}ms: ${method} ${path}`);
785
+ throw new OwneyError(
786
+ "API_SAIL_TIMEOUT",
787
+ `Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
788
+ { timeoutMs, method, path }
789
+ );
739
790
  }
740
791
  throw error;
741
792
  } finally {
@@ -744,7 +795,11 @@ var SailAgent = class {
744
795
  }
745
796
  if (!res.ok) {
746
797
  const text = await res.text().catch(() => "");
747
- throw new Error(`Sail API error ${res.status}: ${text}`);
798
+ throw new OwneyError(
799
+ "API_SAIL_ERROR",
800
+ `Sail API error ${res.status}: ${text}`,
801
+ { statusCode: res.status, method, path, responseBody: text }
802
+ );
748
803
  }
749
804
  return res.json();
750
805
  }
@@ -785,7 +840,10 @@ var SailAgent = class {
785
840
  return;
786
841
  }
787
842
  if (!state.walletAddress) {
788
- throw new Error("Wallet address is required");
843
+ throw new OwneyError(
844
+ "WALLET_ADDRESS_REQUIRED",
845
+ "Wallet address is required. Call sdk.connect(provider) first."
846
+ );
789
847
  }
790
848
  this.connectPromise = (async () => {
791
849
  const { sessionId, signature, message } = await this.signSiwe(
@@ -821,7 +879,10 @@ var SailAgent = class {
821
879
  await this.connectFull(state, chainId);
822
880
  const validChainId = isValidChainId(chainId);
823
881
  if (!this.smartWalletAddress) {
824
- throw new Error("Wallet not connected. Call sdk.connect() first.");
882
+ throw new OwneyError(
883
+ "NOT_CONNECTED",
884
+ "Wallet not connected. Call sdk.connect() first."
885
+ );
825
886
  }
826
887
  logTiming2("resolveSmartWallet ready", startedAt, { chainId: validChainId });
827
888
  return {
@@ -848,7 +909,8 @@ var SailAgent = class {
848
909
  }
849
910
  async deposit(state, chainId, amount, depositCallback) {
850
911
  if (!depositCallback) {
851
- throw new Error(
912
+ throw new OwneyError(
913
+ "DEPOSIT_CALLBACK_REQUIRED",
852
914
  "depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
853
915
  );
854
916
  }
@@ -857,12 +919,16 @@ var SailAgent = class {
857
919
  const depositInfo = await this.getDepositInfo();
858
920
  const resolvedTokenAddress = depositInfo.permittedTokens[0]?.address;
859
921
  if (!resolvedTokenAddress) {
860
- throw new Error("No permitted tokens available for deposit.");
922
+ throw new OwneyError(
923
+ "DEPOSIT_NO_PERMITTED_TOKENS",
924
+ "No permitted tokens available for deposit."
925
+ );
861
926
  }
862
927
  await this.executePreHooks(resolvedTokenAddress, amount, chainId);
863
928
  const txHash = await depositCallback(smartWallet, chainId, amount);
864
929
  if (!txHash) {
865
- throw new Error(
930
+ throw new OwneyError(
931
+ "DEPOSIT_CALLBACK_INVALID",
866
932
  "depositCallback must return a transaction hash (txHash) for Sail deposits."
867
933
  );
868
934
  }
@@ -916,7 +982,10 @@ var SailAgent = class {
916
982
  const withdrawInfo = await this.getWithdrawInfo();
917
983
  const tokenAddress = withdrawInfo.permittedTokens[0]?.address;
918
984
  if (!tokenAddress) {
919
- throw new Error("No permitted tokens available for withdrawal.");
985
+ throw new OwneyError(
986
+ "WITHDRAW_NO_PERMITTED_TOKENS",
987
+ "No permitted tokens available for withdrawal."
988
+ );
920
989
  }
921
990
  const resolvedAmount = amount ?? withdrawInfo.currentBalance;
922
991
  const { signature: adminWalletSignature, message: adminWalletPayload } = await this.signSiwe(state, chainId);
@@ -949,7 +1018,7 @@ var SailAgent = class {
949
1018
  logTiming2("getBalances success", startedAt, { chainId });
950
1019
  return mapPortfolioBalances(raw, smartWallet);
951
1020
  } catch (error) {
952
- if (error instanceof Error && error.message.startsWith("Sail API timeout")) {
1021
+ if (error instanceof OwneyError && error.code === "API_SAIL_TIMEOUT") {
953
1022
  console.warn(
954
1023
  "Sail portfolio request timed out; using empty snapshot.",
955
1024
  {
@@ -1033,8 +1102,10 @@ var SailAgent = class {
1033
1102
  }
1034
1103
  const dayCount = DayFilterMapping[days];
1035
1104
  if (dayCount === void 0) {
1036
- throw new Error(
1037
- `Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`
1105
+ throw new OwneyError(
1106
+ "VALIDATION_INVALID_DAYS",
1107
+ `Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
1108
+ { days }
1038
1109
  );
1039
1110
  }
1040
1111
  const now = /* @__PURE__ */ new Date();
@@ -1111,37 +1182,6 @@ var SailAgent = class {
1111
1182
  }
1112
1183
  };
1113
1184
 
1114
- // src/errors.ts
1115
- var AgentNotFoundError = class extends Error {
1116
- constructor(agentId, available) {
1117
- super(
1118
- `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`
1119
- );
1120
- this.name = "AgentNotFoundError";
1121
- }
1122
- };
1123
- var NotConnectedError = class extends Error {
1124
- constructor() {
1125
- super("Not connected. Call sdk.connect(provider) first.");
1126
- this.name = "NotConnectedError";
1127
- }
1128
- };
1129
- var AgentChainIncompatibleError = class extends Error {
1130
- incompatibleAgents;
1131
- connectedChainId;
1132
- constructor(incompatibleAgents, connectedChainId) {
1133
- const details = incompatibleAgents.map(
1134
- ({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
1135
- ).join("; ");
1136
- super(
1137
- `Chain ${connectedChainId} is not supported by the following agents: ${details}`
1138
- );
1139
- this.name = "AgentChainIncompatibleError";
1140
- this.incompatibleAgents = incompatibleAgents;
1141
- this.connectedChainId = connectedChainId;
1142
- }
1143
- };
1144
-
1145
1185
  // src/lib/routing-api.ts
1146
1186
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1147
1187
  async function fetchAgentKeys(apiKey) {
@@ -1155,11 +1195,19 @@ async function fetchAgentKeys(apiKey) {
1155
1195
  });
1156
1196
  if (!res.ok) {
1157
1197
  const text = await res.text().catch(() => "");
1158
- throw new Error(`Routing API error ${res.status}: ${text}`);
1198
+ throw new OwneyError(
1199
+ "API_ROUTING_ERROR",
1200
+ `Routing API error ${res.status}: ${text}`,
1201
+ { statusCode: res.status, responseBody: text }
1202
+ );
1159
1203
  }
1160
1204
  const json = await res.json();
1161
1205
  if (!json.success) {
1162
- throw new Error(`Routing API request failed: ${json.message}`);
1206
+ throw new OwneyError(
1207
+ "API_ROUTING_FAILED",
1208
+ `Routing API request failed: ${json.message}`,
1209
+ { message: json.message }
1210
+ );
1163
1211
  }
1164
1212
  return json.data;
1165
1213
  }
@@ -1199,7 +1247,8 @@ var OwneySDK = class {
1199
1247
  });
1200
1248
  const walletAddress = accounts[0];
1201
1249
  if (!walletAddress) {
1202
- throw new Error(
1250
+ throw new OwneyError(
1251
+ "WALLET_NO_ACCOUNTS",
1203
1252
  "No accounts found. Ensure the wallet is unlocked and connected."
1204
1253
  );
1205
1254
  }
@@ -1238,7 +1287,8 @@ var OwneySDK = class {
1238
1287
  requireChainId() {
1239
1288
  const chainId = this.state?.chainId ?? null;
1240
1289
  if (chainId === null) {
1241
- throw new Error(
1290
+ throw new OwneyError(
1291
+ "NO_ACTIVE_CHAIN",
1242
1292
  "No active chain. Call sdk.activateAgent(chainId) first."
1243
1293
  );
1244
1294
  }
@@ -1274,7 +1324,8 @@ var OwneySDK = class {
1274
1324
  this.agents.set(agent_type, agent);
1275
1325
  }
1276
1326
  if (this.agents.size === 0) {
1277
- throw new Error(
1327
+ throw new OwneyError(
1328
+ "API_NO_AGENTS",
1278
1329
  "No supported agents returned by the routing API. Check your API key and routing configuration."
1279
1330
  );
1280
1331
  }
@@ -1303,7 +1354,8 @@ var OwneySDK = class {
1303
1354
  await this.ensureAgentsInitialized();
1304
1355
  if (agentId !== void 0) {
1305
1356
  if (agentId.length === 0) {
1306
- throw new Error(
1357
+ throw new OwneyError(
1358
+ "AGENT_EMPTY_LIST",
1307
1359
  "agentId array must not be empty. Omit the argument to activate all compatible agents."
1308
1360
  );
1309
1361
  }
@@ -1345,7 +1397,11 @@ var OwneySDK = class {
1345
1397
  (agent) => agent.supportedChainIds.includes(chainId)
1346
1398
  );
1347
1399
  if (compatible.length === 0) {
1348
- throw new Error(`No agents support chain ${chainId}.`);
1400
+ throw new OwneyError(
1401
+ "CHAIN_NO_COMPATIBLE_AGENTS",
1402
+ `No agents support chain ${chainId}.`,
1403
+ { chainId }
1404
+ );
1349
1405
  }
1350
1406
  this.activeAgents.clear();
1351
1407
  for (const agent of compatible) {
@@ -1393,8 +1449,10 @@ var OwneySDK = class {
1393
1449
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1394
1450
  const totalAmount = BigInt(amount);
1395
1451
  if (totalAmount < BigInt(eligibleAgents.length)) {
1396
- throw new Error(
1397
- `Amount "${amount}" is too small to split across ${eligibleAgents.length} agents.`
1452
+ throw new OwneyError(
1453
+ "DEPOSIT_AMOUNT_TOO_SMALL",
1454
+ `Amount "${amount}" is too small to split across ${eligibleAgents.length} agents.`,
1455
+ { amount, agentCount: eligibleAgents.length }
1398
1456
  );
1399
1457
  }
1400
1458
  const perAgent = totalAmount / BigInt(eligibleAgents.length);
@@ -1417,8 +1475,10 @@ var OwneySDK = class {
1417
1475
  (sa) => sa.chainId === chainId
1418
1476
  );
1419
1477
  if (!chainAssets || !chainAssets.assets.includes(asset)) {
1420
- throw new Error(
1421
- `Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`
1478
+ throw new OwneyError(
1479
+ "ASSET_UNSUPPORTED",
1480
+ `Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
1481
+ { agentId: agent.id, asset, chainId }
1422
1482
  );
1423
1483
  }
1424
1484
  }
@@ -1430,8 +1490,10 @@ var OwneySDK = class {
1430
1490
  return chainAssets && chainAssets.assets.includes(asset);
1431
1491
  });
1432
1492
  if (eligible.length === 0) {
1433
- throw new Error(
1434
- `No agents support asset "${asset}" on chain ${chainId}.`
1493
+ throw new OwneyError(
1494
+ "ASSET_NO_COMPATIBLE_AGENTS",
1495
+ `No agents support asset "${asset}" on chain ${chainId}.`,
1496
+ { asset, chainId }
1435
1497
  );
1436
1498
  }
1437
1499
  return eligible;
@@ -1456,10 +1518,25 @@ var OwneySDK = class {
1456
1518
  return agent.withdraw(state, chainId, amount);
1457
1519
  }
1458
1520
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1521
+ if (!amount) {
1522
+ const results2 = {};
1523
+ for (const agent of eligibleAgents) {
1524
+ results2[agent.id] = await agent.withdraw(state, chainId);
1525
+ }
1526
+ return { agentResult: results2 };
1527
+ }
1459
1528
  const results = {};
1529
+ let lastError;
1460
1530
  for (const agent of eligibleAgents) {
1461
- results[agent.id] = await agent.withdraw(state, chainId, amount);
1531
+ try {
1532
+ results[agent.id] = await agent.withdraw(state, chainId, amount);
1533
+ return { agentResult: results };
1534
+ } catch (err) {
1535
+ lastError = err;
1536
+ }
1462
1537
  }
1538
+ if (lastError)
1539
+ throw lastError;
1463
1540
  return {
1464
1541
  agentResult: results
1465
1542
  };
@@ -1505,7 +1582,10 @@ var OwneySDK = class {
1505
1582
  console.error("getBalances agent failed:", settledResult.reason);
1506
1583
  }
1507
1584
  if (successCount === 0) {
1508
- throw new Error("Failed to fetch balances for all active agents.");
1585
+ throw new OwneyError(
1586
+ "BALANCE_ALL_FAILED",
1587
+ "Failed to fetch balances for all active agents."
1588
+ );
1509
1589
  }
1510
1590
  logTiming3("getBalances aggregate", startedAt, {
1511
1591
  chainId,
@@ -1747,7 +1827,10 @@ var OwneySDK = class {
1747
1827
  }
1748
1828
  }
1749
1829
  if (successCount === 0) {
1750
- throw new Error("Failed to fetch allocation: all agents failed.");
1830
+ throw new OwneyError(
1831
+ "ALLOCATION_ALL_FAILED",
1832
+ "Failed to fetch allocation: all agents failed."
1833
+ );
1751
1834
  }
1752
1835
  const allocation = {};
1753
1836
  if (totalBalance === 0) {
@@ -1775,5 +1858,6 @@ export {
1775
1858
  AgentChainIncompatibleError,
1776
1859
  AgentNotFoundError,
1777
1860
  NotConnectedError,
1861
+ OwneyError,
1778
1862
  OwneySDK
1779
1863
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",