@owney/sdk 0.2.8 → 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/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
  }
@@ -303,8 +309,13 @@ function mapApyByStrategy(raw) {
303
309
  }
304
310
 
305
311
  // src/agents/zyfai/zyfai.agent.ts
312
+ var DEFAULT_ZYFAI_RPC_URLS = {
313
+ 8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
314
+ 42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
315
+ };
306
316
  var ZyfaiAgent = class {
307
317
  id = "zyfai";
318
+ minDepositAmount = "0";
308
319
  supportedChainIds = [8453, 42161];
309
320
  supportedAssets = [
310
321
  {
@@ -324,14 +335,19 @@ var ZyfaiAgent = class {
324
335
  smartWalletCache = /* @__PURE__ */ new Map();
325
336
  connectAuthPromise = null;
326
337
  constructor(apiKey, rpcUrls) {
327
- this.sdk = new import_sdk.ZyfaiSDK({ apiKey, rpcUrls });
338
+ this.sdk = new import_sdk.ZyfaiSDK({
339
+ apiKey,
340
+ rpcUrls: rpcUrls ?? DEFAULT_ZYFAI_RPC_URLS
341
+ });
328
342
  }
329
343
  // --- Private helpers ---
330
344
  getAddress() {
331
345
  if (!this.connectedAddress) {
332
346
  throw new OwneyError(
333
347
  "NOT_CONNECTED",
334
- "Wallet not connected. Call sdk.connect() first."
348
+ "Wallet not connected. Call sdk.connect() first.",
349
+ void 0,
350
+ this.id
335
351
  );
336
352
  }
337
353
  return this.connectedAddress;
@@ -437,13 +453,14 @@ var ZyfaiAgent = class {
437
453
  }
438
454
  }
439
455
  // --- IAgent: Fund operations ---
440
- async withdraw(state, chainId, amount) {
456
+ async withdraw(state, chainId, token, amount) {
441
457
  await this.connectFull(state, chainId);
442
458
  const validChainId = isValidChainId(chainId);
443
459
  const raw = await this.sdk.withdrawFunds(
444
460
  this.getAddress(),
445
461
  validChainId,
446
- amount
462
+ amount,
463
+ token
447
464
  );
448
465
  return mapWithdraw(raw);
449
466
  }
@@ -640,7 +657,7 @@ function mapHistory2(raw) {
640
657
  rewards: "Earned"
641
658
  };
642
659
  const filtered = raw.result.items.filter(
643
- (i) => i.status === "success" && i.transactions.length
660
+ (i) => !(i.type === "execution" && (i.status === "failed" || !i.transactions.length))
644
661
  );
645
662
  return {
646
663
  total: filtered.length,
@@ -701,12 +718,14 @@ function remapApyPerAssetToSymbols(raw) {
701
718
  }
702
719
 
703
720
  // src/agents/sail/sail.agent.ts
721
+ var import_viem = require("viem");
704
722
  var SAIL_API_BASE = "https://app.sail.money";
705
723
  var PROJECT_ID = "sail";
706
724
  var PAGE_ID = "home";
707
725
  var SAIL_PORTFOLIO_TIMEOUT_MS = 8e3;
708
726
  var SailAgent = class {
709
727
  id = "sail";
728
+ minDepositAmount = "4800000000";
710
729
  supportedChainIds = [8453, 42161];
711
730
  supportedAssets = [
712
731
  {
@@ -757,7 +776,8 @@ var SailAgent = class {
757
776
  throw new OwneyError(
758
777
  "API_SAIL_TIMEOUT",
759
778
  `Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
760
- { timeoutMs, method, path }
779
+ { timeoutMs, method, path },
780
+ this.id
761
781
  );
762
782
  }
763
783
  throw error;
@@ -770,7 +790,8 @@ var SailAgent = class {
770
790
  throw new OwneyError(
771
791
  "API_SAIL_ERROR",
772
792
  `Sail API error ${res.status}: ${text}`,
773
- { statusCode: res.status, method, path, responseBody: text }
793
+ { statusCode: res.status, method, path, responseBody: text },
794
+ this.id
774
795
  );
775
796
  }
776
797
  return res.json();
@@ -809,7 +830,9 @@ var SailAgent = class {
809
830
  if (!state.walletAddress) {
810
831
  throw new OwneyError(
811
832
  "WALLET_ADDRESS_REQUIRED",
812
- "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
813
836
  );
814
837
  }
815
838
  this.connectPromise = (async () => {
@@ -846,7 +869,9 @@ var SailAgent = class {
846
869
  if (!this.smartWalletAddress) {
847
870
  throw new OwneyError(
848
871
  "NOT_CONNECTED",
849
- "Wallet not connected. Call sdk.connect() first."
872
+ "Wallet not connected. Call sdk.connect() first.",
873
+ void 0,
874
+ this.id
850
875
  );
851
876
  }
852
877
  return {
@@ -875,7 +900,9 @@ var SailAgent = class {
875
900
  if (!depositCallback) {
876
901
  throw new OwneyError(
877
902
  "DEPOSIT_CALLBACK_REQUIRED",
878
- "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
879
906
  );
880
907
  }
881
908
  try {
@@ -885,7 +912,9 @@ var SailAgent = class {
885
912
  if (!resolvedTokenAddress) {
886
913
  throw new OwneyError(
887
914
  "DEPOSIT_NO_PERMITTED_TOKENS",
888
- "No permitted tokens available for deposit."
915
+ "No permitted tokens available for deposit.",
916
+ void 0,
917
+ this.id
889
918
  );
890
919
  }
891
920
  await this.executePreHooks(resolvedTokenAddress, amount, chainId);
@@ -893,7 +922,9 @@ var SailAgent = class {
893
922
  if (!txHash) {
894
923
  throw new OwneyError(
895
924
  "DEPOSIT_CALLBACK_INVALID",
896
- "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
897
928
  );
898
929
  }
899
930
  const postResult = await this.executePostHooks(
@@ -941,14 +972,29 @@ var SailAgent = class {
941
972
  );
942
973
  }
943
974
  // --- IAgent: Fund operations ---
944
- async withdraw(state, chainId, amount) {
975
+ async withdraw(state, chainId, token, amount) {
945
976
  await this.connectFull(state, chainId);
946
977
  const withdrawInfo = await this.getWithdrawInfo();
947
- 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;
948
992
  if (!tokenAddress) {
949
993
  throw new OwneyError(
950
994
  "WITHDRAW_NO_PERMITTED_TOKENS",
951
- "No permitted tokens available for withdrawal."
995
+ "No permitted tokens available for withdrawal.",
996
+ void 0,
997
+ this.id
952
998
  );
953
999
  }
954
1000
  const resolvedAmount = amount ?? withdrawInfo.currentBalance;
@@ -968,7 +1014,10 @@ var SailAgent = class {
968
1014
  return mapWithdraw2({
969
1015
  txHash: raw.transactionHash,
970
1016
  type: amount ? "partial" : "full",
971
- amount: resolvedAmount
1017
+ amount: (0, import_viem.parseUnits)(
1018
+ resolvedAmount,
1019
+ selectedTokenAddress.decimals
1020
+ ).toString()
972
1021
  });
973
1022
  }
974
1023
  // --- IAgent: Portfolio reads ---
@@ -1072,7 +1121,8 @@ var SailAgent = class {
1072
1121
  throw new OwneyError(
1073
1122
  "VALIDATION_INVALID_DAYS",
1074
1123
  `Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
1075
- { days }
1124
+ { days },
1125
+ this.id
1076
1126
  );
1077
1127
  }
1078
1128
  const now = /* @__PURE__ */ new Date();
@@ -1179,7 +1229,65 @@ async function fetchAgentKeys(apiKey) {
1179
1229
  return json.data;
1180
1230
  }
1181
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
+
1182
1289
  // src/client.ts
1290
+ var import_viem2 = require("viem");
1183
1291
  var OwneySDK = class {
1184
1292
  agents = /* @__PURE__ */ new Map();
1185
1293
  activeAgents = /* @__PURE__ */ new Set();
@@ -1372,7 +1480,7 @@ var OwneySDK = class {
1372
1480
  }
1373
1481
  /**
1374
1482
  * Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
1375
- * Validates that the asset is supported by the target agent(s) on the active chain.
1483
+ * Validates that the asset is supported and amount meets minimums for the target agent(s).
1376
1484
  * @param options - Deposit parameters
1377
1485
  * @param options.amount - Amount to deposit in smallest unit (e.g. "100000000" for 100 USDC)
1378
1486
  * @param options.asset - Asset symbol to deposit (e.g. "USDC")
@@ -1384,28 +1492,29 @@ var OwneySDK = class {
1384
1492
  */
1385
1493
  async deposit(options) {
1386
1494
  const { amount, asset, depositCallback, agentId } = options;
1495
+ if (BigInt(amount) === 0n) {
1496
+ return { agentResults: {} };
1497
+ }
1387
1498
  const state = this.requireState();
1388
1499
  const chainId = this.requireChainId();
1389
1500
  if (agentId) {
1390
1501
  const agent = this.getAgent(agentId);
1391
1502
  this.validateAssetSupport(agent, chainId, asset);
1503
+ this.validateMinDepositAmount(agent, amount);
1392
1504
  return agent.deposit(state, chainId, amount, depositCallback);
1393
1505
  }
1394
- const eligibleAgents = this.getEligibleAgents(chainId, asset);
1395
1506
  const totalAmount = BigInt(amount);
1396
- if (totalAmount < BigInt(eligibleAgents.length)) {
1507
+ const eligibleAgents = this.getEligibleAgents(chainId, asset);
1508
+ const agentAmounts = this.splitDepositAmount(totalAmount, eligibleAgents);
1509
+ if (agentAmounts.length === 0) {
1397
1510
  throw new OwneyError(
1398
- "DEPOSIT_AMOUNT_TOO_SMALL",
1399
- `Amount "${amount}" is too small to split across ${eligibleAgents.length} agents.`,
1400
- { amount, agentCount: eligibleAgents.length }
1511
+ "DEPOSIT_AMOUNT_BELOW_MINIMUM",
1512
+ `Amount "${amount}" cannot satisfy minimum deposit requirements for any eligible agent.`,
1513
+ { amount }
1401
1514
  );
1402
1515
  }
1403
- const perAgent = totalAmount / BigInt(eligibleAgents.length);
1404
- const remainder = totalAmount % BigInt(eligibleAgents.length);
1405
1516
  const agentResults = {};
1406
- for (let i = 0; i < eligibleAgents.length; i++) {
1407
- const agent = eligibleAgents[i];
1408
- const agentAmount = i === eligibleAgents.length - 1 ? String(perAgent + remainder) : String(perAgent);
1517
+ for (const { agent, amount: agentAmount } of agentAmounts) {
1409
1518
  agentResults[agent.id] = await agent.deposit(
1410
1519
  state,
1411
1520
  chainId,
@@ -1415,6 +1524,37 @@ var OwneySDK = class {
1415
1524
  }
1416
1525
  return { agentResults };
1417
1526
  }
1527
+ splitDepositAmount(totalAmount, agents) {
1528
+ if (agents.length === 0)
1529
+ return [];
1530
+ const perAgent = totalAmount / BigInt(agents.length);
1531
+ const remainder = totalAmount % BigInt(agents.length);
1532
+ const splits = agents.map((agent, i) => ({
1533
+ agent,
1534
+ amount: i === agents.length - 1 ? perAgent + remainder : perAgent
1535
+ }));
1536
+ const valid = splits.filter(
1537
+ (s) => s.amount >= BigInt(s.agent.minDepositAmount)
1538
+ );
1539
+ if (valid.length === agents.length) {
1540
+ return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
1541
+ }
1542
+ return this.splitDepositAmount(totalAmount, valid.map((s) => s.agent));
1543
+ }
1544
+ validateMinDepositAmount(agent, amount) {
1545
+ const minDepositAmount = BigInt(agent.minDepositAmount);
1546
+ const parsedAmount = BigInt(amount);
1547
+ if (parsedAmount < minDepositAmount) {
1548
+ throw new OwneyError(
1549
+ "DEPOSIT_AMOUNT_BELOW_MINIMUM",
1550
+ `Amount "${amount}" is below the required minimum deposit for agent "${agent.id}".`,
1551
+ {
1552
+ agentId: agent.id,
1553
+ amount
1554
+ }
1555
+ );
1556
+ }
1557
+ }
1418
1558
  validateAssetSupport(agent, chainId, asset) {
1419
1559
  const chainAssets = agent.supportedAssets.find(
1420
1560
  (sa) => sa.chainId === chainId
@@ -1423,7 +1563,8 @@ var OwneySDK = class {
1423
1563
  throw new OwneyError(
1424
1564
  "ASSET_UNSUPPORTED",
1425
1565
  `Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
1426
- { agentId: agent.id, asset, chainId }
1566
+ { agentId: agent.id, asset, chainId },
1567
+ agent.id
1427
1568
  );
1428
1569
  }
1429
1570
  }
@@ -1457,34 +1598,99 @@ var OwneySDK = class {
1457
1598
  const { asset, amount, agentId } = options;
1458
1599
  const state = this.requireState();
1459
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
+ }
1460
1611
  if (agentId) {
1461
1612
  const agent = this.getAgent(agentId);
1462
1613
  this.validateAssetSupport(agent, chainId, asset);
1463
- return agent.withdraw(state, chainId, amount);
1614
+ return agent.withdraw(state, chainId, token, amount);
1464
1615
  }
1465
1616
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1466
1617
  if (!amount) {
1467
1618
  const results2 = {};
1468
1619
  for (const agent of eligibleAgents) {
1469
- results2[agent.id] = await agent.withdraw(state, chainId);
1620
+ results2[agent.id] = await agent.withdraw(state, chainId, token);
1470
1621
  }
1471
1622
  return { agentResult: results2 };
1472
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);
1473
1649
  const results = {};
1474
- let lastError;
1475
- 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;
1476
1655
  try {
1477
- results[agent.id] = await agent.withdraw(state, chainId, amount);
1478
- 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
+ );
1479
1662
  } catch (err) {
1480
- 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);
1481
1666
  }
1482
1667
  }
1483
- if (lastError)
1484
- throw lastError;
1485
- return {
1486
- agentResult: results
1487
- };
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 };
1488
1694
  }
1489
1695
  // --- Portfolio reads ---
1490
1696
  /**
@@ -1509,7 +1715,10 @@ var OwneySDK = class {
1509
1715
  })
1510
1716
  );
1511
1717
  let successCount = 0;
1512
- 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];
1513
1722
  if (settledResult.status === "fulfilled") {
1514
1723
  const [id, b] = settledResult.value;
1515
1724
  results[id] = b;
@@ -1517,12 +1726,14 @@ var OwneySDK = class {
1517
1726
  successCount += 1;
1518
1727
  continue;
1519
1728
  }
1520
- console.error("getBalances agent failed:", settledResult.reason);
1729
+ const reason = settledResult.reason;
1730
+ agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
1521
1731
  }
1522
1732
  if (successCount === 0) {
1523
1733
  throw new OwneyError(
1524
1734
  "BALANCE_ALL_FAILED",
1525
- "Failed to fetch balances for all active agents."
1735
+ "Failed to fetch balances for all active agents.",
1736
+ { agentErrors }
1526
1737
  );
1527
1738
  }
1528
1739
  return {
@@ -1597,16 +1808,24 @@ var OwneySDK = class {
1597
1808
  results[id] = apy;
1598
1809
  }
1599
1810
  const balances = {};
1600
- let totalBalance = 0;
1601
1811
  for (const [id, bal] of balanceResults) {
1602
1812
  balances[id] = bal;
1603
- totalBalance += bal;
1604
1813
  }
1605
1814
  let totalApy = 0;
1606
- if (totalBalance > 0) {
1815
+ let relevantBalance = 0;
1816
+ for (const id of Object.keys(results)) {
1817
+ const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
1818
+ if (agentApy === 0)
1819
+ continue;
1820
+ relevantBalance += balances[id];
1821
+ }
1822
+ if (relevantBalance > 0) {
1607
1823
  for (const id of Object.keys(results)) {
1608
- const weight = balances[id] / totalBalance;
1609
- totalApy += Number(results[id].weightedApyAfterFee ?? 0) * weight;
1824
+ const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
1825
+ if (agentApy === 0)
1826
+ continue;
1827
+ const weight = balances[id] / relevantBalance;
1828
+ totalApy += agentApy * weight;
1610
1829
  }
1611
1830
  }
1612
1831
  return {
@@ -1749,20 +1968,26 @@ var OwneySDK = class {
1749
1968
  })
1750
1969
  );
1751
1970
  let successCount = 0;
1752
- 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];
1753
1975
  if (settledResult.status === "fulfilled") {
1754
1976
  const [id, bal] = settledResult.value;
1755
1977
  balances[id] = bal;
1756
1978
  totalBalance += bal;
1757
1979
  successCount += 1;
1758
1980
  } else {
1759
- 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);
1760
1984
  }
1761
1985
  }
1762
1986
  if (successCount === 0) {
1763
1987
  throw new OwneyError(
1764
1988
  "ALLOCATION_ALL_FAILED",
1765
- "Failed to fetch allocation: all agents failed."
1989
+ "Failed to fetch allocation: all agents failed.",
1990
+ { agentErrors }
1766
1991
  );
1767
1992
  }
1768
1993
  const allocation = {};
package/dist/index.d.cts CHANGED
@@ -24,10 +24,11 @@ interface IAgent {
24
24
  readonly id: string;
25
25
  readonly supportedChainIds: readonly OwneySupportedChainId[];
26
26
  readonly supportedAssets: readonly AgentSupportedAssets[];
27
+ readonly minDepositAmount: string;
27
28
  disconnect(): Promise<void>;
28
29
  activateAgent(state: ConnectionState, chainId: number): Promise<void>;
29
30
  deposit(state: ConnectionState, chainId: number, amount: string, depositCallback?: DepositCallback): Promise<OwneyDepositResult>;
30
- withdraw(state: ConnectionState, chainId: number, amount?: string): Promise<AgentWithdrawResult>;
31
+ withdraw(state: ConnectionState, chainId: number, token: OwneySupportedTokens, amount?: string): Promise<AgentWithdrawResult>;
31
32
  getBalances(state: ConnectionState, chainId: number): Promise<AgentBalance>;
32
33
  getEarnings(state: ConnectionState, chainId: number): Promise<AgentEarnings>;
33
34
  getAccountApy(state: ConnectionState, chainId: number, days: DailyApyDays): Promise<AccountAgentApy>;
@@ -196,10 +197,6 @@ interface AgentApy {
196
197
  interface OwneyAgentApy {
197
198
  agentApy: Record<AgentId, AgentApy>;
198
199
  }
199
- interface OwneyRouteResponse {
200
- agent: AgentId;
201
- chainId: number;
202
- }
203
200
  interface OwneyAgentAllocation {
204
201
  agentAllocation: Record<AgentId, number>;
205
202
  }
@@ -252,7 +249,7 @@ declare class OwneySDK {
252
249
  activateAgent(chainId: number, agentId?: AgentId[]): Promise<void>;
253
250
  /**
254
251
  * Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
255
- * Validates that the asset is supported by the target agent(s) on the active chain.
252
+ * Validates that the asset is supported and amount meets minimums for the target agent(s).
256
253
  * @param options - Deposit parameters
257
254
  * @param options.amount - Amount to deposit in smallest unit (e.g. "100000000" for 100 USDC)
258
255
  * @param options.asset - Asset symbol to deposit (e.g. "USDC")
@@ -263,6 +260,8 @@ declare class OwneySDK {
263
260
  * @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
264
261
  */
265
262
  deposit(options: DepositOptions): Promise<OwneyDepositResult | OwneyMultiDepositResult>;
263
+ private splitDepositAmount;
264
+ private validateMinDepositAmount;
266
265
  private validateAssetSupport;
267
266
  private getEligibleAgents;
268
267
  /**
@@ -342,11 +341,12 @@ declare class OwneySDK {
342
341
  getAgentAllocation(): Promise<OwneyAgentAllocation>;
343
342
  }
344
343
 
345
- 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";
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";
346
345
  declare class OwneyError extends Error {
347
346
  readonly code: OwneyErrorCode;
348
347
  readonly details?: Record<string, unknown>;
349
- 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);
350
350
  }
351
351
  declare class AgentNotFoundError extends OwneyError {
352
352
  constructor(agentId: string, available: string[]);
@@ -366,4 +366,4 @@ declare class AgentChainIncompatibleError extends OwneyError {
366
366
  }>, connectedChainId: number);
367
367
  }
368
368
 
369
- 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 };
369
+ 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, OwneySDK, OwneySDKConfig, OwneySupportedChainId, OwneySupportedChains, OwneySupportedTokens, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };