@owney/sdk 0.2.9 → 0.2.10

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/README.md CHANGED
@@ -167,6 +167,14 @@ console.log(results.agentResults.sail.amount); // "50000000"
167
167
 
168
168
  Withdraw funds from a specific agent, or all agents that support the active chain+asset if `agentId` is omitted. Validates that the asset is supported.
169
169
 
170
+ When `agentId` is omitted with a partial `amount`, the SDK splits the requested amount proportionally across eligible agents based on each agent's per-asset balance. If an agent's withdraw fails, its share is redistributed to remaining not-yet-attempted agents (capped by their headroom). Throws:
171
+
172
+ - `WITHDRAW_INSUFFICIENT_BALANCE` — total available across agents is less than `amount`.
173
+ - `WITHDRAW_ALL_FAILED` — every agent's withdraw call failed.
174
+ - `WITHDRAW_PARTIAL_FAILURE` — some agents succeeded but total withdrawn is less than `amount`. Successful txs are surfaced via `error.details.partialResults`; per-agent failure messages via `error.details.agentErrors`.
175
+
176
+ `WITHDRAW_ALL_FAILED` also carries per-agent failure messages via `error.details.agentErrors` (a `Record<agentId, string>`). Any error originating from a specific agent also has `error.agentId` set and a `[agent:<id>]` prefix on `error.message`.
177
+
170
178
  | Param | Type | Description |
171
179
  | ----------------- | -------------------- | --------------------------------------------- |
172
180
  | `options.asset` | `string` | Asset symbol (e.g. `"USDC"`) |
@@ -185,6 +193,12 @@ const result = await sdk.withdraw({
185
193
  console.log(result.type); // "partial"
186
194
  console.log(result.amount); // "50000000"
187
195
 
196
+ // Partial withdrawal split proportionally across all agents
197
+ // e.g. zyfai=$5, sail=$2, amount=$4 → zyfai withdraws $3, sail withdraws $1
198
+ const split = await sdk.withdraw({ asset: "USDC", amount: "4000000" });
199
+ console.log(split.agentResult.zyfai.amount); // "3000000"
200
+ console.log(split.agentResult.sail.amount); // "1000000"
201
+
188
202
  // Full withdrawal from all agents
189
203
  const results = await sdk.withdraw({ asset: "USDC" });
190
204
  console.log(results.agentResult.zyfai.type); // "full"
@@ -630,7 +644,7 @@ type DepositCallback = (
630
644
 
631
645
  ### Error Handling
632
646
 
633
- All errors thrown by the SDK are instances of `OwneyError` (which extends `Error`), each carrying a stable `code` string and optional `details` object for programmatic handling.
647
+ All errors thrown by the SDK are instances of `OwneyError` (which extends `Error`), each carrying a stable `code` string, optional `details` object, and optional `agentId` identifying the originating agent. When `agentId` is set, `error.message` is also prefixed with `[agent:<id>]` for logs.
634
648
 
635
649
  ```typescript
636
650
  import { OwneyError } from "@owney/sdk";
@@ -680,6 +694,9 @@ type OwneyErrorCode =
680
694
  | "DEPOSIT_NO_PERMITTED_TOKENS" // No permitted tokens available for deposit
681
695
  // Withdraw
682
696
  | "WITHDRAW_NO_PERMITTED_TOKENS" // No permitted tokens available for withdrawal
697
+ | "WITHDRAW_INSUFFICIENT_BALANCE" // Requested amount exceeds total available across agents
698
+ | "WITHDRAW_ALL_FAILED" // Every eligible agent's withdraw call failed
699
+ | "WITHDRAW_PARTIAL_FAILURE" // Some agents succeeded; total withdrawn < requested
683
700
  // API
684
701
  | "API_ROUTING_ERROR" // Routing API returned a non-OK HTTP status
685
702
  | "API_ROUTING_FAILED" // Routing API returned { success: false }
@@ -695,6 +712,7 @@ type OwneyErrorCode =
695
712
  class OwneyError extends Error {
696
713
  readonly code: OwneyErrorCode;
697
714
  readonly details?: Record<string, unknown>;
715
+ readonly agentId?: string;
698
716
  }
699
717
  ```
700
718
 
package/dist/index.cjs CHANGED
@@ -42,17 +42,20 @@ var SupportedAssets = [
42
42
  {
43
43
  address: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`,
44
44
  symbol: "USDC",
45
- chainId: 8453
45
+ chainId: 8453,
46
+ decimals: 6
46
47
  },
47
48
  {
48
49
  address: `0xaf88d065e77c8cC2239327C5EDb3A432268e5831`,
49
50
  symbol: "USDC",
50
- chainId: 42161
51
+ chainId: 42161,
52
+ decimals: 6
51
53
  },
52
54
  {
53
55
  address: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`,
54
56
  symbol: "USDT",
55
- chainId: 42161
57
+ chainId: 42161,
58
+ decimals: 6
56
59
  }
57
60
  ];
58
61
 
@@ -60,11 +63,13 @@ var SupportedAssets = [
60
63
  var OwneyError = class extends Error {
61
64
  code;
62
65
  details;
63
- constructor(code, message, details) {
64
- super(message);
66
+ agentId;
67
+ constructor(code, message, details, agentId) {
68
+ super(agentId ? `[agent:${agentId}] ${message}` : message);
65
69
  this.name = "OwneyError";
66
70
  this.code = code;
67
71
  this.details = details;
72
+ this.agentId = agentId;
68
73
  }
69
74
  };
70
75
  var AgentNotFoundError = class extends OwneyError {
@@ -72,7 +77,8 @@ var AgentNotFoundError = class extends OwneyError {
72
77
  super(
73
78
  "AGENT_NOT_FOUND",
74
79
  `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
75
- { agentId, available }
80
+ { agentId, available },
81
+ agentId
76
82
  );
77
83
  this.name = "AgentNotFoundError";
78
84
  }
@@ -339,7 +345,9 @@ var ZyfaiAgent = class {
339
345
  if (!this.connectedAddress) {
340
346
  throw new OwneyError(
341
347
  "NOT_CONNECTED",
342
- "Wallet not connected. Call sdk.connect() first."
348
+ "Wallet not connected. Call sdk.connect() first.",
349
+ void 0,
350
+ this.id
343
351
  );
344
352
  }
345
353
  return this.connectedAddress;
@@ -445,13 +453,14 @@ var ZyfaiAgent = class {
445
453
  }
446
454
  }
447
455
  // --- IAgent: Fund operations ---
448
- async withdraw(state, chainId, amount) {
456
+ async withdraw(state, chainId, token, amount) {
449
457
  await this.connectFull(state, chainId);
450
458
  const validChainId = isValidChainId(chainId);
451
459
  const raw = await this.sdk.withdrawFunds(
452
460
  this.getAddress(),
453
461
  validChainId,
454
- amount
462
+ amount,
463
+ token
455
464
  );
456
465
  return mapWithdraw(raw);
457
466
  }
@@ -709,6 +718,7 @@ function remapApyPerAssetToSymbols(raw) {
709
718
  }
710
719
 
711
720
  // src/agents/sail/sail.agent.ts
721
+ var import_viem = require("viem");
712
722
  var SAIL_API_BASE = "https://app.sail.money";
713
723
  var PROJECT_ID = "sail";
714
724
  var PAGE_ID = "home";
@@ -766,7 +776,8 @@ var SailAgent = class {
766
776
  throw new OwneyError(
767
777
  "API_SAIL_TIMEOUT",
768
778
  `Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
769
- { timeoutMs, method, path }
779
+ { timeoutMs, method, path },
780
+ this.id
770
781
  );
771
782
  }
772
783
  throw error;
@@ -779,7 +790,8 @@ var SailAgent = class {
779
790
  throw new OwneyError(
780
791
  "API_SAIL_ERROR",
781
792
  `Sail API error ${res.status}: ${text}`,
782
- { statusCode: res.status, method, path, responseBody: text }
793
+ { statusCode: res.status, method, path, responseBody: text },
794
+ this.id
783
795
  );
784
796
  }
785
797
  return res.json();
@@ -818,7 +830,9 @@ var SailAgent = class {
818
830
  if (!state.walletAddress) {
819
831
  throw new OwneyError(
820
832
  "WALLET_ADDRESS_REQUIRED",
821
- "Wallet address is required. Call sdk.connect(provider) first."
833
+ "Wallet address is required. Call sdk.connect(provider) first.",
834
+ void 0,
835
+ this.id
822
836
  );
823
837
  }
824
838
  this.connectPromise = (async () => {
@@ -855,7 +869,9 @@ var SailAgent = class {
855
869
  if (!this.smartWalletAddress) {
856
870
  throw new OwneyError(
857
871
  "NOT_CONNECTED",
858
- "Wallet not connected. Call sdk.connect() first."
872
+ "Wallet not connected. Call sdk.connect() first.",
873
+ void 0,
874
+ this.id
859
875
  );
860
876
  }
861
877
  return {
@@ -884,7 +900,9 @@ var SailAgent = class {
884
900
  if (!depositCallback) {
885
901
  throw new OwneyError(
886
902
  "DEPOSIT_CALLBACK_REQUIRED",
887
- "depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
903
+ "depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash.",
904
+ void 0,
905
+ this.id
888
906
  );
889
907
  }
890
908
  try {
@@ -894,7 +912,9 @@ var SailAgent = class {
894
912
  if (!resolvedTokenAddress) {
895
913
  throw new OwneyError(
896
914
  "DEPOSIT_NO_PERMITTED_TOKENS",
897
- "No permitted tokens available for deposit."
915
+ "No permitted tokens available for deposit.",
916
+ void 0,
917
+ this.id
898
918
  );
899
919
  }
900
920
  await this.executePreHooks(resolvedTokenAddress, amount, chainId);
@@ -902,7 +922,9 @@ var SailAgent = class {
902
922
  if (!txHash) {
903
923
  throw new OwneyError(
904
924
  "DEPOSIT_CALLBACK_INVALID",
905
- "depositCallback must return a transaction hash (txHash) for Sail deposits."
925
+ "depositCallback must return a transaction hash (txHash) for Sail deposits.",
926
+ void 0,
927
+ this.id
906
928
  );
907
929
  }
908
930
  const postResult = await this.executePostHooks(
@@ -950,14 +972,29 @@ var SailAgent = class {
950
972
  );
951
973
  }
952
974
  // --- IAgent: Fund operations ---
953
- async withdraw(state, chainId, amount) {
975
+ async withdraw(state, chainId, token, amount) {
954
976
  await this.connectFull(state, chainId);
955
977
  const withdrawInfo = await this.getWithdrawInfo();
956
- const tokenAddress = withdrawInfo.permittedTokens[0]?.address;
978
+ const selectedTokenAddress = SupportedAssets.find(
979
+ (a) => a.symbol === token && a.chainId === chainId
980
+ );
981
+ if (!selectedTokenAddress) {
982
+ throw new OwneyError(
983
+ "WITHDRAW_NO_PERMITTED_TOKENS",
984
+ "Unsupported asset.",
985
+ void 0,
986
+ this.id
987
+ );
988
+ }
989
+ const tokenAddress = withdrawInfo.permittedTokens.find(
990
+ (t) => t.address === selectedTokenAddress.address
991
+ )?.address;
957
992
  if (!tokenAddress) {
958
993
  throw new OwneyError(
959
994
  "WITHDRAW_NO_PERMITTED_TOKENS",
960
- "No permitted tokens available for withdrawal."
995
+ "No permitted tokens available for withdrawal.",
996
+ void 0,
997
+ this.id
961
998
  );
962
999
  }
963
1000
  const resolvedAmount = amount ?? withdrawInfo.currentBalance;
@@ -977,7 +1014,10 @@ var SailAgent = class {
977
1014
  return mapWithdraw2({
978
1015
  txHash: raw.transactionHash,
979
1016
  type: amount ? "partial" : "full",
980
- amount: resolvedAmount
1017
+ amount: (0, import_viem.parseUnits)(
1018
+ resolvedAmount,
1019
+ selectedTokenAddress.decimals
1020
+ ).toString()
981
1021
  });
982
1022
  }
983
1023
  // --- IAgent: Portfolio reads ---
@@ -1081,7 +1121,8 @@ var SailAgent = class {
1081
1121
  throw new OwneyError(
1082
1122
  "VALIDATION_INVALID_DAYS",
1083
1123
  `Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
1084
- { days }
1124
+ { days },
1125
+ this.id
1085
1126
  );
1086
1127
  }
1087
1128
  const now = /* @__PURE__ */ new Date();
@@ -1188,7 +1229,65 @@ async function fetchAgentKeys(apiKey) {
1188
1229
  return json.data;
1189
1230
  }
1190
1231
 
1232
+ // src/lib/helpers/withdraw-helper.ts
1233
+ function planProportionalShares(balances, requested, totalAvailable) {
1234
+ const plans = balances.map(({ agent, balance }) => ({
1235
+ agent,
1236
+ balance,
1237
+ planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
1238
+ }));
1239
+ const assigned = plans.reduce((s, p) => s + p.planned, 0n);
1240
+ let remainder = requested - assigned;
1241
+ const byHeadroom = [...plans].sort((a, b) => {
1242
+ const diff = b.balance - b.planned - (a.balance - a.planned);
1243
+ return diff > 0n ? 1 : diff < 0n ? -1 : 0;
1244
+ });
1245
+ for (const p of byHeadroom) {
1246
+ if (remainder === 0n)
1247
+ break;
1248
+ const headroom = p.balance - p.planned;
1249
+ if (headroom <= 0n)
1250
+ continue;
1251
+ const take = headroom < remainder ? headroom : remainder;
1252
+ p.planned += take;
1253
+ remainder -= take;
1254
+ }
1255
+ return plans;
1256
+ }
1257
+ function redistributeShare(plans, fromIndex, amount) {
1258
+ const candidates = plans.slice(fromIndex + 1).filter((c) => c.balance - c.planned > 0n);
1259
+ const totalHeadroom = candidates.reduce(
1260
+ (s, c) => s + (c.balance - c.planned),
1261
+ 0n
1262
+ );
1263
+ if (totalHeadroom === 0n)
1264
+ return;
1265
+ let distributed = 0n;
1266
+ for (const c of candidates) {
1267
+ const headroom = c.balance - c.planned;
1268
+ const proportional = headroom * amount / totalHeadroom;
1269
+ const give = proportional > headroom ? headroom : proportional;
1270
+ c.planned += give;
1271
+ distributed += give;
1272
+ }
1273
+ let leftover = amount - distributed;
1274
+ for (const c of candidates) {
1275
+ if (leftover === 0n)
1276
+ break;
1277
+ const headroom = c.balance - c.planned;
1278
+ if (headroom <= 0n)
1279
+ continue;
1280
+ const take = headroom < leftover ? headroom : leftover;
1281
+ c.planned += take;
1282
+ leftover -= take;
1283
+ }
1284
+ }
1285
+ function sumWithdrawnAmount(results) {
1286
+ return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
1287
+ }
1288
+
1191
1289
  // src/client.ts
1290
+ var import_viem2 = require("viem");
1192
1291
  var OwneySDK = class {
1193
1292
  agents = /* @__PURE__ */ new Map();
1194
1293
  activeAgents = /* @__PURE__ */ new Set();
@@ -1464,7 +1563,8 @@ var OwneySDK = class {
1464
1563
  throw new OwneyError(
1465
1564
  "ASSET_UNSUPPORTED",
1466
1565
  `Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
1467
- { agentId: agent.id, asset, chainId }
1566
+ { agentId: agent.id, asset, chainId },
1567
+ agent.id
1468
1568
  );
1469
1569
  }
1470
1570
  }
@@ -1498,34 +1598,99 @@ var OwneySDK = class {
1498
1598
  const { asset, amount, agentId } = options;
1499
1599
  const state = this.requireState();
1500
1600
  const chainId = this.requireChainId();
1601
+ const token = asset;
1602
+ const assetInfo = SupportedAssets.find(
1603
+ (a) => a.chainId === chainId && a.symbol === token
1604
+ );
1605
+ if (!assetInfo) {
1606
+ throw new OwneyError(
1607
+ "WITHDRAW_NO_PERMITTED_TOKENS",
1608
+ "Asset selected is not supported"
1609
+ );
1610
+ }
1501
1611
  if (agentId) {
1502
1612
  const agent = this.getAgent(agentId);
1503
1613
  this.validateAssetSupport(agent, chainId, asset);
1504
- return agent.withdraw(state, chainId, amount);
1614
+ return agent.withdraw(state, chainId, token, amount);
1505
1615
  }
1506
1616
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1507
1617
  if (!amount) {
1508
1618
  const results2 = {};
1509
1619
  for (const agent of eligibleAgents) {
1510
- results2[agent.id] = await agent.withdraw(state, chainId);
1620
+ results2[agent.id] = await agent.withdraw(state, chainId, token);
1511
1621
  }
1512
1622
  return { agentResult: results2 };
1513
1623
  }
1624
+ const requested = BigInt(amount);
1625
+ const aggregated = await this.getBalances();
1626
+ const balances = eligibleAgents.map((agent) => {
1627
+ const agentBalance = aggregated.agentBalances[agent.id];
1628
+ const tokenBalance = agentBalance?.tokens.find(
1629
+ (t) => t.chainId === chainId && t.asset === asset
1630
+ );
1631
+ if (!tokenBalance)
1632
+ return { agent, balance: 0n };
1633
+ const balance = (0, import_viem2.parseUnits)(tokenBalance.amount, assetInfo.decimals);
1634
+ return { agent, balance };
1635
+ });
1636
+ const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
1637
+ if (totalAvailable < requested) {
1638
+ throw new OwneyError(
1639
+ "WITHDRAW_INSUFFICIENT_BALANCE",
1640
+ `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
1641
+ {
1642
+ asset,
1643
+ requested: amount,
1644
+ available: totalAvailable.toString()
1645
+ }
1646
+ );
1647
+ }
1648
+ const plans = planProportionalShares(balances, requested, totalAvailable);
1514
1649
  const results = {};
1515
- let lastError;
1516
- for (const agent of eligibleAgents) {
1650
+ const agentErrors = {};
1651
+ for (let i = 0; i < plans.length; i++) {
1652
+ const p = plans[i];
1653
+ if (p.planned === 0n)
1654
+ continue;
1517
1655
  try {
1518
- results[agent.id] = await agent.withdraw(state, chainId, amount);
1519
- return { agentResult: results };
1656
+ results[p.agent.id] = await p.agent.withdraw(
1657
+ state,
1658
+ chainId,
1659
+ token,
1660
+ p.agent.id === "sail" ? (0, import_viem2.formatUnits)(p.planned, assetInfo.decimals) : p.planned.toString()
1661
+ );
1520
1662
  } catch (err) {
1521
- lastError = err;
1663
+ console.error(`withdraw failed for agent "${p.agent.id}":`, err);
1664
+ agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
1665
+ redistributeShare(plans, i, p.planned);
1522
1666
  }
1523
1667
  }
1524
- if (lastError)
1525
- throw lastError;
1526
- return {
1527
- agentResult: results
1528
- };
1668
+ if (Object.keys(results).length === 0) {
1669
+ throw new OwneyError(
1670
+ "WITHDRAW_ALL_FAILED",
1671
+ `Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
1672
+ {
1673
+ asset,
1674
+ amount,
1675
+ agentErrors
1676
+ }
1677
+ );
1678
+ }
1679
+ const withdrawn = sumWithdrawnAmount(results);
1680
+ if (withdrawn < requested) {
1681
+ throw new OwneyError(
1682
+ "WITHDRAW_PARTIAL_FAILURE",
1683
+ `Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
1684
+ {
1685
+ asset,
1686
+ requested: amount,
1687
+ withdrawn: withdrawn.toString(),
1688
+ partialResults: results,
1689
+ agentErrors
1690
+ }
1691
+ );
1692
+ }
1693
+ return { agentResult: results };
1529
1694
  }
1530
1695
  // --- Portfolio reads ---
1531
1696
  /**
@@ -1550,7 +1715,10 @@ var OwneySDK = class {
1550
1715
  })
1551
1716
  );
1552
1717
  let successCount = 0;
1553
- for (const settledResult of balanceResults) {
1718
+ const agentErrors = {};
1719
+ for (let i = 0; i < balanceResults.length; i++) {
1720
+ const settledResult = balanceResults[i];
1721
+ const [agentId2] = entries[i];
1554
1722
  if (settledResult.status === "fulfilled") {
1555
1723
  const [id, b] = settledResult.value;
1556
1724
  results[id] = b;
@@ -1558,12 +1726,14 @@ var OwneySDK = class {
1558
1726
  successCount += 1;
1559
1727
  continue;
1560
1728
  }
1561
- console.error("getBalances agent failed:", settledResult.reason);
1729
+ const reason = settledResult.reason;
1730
+ agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
1562
1731
  }
1563
1732
  if (successCount === 0) {
1564
1733
  throw new OwneyError(
1565
1734
  "BALANCE_ALL_FAILED",
1566
- "Failed to fetch balances for all active agents."
1735
+ "Failed to fetch balances for all active agents.",
1736
+ { agentErrors }
1567
1737
  );
1568
1738
  }
1569
1739
  return {
@@ -1658,10 +1828,6 @@ var OwneySDK = class {
1658
1828
  totalApy += agentApy * weight;
1659
1829
  }
1660
1830
  }
1661
- console.log("account apy", {
1662
- totalApy: String(totalApy),
1663
- agentApy: results
1664
- });
1665
1831
  return {
1666
1832
  totalApy: String(totalApy),
1667
1833
  agentApy: results
@@ -1802,20 +1968,26 @@ var OwneySDK = class {
1802
1968
  })
1803
1969
  );
1804
1970
  let successCount = 0;
1805
- for (const settledResult of balanceResults) {
1971
+ const agentErrors = {};
1972
+ for (let i = 0; i < balanceResults.length; i++) {
1973
+ const settledResult = balanceResults[i];
1974
+ const [agentId] = entries[i];
1806
1975
  if (settledResult.status === "fulfilled") {
1807
1976
  const [id, bal] = settledResult.value;
1808
1977
  balances[id] = bal;
1809
1978
  totalBalance += bal;
1810
1979
  successCount += 1;
1811
1980
  } else {
1812
- console.error("getAgentAllocation agent failed:", settledResult.reason);
1981
+ const reason = settledResult.reason;
1982
+ agentErrors[agentId] = reason instanceof Error ? reason.message : String(reason);
1983
+ console.error(`getAgentAllocation agent "${agentId}" failed:`, reason);
1813
1984
  }
1814
1985
  }
1815
1986
  if (successCount === 0) {
1816
1987
  throw new OwneyError(
1817
1988
  "ALLOCATION_ALL_FAILED",
1818
- "Failed to fetch allocation: all agents failed."
1989
+ "Failed to fetch allocation: all agents failed.",
1990
+ { agentErrors }
1819
1991
  );
1820
1992
  }
1821
1993
  const allocation = {};
package/dist/index.d.cts CHANGED
@@ -28,7 +28,7 @@ interface IAgent {
28
28
  disconnect(): Promise<void>;
29
29
  activateAgent(state: ConnectionState, chainId: number): Promise<void>;
30
30
  deposit(state: ConnectionState, chainId: number, amount: string, depositCallback?: DepositCallback): Promise<OwneyDepositResult>;
31
- withdraw(state: ConnectionState, chainId: number, amount?: string): Promise<AgentWithdrawResult>;
31
+ withdraw(state: ConnectionState, chainId: number, token: OwneySupportedTokens, amount?: string): Promise<AgentWithdrawResult>;
32
32
  getBalances(state: ConnectionState, chainId: number): Promise<AgentBalance>;
33
33
  getEarnings(state: ConnectionState, chainId: number): Promise<AgentEarnings>;
34
34
  getAccountApy(state: ConnectionState, chainId: number, days: DailyApyDays): Promise<AccountAgentApy>;
@@ -341,11 +341,12 @@ declare class OwneySDK {
341
341
  getAgentAllocation(): Promise<OwneyAgentAllocation>;
342
342
  }
343
343
 
344
- 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_BELOW_MINIMUM" | "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";
344
+ 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_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
345
345
  declare class OwneyError extends Error {
346
346
  readonly code: OwneyErrorCode;
347
347
  readonly details?: Record<string, unknown>;
348
- constructor(code: OwneyErrorCode, message: string, details?: Record<string, unknown>);
348
+ readonly agentId?: string;
349
+ constructor(code: OwneyErrorCode, message: string, details?: Record<string, unknown>, agentId?: string);
349
350
  }
350
351
  declare class AgentNotFoundError extends OwneyError {
351
352
  constructor(agentId: string, available: string[]);
package/dist/index.d.ts CHANGED
@@ -28,7 +28,7 @@ interface IAgent {
28
28
  disconnect(): Promise<void>;
29
29
  activateAgent(state: ConnectionState, chainId: number): Promise<void>;
30
30
  deposit(state: ConnectionState, chainId: number, amount: string, depositCallback?: DepositCallback): Promise<OwneyDepositResult>;
31
- withdraw(state: ConnectionState, chainId: number, amount?: string): Promise<AgentWithdrawResult>;
31
+ withdraw(state: ConnectionState, chainId: number, token: OwneySupportedTokens, amount?: string): Promise<AgentWithdrawResult>;
32
32
  getBalances(state: ConnectionState, chainId: number): Promise<AgentBalance>;
33
33
  getEarnings(state: ConnectionState, chainId: number): Promise<AgentEarnings>;
34
34
  getAccountApy(state: ConnectionState, chainId: number, days: DailyApyDays): Promise<AccountAgentApy>;
@@ -341,11 +341,12 @@ declare class OwneySDK {
341
341
  getAgentAllocation(): Promise<OwneyAgentAllocation>;
342
342
  }
343
343
 
344
- 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_BELOW_MINIMUM" | "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";
344
+ 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_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
345
345
  declare class OwneyError extends Error {
346
346
  readonly code: OwneyErrorCode;
347
347
  readonly details?: Record<string, unknown>;
348
- constructor(code: OwneyErrorCode, message: string, details?: Record<string, unknown>);
348
+ readonly agentId?: string;
349
+ constructor(code: OwneyErrorCode, message: string, details?: Record<string, unknown>, agentId?: string);
349
350
  }
350
351
  declare class AgentNotFoundError extends OwneyError {
351
352
  constructor(agentId: string, available: string[]);
package/dist/index.js CHANGED
@@ -12,17 +12,20 @@ var SupportedAssets = [
12
12
  {
13
13
  address: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`,
14
14
  symbol: "USDC",
15
- chainId: 8453
15
+ chainId: 8453,
16
+ decimals: 6
16
17
  },
17
18
  {
18
19
  address: `0xaf88d065e77c8cC2239327C5EDb3A432268e5831`,
19
20
  symbol: "USDC",
20
- chainId: 42161
21
+ chainId: 42161,
22
+ decimals: 6
21
23
  },
22
24
  {
23
25
  address: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`,
24
26
  symbol: "USDT",
25
- chainId: 42161
27
+ chainId: 42161,
28
+ decimals: 6
26
29
  }
27
30
  ];
28
31
 
@@ -30,11 +33,13 @@ var SupportedAssets = [
30
33
  var OwneyError = class extends Error {
31
34
  code;
32
35
  details;
33
- constructor(code, message, details) {
34
- super(message);
36
+ agentId;
37
+ constructor(code, message, details, agentId) {
38
+ super(agentId ? `[agent:${agentId}] ${message}` : message);
35
39
  this.name = "OwneyError";
36
40
  this.code = code;
37
41
  this.details = details;
42
+ this.agentId = agentId;
38
43
  }
39
44
  };
40
45
  var AgentNotFoundError = class extends OwneyError {
@@ -42,7 +47,8 @@ var AgentNotFoundError = class extends OwneyError {
42
47
  super(
43
48
  "AGENT_NOT_FOUND",
44
49
  `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
45
- { agentId, available }
50
+ { agentId, available },
51
+ agentId
46
52
  );
47
53
  this.name = "AgentNotFoundError";
48
54
  }
@@ -309,7 +315,9 @@ var ZyfaiAgent = class {
309
315
  if (!this.connectedAddress) {
310
316
  throw new OwneyError(
311
317
  "NOT_CONNECTED",
312
- "Wallet not connected. Call sdk.connect() first."
318
+ "Wallet not connected. Call sdk.connect() first.",
319
+ void 0,
320
+ this.id
313
321
  );
314
322
  }
315
323
  return this.connectedAddress;
@@ -415,13 +423,14 @@ var ZyfaiAgent = class {
415
423
  }
416
424
  }
417
425
  // --- IAgent: Fund operations ---
418
- async withdraw(state, chainId, amount) {
426
+ async withdraw(state, chainId, token, amount) {
419
427
  await this.connectFull(state, chainId);
420
428
  const validChainId = isValidChainId(chainId);
421
429
  const raw = await this.sdk.withdrawFunds(
422
430
  this.getAddress(),
423
431
  validChainId,
424
- amount
432
+ amount,
433
+ token
425
434
  );
426
435
  return mapWithdraw(raw);
427
436
  }
@@ -679,6 +688,7 @@ function remapApyPerAssetToSymbols(raw) {
679
688
  }
680
689
 
681
690
  // src/agents/sail/sail.agent.ts
691
+ import { parseUnits } from "viem";
682
692
  var SAIL_API_BASE = "https://app.sail.money";
683
693
  var PROJECT_ID = "sail";
684
694
  var PAGE_ID = "home";
@@ -736,7 +746,8 @@ var SailAgent = class {
736
746
  throw new OwneyError(
737
747
  "API_SAIL_TIMEOUT",
738
748
  `Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
739
- { timeoutMs, method, path }
749
+ { timeoutMs, method, path },
750
+ this.id
740
751
  );
741
752
  }
742
753
  throw error;
@@ -749,7 +760,8 @@ var SailAgent = class {
749
760
  throw new OwneyError(
750
761
  "API_SAIL_ERROR",
751
762
  `Sail API error ${res.status}: ${text}`,
752
- { statusCode: res.status, method, path, responseBody: text }
763
+ { statusCode: res.status, method, path, responseBody: text },
764
+ this.id
753
765
  );
754
766
  }
755
767
  return res.json();
@@ -788,7 +800,9 @@ var SailAgent = class {
788
800
  if (!state.walletAddress) {
789
801
  throw new OwneyError(
790
802
  "WALLET_ADDRESS_REQUIRED",
791
- "Wallet address is required. Call sdk.connect(provider) first."
803
+ "Wallet address is required. Call sdk.connect(provider) first.",
804
+ void 0,
805
+ this.id
792
806
  );
793
807
  }
794
808
  this.connectPromise = (async () => {
@@ -825,7 +839,9 @@ var SailAgent = class {
825
839
  if (!this.smartWalletAddress) {
826
840
  throw new OwneyError(
827
841
  "NOT_CONNECTED",
828
- "Wallet not connected. Call sdk.connect() first."
842
+ "Wallet not connected. Call sdk.connect() first.",
843
+ void 0,
844
+ this.id
829
845
  );
830
846
  }
831
847
  return {
@@ -854,7 +870,9 @@ var SailAgent = class {
854
870
  if (!depositCallback) {
855
871
  throw new OwneyError(
856
872
  "DEPOSIT_CALLBACK_REQUIRED",
857
- "depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
873
+ "depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash.",
874
+ void 0,
875
+ this.id
858
876
  );
859
877
  }
860
878
  try {
@@ -864,7 +882,9 @@ var SailAgent = class {
864
882
  if (!resolvedTokenAddress) {
865
883
  throw new OwneyError(
866
884
  "DEPOSIT_NO_PERMITTED_TOKENS",
867
- "No permitted tokens available for deposit."
885
+ "No permitted tokens available for deposit.",
886
+ void 0,
887
+ this.id
868
888
  );
869
889
  }
870
890
  await this.executePreHooks(resolvedTokenAddress, amount, chainId);
@@ -872,7 +892,9 @@ var SailAgent = class {
872
892
  if (!txHash) {
873
893
  throw new OwneyError(
874
894
  "DEPOSIT_CALLBACK_INVALID",
875
- "depositCallback must return a transaction hash (txHash) for Sail deposits."
895
+ "depositCallback must return a transaction hash (txHash) for Sail deposits.",
896
+ void 0,
897
+ this.id
876
898
  );
877
899
  }
878
900
  const postResult = await this.executePostHooks(
@@ -920,14 +942,29 @@ var SailAgent = class {
920
942
  );
921
943
  }
922
944
  // --- IAgent: Fund operations ---
923
- async withdraw(state, chainId, amount) {
945
+ async withdraw(state, chainId, token, amount) {
924
946
  await this.connectFull(state, chainId);
925
947
  const withdrawInfo = await this.getWithdrawInfo();
926
- const tokenAddress = withdrawInfo.permittedTokens[0]?.address;
948
+ const selectedTokenAddress = SupportedAssets.find(
949
+ (a) => a.symbol === token && a.chainId === chainId
950
+ );
951
+ if (!selectedTokenAddress) {
952
+ throw new OwneyError(
953
+ "WITHDRAW_NO_PERMITTED_TOKENS",
954
+ "Unsupported asset.",
955
+ void 0,
956
+ this.id
957
+ );
958
+ }
959
+ const tokenAddress = withdrawInfo.permittedTokens.find(
960
+ (t) => t.address === selectedTokenAddress.address
961
+ )?.address;
927
962
  if (!tokenAddress) {
928
963
  throw new OwneyError(
929
964
  "WITHDRAW_NO_PERMITTED_TOKENS",
930
- "No permitted tokens available for withdrawal."
965
+ "No permitted tokens available for withdrawal.",
966
+ void 0,
967
+ this.id
931
968
  );
932
969
  }
933
970
  const resolvedAmount = amount ?? withdrawInfo.currentBalance;
@@ -947,7 +984,10 @@ var SailAgent = class {
947
984
  return mapWithdraw2({
948
985
  txHash: raw.transactionHash,
949
986
  type: amount ? "partial" : "full",
950
- amount: resolvedAmount
987
+ amount: parseUnits(
988
+ resolvedAmount,
989
+ selectedTokenAddress.decimals
990
+ ).toString()
951
991
  });
952
992
  }
953
993
  // --- IAgent: Portfolio reads ---
@@ -1051,7 +1091,8 @@ var SailAgent = class {
1051
1091
  throw new OwneyError(
1052
1092
  "VALIDATION_INVALID_DAYS",
1053
1093
  `Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
1054
- { days }
1094
+ { days },
1095
+ this.id
1055
1096
  );
1056
1097
  }
1057
1098
  const now = /* @__PURE__ */ new Date();
@@ -1158,7 +1199,65 @@ async function fetchAgentKeys(apiKey) {
1158
1199
  return json.data;
1159
1200
  }
1160
1201
 
1202
+ // src/lib/helpers/withdraw-helper.ts
1203
+ function planProportionalShares(balances, requested, totalAvailable) {
1204
+ const plans = balances.map(({ agent, balance }) => ({
1205
+ agent,
1206
+ balance,
1207
+ planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
1208
+ }));
1209
+ const assigned = plans.reduce((s, p) => s + p.planned, 0n);
1210
+ let remainder = requested - assigned;
1211
+ const byHeadroom = [...plans].sort((a, b) => {
1212
+ const diff = b.balance - b.planned - (a.balance - a.planned);
1213
+ return diff > 0n ? 1 : diff < 0n ? -1 : 0;
1214
+ });
1215
+ for (const p of byHeadroom) {
1216
+ if (remainder === 0n)
1217
+ break;
1218
+ const headroom = p.balance - p.planned;
1219
+ if (headroom <= 0n)
1220
+ continue;
1221
+ const take = headroom < remainder ? headroom : remainder;
1222
+ p.planned += take;
1223
+ remainder -= take;
1224
+ }
1225
+ return plans;
1226
+ }
1227
+ function redistributeShare(plans, fromIndex, amount) {
1228
+ const candidates = plans.slice(fromIndex + 1).filter((c) => c.balance - c.planned > 0n);
1229
+ const totalHeadroom = candidates.reduce(
1230
+ (s, c) => s + (c.balance - c.planned),
1231
+ 0n
1232
+ );
1233
+ if (totalHeadroom === 0n)
1234
+ return;
1235
+ let distributed = 0n;
1236
+ for (const c of candidates) {
1237
+ const headroom = c.balance - c.planned;
1238
+ const proportional = headroom * amount / totalHeadroom;
1239
+ const give = proportional > headroom ? headroom : proportional;
1240
+ c.planned += give;
1241
+ distributed += give;
1242
+ }
1243
+ let leftover = amount - distributed;
1244
+ for (const c of candidates) {
1245
+ if (leftover === 0n)
1246
+ break;
1247
+ const headroom = c.balance - c.planned;
1248
+ if (headroom <= 0n)
1249
+ continue;
1250
+ const take = headroom < leftover ? headroom : leftover;
1251
+ c.planned += take;
1252
+ leftover -= take;
1253
+ }
1254
+ }
1255
+ function sumWithdrawnAmount(results) {
1256
+ return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
1257
+ }
1258
+
1161
1259
  // src/client.ts
1260
+ import { formatUnits, parseUnits as parseUnits2 } from "viem";
1162
1261
  var OwneySDK = class {
1163
1262
  agents = /* @__PURE__ */ new Map();
1164
1263
  activeAgents = /* @__PURE__ */ new Set();
@@ -1434,7 +1533,8 @@ var OwneySDK = class {
1434
1533
  throw new OwneyError(
1435
1534
  "ASSET_UNSUPPORTED",
1436
1535
  `Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
1437
- { agentId: agent.id, asset, chainId }
1536
+ { agentId: agent.id, asset, chainId },
1537
+ agent.id
1438
1538
  );
1439
1539
  }
1440
1540
  }
@@ -1468,34 +1568,99 @@ var OwneySDK = class {
1468
1568
  const { asset, amount, agentId } = options;
1469
1569
  const state = this.requireState();
1470
1570
  const chainId = this.requireChainId();
1571
+ const token = asset;
1572
+ const assetInfo = SupportedAssets.find(
1573
+ (a) => a.chainId === chainId && a.symbol === token
1574
+ );
1575
+ if (!assetInfo) {
1576
+ throw new OwneyError(
1577
+ "WITHDRAW_NO_PERMITTED_TOKENS",
1578
+ "Asset selected is not supported"
1579
+ );
1580
+ }
1471
1581
  if (agentId) {
1472
1582
  const agent = this.getAgent(agentId);
1473
1583
  this.validateAssetSupport(agent, chainId, asset);
1474
- return agent.withdraw(state, chainId, amount);
1584
+ return agent.withdraw(state, chainId, token, amount);
1475
1585
  }
1476
1586
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1477
1587
  if (!amount) {
1478
1588
  const results2 = {};
1479
1589
  for (const agent of eligibleAgents) {
1480
- results2[agent.id] = await agent.withdraw(state, chainId);
1590
+ results2[agent.id] = await agent.withdraw(state, chainId, token);
1481
1591
  }
1482
1592
  return { agentResult: results2 };
1483
1593
  }
1594
+ const requested = BigInt(amount);
1595
+ const aggregated = await this.getBalances();
1596
+ const balances = eligibleAgents.map((agent) => {
1597
+ const agentBalance = aggregated.agentBalances[agent.id];
1598
+ const tokenBalance = agentBalance?.tokens.find(
1599
+ (t) => t.chainId === chainId && t.asset === asset
1600
+ );
1601
+ if (!tokenBalance)
1602
+ return { agent, balance: 0n };
1603
+ const balance = parseUnits2(tokenBalance.amount, assetInfo.decimals);
1604
+ return { agent, balance };
1605
+ });
1606
+ const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
1607
+ if (totalAvailable < requested) {
1608
+ throw new OwneyError(
1609
+ "WITHDRAW_INSUFFICIENT_BALANCE",
1610
+ `Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
1611
+ {
1612
+ asset,
1613
+ requested: amount,
1614
+ available: totalAvailable.toString()
1615
+ }
1616
+ );
1617
+ }
1618
+ const plans = planProportionalShares(balances, requested, totalAvailable);
1484
1619
  const results = {};
1485
- let lastError;
1486
- for (const agent of eligibleAgents) {
1620
+ const agentErrors = {};
1621
+ for (let i = 0; i < plans.length; i++) {
1622
+ const p = plans[i];
1623
+ if (p.planned === 0n)
1624
+ continue;
1487
1625
  try {
1488
- results[agent.id] = await agent.withdraw(state, chainId, amount);
1489
- return { agentResult: results };
1626
+ results[p.agent.id] = await p.agent.withdraw(
1627
+ state,
1628
+ chainId,
1629
+ token,
1630
+ p.agent.id === "sail" ? formatUnits(p.planned, assetInfo.decimals) : p.planned.toString()
1631
+ );
1490
1632
  } catch (err) {
1491
- lastError = err;
1633
+ console.error(`withdraw failed for agent "${p.agent.id}":`, err);
1634
+ agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
1635
+ redistributeShare(plans, i, p.planned);
1492
1636
  }
1493
1637
  }
1494
- if (lastError)
1495
- throw lastError;
1496
- return {
1497
- agentResult: results
1498
- };
1638
+ if (Object.keys(results).length === 0) {
1639
+ throw new OwneyError(
1640
+ "WITHDRAW_ALL_FAILED",
1641
+ `Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
1642
+ {
1643
+ asset,
1644
+ amount,
1645
+ agentErrors
1646
+ }
1647
+ );
1648
+ }
1649
+ const withdrawn = sumWithdrawnAmount(results);
1650
+ if (withdrawn < requested) {
1651
+ throw new OwneyError(
1652
+ "WITHDRAW_PARTIAL_FAILURE",
1653
+ `Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
1654
+ {
1655
+ asset,
1656
+ requested: amount,
1657
+ withdrawn: withdrawn.toString(),
1658
+ partialResults: results,
1659
+ agentErrors
1660
+ }
1661
+ );
1662
+ }
1663
+ return { agentResult: results };
1499
1664
  }
1500
1665
  // --- Portfolio reads ---
1501
1666
  /**
@@ -1520,7 +1685,10 @@ var OwneySDK = class {
1520
1685
  })
1521
1686
  );
1522
1687
  let successCount = 0;
1523
- for (const settledResult of balanceResults) {
1688
+ const agentErrors = {};
1689
+ for (let i = 0; i < balanceResults.length; i++) {
1690
+ const settledResult = balanceResults[i];
1691
+ const [agentId2] = entries[i];
1524
1692
  if (settledResult.status === "fulfilled") {
1525
1693
  const [id, b] = settledResult.value;
1526
1694
  results[id] = b;
@@ -1528,12 +1696,14 @@ var OwneySDK = class {
1528
1696
  successCount += 1;
1529
1697
  continue;
1530
1698
  }
1531
- console.error("getBalances agent failed:", settledResult.reason);
1699
+ const reason = settledResult.reason;
1700
+ agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
1532
1701
  }
1533
1702
  if (successCount === 0) {
1534
1703
  throw new OwneyError(
1535
1704
  "BALANCE_ALL_FAILED",
1536
- "Failed to fetch balances for all active agents."
1705
+ "Failed to fetch balances for all active agents.",
1706
+ { agentErrors }
1537
1707
  );
1538
1708
  }
1539
1709
  return {
@@ -1628,10 +1798,6 @@ var OwneySDK = class {
1628
1798
  totalApy += agentApy * weight;
1629
1799
  }
1630
1800
  }
1631
- console.log("account apy", {
1632
- totalApy: String(totalApy),
1633
- agentApy: results
1634
- });
1635
1801
  return {
1636
1802
  totalApy: String(totalApy),
1637
1803
  agentApy: results
@@ -1772,20 +1938,26 @@ var OwneySDK = class {
1772
1938
  })
1773
1939
  );
1774
1940
  let successCount = 0;
1775
- for (const settledResult of balanceResults) {
1941
+ const agentErrors = {};
1942
+ for (let i = 0; i < balanceResults.length; i++) {
1943
+ const settledResult = balanceResults[i];
1944
+ const [agentId] = entries[i];
1776
1945
  if (settledResult.status === "fulfilled") {
1777
1946
  const [id, bal] = settledResult.value;
1778
1947
  balances[id] = bal;
1779
1948
  totalBalance += bal;
1780
1949
  successCount += 1;
1781
1950
  } else {
1782
- console.error("getAgentAllocation agent failed:", settledResult.reason);
1951
+ const reason = settledResult.reason;
1952
+ agentErrors[agentId] = reason instanceof Error ? reason.message : String(reason);
1953
+ console.error(`getAgentAllocation agent "${agentId}" failed:`, reason);
1783
1954
  }
1784
1955
  }
1785
1956
  if (successCount === 0) {
1786
1957
  throw new OwneyError(
1787
1958
  "ALLOCATION_ALL_FAILED",
1788
- "Failed to fetch allocation: all agents failed."
1959
+ "Failed to fetch allocation: all agents failed.",
1960
+ { agentErrors }
1789
1961
  );
1790
1962
  }
1791
1963
  const allocation = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -20,7 +20,8 @@
20
20
  "dev": "tsup --watch"
21
21
  },
22
22
  "dependencies": {
23
- "@zyfai/sdk": "^0.2.30"
23
+ "@zyfai/sdk": "^0.2.30",
24
+ "viem": "^2.48.1"
24
25
  },
25
26
  "devDependencies": {
26
27
  "dotenv": "^17.3.1",