@owney/sdk 0.2.5 → 0.2.6

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
@@ -8,6 +8,23 @@ var P_SUPPORTED_CHAINS = {
8
8
  };
9
9
  var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
10
10
  var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
11
+ var SupportedAssets = [
12
+ {
13
+ address: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`,
14
+ symbol: "USDC",
15
+ chainId: 8453
16
+ },
17
+ {
18
+ address: `0xaf88d065e77c8cC2239327C5EDb3A432268e5831`,
19
+ symbol: "USDC",
20
+ chainId: 42161
21
+ },
22
+ {
23
+ address: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`,
24
+ symbol: "USDT",
25
+ chainId: 42161
26
+ }
27
+ ];
11
28
 
12
29
  // src/lib/utils.ts
13
30
  var isValidChainId = (chainId) => {
@@ -185,13 +202,41 @@ function mapUserProfile(raw, userAddress) {
185
202
  };
186
203
  }
187
204
  function mapApyByStrategy(raw) {
188
- const first = raw.data[0];
205
+ const apyPerAsset = {};
206
+ let apySum = 0;
207
+ let apyCount = 0;
208
+ for (const entry of raw.data) {
209
+ const supported = SupportedAssets.find(
210
+ (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
211
+ );
212
+ if (!supported)
213
+ continue;
214
+ const chainKey = supported.chainId;
215
+ const symbolKey = supported.symbol;
216
+ const bucket = apyPerAsset[chainKey] ?? {};
217
+ bucket[symbolKey] = entry.average_apy;
218
+ apyPerAsset[chainKey] = bucket;
219
+ apySum += entry.average_apy;
220
+ apyCount += 1;
221
+ }
222
+ const averageApy = apyCount > 0 ? apySum / apyCount : 0;
223
+ const hasDetails = Object.keys(apyPerAsset).length > 0;
189
224
  return {
190
- averageApy: first?.average_apy ?? 0
225
+ averageApy,
226
+ detailedApys: hasDetails ? { apyPerAsset } : void 0
191
227
  };
192
228
  }
193
229
 
194
230
  // 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
+ }
195
240
  var ZyfaiAgent = class {
196
241
  id = "zyfai";
197
242
  supportedChainIds = [8453, 42161];
@@ -210,6 +255,8 @@ var ZyfaiAgent = class {
210
255
  sdk;
211
256
  connectedAddress = null;
212
257
  connectedChainId = null;
258
+ smartWalletCache = /* @__PURE__ */ new Map();
259
+ connectAuthPromise = null;
213
260
  constructor(apiKey) {
214
261
  this.sdk = new ZyfaiSDK({ apiKey });
215
262
  }
@@ -222,50 +269,105 @@ var ZyfaiAgent = class {
222
269
  }
223
270
  /** Light connect: SIWE auth only. Re-auths if chain changed. */
224
271
  async connectAuth(state, chainId) {
272
+ const startedAt = Date.now();
225
273
  const validChainId = isValidChainId(chainId);
226
- if (!this.connectedAddress || this.connectedChainId !== validChainId) {
274
+ if (this.connectedAddress && this.connectedChainId === validChainId) {
275
+ logTiming("connectAuth cache-hit", startedAt, { chainId: validChainId });
276
+ return;
277
+ }
278
+ if (this.connectAuthPromise) {
279
+ await this.connectAuthPromise;
280
+ logTiming("connectAuth awaited-inflight", startedAt, {
281
+ chainId: validChainId
282
+ });
283
+ return;
284
+ }
285
+ this.connectAuthPromise = (async () => {
227
286
  const address = await this.sdk.connectAccount(
228
287
  state.provider,
229
288
  validChainId
230
289
  );
231
290
  this.connectedAddress = address;
232
291
  this.connectedChainId = validChainId;
292
+ })();
293
+ try {
294
+ await this.connectAuthPromise;
295
+ logTiming("connectAuth success", startedAt, { chainId: validChainId });
296
+ } finally {
297
+ this.connectAuthPromise = null;
233
298
  }
234
299
  }
235
300
  /** Full connect: SIWE auth + Safe deploy check + session key. */
236
301
  async connectFull(state, chainId) {
302
+ const startedAt = Date.now();
237
303
  await this.connectAuth(state, chainId);
238
304
  const validChainId = isValidChainId(chainId);
239
305
  const address = this.getAddress();
306
+ const walletLookupStart = Date.now();
240
307
  const wallet = await this.sdk.getSmartWalletAddress(address, validChainId);
308
+ logTiming("connectFull getSmartWalletAddress", walletLookupStart, {
309
+ chainId: validChainId
310
+ });
311
+ this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
241
312
  if (!wallet.isDeployed) {
313
+ const deployStart = Date.now();
242
314
  await this.sdk.deploySafe(address, validChainId, "aggressive", true);
315
+ logTiming("connectFull deploySafe", deployStart, {
316
+ chainId: validChainId
317
+ });
243
318
  }
319
+ const userDetailsStart = Date.now();
244
320
  const user = await this.sdk.getUserDetails();
321
+ logTiming("connectFull getUserDetails", userDetailsStart, {
322
+ chainId: validChainId
323
+ });
245
324
  if (!user.hasActiveSessionKey) {
325
+ const sessionKeyStart = Date.now();
246
326
  await this.sdk.createSessionKey(address, validChainId);
327
+ logTiming("connectFull createSessionKey", sessionKeyStart, {
328
+ chainId: validChainId
329
+ });
247
330
  }
331
+ logTiming("connectFull success", startedAt, { chainId: validChainId });
248
332
  }
249
333
  /** Connect + resolve smart wallet address for read operations. */
250
334
  async resolveSmartWallet(state, chainId) {
335
+ const startedAt = Date.now();
251
336
  await this.connectAuth(state, chainId);
252
337
  const validChainId = isValidChainId(chainId);
253
- const { address } = await this.sdk.getSmartWalletAddress(
254
- this.getAddress(),
255
- validChainId
256
- );
257
- return { smartWallet: address, chainId: validChainId };
338
+ const ownerAddress = this.getAddress();
339
+ const cacheKey = `${ownerAddress}_${validChainId}`;
340
+ let smartWallet = this.smartWalletCache.get(cacheKey);
341
+ if (!smartWallet) {
342
+ const lookupStart = Date.now();
343
+ const { address } = await this.sdk.getSmartWalletAddress(
344
+ ownerAddress,
345
+ validChainId
346
+ );
347
+ logTiming("resolveSmartWallet remote-lookup", lookupStart, {
348
+ chainId: validChainId
349
+ });
350
+ smartWallet = address;
351
+ this.smartWalletCache.set(cacheKey, smartWallet);
352
+ } else {
353
+ logTiming("resolveSmartWallet cache-hit", startedAt, {
354
+ chainId: validChainId
355
+ });
356
+ }
357
+ logTiming("resolveSmartWallet ready", startedAt, { chainId: validChainId });
358
+ return { smartWallet, chainId: validChainId };
258
359
  }
259
360
  // --- IAgent: Connection lifecycle ---
260
361
  async disconnect() {
261
362
  await this.sdk.disconnectAccount();
262
363
  this.connectedAddress = null;
263
364
  this.connectedChainId = null;
365
+ this.smartWalletCache.clear();
264
366
  }
265
367
  // --- IAgent: Account lifecycle ---
266
368
  async activateAgent(state, chainId) {
267
369
  try {
268
- await this.connectFull(state, chainId);
370
+ await this.connectAuth(state, chainId);
269
371
  } catch (error) {
270
372
  await this.disconnect();
271
373
  throw error;
@@ -312,9 +414,15 @@ var ZyfaiAgent = class {
312
414
  }
313
415
  // --- IAgent: Portfolio reads ---
314
416
  async getBalances(state, chainId) {
417
+ const startedAt = Date.now();
315
418
  await this.connectAuth(state, chainId);
316
419
  const validChainId = isValidChainId(chainId);
420
+ const positionsStart = Date.now();
317
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 });
318
426
  return mapBalances(raw, validChainId);
319
427
  }
320
428
  async getEarnings(state, chainId) {
@@ -349,11 +457,13 @@ var ZyfaiAgent = class {
349
457
  return mapUserProfile(raw, this.connectedAddress);
350
458
  }
351
459
  // --- IAgent: Discovery (no wallet required) ---
352
- async getAgentApy(days) {
460
+ async getAgentApy(days, options) {
353
461
  const raw = await this.sdk.getAPYPerStrategy(
354
462
  false,
355
463
  DayFilterMapping[days],
356
- "aggressive"
464
+ "aggressive",
465
+ options?.chainId,
466
+ options?.tokenSymbol
357
467
  );
358
468
  return mapApyByStrategy(raw);
359
469
  }
@@ -399,9 +509,11 @@ function mapEarnings2(raw, smartWallet) {
399
509
  };
400
510
  }
401
511
  function mapApyHistory2(raw, walletAddress) {
512
+ const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
402
513
  return {
403
514
  walletAddress,
404
- weightedApyAfterFee: raw.result
515
+ weightedApyAfterFee: raw.result.apy,
516
+ weightedApyAfterFeeDetails: apyPerAsset ? { apyPerAsset } : void 0
405
517
  };
406
518
  }
407
519
  function mapSailTransaction(tx) {
@@ -535,15 +647,45 @@ function mapUserProfile2(raw) {
535
647
  };
536
648
  }
537
649
  function mapApyByStrategy2(raw) {
650
+ const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
538
651
  return {
539
- averageApy: raw.result
652
+ averageApy: raw.result.apy,
653
+ detailedApys: apyPerAsset ? { apyPerAsset } : void 0
540
654
  };
541
655
  }
656
+ function remapApyPerAssetToSymbols(raw) {
657
+ if (!raw)
658
+ return void 0;
659
+ const remapped = {};
660
+ for (const asset of SupportedAssets) {
661
+ const chainKey = asset.chainId;
662
+ const apyByAddress = raw[String(chainKey)];
663
+ if (!apyByAddress)
664
+ continue;
665
+ const apy = apyByAddress[asset.address] ?? apyByAddress[asset.address.toLowerCase()];
666
+ if (apy === void 0)
667
+ continue;
668
+ const bucket = remapped[chainKey] ?? {};
669
+ bucket[asset.symbol] = apy;
670
+ remapped[chainKey] = bucket;
671
+ }
672
+ return Object.keys(remapped).length > 0 ? remapped : void 0;
673
+ }
542
674
 
543
675
  // src/agents/sail/sail.agent.ts
544
676
  var SAIL_API_BASE = "https://app.sail.money";
545
677
  var PROJECT_ID = "sail";
546
678
  var PAGE_ID = "home";
679
+ var SAIL_TIMING_PREFIX = "[AgentTiming][sail]";
680
+ 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
+ }
547
689
  var SailAgent = class {
548
690
  id = "sail";
549
691
  supportedChainIds = [8453, 42161];
@@ -565,11 +707,12 @@ var SailAgent = class {
565
707
  connectedAddress = null;
566
708
  smartWalletAddress = null;
567
709
  connectedChainId = null;
710
+ connectPromise = null;
568
711
  constructor(apiKey) {
569
712
  this.apiKey = apiKey;
570
713
  }
571
714
  // --- Private helpers ---
572
- async request(method, path, body) {
715
+ async request(method, path, body, timeoutMs) {
573
716
  const headers = {
574
717
  "Content-Type": "application/json",
575
718
  "X-Business-API-Key": this.apiKey
@@ -580,11 +723,25 @@ var SailAgent = class {
580
723
  if (this.accountToken) {
581
724
  headers["X-Account-Token"] = this.accountToken;
582
725
  }
583
- const res = await fetch(`${SAIL_API_BASE}${path}`, {
584
- method,
585
- headers,
586
- body: body ? JSON.stringify(body) : void 0
587
- });
726
+ const controller = new AbortController();
727
+ const timeoutId = typeof timeoutMs === "number" && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
728
+ let res;
729
+ try {
730
+ res = await fetch(`${SAIL_API_BASE}${path}`, {
731
+ method,
732
+ headers,
733
+ body: body ? JSON.stringify(body) : void 0,
734
+ signal: controller.signal
735
+ });
736
+ } catch (error) {
737
+ if (error instanceof Error && error.name === "AbortError" && timeoutMs) {
738
+ throw new Error(`Sail API timeout ${timeoutMs}ms: ${method} ${path}`);
739
+ }
740
+ throw error;
741
+ } finally {
742
+ if (timeoutId)
743
+ clearTimeout(timeoutId);
744
+ }
588
745
  if (!res.ok) {
589
746
  const text = await res.text().catch(() => "");
590
747
  throw new Error(`Sail API error ${res.status}: ${text}`);
@@ -593,19 +750,22 @@ var SailAgent = class {
593
750
  }
594
751
  /** Initiate SIWE and sign with the user's wallet. */
595
752
  async signSiwe(state, chainId) {
753
+ const startedAt = Date.now();
596
754
  const initiate = await this.request(
597
755
  "POST",
598
756
  `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/initiate`,
599
757
  {
600
758
  method: "siwe",
601
759
  address: state.walletAddress,
602
- chainId
760
+ chainId,
761
+ identityMode: "byWallet"
603
762
  }
604
763
  );
605
764
  const signature = await state.provider.request({
606
765
  method: "personal_sign",
607
766
  params: [initiate.message, state.walletAddress]
608
767
  });
768
+ logTiming2("signSiwe success", startedAt, { chainId });
609
769
  return {
610
770
  sessionId: initiate.sessionId,
611
771
  message: initiate.message,
@@ -614,38 +774,56 @@ var SailAgent = class {
614
774
  }
615
775
  /** Full connect: auth handles smart account creation via SIWE flow. */
616
776
  async connectFull(state, chainId) {
777
+ const startedAt = Date.now();
617
778
  if (this.connectedAddress && this.connectedChainId === chainId) {
779
+ logTiming2("connectFull cache-hit", startedAt, { chainId });
780
+ return;
781
+ }
782
+ if (this.connectPromise) {
783
+ await this.connectPromise;
784
+ logTiming2("connectFull awaited-inflight", startedAt, { chainId });
618
785
  return;
619
786
  }
620
787
  if (!state.walletAddress) {
621
788
  throw new Error("Wallet address is required");
622
789
  }
623
- const { sessionId, signature, message } = await this.signSiwe(
624
- state,
625
- chainId
626
- );
627
- const complete = await this.request(
628
- "POST",
629
- `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/complete`,
630
- {
631
- method: "custom",
632
- sessionId,
633
- signature,
634
- walletAddress: state.walletAddress
635
- }
636
- );
637
- this.authToken = complete.token;
638
- this.accountToken = complete.accountToken;
639
- this.connectedAddress = state.walletAddress;
640
- this.smartWalletAddress = complete.walletAddress;
641
- this.connectedChainId = chainId;
790
+ this.connectPromise = (async () => {
791
+ const { sessionId, signature, message } = await this.signSiwe(
792
+ state,
793
+ chainId
794
+ );
795
+ const complete = await this.request(
796
+ "POST",
797
+ `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/complete`,
798
+ {
799
+ method: "custom",
800
+ sessionId,
801
+ signature,
802
+ walletAddress: state.walletAddress,
803
+ identityMode: "byWallet"
804
+ }
805
+ );
806
+ this.authToken = complete.token;
807
+ this.accountToken = complete.accountToken;
808
+ this.connectedAddress = state.walletAddress;
809
+ this.smartWalletAddress = complete.walletAddress;
810
+ this.connectedChainId = chainId;
811
+ })();
812
+ try {
813
+ await this.connectPromise;
814
+ logTiming2("connectFull success", startedAt, { chainId });
815
+ } finally {
816
+ this.connectPromise = null;
817
+ }
642
818
  }
643
819
  async resolveSmartWallet(state, chainId) {
820
+ const startedAt = Date.now();
644
821
  await this.connectFull(state, chainId);
645
822
  const validChainId = isValidChainId(chainId);
646
823
  if (!this.smartWalletAddress) {
647
824
  throw new Error("Wallet not connected. Call sdk.connect() first.");
648
825
  }
826
+ logTiming2("resolveSmartWallet ready", startedAt, { chainId: validChainId });
649
827
  return {
650
828
  smartWallet: this.smartWalletAddress,
651
829
  chainId: validChainId
@@ -762,9 +940,30 @@ var SailAgent = class {
762
940
  }
763
941
  // --- IAgent: Portfolio reads ---
764
942
  async getBalances(state, chainId) {
943
+ const startedAt = Date.now();
765
944
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
766
- const raw = await this.getMetricsPortfolio(state, chainId);
767
- return mapPortfolioBalances(raw, smartWallet);
945
+ const portfolioStart = Date.now();
946
+ try {
947
+ const raw = await this.getMetricsPortfolio(state, chainId);
948
+ logTiming2("getBalances getMetricsPortfolio", portfolioStart, { chainId });
949
+ logTiming2("getBalances success", startedAt, { chainId });
950
+ return mapPortfolioBalances(raw, smartWallet);
951
+ } catch (error) {
952
+ if (error instanceof Error && error.message.startsWith("Sail API timeout")) {
953
+ console.warn(
954
+ "Sail portfolio request timed out; using empty snapshot.",
955
+ {
956
+ chainId
957
+ }
958
+ );
959
+ logTiming2("getBalances timeout-fallback", startedAt, { chainId });
960
+ return mapPortfolioBalances(
961
+ { message: "timeout", result: [] },
962
+ smartWallet
963
+ );
964
+ }
965
+ throw error;
966
+ }
768
967
  }
769
968
  async getEarnings(state, chainId) {
770
969
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
@@ -785,7 +984,9 @@ var SailAgent = class {
785
984
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
786
985
  return this.request(
787
986
  "GET",
788
- `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}`
987
+ `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}`,
988
+ void 0,
989
+ SAIL_PORTFOLIO_TIMEOUT_MS
789
990
  );
790
991
  }
791
992
  async getMetricsEarnings(state, chainId) {
@@ -904,7 +1105,7 @@ var SailAgent = class {
904
1105
  return mapUserProfile2(raw);
905
1106
  }
906
1107
  // --- IAgent: Discovery (no wallet required) ---
907
- async getAgentApy(days) {
1108
+ async getAgentApy(days, _options) {
908
1109
  const raw = await this.getMetricsYield(days);
909
1110
  return mapApyByStrategy2(raw);
910
1111
  }
@@ -968,11 +1169,21 @@ var AGENT_CONSTRUCTORS = {
968
1169
  zyfai: ZyfaiAgent,
969
1170
  sail: SailAgent
970
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
+ }
971
1181
  var OwneySDK = class {
972
1182
  agents = /* @__PURE__ */ new Map();
973
1183
  activeAgents = /* @__PURE__ */ new Set();
974
1184
  state = null;
975
1185
  apiKey;
1186
+ initializingAgentsPromise = null;
976
1187
  constructor(config) {
977
1188
  this.apiKey = config.apiKey;
978
1189
  }
@@ -1049,6 +1260,32 @@ var OwneySDK = class {
1049
1260
  }
1050
1261
  return active;
1051
1262
  }
1263
+ async ensureAgentsInitialized() {
1264
+ if (this.agents.size > 0)
1265
+ return;
1266
+ if (!this.initializingAgentsPromise) {
1267
+ this.initializingAgentsPromise = (async () => {
1268
+ const agentKeys = await fetchAgentKeys(this.apiKey);
1269
+ for (const { key, agent_type } of agentKeys) {
1270
+ const AgentClass = AGENT_CONSTRUCTORS[agent_type];
1271
+ if (!AgentClass)
1272
+ continue;
1273
+ const agent = new AgentClass(key);
1274
+ this.agents.set(agent_type, agent);
1275
+ }
1276
+ if (this.agents.size === 0) {
1277
+ throw new Error(
1278
+ "No supported agents returned by the routing API. Check your API key and routing configuration."
1279
+ );
1280
+ }
1281
+ })();
1282
+ }
1283
+ try {
1284
+ await this.initializingAgentsPromise;
1285
+ } finally {
1286
+ this.initializingAgentsPromise = null;
1287
+ }
1288
+ }
1052
1289
  // --- Account lifecycle ---
1053
1290
  /**
1054
1291
  * Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
@@ -1061,22 +1298,9 @@ var OwneySDK = class {
1061
1298
  * throws before activating any agent.
1062
1299
  */
1063
1300
  async activateAgent(chainId, agentId) {
1301
+ const startedAt = Date.now();
1064
1302
  const state = this.requireState();
1065
- if (this.agents.size === 0) {
1066
- const agentKeys = await fetchAgentKeys(this.apiKey);
1067
- for (const { key, agent_type } of agentKeys) {
1068
- const AgentClass = AGENT_CONSTRUCTORS[agent_type];
1069
- if (!AgentClass)
1070
- continue;
1071
- const agent = new AgentClass(key);
1072
- this.agents.set(agent_type, agent);
1073
- }
1074
- if (this.agents.size === 0) {
1075
- throw new Error(
1076
- "No supported agents returned by the routing API. Check your API key and routing configuration."
1077
- );
1078
- }
1079
- }
1303
+ await this.ensureAgentsInitialized();
1080
1304
  if (agentId !== void 0) {
1081
1305
  if (agentId.length === 0) {
1082
1306
  throw new Error(
@@ -1094,14 +1318,27 @@ var OwneySDK = class {
1094
1318
  if (incompatibleAgents.length > 0) {
1095
1319
  throw new AgentChainIncompatibleError(incompatibleAgents, chainId);
1096
1320
  }
1097
- for (const agent of agents) {
1098
- await agent.activateAgent(state, chainId);
1099
- }
1100
1321
  this.activeAgents.clear();
1101
1322
  for (const id of uniqueIds) {
1102
1323
  this.activeAgents.add(id);
1103
1324
  }
1104
1325
  state.chainId = chainId;
1326
+ Promise.all(
1327
+ agents.map(async (agent) => {
1328
+ const agentStart = Date.now();
1329
+ await agent.activateAgent(state, chainId);
1330
+ logTiming3("activateAgent background-agent-ready", agentStart, {
1331
+ chainId,
1332
+ agentId: agent.id
1333
+ });
1334
+ })
1335
+ ).catch((error) => {
1336
+ console.error("activateAgent background init failed:", error);
1337
+ });
1338
+ logTiming3("activateAgent scheduled", startedAt, {
1339
+ chainId,
1340
+ agentIds: uniqueIds
1341
+ });
1105
1342
  return;
1106
1343
  }
1107
1344
  const compatible = [...this.agents.values()].filter(
@@ -1110,14 +1347,27 @@ var OwneySDK = class {
1110
1347
  if (compatible.length === 0) {
1111
1348
  throw new Error(`No agents support chain ${chainId}.`);
1112
1349
  }
1113
- for (const agent of compatible) {
1114
- await agent.activateAgent(state, chainId);
1115
- }
1116
1350
  this.activeAgents.clear();
1117
1351
  for (const agent of compatible) {
1118
1352
  this.activeAgents.add(agent.id);
1119
1353
  }
1120
1354
  state.chainId = chainId;
1355
+ Promise.all(
1356
+ compatible.map(async (agent) => {
1357
+ const agentStart = Date.now();
1358
+ await agent.activateAgent(state, chainId);
1359
+ logTiming3("activateAgent background-agent-ready", agentStart, {
1360
+ chainId,
1361
+ agentId: agent.id
1362
+ });
1363
+ })
1364
+ ).catch((error) => {
1365
+ console.error("activateAgent background init failed:", error);
1366
+ });
1367
+ logTiming3("activateAgent scheduled", startedAt, {
1368
+ chainId,
1369
+ agentIds: compatible.map((agent) => agent.id)
1370
+ });
1121
1371
  }
1122
1372
  /**
1123
1373
  * Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
@@ -1221,17 +1471,47 @@ var OwneySDK = class {
1221
1471
  * @returns {AgentBalance} for a single agent, or {OwneyBalances} with totalBalance and per-agent breakdown
1222
1472
  */
1223
1473
  async getBalances(agentId) {
1474
+ const startedAt = Date.now();
1224
1475
  const state = this.requireState();
1225
1476
  const chainId = this.requireChainId();
1226
1477
  if (agentId) {
1227
- return this.getAgent(agentId).getBalances(state, chainId);
1478
+ const result = await this.getAgent(agentId).getBalances(state, chainId);
1479
+ logTiming3("getBalances single-agent", startedAt, { chainId, agentId });
1480
+ return result;
1228
1481
  }
1229
1482
  let totalBalance = 0;
1230
1483
  const results = {};
1231
- for (const [id, agent] of this.getActiveAgents()) {
1232
- results[id] = await agent.getBalances(state, chainId);
1233
- totalBalance = totalBalance + Number(results[id].totalBalance);
1484
+ const entries = [...this.getActiveAgents().entries()];
1485
+ const balanceResults = await Promise.allSettled(
1486
+ entries.map(async ([id, agent]) => {
1487
+ const agentStartedAt = Date.now();
1488
+ const b = await agent.getBalances(state, chainId);
1489
+ logTiming3("getBalances per-agent", agentStartedAt, {
1490
+ chainId,
1491
+ agentId: id
1492
+ });
1493
+ return [id, b];
1494
+ })
1495
+ );
1496
+ let successCount = 0;
1497
+ for (const settledResult of balanceResults) {
1498
+ if (settledResult.status === "fulfilled") {
1499
+ const [id, b] = settledResult.value;
1500
+ results[id] = b;
1501
+ totalBalance += Number(b.totalBalance);
1502
+ successCount += 1;
1503
+ continue;
1504
+ }
1505
+ console.error("getBalances agent failed:", settledResult.reason);
1506
+ }
1507
+ if (successCount === 0) {
1508
+ throw new Error("Failed to fetch balances for all active agents.");
1234
1509
  }
1510
+ logTiming3("getBalances aggregate", startedAt, {
1511
+ chainId,
1512
+ totalAgents: entries.length,
1513
+ successCount
1514
+ });
1235
1515
  return {
1236
1516
  totalBalance: String(totalBalance),
1237
1517
  agentBalances: results
@@ -1250,9 +1530,16 @@ var OwneySDK = class {
1250
1530
  }
1251
1531
  let totalEarnings = 0;
1252
1532
  const results = {};
1253
- for (const [id, agent] of this.getActiveAgents()) {
1254
- results[id] = await agent.getEarnings(state, chainId);
1255
- totalEarnings = totalEarnings + Number(results[id].lifetimeEarnings);
1533
+ const entries = [...this.getActiveAgents().entries()];
1534
+ const earningsResults = await Promise.all(
1535
+ entries.map(async ([id, agent]) => {
1536
+ const e = await agent.getEarnings(state, chainId);
1537
+ return [id, e];
1538
+ })
1539
+ );
1540
+ for (const [id, e] of earningsResults) {
1541
+ results[id] = e;
1542
+ totalEarnings += Number(e.lifetimeEarnings);
1256
1543
  }
1257
1544
  return {
1258
1545
  totalEarnings: String(totalEarnings),
@@ -1277,16 +1564,30 @@ var OwneySDK = class {
1277
1564
  return this.getAgent(agentId).getAccountApy(state, chainId, days);
1278
1565
  }
1279
1566
  const activeAgents = this.getActiveAgents();
1567
+ const entries = [...activeAgents.entries()];
1568
+ const [apyResults, balanceResults] = await Promise.all([
1569
+ Promise.all(
1570
+ entries.map(async ([id, agent]) => {
1571
+ const apy = await agent.getAccountApy(state, chainId, days);
1572
+ return [id, apy];
1573
+ })
1574
+ ),
1575
+ Promise.all(
1576
+ entries.map(async ([id, agent]) => {
1577
+ const b = await agent.getBalances(state, chainId);
1578
+ return [id, Number(b.totalBalance)];
1579
+ })
1580
+ )
1581
+ ]);
1280
1582
  const results = {};
1281
- for (const [id, agent] of activeAgents) {
1282
- results[id] = await agent.getAccountApy(state, chainId, days);
1583
+ for (const [id, apy] of apyResults) {
1584
+ results[id] = apy;
1283
1585
  }
1284
1586
  const balances = {};
1285
1587
  let totalBalance = 0;
1286
- for (const [id, agent] of activeAgents) {
1287
- const b = await agent.getBalances(state, chainId);
1288
- balances[id] = Number(b.totalBalance);
1289
- totalBalance += balances[id];
1588
+ for (const [id, bal] of balanceResults) {
1589
+ balances[id] = bal;
1590
+ totalBalance += bal;
1290
1591
  }
1291
1592
  let totalApy = 0;
1292
1593
  if (totalBalance > 0) {
@@ -1314,9 +1615,13 @@ var OwneySDK = class {
1314
1615
  if (agentId) {
1315
1616
  return this.getAgent(agentId).getHistory(state, chainId, filters);
1316
1617
  }
1618
+ const historyResults = await Promise.all(
1619
+ [...this.getActiveAgents().values()].map(
1620
+ (agent) => agent.getHistory(state, chainId, filters)
1621
+ )
1622
+ );
1317
1623
  let results = [];
1318
- for (const [, agent] of this.getActiveAgents()) {
1319
- const { data } = await agent.getHistory(state, chainId, filters);
1624
+ for (const { data } of historyResults) {
1320
1625
  results = [...results, ...data];
1321
1626
  }
1322
1627
  results.sort(
@@ -1360,8 +1665,15 @@ var OwneySDK = class {
1360
1665
  return this.getAgent(agentId).getUserProfile(state, chainId);
1361
1666
  }
1362
1667
  const results = {};
1363
- for (const [id, agent] of this.getActiveAgents()) {
1364
- results[id] = await agent.getUserProfile(state, chainId);
1668
+ const entries = [...this.getActiveAgents().entries()];
1669
+ const profileResults = await Promise.all(
1670
+ entries.map(async ([id, agent]) => {
1671
+ const p = await agent.getUserProfile(state, chainId);
1672
+ return [id, p];
1673
+ })
1674
+ );
1675
+ for (const [id, p] of profileResults) {
1676
+ results[id] = p;
1365
1677
  }
1366
1678
  return {
1367
1679
  agentUserProfile: results
@@ -1373,18 +1685,34 @@ var OwneySDK = class {
1373
1685
  * @param options - Contains agentId (optional) and days ("7D", "14D", or "30D")
1374
1686
  * @param options.agentId - Optional. Agent to query. Omit for all agents.
1375
1687
  * @param options.days - Lookback period: "7D", "14D", or "30D"
1688
+ * @param options.tokenSymbol - Optional asset symbol ("USDC" / "WETH") to
1689
+ * scope the APY to a specific asset+chain. Agents whose backends do not
1690
+ * support per-asset APY (currently Sail) ignore this.
1691
+ * @param options.chainId - Optional chain id for per-asset APY lookups.
1692
+ * Typically paired with `tokenSymbol`.
1376
1693
  * @returns {AgentApy} for a single agent, or {OwneyAgentApy} with per-agent APY
1377
1694
  */
1378
1695
  async getAgentApy({
1379
1696
  agentId,
1380
- days
1697
+ days,
1698
+ tokenSymbol,
1699
+ chainId
1381
1700
  }) {
1701
+ await this.ensureAgentsInitialized();
1702
+ const agentOptions = { tokenSymbol, chainId };
1382
1703
  if (agentId) {
1383
- return this.getAgent(agentId).getAgentApy(days);
1704
+ return this.getAgent(agentId).getAgentApy(days, agentOptions);
1384
1705
  }
1385
1706
  const results = {};
1386
- for (const [id, agent] of this.agents) {
1387
- results[id] = await agent.getAgentApy(days);
1707
+ const agentEntries = [...this.agents.entries()];
1708
+ const apyResults = await Promise.all(
1709
+ agentEntries.map(async ([id, agent]) => {
1710
+ const apy = await agent.getAgentApy(days, agentOptions);
1711
+ return [id, apy];
1712
+ })
1713
+ );
1714
+ for (const [id, apy] of apyResults) {
1715
+ results[id] = apy;
1388
1716
  }
1389
1717
  return {
1390
1718
  agentApy: results
@@ -1400,10 +1728,26 @@ var OwneySDK = class {
1400
1728
  const activeAgents = this.getActiveAgents();
1401
1729
  const balances = {};
1402
1730
  let totalBalance = 0;
1403
- for (const [id, agent] of activeAgents) {
1404
- const b = await agent.getBalances(state, chainId);
1405
- balances[id] = Number(b.totalBalance);
1406
- totalBalance += balances[id];
1731
+ const entries = [...activeAgents.entries()];
1732
+ const balanceResults = await Promise.allSettled(
1733
+ entries.map(async ([id, agent]) => {
1734
+ const b = await agent.getBalances(state, chainId);
1735
+ return [id, Number(b.totalBalance)];
1736
+ })
1737
+ );
1738
+ let successCount = 0;
1739
+ for (const settledResult of balanceResults) {
1740
+ if (settledResult.status === "fulfilled") {
1741
+ const [id, bal] = settledResult.value;
1742
+ balances[id] = bal;
1743
+ totalBalance += bal;
1744
+ successCount += 1;
1745
+ } else {
1746
+ console.error("getAgentAllocation agent failed:", settledResult.reason);
1747
+ }
1748
+ }
1749
+ if (successCount === 0) {
1750
+ throw new Error("Failed to fetch allocation: all agents failed.");
1407
1751
  }
1408
1752
  const allocation = {};
1409
1753
  if (totalBalance === 0) {