@owney/sdk 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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
  };
@@ -228,15 +273,6 @@ function mapApyByStrategy(raw) {
228
273
  }
229
274
 
230
275
  // src/agents/zyfai/zyfai.agent.ts
231
- var ZYFAI_TIMING_PREFIX = "[AgentTiming][zyfai]";
232
- function logTiming(label, startedAt, meta) {
233
- const elapsedMs = Date.now() - startedAt;
234
- if (meta) {
235
- console.log(`${ZYFAI_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
236
- return;
237
- }
238
- console.log(`${ZYFAI_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
239
- }
240
276
  var ZyfaiAgent = class {
241
277
  id = "zyfai";
242
278
  supportedChainIds = [8453, 42161];
@@ -257,29 +293,27 @@ var ZyfaiAgent = class {
257
293
  connectedChainId = null;
258
294
  smartWalletCache = /* @__PURE__ */ new Map();
259
295
  connectAuthPromise = null;
260
- constructor(apiKey) {
261
- this.sdk = new ZyfaiSDK({ apiKey });
296
+ constructor(apiKey, rpcUrls) {
297
+ this.sdk = new ZyfaiSDK({ apiKey, rpcUrls });
262
298
  }
263
299
  // --- Private helpers ---
264
300
  getAddress() {
265
301
  if (!this.connectedAddress) {
266
- throw new Error("Wallet not connected. Call sdk.connect() first.");
302
+ throw new OwneyError(
303
+ "NOT_CONNECTED",
304
+ "Wallet not connected. Call sdk.connect() first."
305
+ );
267
306
  }
268
307
  return this.connectedAddress;
269
308
  }
270
309
  /** Light connect: SIWE auth only. Re-auths if chain changed. */
271
310
  async connectAuth(state, chainId) {
272
- const startedAt = Date.now();
273
311
  const validChainId = isValidChainId(chainId);
274
312
  if (this.connectedAddress && this.connectedChainId === validChainId) {
275
- logTiming("connectAuth cache-hit", startedAt, { chainId: validChainId });
276
313
  return;
277
314
  }
278
315
  if (this.connectAuthPromise) {
279
316
  await this.connectAuthPromise;
280
- logTiming("connectAuth awaited-inflight", startedAt, {
281
- chainId: validChainId
282
- });
283
317
  return;
284
318
  }
285
319
  this.connectAuthPromise = (async () => {
@@ -292,69 +326,40 @@ var ZyfaiAgent = class {
292
326
  })();
293
327
  try {
294
328
  await this.connectAuthPromise;
295
- logTiming("connectAuth success", startedAt, { chainId: validChainId });
296
329
  } finally {
297
330
  this.connectAuthPromise = null;
298
331
  }
299
332
  }
300
333
  /** Full connect: SIWE auth + Safe deploy check + session key. */
301
334
  async connectFull(state, chainId) {
302
- const startedAt = Date.now();
303
335
  await this.connectAuth(state, chainId);
304
336
  const validChainId = isValidChainId(chainId);
305
337
  const address = this.getAddress();
306
- const walletLookupStart = Date.now();
307
338
  const wallet = await this.sdk.getSmartWalletAddress(address, validChainId);
308
- logTiming("connectFull getSmartWalletAddress", walletLookupStart, {
309
- chainId: validChainId
310
- });
311
339
  this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
312
340
  if (!wallet.isDeployed) {
313
- const deployStart = Date.now();
314
341
  await this.sdk.deploySafe(address, validChainId, "aggressive", true);
315
- logTiming("connectFull deploySafe", deployStart, {
316
- chainId: validChainId
317
- });
318
342
  }
319
- const userDetailsStart = Date.now();
320
343
  const user = await this.sdk.getUserDetails();
321
- logTiming("connectFull getUserDetails", userDetailsStart, {
322
- chainId: validChainId
323
- });
324
344
  if (!user.hasActiveSessionKey) {
325
- const sessionKeyStart = Date.now();
326
345
  await this.sdk.createSessionKey(address, validChainId);
327
- logTiming("connectFull createSessionKey", sessionKeyStart, {
328
- chainId: validChainId
329
- });
330
346
  }
331
- logTiming("connectFull success", startedAt, { chainId: validChainId });
332
347
  }
333
348
  /** Connect + resolve smart wallet address for read operations. */
334
349
  async resolveSmartWallet(state, chainId) {
335
- const startedAt = Date.now();
336
350
  await this.connectAuth(state, chainId);
337
351
  const validChainId = isValidChainId(chainId);
338
352
  const ownerAddress = this.getAddress();
339
353
  const cacheKey = `${ownerAddress}_${validChainId}`;
340
354
  let smartWallet = this.smartWalletCache.get(cacheKey);
341
355
  if (!smartWallet) {
342
- const lookupStart = Date.now();
343
356
  const { address } = await this.sdk.getSmartWalletAddress(
344
357
  ownerAddress,
345
358
  validChainId
346
359
  );
347
- logTiming("resolveSmartWallet remote-lookup", lookupStart, {
348
- chainId: validChainId
349
- });
350
360
  smartWallet = address;
351
361
  this.smartWalletCache.set(cacheKey, smartWallet);
352
- } else {
353
- logTiming("resolveSmartWallet cache-hit", startedAt, {
354
- chainId: validChainId
355
- });
356
362
  }
357
- logTiming("resolveSmartWallet ready", startedAt, { chainId: validChainId });
358
363
  return { smartWallet, chainId: validChainId };
359
364
  }
360
365
  // --- IAgent: Connection lifecycle ---
@@ -414,16 +419,9 @@ var ZyfaiAgent = class {
414
419
  }
415
420
  // --- IAgent: Portfolio reads ---
416
421
  async getBalances(state, chainId) {
417
- 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, {
423
- chainId: validChainId
424
- });
425
- logTiming("getBalances success", startedAt, { chainId: validChainId });
426
- return mapBalances(raw, validChainId);
422
+ const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
423
+ const raw = await this.sdk.getPortfolio(this.getAddress());
424
+ return mapBalances(raw, validChainId, smartWallet);
427
425
  }
428
426
  async getEarnings(state, chainId) {
429
427
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
@@ -487,13 +485,13 @@ function mapWithdraw2(raw) {
487
485
  function mapPortfolioBalances(raw, smartWallet) {
488
486
  let total = 0;
489
487
  const tokens = [];
490
- for (const item of raw.result) {
491
- total += item.usd_value;
488
+ for (const item of raw.tokens) {
489
+ total += item.balanceFormatted;
492
490
  tokens.push({
493
- chain: item.chain.toUpperCase(),
494
- chainId: item.chain_id,
495
- asset: item.token === "USD\u20AE0" ? "USDT" : item.token,
496
- amount: String(item.usd_value)
491
+ chain: SUPPORTED_CHAINS[item.chain],
492
+ chainId: item.chain,
493
+ asset: item.symbol === "USD\u20AE0" ? "USDT" : item.symbol,
494
+ amount: item.balance
497
495
  });
498
496
  }
499
497
  return {
@@ -676,16 +674,7 @@ function remapApyPerAssetToSymbols(raw) {
676
674
  var SAIL_API_BASE = "https://app.sail.money";
677
675
  var PROJECT_ID = "sail";
678
676
  var PAGE_ID = "home";
679
- var SAIL_TIMING_PREFIX = "[AgentTiming][sail]";
680
677
  var SAIL_PORTFOLIO_TIMEOUT_MS = 8e3;
681
- function logTiming2(label, startedAt, meta) {
682
- const elapsedMs = Date.now() - startedAt;
683
- if (meta) {
684
- console.log(`${SAIL_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
685
- return;
686
- }
687
- console.log(`${SAIL_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
688
- }
689
678
  var SailAgent = class {
690
679
  id = "sail";
691
680
  supportedChainIds = [8453, 42161];
@@ -735,7 +724,11 @@ var SailAgent = class {
735
724
  });
736
725
  } catch (error) {
737
726
  if (error instanceof Error && error.name === "AbortError" && timeoutMs) {
738
- throw new Error(`Sail API timeout ${timeoutMs}ms: ${method} ${path}`);
727
+ throw new OwneyError(
728
+ "API_SAIL_TIMEOUT",
729
+ `Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
730
+ { timeoutMs, method, path }
731
+ );
739
732
  }
740
733
  throw error;
741
734
  } finally {
@@ -744,13 +737,16 @@ var SailAgent = class {
744
737
  }
745
738
  if (!res.ok) {
746
739
  const text = await res.text().catch(() => "");
747
- throw new Error(`Sail API error ${res.status}: ${text}`);
740
+ throw new OwneyError(
741
+ "API_SAIL_ERROR",
742
+ `Sail API error ${res.status}: ${text}`,
743
+ { statusCode: res.status, method, path, responseBody: text }
744
+ );
748
745
  }
749
746
  return res.json();
750
747
  }
751
748
  /** Initiate SIWE and sign with the user's wallet. */
752
749
  async signSiwe(state, chainId) {
753
- const startedAt = Date.now();
754
750
  const initiate = await this.request(
755
751
  "POST",
756
752
  `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/initiate`,
@@ -765,7 +761,6 @@ var SailAgent = class {
765
761
  method: "personal_sign",
766
762
  params: [initiate.message, state.walletAddress]
767
763
  });
768
- logTiming2("signSiwe success", startedAt, { chainId });
769
764
  return {
770
765
  sessionId: initiate.sessionId,
771
766
  message: initiate.message,
@@ -774,18 +769,18 @@ var SailAgent = class {
774
769
  }
775
770
  /** Full connect: auth handles smart account creation via SIWE flow. */
776
771
  async connectFull(state, chainId) {
777
- const startedAt = Date.now();
778
772
  if (this.connectedAddress && this.connectedChainId === chainId) {
779
- logTiming2("connectFull cache-hit", startedAt, { chainId });
780
773
  return;
781
774
  }
782
775
  if (this.connectPromise) {
783
776
  await this.connectPromise;
784
- logTiming2("connectFull awaited-inflight", startedAt, { chainId });
785
777
  return;
786
778
  }
787
779
  if (!state.walletAddress) {
788
- throw new Error("Wallet address is required");
780
+ throw new OwneyError(
781
+ "WALLET_ADDRESS_REQUIRED",
782
+ "Wallet address is required. Call sdk.connect(provider) first."
783
+ );
789
784
  }
790
785
  this.connectPromise = (async () => {
791
786
  const { sessionId, signature, message } = await this.signSiwe(
@@ -811,19 +806,19 @@ var SailAgent = class {
811
806
  })();
812
807
  try {
813
808
  await this.connectPromise;
814
- logTiming2("connectFull success", startedAt, { chainId });
815
809
  } finally {
816
810
  this.connectPromise = null;
817
811
  }
818
812
  }
819
813
  async resolveSmartWallet(state, chainId) {
820
- const startedAt = Date.now();
821
814
  await this.connectFull(state, chainId);
822
815
  const validChainId = isValidChainId(chainId);
823
816
  if (!this.smartWalletAddress) {
824
- throw new Error("Wallet not connected. Call sdk.connect() first.");
817
+ throw new OwneyError(
818
+ "NOT_CONNECTED",
819
+ "Wallet not connected. Call sdk.connect() first."
820
+ );
825
821
  }
826
- logTiming2("resolveSmartWallet ready", startedAt, { chainId: validChainId });
827
822
  return {
828
823
  smartWallet: this.smartWalletAddress,
829
824
  chainId: validChainId
@@ -848,7 +843,8 @@ var SailAgent = class {
848
843
  }
849
844
  async deposit(state, chainId, amount, depositCallback) {
850
845
  if (!depositCallback) {
851
- throw new Error(
846
+ throw new OwneyError(
847
+ "DEPOSIT_CALLBACK_REQUIRED",
852
848
  "depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
853
849
  );
854
850
  }
@@ -857,12 +853,16 @@ var SailAgent = class {
857
853
  const depositInfo = await this.getDepositInfo();
858
854
  const resolvedTokenAddress = depositInfo.permittedTokens[0]?.address;
859
855
  if (!resolvedTokenAddress) {
860
- throw new Error("No permitted tokens available for deposit.");
856
+ throw new OwneyError(
857
+ "DEPOSIT_NO_PERMITTED_TOKENS",
858
+ "No permitted tokens available for deposit."
859
+ );
861
860
  }
862
861
  await this.executePreHooks(resolvedTokenAddress, amount, chainId);
863
862
  const txHash = await depositCallback(smartWallet, chainId, amount);
864
863
  if (!txHash) {
865
- throw new Error(
864
+ throw new OwneyError(
865
+ "DEPOSIT_CALLBACK_INVALID",
866
866
  "depositCallback must return a transaction hash (txHash) for Sail deposits."
867
867
  );
868
868
  }
@@ -916,7 +916,10 @@ var SailAgent = class {
916
916
  const withdrawInfo = await this.getWithdrawInfo();
917
917
  const tokenAddress = withdrawInfo.permittedTokens[0]?.address;
918
918
  if (!tokenAddress) {
919
- throw new Error("No permitted tokens available for withdrawal.");
919
+ throw new OwneyError(
920
+ "WITHDRAW_NO_PERMITTED_TOKENS",
921
+ "No permitted tokens available for withdrawal."
922
+ );
920
923
  }
921
924
  const resolvedAmount = amount ?? withdrawInfo.currentBalance;
922
925
  const { signature: adminWalletSignature, message: adminWalletPayload } = await this.signSiwe(state, chainId);
@@ -940,25 +943,20 @@ var SailAgent = class {
940
943
  }
941
944
  // --- IAgent: Portfolio reads ---
942
945
  async getBalances(state, chainId) {
943
- const startedAt = Date.now();
944
946
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
945
- const portfolioStart = Date.now();
946
947
  try {
947
- const raw = await this.getMetricsPortfolio(state, chainId);
948
- logTiming2("getBalances getMetricsPortfolio", portfolioStart, { chainId });
949
- logTiming2("getBalances success", startedAt, { chainId });
948
+ const raw = await this.getPortfolio();
950
949
  return mapPortfolioBalances(raw, smartWallet);
951
950
  } catch (error) {
952
- if (error instanceof Error && error.message.startsWith("Sail API timeout")) {
951
+ if (error instanceof OwneyError && error.code === "API_SAIL_TIMEOUT") {
953
952
  console.warn(
954
953
  "Sail portfolio request timed out; using empty snapshot.",
955
954
  {
956
955
  chainId
957
956
  }
958
957
  );
959
- logTiming2("getBalances timeout-fallback", startedAt, { chainId });
960
958
  return mapPortfolioBalances(
961
- { message: "timeout", result: [] },
959
+ { message: "timeout", tokens: [] },
962
960
  smartWallet
963
961
  );
964
962
  }
@@ -989,6 +987,14 @@ var SailAgent = class {
989
987
  SAIL_PORTFOLIO_TIMEOUT_MS
990
988
  );
991
989
  }
990
+ async getPortfolio() {
991
+ return this.request(
992
+ "GET",
993
+ `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/portfolio/tokens`,
994
+ void 0,
995
+ SAIL_PORTFOLIO_TIMEOUT_MS
996
+ );
997
+ }
992
998
  async getMetricsEarnings(state, chainId) {
993
999
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
994
1000
  return this.request(
@@ -996,7 +1002,7 @@ var SailAgent = class {
996
1002
  `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/earnings?smart_account=${smartWallet}`
997
1003
  );
998
1004
  }
999
- async getMetricsBalance(state, chainId, currency) {
1005
+ async getMetricsBalance(state, chainId, currency = "usd") {
1000
1006
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
1001
1007
  return this.request(
1002
1008
  "GET",
@@ -1033,8 +1039,10 @@ var SailAgent = class {
1033
1039
  }
1034
1040
  const dayCount = DayFilterMapping[days];
1035
1041
  if (dayCount === void 0) {
1036
- throw new Error(
1037
- `Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`
1042
+ throw new OwneyError(
1043
+ "VALIDATION_INVALID_DAYS",
1044
+ `Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
1045
+ { days }
1038
1046
  );
1039
1047
  }
1040
1048
  const now = /* @__PURE__ */ new Date();
@@ -1111,37 +1119,6 @@ var SailAgent = class {
1111
1119
  }
1112
1120
  };
1113
1121
 
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
1122
  // src/lib/routing-api.ts
1146
1123
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
1147
1124
  async function fetchAgentKeys(apiKey) {
@@ -1155,37 +1132,34 @@ async function fetchAgentKeys(apiKey) {
1155
1132
  });
1156
1133
  if (!res.ok) {
1157
1134
  const text = await res.text().catch(() => "");
1158
- throw new Error(`Routing API error ${res.status}: ${text}`);
1135
+ throw new OwneyError(
1136
+ "API_ROUTING_ERROR",
1137
+ `Routing API error ${res.status}: ${text}`,
1138
+ { statusCode: res.status, responseBody: text }
1139
+ );
1159
1140
  }
1160
1141
  const json = await res.json();
1161
1142
  if (!json.success) {
1162
- throw new Error(`Routing API request failed: ${json.message}`);
1143
+ throw new OwneyError(
1144
+ "API_ROUTING_FAILED",
1145
+ `Routing API request failed: ${json.message}`,
1146
+ { message: json.message }
1147
+ );
1163
1148
  }
1164
1149
  return json.data;
1165
1150
  }
1166
1151
 
1167
1152
  // src/client.ts
1168
- var AGENT_CONSTRUCTORS = {
1169
- zyfai: ZyfaiAgent,
1170
- sail: SailAgent
1171
- };
1172
- var SDK_TIMING_PREFIX = "[OwneySDKTiming]";
1173
- function logTiming3(label, startedAt, meta) {
1174
- const elapsedMs = Date.now() - startedAt;
1175
- if (meta) {
1176
- console.log(`${SDK_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
1177
- return;
1178
- }
1179
- console.log(`${SDK_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
1180
- }
1181
1153
  var OwneySDK = class {
1182
1154
  agents = /* @__PURE__ */ new Map();
1183
1155
  activeAgents = /* @__PURE__ */ new Set();
1184
1156
  state = null;
1185
1157
  apiKey;
1158
+ zyfaiRpcUrls;
1186
1159
  initializingAgentsPromise = null;
1187
1160
  constructor(config) {
1188
1161
  this.apiKey = config.apiKey;
1162
+ this.zyfaiRpcUrls = config.zyfaiRpcUrls;
1189
1163
  }
1190
1164
  /**
1191
1165
  * Establish connection state for all subsequent SDK calls.
@@ -1199,7 +1173,8 @@ var OwneySDK = class {
1199
1173
  });
1200
1174
  const walletAddress = accounts[0];
1201
1175
  if (!walletAddress) {
1202
- throw new Error(
1176
+ throw new OwneyError(
1177
+ "WALLET_NO_ACCOUNTS",
1203
1178
  "No accounts found. Ensure the wallet is unlocked and connected."
1204
1179
  );
1205
1180
  }
@@ -1238,7 +1213,8 @@ var OwneySDK = class {
1238
1213
  requireChainId() {
1239
1214
  const chainId = this.state?.chainId ?? null;
1240
1215
  if (chainId === null) {
1241
- throw new Error(
1216
+ throw new OwneyError(
1217
+ "NO_ACTIVE_CHAIN",
1242
1218
  "No active chain. Call sdk.activateAgent(chainId) first."
1243
1219
  );
1244
1220
  }
@@ -1267,14 +1243,14 @@ var OwneySDK = class {
1267
1243
  this.initializingAgentsPromise = (async () => {
1268
1244
  const agentKeys = await fetchAgentKeys(this.apiKey);
1269
1245
  for (const { key, agent_type } of agentKeys) {
1270
- const AgentClass = AGENT_CONSTRUCTORS[agent_type];
1271
- if (!AgentClass)
1246
+ const agent = this.createAgent(agent_type, key);
1247
+ if (!agent)
1272
1248
  continue;
1273
- const agent = new AgentClass(key);
1274
1249
  this.agents.set(agent_type, agent);
1275
1250
  }
1276
1251
  if (this.agents.size === 0) {
1277
- throw new Error(
1252
+ throw new OwneyError(
1253
+ "API_NO_AGENTS",
1278
1254
  "No supported agents returned by the routing API. Check your API key and routing configuration."
1279
1255
  );
1280
1256
  }
@@ -1286,6 +1262,15 @@ var OwneySDK = class {
1286
1262
  this.initializingAgentsPromise = null;
1287
1263
  }
1288
1264
  }
1265
+ createAgent(agentId, key) {
1266
+ if (agentId === "zyfai") {
1267
+ return new ZyfaiAgent(key, this.zyfaiRpcUrls);
1268
+ }
1269
+ if (agentId === "sail") {
1270
+ return new SailAgent(key);
1271
+ }
1272
+ return null;
1273
+ }
1289
1274
  // --- Account lifecycle ---
1290
1275
  /**
1291
1276
  * Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
@@ -1298,12 +1283,12 @@ var OwneySDK = class {
1298
1283
  * throws before activating any agent.
1299
1284
  */
1300
1285
  async activateAgent(chainId, agentId) {
1301
- const startedAt = Date.now();
1302
1286
  const state = this.requireState();
1303
1287
  await this.ensureAgentsInitialized();
1304
1288
  if (agentId !== void 0) {
1305
1289
  if (agentId.length === 0) {
1306
- throw new Error(
1290
+ throw new OwneyError(
1291
+ "AGENT_EMPTY_LIST",
1307
1292
  "agentId array must not be empty. Omit the argument to activate all compatible agents."
1308
1293
  );
1309
1294
  }
@@ -1325,27 +1310,22 @@ var OwneySDK = class {
1325
1310
  state.chainId = chainId;
1326
1311
  Promise.all(
1327
1312
  agents.map(async (agent) => {
1328
- const agentStart = Date.now();
1329
1313
  await agent.activateAgent(state, chainId);
1330
- logTiming3("activateAgent background-agent-ready", agentStart, {
1331
- chainId,
1332
- agentId: agent.id
1333
- });
1334
1314
  })
1335
1315
  ).catch((error) => {
1336
1316
  console.error("activateAgent background init failed:", error);
1337
1317
  });
1338
- logTiming3("activateAgent scheduled", startedAt, {
1339
- chainId,
1340
- agentIds: uniqueIds
1341
- });
1342
1318
  return;
1343
1319
  }
1344
1320
  const compatible = [...this.agents.values()].filter(
1345
1321
  (agent) => agent.supportedChainIds.includes(chainId)
1346
1322
  );
1347
1323
  if (compatible.length === 0) {
1348
- throw new Error(`No agents support chain ${chainId}.`);
1324
+ throw new OwneyError(
1325
+ "CHAIN_NO_COMPATIBLE_AGENTS",
1326
+ `No agents support chain ${chainId}.`,
1327
+ { chainId }
1328
+ );
1349
1329
  }
1350
1330
  this.activeAgents.clear();
1351
1331
  for (const agent of compatible) {
@@ -1354,20 +1334,11 @@ var OwneySDK = class {
1354
1334
  state.chainId = chainId;
1355
1335
  Promise.all(
1356
1336
  compatible.map(async (agent) => {
1357
- const agentStart = Date.now();
1358
1337
  await agent.activateAgent(state, chainId);
1359
- logTiming3("activateAgent background-agent-ready", agentStart, {
1360
- chainId,
1361
- agentId: agent.id
1362
- });
1363
1338
  })
1364
1339
  ).catch((error) => {
1365
1340
  console.error("activateAgent background init failed:", error);
1366
1341
  });
1367
- logTiming3("activateAgent scheduled", startedAt, {
1368
- chainId,
1369
- agentIds: compatible.map((agent) => agent.id)
1370
- });
1371
1342
  }
1372
1343
  /**
1373
1344
  * Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
@@ -1393,8 +1364,10 @@ var OwneySDK = class {
1393
1364
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1394
1365
  const totalAmount = BigInt(amount);
1395
1366
  if (totalAmount < BigInt(eligibleAgents.length)) {
1396
- throw new Error(
1397
- `Amount "${amount}" is too small to split across ${eligibleAgents.length} agents.`
1367
+ throw new OwneyError(
1368
+ "DEPOSIT_AMOUNT_TOO_SMALL",
1369
+ `Amount "${amount}" is too small to split across ${eligibleAgents.length} agents.`,
1370
+ { amount, agentCount: eligibleAgents.length }
1398
1371
  );
1399
1372
  }
1400
1373
  const perAgent = totalAmount / BigInt(eligibleAgents.length);
@@ -1417,8 +1390,10 @@ var OwneySDK = class {
1417
1390
  (sa) => sa.chainId === chainId
1418
1391
  );
1419
1392
  if (!chainAssets || !chainAssets.assets.includes(asset)) {
1420
- throw new Error(
1421
- `Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`
1393
+ throw new OwneyError(
1394
+ "ASSET_UNSUPPORTED",
1395
+ `Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
1396
+ { agentId: agent.id, asset, chainId }
1422
1397
  );
1423
1398
  }
1424
1399
  }
@@ -1430,8 +1405,10 @@ var OwneySDK = class {
1430
1405
  return chainAssets && chainAssets.assets.includes(asset);
1431
1406
  });
1432
1407
  if (eligible.length === 0) {
1433
- throw new Error(
1434
- `No agents support asset "${asset}" on chain ${chainId}.`
1408
+ throw new OwneyError(
1409
+ "ASSET_NO_COMPATIBLE_AGENTS",
1410
+ `No agents support asset "${asset}" on chain ${chainId}.`,
1411
+ { asset, chainId }
1435
1412
  );
1436
1413
  }
1437
1414
  return eligible;
@@ -1456,10 +1433,25 @@ var OwneySDK = class {
1456
1433
  return agent.withdraw(state, chainId, amount);
1457
1434
  }
1458
1435
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
1436
+ if (!amount) {
1437
+ const results2 = {};
1438
+ for (const agent of eligibleAgents) {
1439
+ results2[agent.id] = await agent.withdraw(state, chainId);
1440
+ }
1441
+ return { agentResult: results2 };
1442
+ }
1459
1443
  const results = {};
1444
+ let lastError;
1460
1445
  for (const agent of eligibleAgents) {
1461
- results[agent.id] = await agent.withdraw(state, chainId, amount);
1446
+ try {
1447
+ results[agent.id] = await agent.withdraw(state, chainId, amount);
1448
+ return { agentResult: results };
1449
+ } catch (err) {
1450
+ lastError = err;
1451
+ }
1462
1452
  }
1453
+ if (lastError)
1454
+ throw lastError;
1463
1455
  return {
1464
1456
  agentResult: results
1465
1457
  };
@@ -1471,12 +1463,10 @@ var OwneySDK = class {
1471
1463
  * @returns {AgentBalance} for a single agent, or {OwneyBalances} with totalBalance and per-agent breakdown
1472
1464
  */
1473
1465
  async getBalances(agentId) {
1474
- const startedAt = Date.now();
1475
1466
  const state = this.requireState();
1476
1467
  const chainId = this.requireChainId();
1477
1468
  if (agentId) {
1478
1469
  const result = await this.getAgent(agentId).getBalances(state, chainId);
1479
- logTiming3("getBalances single-agent", startedAt, { chainId, agentId });
1480
1470
  return result;
1481
1471
  }
1482
1472
  let totalBalance = 0;
@@ -1484,12 +1474,7 @@ var OwneySDK = class {
1484
1474
  const entries = [...this.getActiveAgents().entries()];
1485
1475
  const balanceResults = await Promise.allSettled(
1486
1476
  entries.map(async ([id, agent]) => {
1487
- const agentStartedAt = Date.now();
1488
1477
  const b = await agent.getBalances(state, chainId);
1489
- logTiming3("getBalances per-agent", agentStartedAt, {
1490
- chainId,
1491
- agentId: id
1492
- });
1493
1478
  return [id, b];
1494
1479
  })
1495
1480
  );
@@ -1505,13 +1490,11 @@ var OwneySDK = class {
1505
1490
  console.error("getBalances agent failed:", settledResult.reason);
1506
1491
  }
1507
1492
  if (successCount === 0) {
1508
- throw new Error("Failed to fetch balances for all active agents.");
1493
+ throw new OwneyError(
1494
+ "BALANCE_ALL_FAILED",
1495
+ "Failed to fetch balances for all active agents."
1496
+ );
1509
1497
  }
1510
- logTiming3("getBalances aggregate", startedAt, {
1511
- chainId,
1512
- totalAgents: entries.length,
1513
- successCount
1514
- });
1515
1498
  return {
1516
1499
  totalBalance: String(totalBalance),
1517
1500
  agentBalances: results
@@ -1747,7 +1730,10 @@ var OwneySDK = class {
1747
1730
  }
1748
1731
  }
1749
1732
  if (successCount === 0) {
1750
- throw new Error("Failed to fetch allocation: all agents failed.");
1733
+ throw new OwneyError(
1734
+ "ALLOCATION_ALL_FAILED",
1735
+ "Failed to fetch allocation: all agents failed."
1736
+ );
1751
1737
  }
1752
1738
  const allocation = {};
1753
1739
  if (totalBalance === 0) {
@@ -1775,5 +1761,6 @@ export {
1775
1761
  AgentChainIncompatibleError,
1776
1762
  AgentNotFoundError,
1777
1763
  NotConnectedError,
1764
+ OwneyError,
1778
1765
  OwneySDK
1779
1766
  };