@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.cjs CHANGED
@@ -37,6 +37,23 @@ var P_SUPPORTED_CHAINS = {
37
37
  };
38
38
  var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
39
39
  var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
40
+ var SupportedAssets = [
41
+ {
42
+ address: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`,
43
+ symbol: "USDC",
44
+ chainId: 8453
45
+ },
46
+ {
47
+ address: `0xaf88d065e77c8cC2239327C5EDb3A432268e5831`,
48
+ symbol: "USDC",
49
+ chainId: 42161
50
+ },
51
+ {
52
+ address: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`,
53
+ symbol: "USDT",
54
+ chainId: 42161
55
+ }
56
+ ];
40
57
 
41
58
  // src/lib/utils.ts
42
59
  var isValidChainId = (chainId) => {
@@ -214,13 +231,41 @@ function mapUserProfile(raw, userAddress) {
214
231
  };
215
232
  }
216
233
  function mapApyByStrategy(raw) {
217
- const first = raw.data[0];
234
+ const apyPerAsset = {};
235
+ let apySum = 0;
236
+ let apyCount = 0;
237
+ for (const entry of raw.data) {
238
+ const supported = SupportedAssets.find(
239
+ (asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
240
+ );
241
+ if (!supported)
242
+ continue;
243
+ const chainKey = supported.chainId;
244
+ const symbolKey = supported.symbol;
245
+ const bucket = apyPerAsset[chainKey] ?? {};
246
+ bucket[symbolKey] = entry.average_apy;
247
+ apyPerAsset[chainKey] = bucket;
248
+ apySum += entry.average_apy;
249
+ apyCount += 1;
250
+ }
251
+ const averageApy = apyCount > 0 ? apySum / apyCount : 0;
252
+ const hasDetails = Object.keys(apyPerAsset).length > 0;
218
253
  return {
219
- averageApy: first?.average_apy ?? 0
254
+ averageApy,
255
+ detailedApys: hasDetails ? { apyPerAsset } : void 0
220
256
  };
221
257
  }
222
258
 
223
259
  // src/agents/zyfai/zyfai.agent.ts
260
+ var ZYFAI_TIMING_PREFIX = "[AgentTiming][zyfai]";
261
+ function logTiming(label, startedAt, meta) {
262
+ const elapsedMs = Date.now() - startedAt;
263
+ if (meta) {
264
+ console.log(`${ZYFAI_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
265
+ return;
266
+ }
267
+ console.log(`${ZYFAI_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
268
+ }
224
269
  var ZyfaiAgent = class {
225
270
  id = "zyfai";
226
271
  supportedChainIds = [8453, 42161];
@@ -239,6 +284,8 @@ var ZyfaiAgent = class {
239
284
  sdk;
240
285
  connectedAddress = null;
241
286
  connectedChainId = null;
287
+ smartWalletCache = /* @__PURE__ */ new Map();
288
+ connectAuthPromise = null;
242
289
  constructor(apiKey) {
243
290
  this.sdk = new import_sdk.ZyfaiSDK({ apiKey });
244
291
  }
@@ -251,50 +298,105 @@ var ZyfaiAgent = class {
251
298
  }
252
299
  /** Light connect: SIWE auth only. Re-auths if chain changed. */
253
300
  async connectAuth(state, chainId) {
301
+ const startedAt = Date.now();
254
302
  const validChainId = isValidChainId(chainId);
255
- if (!this.connectedAddress || this.connectedChainId !== validChainId) {
303
+ if (this.connectedAddress && this.connectedChainId === validChainId) {
304
+ logTiming("connectAuth cache-hit", startedAt, { chainId: validChainId });
305
+ return;
306
+ }
307
+ if (this.connectAuthPromise) {
308
+ await this.connectAuthPromise;
309
+ logTiming("connectAuth awaited-inflight", startedAt, {
310
+ chainId: validChainId
311
+ });
312
+ return;
313
+ }
314
+ this.connectAuthPromise = (async () => {
256
315
  const address = await this.sdk.connectAccount(
257
316
  state.provider,
258
317
  validChainId
259
318
  );
260
319
  this.connectedAddress = address;
261
320
  this.connectedChainId = validChainId;
321
+ })();
322
+ try {
323
+ await this.connectAuthPromise;
324
+ logTiming("connectAuth success", startedAt, { chainId: validChainId });
325
+ } finally {
326
+ this.connectAuthPromise = null;
262
327
  }
263
328
  }
264
329
  /** Full connect: SIWE auth + Safe deploy check + session key. */
265
330
  async connectFull(state, chainId) {
331
+ const startedAt = Date.now();
266
332
  await this.connectAuth(state, chainId);
267
333
  const validChainId = isValidChainId(chainId);
268
334
  const address = this.getAddress();
335
+ const walletLookupStart = Date.now();
269
336
  const wallet = await this.sdk.getSmartWalletAddress(address, validChainId);
337
+ logTiming("connectFull getSmartWalletAddress", walletLookupStart, {
338
+ chainId: validChainId
339
+ });
340
+ this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
270
341
  if (!wallet.isDeployed) {
342
+ const deployStart = Date.now();
271
343
  await this.sdk.deploySafe(address, validChainId, "aggressive", true);
344
+ logTiming("connectFull deploySafe", deployStart, {
345
+ chainId: validChainId
346
+ });
272
347
  }
348
+ const userDetailsStart = Date.now();
273
349
  const user = await this.sdk.getUserDetails();
350
+ logTiming("connectFull getUserDetails", userDetailsStart, {
351
+ chainId: validChainId
352
+ });
274
353
  if (!user.hasActiveSessionKey) {
354
+ const sessionKeyStart = Date.now();
275
355
  await this.sdk.createSessionKey(address, validChainId);
356
+ logTiming("connectFull createSessionKey", sessionKeyStart, {
357
+ chainId: validChainId
358
+ });
276
359
  }
360
+ logTiming("connectFull success", startedAt, { chainId: validChainId });
277
361
  }
278
362
  /** Connect + resolve smart wallet address for read operations. */
279
363
  async resolveSmartWallet(state, chainId) {
364
+ const startedAt = Date.now();
280
365
  await this.connectAuth(state, chainId);
281
366
  const validChainId = isValidChainId(chainId);
282
- const { address } = await this.sdk.getSmartWalletAddress(
283
- this.getAddress(),
284
- validChainId
285
- );
286
- return { smartWallet: address, chainId: validChainId };
367
+ const ownerAddress = this.getAddress();
368
+ const cacheKey = `${ownerAddress}_${validChainId}`;
369
+ let smartWallet = this.smartWalletCache.get(cacheKey);
370
+ if (!smartWallet) {
371
+ const lookupStart = Date.now();
372
+ const { address } = await this.sdk.getSmartWalletAddress(
373
+ ownerAddress,
374
+ validChainId
375
+ );
376
+ logTiming("resolveSmartWallet remote-lookup", lookupStart, {
377
+ chainId: validChainId
378
+ });
379
+ smartWallet = address;
380
+ this.smartWalletCache.set(cacheKey, smartWallet);
381
+ } else {
382
+ logTiming("resolveSmartWallet cache-hit", startedAt, {
383
+ chainId: validChainId
384
+ });
385
+ }
386
+ logTiming("resolveSmartWallet ready", startedAt, { chainId: validChainId });
387
+ return { smartWallet, chainId: validChainId };
287
388
  }
288
389
  // --- IAgent: Connection lifecycle ---
289
390
  async disconnect() {
290
391
  await this.sdk.disconnectAccount();
291
392
  this.connectedAddress = null;
292
393
  this.connectedChainId = null;
394
+ this.smartWalletCache.clear();
293
395
  }
294
396
  // --- IAgent: Account lifecycle ---
295
397
  async activateAgent(state, chainId) {
296
398
  try {
297
- await this.connectFull(state, chainId);
399
+ await this.connectAuth(state, chainId);
298
400
  } catch (error) {
299
401
  await this.disconnect();
300
402
  throw error;
@@ -341,9 +443,15 @@ var ZyfaiAgent = class {
341
443
  }
342
444
  // --- IAgent: Portfolio reads ---
343
445
  async getBalances(state, chainId) {
446
+ const startedAt = Date.now();
344
447
  await this.connectAuth(state, chainId);
345
448
  const validChainId = isValidChainId(chainId);
449
+ const positionsStart = Date.now();
346
450
  const raw = await this.sdk.getPositions(this.getAddress(), validChainId);
451
+ logTiming("getBalances getPositions", positionsStart, {
452
+ chainId: validChainId
453
+ });
454
+ logTiming("getBalances success", startedAt, { chainId: validChainId });
347
455
  return mapBalances(raw, validChainId);
348
456
  }
349
457
  async getEarnings(state, chainId) {
@@ -378,11 +486,13 @@ var ZyfaiAgent = class {
378
486
  return mapUserProfile(raw, this.connectedAddress);
379
487
  }
380
488
  // --- IAgent: Discovery (no wallet required) ---
381
- async getAgentApy(days) {
489
+ async getAgentApy(days, options) {
382
490
  const raw = await this.sdk.getAPYPerStrategy(
383
491
  false,
384
492
  DayFilterMapping[days],
385
- "aggressive"
493
+ "aggressive",
494
+ options?.chainId,
495
+ options?.tokenSymbol
386
496
  );
387
497
  return mapApyByStrategy(raw);
388
498
  }
@@ -428,9 +538,11 @@ function mapEarnings2(raw, smartWallet) {
428
538
  };
429
539
  }
430
540
  function mapApyHistory2(raw, walletAddress) {
541
+ const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
431
542
  return {
432
543
  walletAddress,
433
- weightedApyAfterFee: raw.result
544
+ weightedApyAfterFee: raw.result.apy,
545
+ weightedApyAfterFeeDetails: apyPerAsset ? { apyPerAsset } : void 0
434
546
  };
435
547
  }
436
548
  function mapSailTransaction(tx) {
@@ -564,15 +676,45 @@ function mapUserProfile2(raw) {
564
676
  };
565
677
  }
566
678
  function mapApyByStrategy2(raw) {
679
+ const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
567
680
  return {
568
- averageApy: raw.result
681
+ averageApy: raw.result.apy,
682
+ detailedApys: apyPerAsset ? { apyPerAsset } : void 0
569
683
  };
570
684
  }
685
+ function remapApyPerAssetToSymbols(raw) {
686
+ if (!raw)
687
+ return void 0;
688
+ const remapped = {};
689
+ for (const asset of SupportedAssets) {
690
+ const chainKey = asset.chainId;
691
+ const apyByAddress = raw[String(chainKey)];
692
+ if (!apyByAddress)
693
+ continue;
694
+ const apy = apyByAddress[asset.address] ?? apyByAddress[asset.address.toLowerCase()];
695
+ if (apy === void 0)
696
+ continue;
697
+ const bucket = remapped[chainKey] ?? {};
698
+ bucket[asset.symbol] = apy;
699
+ remapped[chainKey] = bucket;
700
+ }
701
+ return Object.keys(remapped).length > 0 ? remapped : void 0;
702
+ }
571
703
 
572
704
  // src/agents/sail/sail.agent.ts
573
705
  var SAIL_API_BASE = "https://app.sail.money";
574
706
  var PROJECT_ID = "sail";
575
707
  var PAGE_ID = "home";
708
+ var SAIL_TIMING_PREFIX = "[AgentTiming][sail]";
709
+ var SAIL_PORTFOLIO_TIMEOUT_MS = 8e3;
710
+ function logTiming2(label, startedAt, meta) {
711
+ const elapsedMs = Date.now() - startedAt;
712
+ if (meta) {
713
+ console.log(`${SAIL_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
714
+ return;
715
+ }
716
+ console.log(`${SAIL_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
717
+ }
576
718
  var SailAgent = class {
577
719
  id = "sail";
578
720
  supportedChainIds = [8453, 42161];
@@ -594,11 +736,12 @@ var SailAgent = class {
594
736
  connectedAddress = null;
595
737
  smartWalletAddress = null;
596
738
  connectedChainId = null;
739
+ connectPromise = null;
597
740
  constructor(apiKey) {
598
741
  this.apiKey = apiKey;
599
742
  }
600
743
  // --- Private helpers ---
601
- async request(method, path, body) {
744
+ async request(method, path, body, timeoutMs) {
602
745
  const headers = {
603
746
  "Content-Type": "application/json",
604
747
  "X-Business-API-Key": this.apiKey
@@ -609,11 +752,25 @@ var SailAgent = class {
609
752
  if (this.accountToken) {
610
753
  headers["X-Account-Token"] = this.accountToken;
611
754
  }
612
- const res = await fetch(`${SAIL_API_BASE}${path}`, {
613
- method,
614
- headers,
615
- body: body ? JSON.stringify(body) : void 0
616
- });
755
+ const controller = new AbortController();
756
+ const timeoutId = typeof timeoutMs === "number" && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
757
+ let res;
758
+ try {
759
+ res = await fetch(`${SAIL_API_BASE}${path}`, {
760
+ method,
761
+ headers,
762
+ body: body ? JSON.stringify(body) : void 0,
763
+ signal: controller.signal
764
+ });
765
+ } catch (error) {
766
+ if (error instanceof Error && error.name === "AbortError" && timeoutMs) {
767
+ throw new Error(`Sail API timeout ${timeoutMs}ms: ${method} ${path}`);
768
+ }
769
+ throw error;
770
+ } finally {
771
+ if (timeoutId)
772
+ clearTimeout(timeoutId);
773
+ }
617
774
  if (!res.ok) {
618
775
  const text = await res.text().catch(() => "");
619
776
  throw new Error(`Sail API error ${res.status}: ${text}`);
@@ -622,19 +779,22 @@ var SailAgent = class {
622
779
  }
623
780
  /** Initiate SIWE and sign with the user's wallet. */
624
781
  async signSiwe(state, chainId) {
782
+ const startedAt = Date.now();
625
783
  const initiate = await this.request(
626
784
  "POST",
627
785
  `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/initiate`,
628
786
  {
629
787
  method: "siwe",
630
788
  address: state.walletAddress,
631
- chainId
789
+ chainId,
790
+ identityMode: "byWallet"
632
791
  }
633
792
  );
634
793
  const signature = await state.provider.request({
635
794
  method: "personal_sign",
636
795
  params: [initiate.message, state.walletAddress]
637
796
  });
797
+ logTiming2("signSiwe success", startedAt, { chainId });
638
798
  return {
639
799
  sessionId: initiate.sessionId,
640
800
  message: initiate.message,
@@ -643,38 +803,56 @@ var SailAgent = class {
643
803
  }
644
804
  /** Full connect: auth handles smart account creation via SIWE flow. */
645
805
  async connectFull(state, chainId) {
806
+ const startedAt = Date.now();
646
807
  if (this.connectedAddress && this.connectedChainId === chainId) {
808
+ logTiming2("connectFull cache-hit", startedAt, { chainId });
809
+ return;
810
+ }
811
+ if (this.connectPromise) {
812
+ await this.connectPromise;
813
+ logTiming2("connectFull awaited-inflight", startedAt, { chainId });
647
814
  return;
648
815
  }
649
816
  if (!state.walletAddress) {
650
817
  throw new Error("Wallet address is required");
651
818
  }
652
- const { sessionId, signature, message } = await this.signSiwe(
653
- state,
654
- chainId
655
- );
656
- const complete = await this.request(
657
- "POST",
658
- `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/complete`,
659
- {
660
- method: "custom",
661
- sessionId,
662
- signature,
663
- walletAddress: state.walletAddress
664
- }
665
- );
666
- this.authToken = complete.token;
667
- this.accountToken = complete.accountToken;
668
- this.connectedAddress = state.walletAddress;
669
- this.smartWalletAddress = complete.walletAddress;
670
- this.connectedChainId = chainId;
819
+ this.connectPromise = (async () => {
820
+ const { sessionId, signature, message } = await this.signSiwe(
821
+ state,
822
+ chainId
823
+ );
824
+ const complete = await this.request(
825
+ "POST",
826
+ `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/complete`,
827
+ {
828
+ method: "custom",
829
+ sessionId,
830
+ signature,
831
+ walletAddress: state.walletAddress,
832
+ identityMode: "byWallet"
833
+ }
834
+ );
835
+ this.authToken = complete.token;
836
+ this.accountToken = complete.accountToken;
837
+ this.connectedAddress = state.walletAddress;
838
+ this.smartWalletAddress = complete.walletAddress;
839
+ this.connectedChainId = chainId;
840
+ })();
841
+ try {
842
+ await this.connectPromise;
843
+ logTiming2("connectFull success", startedAt, { chainId });
844
+ } finally {
845
+ this.connectPromise = null;
846
+ }
671
847
  }
672
848
  async resolveSmartWallet(state, chainId) {
849
+ const startedAt = Date.now();
673
850
  await this.connectFull(state, chainId);
674
851
  const validChainId = isValidChainId(chainId);
675
852
  if (!this.smartWalletAddress) {
676
853
  throw new Error("Wallet not connected. Call sdk.connect() first.");
677
854
  }
855
+ logTiming2("resolveSmartWallet ready", startedAt, { chainId: validChainId });
678
856
  return {
679
857
  smartWallet: this.smartWalletAddress,
680
858
  chainId: validChainId
@@ -791,9 +969,30 @@ var SailAgent = class {
791
969
  }
792
970
  // --- IAgent: Portfolio reads ---
793
971
  async getBalances(state, chainId) {
972
+ const startedAt = Date.now();
794
973
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
795
- const raw = await this.getMetricsPortfolio(state, chainId);
796
- return mapPortfolioBalances(raw, smartWallet);
974
+ const portfolioStart = Date.now();
975
+ try {
976
+ const raw = await this.getMetricsPortfolio(state, chainId);
977
+ logTiming2("getBalances getMetricsPortfolio", portfolioStart, { chainId });
978
+ logTiming2("getBalances success", startedAt, { chainId });
979
+ return mapPortfolioBalances(raw, smartWallet);
980
+ } catch (error) {
981
+ if (error instanceof Error && error.message.startsWith("Sail API timeout")) {
982
+ console.warn(
983
+ "Sail portfolio request timed out; using empty snapshot.",
984
+ {
985
+ chainId
986
+ }
987
+ );
988
+ logTiming2("getBalances timeout-fallback", startedAt, { chainId });
989
+ return mapPortfolioBalances(
990
+ { message: "timeout", result: [] },
991
+ smartWallet
992
+ );
993
+ }
994
+ throw error;
995
+ }
797
996
  }
798
997
  async getEarnings(state, chainId) {
799
998
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
@@ -814,7 +1013,9 @@ var SailAgent = class {
814
1013
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
815
1014
  return this.request(
816
1015
  "GET",
817
- `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}`
1016
+ `/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}`,
1017
+ void 0,
1018
+ SAIL_PORTFOLIO_TIMEOUT_MS
818
1019
  );
819
1020
  }
820
1021
  async getMetricsEarnings(state, chainId) {
@@ -933,7 +1134,7 @@ var SailAgent = class {
933
1134
  return mapUserProfile2(raw);
934
1135
  }
935
1136
  // --- IAgent: Discovery (no wallet required) ---
936
- async getAgentApy(days) {
1137
+ async getAgentApy(days, _options) {
937
1138
  const raw = await this.getMetricsYield(days);
938
1139
  return mapApyByStrategy2(raw);
939
1140
  }
@@ -997,11 +1198,21 @@ var AGENT_CONSTRUCTORS = {
997
1198
  zyfai: ZyfaiAgent,
998
1199
  sail: SailAgent
999
1200
  };
1201
+ var SDK_TIMING_PREFIX = "[OwneySDKTiming]";
1202
+ function logTiming3(label, startedAt, meta) {
1203
+ const elapsedMs = Date.now() - startedAt;
1204
+ if (meta) {
1205
+ console.log(`${SDK_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
1206
+ return;
1207
+ }
1208
+ console.log(`${SDK_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
1209
+ }
1000
1210
  var OwneySDK = class {
1001
1211
  agents = /* @__PURE__ */ new Map();
1002
1212
  activeAgents = /* @__PURE__ */ new Set();
1003
1213
  state = null;
1004
1214
  apiKey;
1215
+ initializingAgentsPromise = null;
1005
1216
  constructor(config) {
1006
1217
  this.apiKey = config.apiKey;
1007
1218
  }
@@ -1078,6 +1289,32 @@ var OwneySDK = class {
1078
1289
  }
1079
1290
  return active;
1080
1291
  }
1292
+ async ensureAgentsInitialized() {
1293
+ if (this.agents.size > 0)
1294
+ return;
1295
+ if (!this.initializingAgentsPromise) {
1296
+ this.initializingAgentsPromise = (async () => {
1297
+ const agentKeys = await fetchAgentKeys(this.apiKey);
1298
+ for (const { key, agent_type } of agentKeys) {
1299
+ const AgentClass = AGENT_CONSTRUCTORS[agent_type];
1300
+ if (!AgentClass)
1301
+ continue;
1302
+ const agent = new AgentClass(key);
1303
+ this.agents.set(agent_type, agent);
1304
+ }
1305
+ if (this.agents.size === 0) {
1306
+ throw new Error(
1307
+ "No supported agents returned by the routing API. Check your API key and routing configuration."
1308
+ );
1309
+ }
1310
+ })();
1311
+ }
1312
+ try {
1313
+ await this.initializingAgentsPromise;
1314
+ } finally {
1315
+ this.initializingAgentsPromise = null;
1316
+ }
1317
+ }
1081
1318
  // --- Account lifecycle ---
1082
1319
  /**
1083
1320
  * Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
@@ -1090,22 +1327,9 @@ var OwneySDK = class {
1090
1327
  * throws before activating any agent.
1091
1328
  */
1092
1329
  async activateAgent(chainId, agentId) {
1330
+ const startedAt = Date.now();
1093
1331
  const state = this.requireState();
1094
- if (this.agents.size === 0) {
1095
- const agentKeys = await fetchAgentKeys(this.apiKey);
1096
- for (const { key, agent_type } of agentKeys) {
1097
- const AgentClass = AGENT_CONSTRUCTORS[agent_type];
1098
- if (!AgentClass)
1099
- continue;
1100
- const agent = new AgentClass(key);
1101
- this.agents.set(agent_type, agent);
1102
- }
1103
- if (this.agents.size === 0) {
1104
- throw new Error(
1105
- "No supported agents returned by the routing API. Check your API key and routing configuration."
1106
- );
1107
- }
1108
- }
1332
+ await this.ensureAgentsInitialized();
1109
1333
  if (agentId !== void 0) {
1110
1334
  if (agentId.length === 0) {
1111
1335
  throw new Error(
@@ -1123,14 +1347,27 @@ var OwneySDK = class {
1123
1347
  if (incompatibleAgents.length > 0) {
1124
1348
  throw new AgentChainIncompatibleError(incompatibleAgents, chainId);
1125
1349
  }
1126
- for (const agent of agents) {
1127
- await agent.activateAgent(state, chainId);
1128
- }
1129
1350
  this.activeAgents.clear();
1130
1351
  for (const id of uniqueIds) {
1131
1352
  this.activeAgents.add(id);
1132
1353
  }
1133
1354
  state.chainId = chainId;
1355
+ Promise.all(
1356
+ agents.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: uniqueIds
1370
+ });
1134
1371
  return;
1135
1372
  }
1136
1373
  const compatible = [...this.agents.values()].filter(
@@ -1139,14 +1376,27 @@ var OwneySDK = class {
1139
1376
  if (compatible.length === 0) {
1140
1377
  throw new Error(`No agents support chain ${chainId}.`);
1141
1378
  }
1142
- for (const agent of compatible) {
1143
- await agent.activateAgent(state, chainId);
1144
- }
1145
1379
  this.activeAgents.clear();
1146
1380
  for (const agent of compatible) {
1147
1381
  this.activeAgents.add(agent.id);
1148
1382
  }
1149
1383
  state.chainId = chainId;
1384
+ Promise.all(
1385
+ compatible.map(async (agent) => {
1386
+ const agentStart = Date.now();
1387
+ await agent.activateAgent(state, chainId);
1388
+ logTiming3("activateAgent background-agent-ready", agentStart, {
1389
+ chainId,
1390
+ agentId: agent.id
1391
+ });
1392
+ })
1393
+ ).catch((error) => {
1394
+ console.error("activateAgent background init failed:", error);
1395
+ });
1396
+ logTiming3("activateAgent scheduled", startedAt, {
1397
+ chainId,
1398
+ agentIds: compatible.map((agent) => agent.id)
1399
+ });
1150
1400
  }
1151
1401
  /**
1152
1402
  * Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
@@ -1250,17 +1500,47 @@ var OwneySDK = class {
1250
1500
  * @returns {AgentBalance} for a single agent, or {OwneyBalances} with totalBalance and per-agent breakdown
1251
1501
  */
1252
1502
  async getBalances(agentId) {
1503
+ const startedAt = Date.now();
1253
1504
  const state = this.requireState();
1254
1505
  const chainId = this.requireChainId();
1255
1506
  if (agentId) {
1256
- return this.getAgent(agentId).getBalances(state, chainId);
1507
+ const result = await this.getAgent(agentId).getBalances(state, chainId);
1508
+ logTiming3("getBalances single-agent", startedAt, { chainId, agentId });
1509
+ return result;
1257
1510
  }
1258
1511
  let totalBalance = 0;
1259
1512
  const results = {};
1260
- for (const [id, agent] of this.getActiveAgents()) {
1261
- results[id] = await agent.getBalances(state, chainId);
1262
- totalBalance = totalBalance + Number(results[id].totalBalance);
1513
+ const entries = [...this.getActiveAgents().entries()];
1514
+ const balanceResults = await Promise.allSettled(
1515
+ entries.map(async ([id, agent]) => {
1516
+ const agentStartedAt = Date.now();
1517
+ const b = await agent.getBalances(state, chainId);
1518
+ logTiming3("getBalances per-agent", agentStartedAt, {
1519
+ chainId,
1520
+ agentId: id
1521
+ });
1522
+ return [id, b];
1523
+ })
1524
+ );
1525
+ let successCount = 0;
1526
+ for (const settledResult of balanceResults) {
1527
+ if (settledResult.status === "fulfilled") {
1528
+ const [id, b] = settledResult.value;
1529
+ results[id] = b;
1530
+ totalBalance += Number(b.totalBalance);
1531
+ successCount += 1;
1532
+ continue;
1533
+ }
1534
+ console.error("getBalances agent failed:", settledResult.reason);
1535
+ }
1536
+ if (successCount === 0) {
1537
+ throw new Error("Failed to fetch balances for all active agents.");
1263
1538
  }
1539
+ logTiming3("getBalances aggregate", startedAt, {
1540
+ chainId,
1541
+ totalAgents: entries.length,
1542
+ successCount
1543
+ });
1264
1544
  return {
1265
1545
  totalBalance: String(totalBalance),
1266
1546
  agentBalances: results
@@ -1279,9 +1559,16 @@ var OwneySDK = class {
1279
1559
  }
1280
1560
  let totalEarnings = 0;
1281
1561
  const results = {};
1282
- for (const [id, agent] of this.getActiveAgents()) {
1283
- results[id] = await agent.getEarnings(state, chainId);
1284
- totalEarnings = totalEarnings + Number(results[id].lifetimeEarnings);
1562
+ const entries = [...this.getActiveAgents().entries()];
1563
+ const earningsResults = await Promise.all(
1564
+ entries.map(async ([id, agent]) => {
1565
+ const e = await agent.getEarnings(state, chainId);
1566
+ return [id, e];
1567
+ })
1568
+ );
1569
+ for (const [id, e] of earningsResults) {
1570
+ results[id] = e;
1571
+ totalEarnings += Number(e.lifetimeEarnings);
1285
1572
  }
1286
1573
  return {
1287
1574
  totalEarnings: String(totalEarnings),
@@ -1306,16 +1593,30 @@ var OwneySDK = class {
1306
1593
  return this.getAgent(agentId).getAccountApy(state, chainId, days);
1307
1594
  }
1308
1595
  const activeAgents = this.getActiveAgents();
1596
+ const entries = [...activeAgents.entries()];
1597
+ const [apyResults, balanceResults] = await Promise.all([
1598
+ Promise.all(
1599
+ entries.map(async ([id, agent]) => {
1600
+ const apy = await agent.getAccountApy(state, chainId, days);
1601
+ return [id, apy];
1602
+ })
1603
+ ),
1604
+ Promise.all(
1605
+ entries.map(async ([id, agent]) => {
1606
+ const b = await agent.getBalances(state, chainId);
1607
+ return [id, Number(b.totalBalance)];
1608
+ })
1609
+ )
1610
+ ]);
1309
1611
  const results = {};
1310
- for (const [id, agent] of activeAgents) {
1311
- results[id] = await agent.getAccountApy(state, chainId, days);
1612
+ for (const [id, apy] of apyResults) {
1613
+ results[id] = apy;
1312
1614
  }
1313
1615
  const balances = {};
1314
1616
  let totalBalance = 0;
1315
- for (const [id, agent] of activeAgents) {
1316
- const b = await agent.getBalances(state, chainId);
1317
- balances[id] = Number(b.totalBalance);
1318
- totalBalance += balances[id];
1617
+ for (const [id, bal] of balanceResults) {
1618
+ balances[id] = bal;
1619
+ totalBalance += bal;
1319
1620
  }
1320
1621
  let totalApy = 0;
1321
1622
  if (totalBalance > 0) {
@@ -1343,9 +1644,13 @@ var OwneySDK = class {
1343
1644
  if (agentId) {
1344
1645
  return this.getAgent(agentId).getHistory(state, chainId, filters);
1345
1646
  }
1647
+ const historyResults = await Promise.all(
1648
+ [...this.getActiveAgents().values()].map(
1649
+ (agent) => agent.getHistory(state, chainId, filters)
1650
+ )
1651
+ );
1346
1652
  let results = [];
1347
- for (const [, agent] of this.getActiveAgents()) {
1348
- const { data } = await agent.getHistory(state, chainId, filters);
1653
+ for (const { data } of historyResults) {
1349
1654
  results = [...results, ...data];
1350
1655
  }
1351
1656
  results.sort(
@@ -1389,8 +1694,15 @@ var OwneySDK = class {
1389
1694
  return this.getAgent(agentId).getUserProfile(state, chainId);
1390
1695
  }
1391
1696
  const results = {};
1392
- for (const [id, agent] of this.getActiveAgents()) {
1393
- results[id] = await agent.getUserProfile(state, chainId);
1697
+ const entries = [...this.getActiveAgents().entries()];
1698
+ const profileResults = await Promise.all(
1699
+ entries.map(async ([id, agent]) => {
1700
+ const p = await agent.getUserProfile(state, chainId);
1701
+ return [id, p];
1702
+ })
1703
+ );
1704
+ for (const [id, p] of profileResults) {
1705
+ results[id] = p;
1394
1706
  }
1395
1707
  return {
1396
1708
  agentUserProfile: results
@@ -1402,18 +1714,34 @@ var OwneySDK = class {
1402
1714
  * @param options - Contains agentId (optional) and days ("7D", "14D", or "30D")
1403
1715
  * @param options.agentId - Optional. Agent to query. Omit for all agents.
1404
1716
  * @param options.days - Lookback period: "7D", "14D", or "30D"
1717
+ * @param options.tokenSymbol - Optional asset symbol ("USDC" / "WETH") to
1718
+ * scope the APY to a specific asset+chain. Agents whose backends do not
1719
+ * support per-asset APY (currently Sail) ignore this.
1720
+ * @param options.chainId - Optional chain id for per-asset APY lookups.
1721
+ * Typically paired with `tokenSymbol`.
1405
1722
  * @returns {AgentApy} for a single agent, or {OwneyAgentApy} with per-agent APY
1406
1723
  */
1407
1724
  async getAgentApy({
1408
1725
  agentId,
1409
- days
1726
+ days,
1727
+ tokenSymbol,
1728
+ chainId
1410
1729
  }) {
1730
+ await this.ensureAgentsInitialized();
1731
+ const agentOptions = { tokenSymbol, chainId };
1411
1732
  if (agentId) {
1412
- return this.getAgent(agentId).getAgentApy(days);
1733
+ return this.getAgent(agentId).getAgentApy(days, agentOptions);
1413
1734
  }
1414
1735
  const results = {};
1415
- for (const [id, agent] of this.agents) {
1416
- results[id] = await agent.getAgentApy(days);
1736
+ const agentEntries = [...this.agents.entries()];
1737
+ const apyResults = await Promise.all(
1738
+ agentEntries.map(async ([id, agent]) => {
1739
+ const apy = await agent.getAgentApy(days, agentOptions);
1740
+ return [id, apy];
1741
+ })
1742
+ );
1743
+ for (const [id, apy] of apyResults) {
1744
+ results[id] = apy;
1417
1745
  }
1418
1746
  return {
1419
1747
  agentApy: results
@@ -1429,10 +1757,26 @@ var OwneySDK = class {
1429
1757
  const activeAgents = this.getActiveAgents();
1430
1758
  const balances = {};
1431
1759
  let totalBalance = 0;
1432
- for (const [id, agent] of activeAgents) {
1433
- const b = await agent.getBalances(state, chainId);
1434
- balances[id] = Number(b.totalBalance);
1435
- totalBalance += balances[id];
1760
+ const entries = [...activeAgents.entries()];
1761
+ const balanceResults = await Promise.allSettled(
1762
+ entries.map(async ([id, agent]) => {
1763
+ const b = await agent.getBalances(state, chainId);
1764
+ return [id, Number(b.totalBalance)];
1765
+ })
1766
+ );
1767
+ let successCount = 0;
1768
+ for (const settledResult of balanceResults) {
1769
+ if (settledResult.status === "fulfilled") {
1770
+ const [id, bal] = settledResult.value;
1771
+ balances[id] = bal;
1772
+ totalBalance += bal;
1773
+ successCount += 1;
1774
+ } else {
1775
+ console.error("getAgentAllocation agent failed:", settledResult.reason);
1776
+ }
1777
+ }
1778
+ if (successCount === 0) {
1779
+ throw new Error("Failed to fetch allocation: all agents failed.");
1436
1780
  }
1437
1781
  const allocation = {};
1438
1782
  if (totalBalance === 0) {