@owney/sdk 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -1
- package/dist/index.cjs +592 -163
- package/dist/index.d.cts +45 -6
- package/dist/index.d.ts +45 -6
- package/dist/index.js +591 -163
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,7 @@ __export(src_exports, {
|
|
|
23
23
|
AgentChainIncompatibleError: () => AgentChainIncompatibleError,
|
|
24
24
|
AgentNotFoundError: () => AgentNotFoundError,
|
|
25
25
|
NotConnectedError: () => NotConnectedError,
|
|
26
|
+
OwneyError: () => OwneyError,
|
|
26
27
|
OwneySDK: () => OwneySDK
|
|
27
28
|
});
|
|
28
29
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -37,12 +38,76 @@ var P_SUPPORTED_CHAINS = {
|
|
|
37
38
|
};
|
|
38
39
|
var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
|
|
39
40
|
var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
|
|
41
|
+
var SupportedAssets = [
|
|
42
|
+
{
|
|
43
|
+
address: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`,
|
|
44
|
+
symbol: "USDC",
|
|
45
|
+
chainId: 8453
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
address: `0xaf88d065e77c8cC2239327C5EDb3A432268e5831`,
|
|
49
|
+
symbol: "USDC",
|
|
50
|
+
chainId: 42161
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
address: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`,
|
|
54
|
+
symbol: "USDT",
|
|
55
|
+
chainId: 42161
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// src/errors.ts
|
|
60
|
+
var OwneyError = class extends Error {
|
|
61
|
+
code;
|
|
62
|
+
details;
|
|
63
|
+
constructor(code, message, details) {
|
|
64
|
+
super(message);
|
|
65
|
+
this.name = "OwneyError";
|
|
66
|
+
this.code = code;
|
|
67
|
+
this.details = details;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var AgentNotFoundError = class extends OwneyError {
|
|
71
|
+
constructor(agentId, available) {
|
|
72
|
+
super(
|
|
73
|
+
"AGENT_NOT_FOUND",
|
|
74
|
+
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
75
|
+
{ agentId, available }
|
|
76
|
+
);
|
|
77
|
+
this.name = "AgentNotFoundError";
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var NotConnectedError = class extends OwneyError {
|
|
81
|
+
constructor() {
|
|
82
|
+
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
83
|
+
this.name = "NotConnectedError";
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var AgentChainIncompatibleError = class extends OwneyError {
|
|
87
|
+
incompatibleAgents;
|
|
88
|
+
connectedChainId;
|
|
89
|
+
constructor(incompatibleAgents, connectedChainId) {
|
|
90
|
+
const details = incompatibleAgents.map(
|
|
91
|
+
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
92
|
+
).join("; ");
|
|
93
|
+
super(
|
|
94
|
+
"AGENT_CHAIN_INCOMPATIBLE",
|
|
95
|
+
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
96
|
+
{ incompatibleAgents, connectedChainId }
|
|
97
|
+
);
|
|
98
|
+
this.name = "AgentChainIncompatibleError";
|
|
99
|
+
this.incompatibleAgents = incompatibleAgents;
|
|
100
|
+
this.connectedChainId = connectedChainId;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
40
103
|
|
|
41
104
|
// src/lib/utils.ts
|
|
42
105
|
var isValidChainId = (chainId) => {
|
|
43
106
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
|
|
44
|
-
throw new
|
|
45
|
-
|
|
107
|
+
throw new OwneyError(
|
|
108
|
+
"CHAIN_UNSUPPORTED",
|
|
109
|
+
`Unsupported chain ID: ${chainId}. Supported chains: ${SUPPORTED_CHAIN_IDS.join(", ")}`,
|
|
110
|
+
{ chainId, supportedChainIds: [...SUPPORTED_CHAIN_IDS] }
|
|
46
111
|
);
|
|
47
112
|
}
|
|
48
113
|
return chainId;
|
|
@@ -57,6 +122,15 @@ function hexToDecimal(hex, decimals = 6) {
|
|
|
57
122
|
const remainder = parsed % divisor;
|
|
58
123
|
return Number(whole) + Number(remainder) / Number(divisor);
|
|
59
124
|
}
|
|
125
|
+
function normalizeDecimalPlace(value, decimals = 6) {
|
|
126
|
+
const parsed = BigInt(value);
|
|
127
|
+
if (decimals <= 0)
|
|
128
|
+
return Number(parsed);
|
|
129
|
+
const divisor = BigInt(10 ** decimals);
|
|
130
|
+
const whole = parsed / divisor;
|
|
131
|
+
const remainder = parsed % divisor;
|
|
132
|
+
return Number(whole) + Number(remainder) / Number(divisor);
|
|
133
|
+
}
|
|
60
134
|
var DayFilterMapping = {
|
|
61
135
|
"30D": 30,
|
|
62
136
|
"14D": 14,
|
|
@@ -86,7 +160,7 @@ var CHAIN_NAME_TO_ID = {
|
|
|
86
160
|
BASE: 8453,
|
|
87
161
|
ARBITRUM: 42161
|
|
88
162
|
};
|
|
89
|
-
function mapBalances(raw, chainId) {
|
|
163
|
+
function mapBalances(raw, chainId, smartWallet) {
|
|
90
164
|
const portfolio = raw.portfolio;
|
|
91
165
|
const tokenMap = /* @__PURE__ */ new Map();
|
|
92
166
|
for (const p of portfolio.positions || []) {
|
|
@@ -97,18 +171,7 @@ function mapBalances(raw, chainId) {
|
|
|
97
171
|
tokenMap.set(key, {
|
|
98
172
|
symbol,
|
|
99
173
|
chainId: positionChainId,
|
|
100
|
-
amount: (current?.amount ?? 0) +
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
for (const s of portfolio.staleBalances || []) {
|
|
104
|
-
const symbol = s.tokenSymbol || "USDC";
|
|
105
|
-
const staleChainId = s.chainId ?? chainId;
|
|
106
|
-
const key = `${symbol}:${staleChainId}`;
|
|
107
|
-
const current = tokenMap.get(key);
|
|
108
|
-
tokenMap.set(key, {
|
|
109
|
-
symbol,
|
|
110
|
-
chainId: staleChainId,
|
|
111
|
-
amount: (current?.amount ?? 0) + hexToDecimal(s.balance || "0")
|
|
174
|
+
amount: (current?.amount ?? 0) + normalizeDecimalPlace(p.underlyingAmount || "0")
|
|
112
175
|
});
|
|
113
176
|
}
|
|
114
177
|
let totalBalance = 0;
|
|
@@ -123,7 +186,7 @@ function mapBalances(raw, chainId) {
|
|
|
123
186
|
});
|
|
124
187
|
}
|
|
125
188
|
return {
|
|
126
|
-
smartWallet
|
|
189
|
+
smartWallet,
|
|
127
190
|
totalBalance: String(totalBalance),
|
|
128
191
|
tokens
|
|
129
192
|
};
|
|
@@ -214,13 +277,41 @@ function mapUserProfile(raw, userAddress) {
|
|
|
214
277
|
};
|
|
215
278
|
}
|
|
216
279
|
function mapApyByStrategy(raw) {
|
|
217
|
-
const
|
|
280
|
+
const apyPerAsset = {};
|
|
281
|
+
let apySum = 0;
|
|
282
|
+
let apyCount = 0;
|
|
283
|
+
for (const entry of raw.data) {
|
|
284
|
+
const supported = SupportedAssets.find(
|
|
285
|
+
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
286
|
+
);
|
|
287
|
+
if (!supported)
|
|
288
|
+
continue;
|
|
289
|
+
const chainKey = supported.chainId;
|
|
290
|
+
const symbolKey = supported.symbol;
|
|
291
|
+
const bucket = apyPerAsset[chainKey] ?? {};
|
|
292
|
+
bucket[symbolKey] = entry.average_apy;
|
|
293
|
+
apyPerAsset[chainKey] = bucket;
|
|
294
|
+
apySum += entry.average_apy;
|
|
295
|
+
apyCount += 1;
|
|
296
|
+
}
|
|
297
|
+
const averageApy = apyCount > 0 ? apySum / apyCount : 0;
|
|
298
|
+
const hasDetails = Object.keys(apyPerAsset).length > 0;
|
|
218
299
|
return {
|
|
219
|
-
averageApy
|
|
300
|
+
averageApy,
|
|
301
|
+
detailedApys: hasDetails ? { apyPerAsset } : void 0
|
|
220
302
|
};
|
|
221
303
|
}
|
|
222
304
|
|
|
223
305
|
// src/agents/zyfai/zyfai.agent.ts
|
|
306
|
+
var ZYFAI_TIMING_PREFIX = "[AgentTiming][zyfai]";
|
|
307
|
+
function logTiming(label, startedAt, meta) {
|
|
308
|
+
const elapsedMs = Date.now() - startedAt;
|
|
309
|
+
if (meta) {
|
|
310
|
+
console.log(`${ZYFAI_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
console.log(`${ZYFAI_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
|
|
314
|
+
}
|
|
224
315
|
var ZyfaiAgent = class {
|
|
225
316
|
id = "zyfai";
|
|
226
317
|
supportedChainIds = [8453, 42161];
|
|
@@ -239,62 +330,122 @@ var ZyfaiAgent = class {
|
|
|
239
330
|
sdk;
|
|
240
331
|
connectedAddress = null;
|
|
241
332
|
connectedChainId = null;
|
|
333
|
+
smartWalletCache = /* @__PURE__ */ new Map();
|
|
334
|
+
connectAuthPromise = null;
|
|
242
335
|
constructor(apiKey) {
|
|
243
336
|
this.sdk = new import_sdk.ZyfaiSDK({ apiKey });
|
|
244
337
|
}
|
|
245
338
|
// --- Private helpers ---
|
|
246
339
|
getAddress() {
|
|
247
340
|
if (!this.connectedAddress) {
|
|
248
|
-
throw new
|
|
341
|
+
throw new OwneyError(
|
|
342
|
+
"NOT_CONNECTED",
|
|
343
|
+
"Wallet not connected. Call sdk.connect() first."
|
|
344
|
+
);
|
|
249
345
|
}
|
|
250
346
|
return this.connectedAddress;
|
|
251
347
|
}
|
|
252
348
|
/** Light connect: SIWE auth only. Re-auths if chain changed. */
|
|
253
349
|
async connectAuth(state, chainId) {
|
|
350
|
+
const startedAt = Date.now();
|
|
254
351
|
const validChainId = isValidChainId(chainId);
|
|
255
|
-
if (
|
|
352
|
+
if (this.connectedAddress && this.connectedChainId === validChainId) {
|
|
353
|
+
logTiming("connectAuth cache-hit", startedAt, { chainId: validChainId });
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (this.connectAuthPromise) {
|
|
357
|
+
await this.connectAuthPromise;
|
|
358
|
+
logTiming("connectAuth awaited-inflight", startedAt, {
|
|
359
|
+
chainId: validChainId
|
|
360
|
+
});
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
this.connectAuthPromise = (async () => {
|
|
256
364
|
const address = await this.sdk.connectAccount(
|
|
257
365
|
state.provider,
|
|
258
366
|
validChainId
|
|
259
367
|
);
|
|
260
368
|
this.connectedAddress = address;
|
|
261
369
|
this.connectedChainId = validChainId;
|
|
370
|
+
})();
|
|
371
|
+
try {
|
|
372
|
+
await this.connectAuthPromise;
|
|
373
|
+
logTiming("connectAuth success", startedAt, { chainId: validChainId });
|
|
374
|
+
} finally {
|
|
375
|
+
this.connectAuthPromise = null;
|
|
262
376
|
}
|
|
263
377
|
}
|
|
264
378
|
/** Full connect: SIWE auth + Safe deploy check + session key. */
|
|
265
379
|
async connectFull(state, chainId) {
|
|
380
|
+
const startedAt = Date.now();
|
|
266
381
|
await this.connectAuth(state, chainId);
|
|
267
382
|
const validChainId = isValidChainId(chainId);
|
|
268
383
|
const address = this.getAddress();
|
|
384
|
+
const walletLookupStart = Date.now();
|
|
269
385
|
const wallet = await this.sdk.getSmartWalletAddress(address, validChainId);
|
|
386
|
+
logTiming("connectFull getSmartWalletAddress", walletLookupStart, {
|
|
387
|
+
chainId: validChainId
|
|
388
|
+
});
|
|
389
|
+
this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
|
|
270
390
|
if (!wallet.isDeployed) {
|
|
391
|
+
const deployStart = Date.now();
|
|
271
392
|
await this.sdk.deploySafe(address, validChainId, "aggressive", true);
|
|
393
|
+
logTiming("connectFull deploySafe", deployStart, {
|
|
394
|
+
chainId: validChainId
|
|
395
|
+
});
|
|
272
396
|
}
|
|
397
|
+
const userDetailsStart = Date.now();
|
|
273
398
|
const user = await this.sdk.getUserDetails();
|
|
399
|
+
logTiming("connectFull getUserDetails", userDetailsStart, {
|
|
400
|
+
chainId: validChainId
|
|
401
|
+
});
|
|
274
402
|
if (!user.hasActiveSessionKey) {
|
|
403
|
+
const sessionKeyStart = Date.now();
|
|
275
404
|
await this.sdk.createSessionKey(address, validChainId);
|
|
405
|
+
logTiming("connectFull createSessionKey", sessionKeyStart, {
|
|
406
|
+
chainId: validChainId
|
|
407
|
+
});
|
|
276
408
|
}
|
|
409
|
+
logTiming("connectFull success", startedAt, { chainId: validChainId });
|
|
277
410
|
}
|
|
278
411
|
/** Connect + resolve smart wallet address for read operations. */
|
|
279
412
|
async resolveSmartWallet(state, chainId) {
|
|
413
|
+
const startedAt = Date.now();
|
|
280
414
|
await this.connectAuth(state, chainId);
|
|
281
415
|
const validChainId = isValidChainId(chainId);
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
)
|
|
286
|
-
|
|
416
|
+
const ownerAddress = this.getAddress();
|
|
417
|
+
const cacheKey = `${ownerAddress}_${validChainId}`;
|
|
418
|
+
let smartWallet = this.smartWalletCache.get(cacheKey);
|
|
419
|
+
if (!smartWallet) {
|
|
420
|
+
const lookupStart = Date.now();
|
|
421
|
+
const { address } = await this.sdk.getSmartWalletAddress(
|
|
422
|
+
ownerAddress,
|
|
423
|
+
validChainId
|
|
424
|
+
);
|
|
425
|
+
logTiming("resolveSmartWallet remote-lookup", lookupStart, {
|
|
426
|
+
chainId: validChainId
|
|
427
|
+
});
|
|
428
|
+
smartWallet = address;
|
|
429
|
+
this.smartWalletCache.set(cacheKey, smartWallet);
|
|
430
|
+
} else {
|
|
431
|
+
logTiming("resolveSmartWallet cache-hit", startedAt, {
|
|
432
|
+
chainId: validChainId
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
logTiming("resolveSmartWallet ready", startedAt, { chainId: validChainId });
|
|
436
|
+
return { smartWallet, chainId: validChainId };
|
|
287
437
|
}
|
|
288
438
|
// --- IAgent: Connection lifecycle ---
|
|
289
439
|
async disconnect() {
|
|
290
440
|
await this.sdk.disconnectAccount();
|
|
291
441
|
this.connectedAddress = null;
|
|
292
442
|
this.connectedChainId = null;
|
|
443
|
+
this.smartWalletCache.clear();
|
|
293
444
|
}
|
|
294
445
|
// --- IAgent: Account lifecycle ---
|
|
295
446
|
async activateAgent(state, chainId) {
|
|
296
447
|
try {
|
|
297
|
-
await this.
|
|
448
|
+
await this.connectAuth(state, chainId);
|
|
298
449
|
} catch (error) {
|
|
299
450
|
await this.disconnect();
|
|
300
451
|
throw error;
|
|
@@ -341,10 +492,15 @@ var ZyfaiAgent = class {
|
|
|
341
492
|
}
|
|
342
493
|
// --- IAgent: Portfolio reads ---
|
|
343
494
|
async getBalances(state, chainId) {
|
|
344
|
-
|
|
345
|
-
const validChainId =
|
|
346
|
-
const
|
|
347
|
-
|
|
495
|
+
const startedAt = Date.now();
|
|
496
|
+
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
497
|
+
const portfolioStart = Date.now();
|
|
498
|
+
const raw = await this.sdk.getPortfolio(this.getAddress());
|
|
499
|
+
logTiming("getBalances getPortfolio", portfolioStart, {
|
|
500
|
+
chainId: validChainId
|
|
501
|
+
});
|
|
502
|
+
logTiming("getBalances success", startedAt, { chainId: validChainId });
|
|
503
|
+
return mapBalances(raw, validChainId, smartWallet);
|
|
348
504
|
}
|
|
349
505
|
async getEarnings(state, chainId) {
|
|
350
506
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -378,11 +534,13 @@ var ZyfaiAgent = class {
|
|
|
378
534
|
return mapUserProfile(raw, this.connectedAddress);
|
|
379
535
|
}
|
|
380
536
|
// --- IAgent: Discovery (no wallet required) ---
|
|
381
|
-
async getAgentApy(days) {
|
|
537
|
+
async getAgentApy(days, options) {
|
|
382
538
|
const raw = await this.sdk.getAPYPerStrategy(
|
|
383
539
|
false,
|
|
384
540
|
DayFilterMapping[days],
|
|
385
|
-
"aggressive"
|
|
541
|
+
"aggressive",
|
|
542
|
+
options?.chainId,
|
|
543
|
+
options?.tokenSymbol
|
|
386
544
|
);
|
|
387
545
|
return mapApyByStrategy(raw);
|
|
388
546
|
}
|
|
@@ -428,9 +586,11 @@ function mapEarnings2(raw, smartWallet) {
|
|
|
428
586
|
};
|
|
429
587
|
}
|
|
430
588
|
function mapApyHistory2(raw, walletAddress) {
|
|
589
|
+
const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
|
|
431
590
|
return {
|
|
432
591
|
walletAddress,
|
|
433
|
-
weightedApyAfterFee: raw.result
|
|
592
|
+
weightedApyAfterFee: raw.result.apy,
|
|
593
|
+
weightedApyAfterFeeDetails: apyPerAsset ? { apyPerAsset } : void 0
|
|
434
594
|
};
|
|
435
595
|
}
|
|
436
596
|
function mapSailTransaction(tx) {
|
|
@@ -564,15 +724,45 @@ function mapUserProfile2(raw) {
|
|
|
564
724
|
};
|
|
565
725
|
}
|
|
566
726
|
function mapApyByStrategy2(raw) {
|
|
727
|
+
const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
|
|
567
728
|
return {
|
|
568
|
-
averageApy: raw.result
|
|
729
|
+
averageApy: raw.result.apy,
|
|
730
|
+
detailedApys: apyPerAsset ? { apyPerAsset } : void 0
|
|
569
731
|
};
|
|
570
732
|
}
|
|
733
|
+
function remapApyPerAssetToSymbols(raw) {
|
|
734
|
+
if (!raw)
|
|
735
|
+
return void 0;
|
|
736
|
+
const remapped = {};
|
|
737
|
+
for (const asset of SupportedAssets) {
|
|
738
|
+
const chainKey = asset.chainId;
|
|
739
|
+
const apyByAddress = raw[String(chainKey)];
|
|
740
|
+
if (!apyByAddress)
|
|
741
|
+
continue;
|
|
742
|
+
const apy = apyByAddress[asset.address] ?? apyByAddress[asset.address.toLowerCase()];
|
|
743
|
+
if (apy === void 0)
|
|
744
|
+
continue;
|
|
745
|
+
const bucket = remapped[chainKey] ?? {};
|
|
746
|
+
bucket[asset.symbol] = apy;
|
|
747
|
+
remapped[chainKey] = bucket;
|
|
748
|
+
}
|
|
749
|
+
return Object.keys(remapped).length > 0 ? remapped : void 0;
|
|
750
|
+
}
|
|
571
751
|
|
|
572
752
|
// src/agents/sail/sail.agent.ts
|
|
573
753
|
var SAIL_API_BASE = "https://app.sail.money";
|
|
574
754
|
var PROJECT_ID = "sail";
|
|
575
755
|
var PAGE_ID = "home";
|
|
756
|
+
var SAIL_TIMING_PREFIX = "[AgentTiming][sail]";
|
|
757
|
+
var SAIL_PORTFOLIO_TIMEOUT_MS = 8e3;
|
|
758
|
+
function logTiming2(label, startedAt, meta) {
|
|
759
|
+
const elapsedMs = Date.now() - startedAt;
|
|
760
|
+
if (meta) {
|
|
761
|
+
console.log(`${SAIL_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
console.log(`${SAIL_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
|
|
765
|
+
}
|
|
576
766
|
var SailAgent = class {
|
|
577
767
|
id = "sail";
|
|
578
768
|
supportedChainIds = [8453, 42161];
|
|
@@ -594,11 +784,12 @@ var SailAgent = class {
|
|
|
594
784
|
connectedAddress = null;
|
|
595
785
|
smartWalletAddress = null;
|
|
596
786
|
connectedChainId = null;
|
|
787
|
+
connectPromise = null;
|
|
597
788
|
constructor(apiKey) {
|
|
598
789
|
this.apiKey = apiKey;
|
|
599
790
|
}
|
|
600
791
|
// --- Private helpers ---
|
|
601
|
-
async request(method, path, body) {
|
|
792
|
+
async request(method, path, body, timeoutMs) {
|
|
602
793
|
const headers = {
|
|
603
794
|
"Content-Type": "application/json",
|
|
604
795
|
"X-Business-API-Key": this.apiKey
|
|
@@ -609,32 +800,57 @@ var SailAgent = class {
|
|
|
609
800
|
if (this.accountToken) {
|
|
610
801
|
headers["X-Account-Token"] = this.accountToken;
|
|
611
802
|
}
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
803
|
+
const controller = new AbortController();
|
|
804
|
+
const timeoutId = typeof timeoutMs === "number" && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
805
|
+
let res;
|
|
806
|
+
try {
|
|
807
|
+
res = await fetch(`${SAIL_API_BASE}${path}`, {
|
|
808
|
+
method,
|
|
809
|
+
headers,
|
|
810
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
811
|
+
signal: controller.signal
|
|
812
|
+
});
|
|
813
|
+
} catch (error) {
|
|
814
|
+
if (error instanceof Error && error.name === "AbortError" && timeoutMs) {
|
|
815
|
+
throw new OwneyError(
|
|
816
|
+
"API_SAIL_TIMEOUT",
|
|
817
|
+
`Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
|
|
818
|
+
{ timeoutMs, method, path }
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
throw error;
|
|
822
|
+
} finally {
|
|
823
|
+
if (timeoutId)
|
|
824
|
+
clearTimeout(timeoutId);
|
|
825
|
+
}
|
|
617
826
|
if (!res.ok) {
|
|
618
827
|
const text = await res.text().catch(() => "");
|
|
619
|
-
throw new
|
|
828
|
+
throw new OwneyError(
|
|
829
|
+
"API_SAIL_ERROR",
|
|
830
|
+
`Sail API error ${res.status}: ${text}`,
|
|
831
|
+
{ statusCode: res.status, method, path, responseBody: text }
|
|
832
|
+
);
|
|
620
833
|
}
|
|
621
834
|
return res.json();
|
|
622
835
|
}
|
|
623
836
|
/** Initiate SIWE and sign with the user's wallet. */
|
|
624
837
|
async signSiwe(state, chainId) {
|
|
838
|
+
const startedAt = Date.now();
|
|
625
839
|
const initiate = await this.request(
|
|
626
840
|
"POST",
|
|
627
841
|
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/initiate`,
|
|
628
842
|
{
|
|
629
843
|
method: "siwe",
|
|
630
844
|
address: state.walletAddress,
|
|
631
|
-
chainId
|
|
845
|
+
chainId,
|
|
846
|
+
identityMode: "byWallet"
|
|
632
847
|
}
|
|
633
848
|
);
|
|
634
849
|
const signature = await state.provider.request({
|
|
635
850
|
method: "personal_sign",
|
|
636
851
|
params: [initiate.message, state.walletAddress]
|
|
637
852
|
});
|
|
853
|
+
logTiming2("signSiwe success", startedAt, { chainId });
|
|
638
854
|
return {
|
|
639
855
|
sessionId: initiate.sessionId,
|
|
640
856
|
message: initiate.message,
|
|
@@ -643,38 +859,62 @@ var SailAgent = class {
|
|
|
643
859
|
}
|
|
644
860
|
/** Full connect: auth handles smart account creation via SIWE flow. */
|
|
645
861
|
async connectFull(state, chainId) {
|
|
862
|
+
const startedAt = Date.now();
|
|
646
863
|
if (this.connectedAddress && this.connectedChainId === chainId) {
|
|
864
|
+
logTiming2("connectFull cache-hit", startedAt, { chainId });
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (this.connectPromise) {
|
|
868
|
+
await this.connectPromise;
|
|
869
|
+
logTiming2("connectFull awaited-inflight", startedAt, { chainId });
|
|
647
870
|
return;
|
|
648
871
|
}
|
|
649
872
|
if (!state.walletAddress) {
|
|
650
|
-
throw new
|
|
873
|
+
throw new OwneyError(
|
|
874
|
+
"WALLET_ADDRESS_REQUIRED",
|
|
875
|
+
"Wallet address is required. Call sdk.connect(provider) first."
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
this.connectPromise = (async () => {
|
|
879
|
+
const { sessionId, signature, message } = await this.signSiwe(
|
|
880
|
+
state,
|
|
881
|
+
chainId
|
|
882
|
+
);
|
|
883
|
+
const complete = await this.request(
|
|
884
|
+
"POST",
|
|
885
|
+
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/complete`,
|
|
886
|
+
{
|
|
887
|
+
method: "custom",
|
|
888
|
+
sessionId,
|
|
889
|
+
signature,
|
|
890
|
+
walletAddress: state.walletAddress,
|
|
891
|
+
identityMode: "byWallet"
|
|
892
|
+
}
|
|
893
|
+
);
|
|
894
|
+
this.authToken = complete.token;
|
|
895
|
+
this.accountToken = complete.accountToken;
|
|
896
|
+
this.connectedAddress = state.walletAddress;
|
|
897
|
+
this.smartWalletAddress = complete.walletAddress;
|
|
898
|
+
this.connectedChainId = chainId;
|
|
899
|
+
})();
|
|
900
|
+
try {
|
|
901
|
+
await this.connectPromise;
|
|
902
|
+
logTiming2("connectFull success", startedAt, { chainId });
|
|
903
|
+
} finally {
|
|
904
|
+
this.connectPromise = null;
|
|
651
905
|
}
|
|
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;
|
|
671
906
|
}
|
|
672
907
|
async resolveSmartWallet(state, chainId) {
|
|
908
|
+
const startedAt = Date.now();
|
|
673
909
|
await this.connectFull(state, chainId);
|
|
674
910
|
const validChainId = isValidChainId(chainId);
|
|
675
911
|
if (!this.smartWalletAddress) {
|
|
676
|
-
throw new
|
|
912
|
+
throw new OwneyError(
|
|
913
|
+
"NOT_CONNECTED",
|
|
914
|
+
"Wallet not connected. Call sdk.connect() first."
|
|
915
|
+
);
|
|
677
916
|
}
|
|
917
|
+
logTiming2("resolveSmartWallet ready", startedAt, { chainId: validChainId });
|
|
678
918
|
return {
|
|
679
919
|
smartWallet: this.smartWalletAddress,
|
|
680
920
|
chainId: validChainId
|
|
@@ -699,7 +939,8 @@ var SailAgent = class {
|
|
|
699
939
|
}
|
|
700
940
|
async deposit(state, chainId, amount, depositCallback) {
|
|
701
941
|
if (!depositCallback) {
|
|
702
|
-
throw new
|
|
942
|
+
throw new OwneyError(
|
|
943
|
+
"DEPOSIT_CALLBACK_REQUIRED",
|
|
703
944
|
"depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
|
|
704
945
|
);
|
|
705
946
|
}
|
|
@@ -708,12 +949,16 @@ var SailAgent = class {
|
|
|
708
949
|
const depositInfo = await this.getDepositInfo();
|
|
709
950
|
const resolvedTokenAddress = depositInfo.permittedTokens[0]?.address;
|
|
710
951
|
if (!resolvedTokenAddress) {
|
|
711
|
-
throw new
|
|
952
|
+
throw new OwneyError(
|
|
953
|
+
"DEPOSIT_NO_PERMITTED_TOKENS",
|
|
954
|
+
"No permitted tokens available for deposit."
|
|
955
|
+
);
|
|
712
956
|
}
|
|
713
957
|
await this.executePreHooks(resolvedTokenAddress, amount, chainId);
|
|
714
958
|
const txHash = await depositCallback(smartWallet, chainId, amount);
|
|
715
959
|
if (!txHash) {
|
|
716
|
-
throw new
|
|
960
|
+
throw new OwneyError(
|
|
961
|
+
"DEPOSIT_CALLBACK_INVALID",
|
|
717
962
|
"depositCallback must return a transaction hash (txHash) for Sail deposits."
|
|
718
963
|
);
|
|
719
964
|
}
|
|
@@ -767,7 +1012,10 @@ var SailAgent = class {
|
|
|
767
1012
|
const withdrawInfo = await this.getWithdrawInfo();
|
|
768
1013
|
const tokenAddress = withdrawInfo.permittedTokens[0]?.address;
|
|
769
1014
|
if (!tokenAddress) {
|
|
770
|
-
throw new
|
|
1015
|
+
throw new OwneyError(
|
|
1016
|
+
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
1017
|
+
"No permitted tokens available for withdrawal."
|
|
1018
|
+
);
|
|
771
1019
|
}
|
|
772
1020
|
const resolvedAmount = amount ?? withdrawInfo.currentBalance;
|
|
773
1021
|
const { signature: adminWalletSignature, message: adminWalletPayload } = await this.signSiwe(state, chainId);
|
|
@@ -791,9 +1039,30 @@ var SailAgent = class {
|
|
|
791
1039
|
}
|
|
792
1040
|
// --- IAgent: Portfolio reads ---
|
|
793
1041
|
async getBalances(state, chainId) {
|
|
1042
|
+
const startedAt = Date.now();
|
|
794
1043
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
795
|
-
const
|
|
796
|
-
|
|
1044
|
+
const portfolioStart = Date.now();
|
|
1045
|
+
try {
|
|
1046
|
+
const raw = await this.getMetricsPortfolio(state, chainId);
|
|
1047
|
+
logTiming2("getBalances getMetricsPortfolio", portfolioStart, { chainId });
|
|
1048
|
+
logTiming2("getBalances success", startedAt, { chainId });
|
|
1049
|
+
return mapPortfolioBalances(raw, smartWallet);
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
if (error instanceof OwneyError && error.code === "API_SAIL_TIMEOUT") {
|
|
1052
|
+
console.warn(
|
|
1053
|
+
"Sail portfolio request timed out; using empty snapshot.",
|
|
1054
|
+
{
|
|
1055
|
+
chainId
|
|
1056
|
+
}
|
|
1057
|
+
);
|
|
1058
|
+
logTiming2("getBalances timeout-fallback", startedAt, { chainId });
|
|
1059
|
+
return mapPortfolioBalances(
|
|
1060
|
+
{ message: "timeout", result: [] },
|
|
1061
|
+
smartWallet
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
throw error;
|
|
1065
|
+
}
|
|
797
1066
|
}
|
|
798
1067
|
async getEarnings(state, chainId) {
|
|
799
1068
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -814,7 +1083,9 @@ var SailAgent = class {
|
|
|
814
1083
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
815
1084
|
return this.request(
|
|
816
1085
|
"GET",
|
|
817
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}
|
|
1086
|
+
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}`,
|
|
1087
|
+
void 0,
|
|
1088
|
+
SAIL_PORTFOLIO_TIMEOUT_MS
|
|
818
1089
|
);
|
|
819
1090
|
}
|
|
820
1091
|
async getMetricsEarnings(state, chainId) {
|
|
@@ -861,8 +1132,10 @@ var SailAgent = class {
|
|
|
861
1132
|
}
|
|
862
1133
|
const dayCount = DayFilterMapping[days];
|
|
863
1134
|
if (dayCount === void 0) {
|
|
864
|
-
throw new
|
|
865
|
-
|
|
1135
|
+
throw new OwneyError(
|
|
1136
|
+
"VALIDATION_INVALID_DAYS",
|
|
1137
|
+
`Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
|
|
1138
|
+
{ days }
|
|
866
1139
|
);
|
|
867
1140
|
}
|
|
868
1141
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -933,43 +1206,12 @@ var SailAgent = class {
|
|
|
933
1206
|
return mapUserProfile2(raw);
|
|
934
1207
|
}
|
|
935
1208
|
// --- IAgent: Discovery (no wallet required) ---
|
|
936
|
-
async getAgentApy(days) {
|
|
1209
|
+
async getAgentApy(days, _options) {
|
|
937
1210
|
const raw = await this.getMetricsYield(days);
|
|
938
1211
|
return mapApyByStrategy2(raw);
|
|
939
1212
|
}
|
|
940
1213
|
};
|
|
941
1214
|
|
|
942
|
-
// src/errors.ts
|
|
943
|
-
var AgentNotFoundError = class extends Error {
|
|
944
|
-
constructor(agentId, available) {
|
|
945
|
-
super(
|
|
946
|
-
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`
|
|
947
|
-
);
|
|
948
|
-
this.name = "AgentNotFoundError";
|
|
949
|
-
}
|
|
950
|
-
};
|
|
951
|
-
var NotConnectedError = class extends Error {
|
|
952
|
-
constructor() {
|
|
953
|
-
super("Not connected. Call sdk.connect(provider) first.");
|
|
954
|
-
this.name = "NotConnectedError";
|
|
955
|
-
}
|
|
956
|
-
};
|
|
957
|
-
var AgentChainIncompatibleError = class extends Error {
|
|
958
|
-
incompatibleAgents;
|
|
959
|
-
connectedChainId;
|
|
960
|
-
constructor(incompatibleAgents, connectedChainId) {
|
|
961
|
-
const details = incompatibleAgents.map(
|
|
962
|
-
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
963
|
-
).join("; ");
|
|
964
|
-
super(
|
|
965
|
-
`Chain ${connectedChainId} is not supported by the following agents: ${details}`
|
|
966
|
-
);
|
|
967
|
-
this.name = "AgentChainIncompatibleError";
|
|
968
|
-
this.incompatibleAgents = incompatibleAgents;
|
|
969
|
-
this.connectedChainId = connectedChainId;
|
|
970
|
-
}
|
|
971
|
-
};
|
|
972
|
-
|
|
973
1215
|
// src/lib/routing-api.ts
|
|
974
1216
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
975
1217
|
async function fetchAgentKeys(apiKey) {
|
|
@@ -983,11 +1225,19 @@ async function fetchAgentKeys(apiKey) {
|
|
|
983
1225
|
});
|
|
984
1226
|
if (!res.ok) {
|
|
985
1227
|
const text = await res.text().catch(() => "");
|
|
986
|
-
throw new
|
|
1228
|
+
throw new OwneyError(
|
|
1229
|
+
"API_ROUTING_ERROR",
|
|
1230
|
+
`Routing API error ${res.status}: ${text}`,
|
|
1231
|
+
{ statusCode: res.status, responseBody: text }
|
|
1232
|
+
);
|
|
987
1233
|
}
|
|
988
1234
|
const json = await res.json();
|
|
989
1235
|
if (!json.success) {
|
|
990
|
-
throw new
|
|
1236
|
+
throw new OwneyError(
|
|
1237
|
+
"API_ROUTING_FAILED",
|
|
1238
|
+
`Routing API request failed: ${json.message}`,
|
|
1239
|
+
{ message: json.message }
|
|
1240
|
+
);
|
|
991
1241
|
}
|
|
992
1242
|
return json.data;
|
|
993
1243
|
}
|
|
@@ -997,11 +1247,21 @@ var AGENT_CONSTRUCTORS = {
|
|
|
997
1247
|
zyfai: ZyfaiAgent,
|
|
998
1248
|
sail: SailAgent
|
|
999
1249
|
};
|
|
1250
|
+
var SDK_TIMING_PREFIX = "[OwneySDKTiming]";
|
|
1251
|
+
function logTiming3(label, startedAt, meta) {
|
|
1252
|
+
const elapsedMs = Date.now() - startedAt;
|
|
1253
|
+
if (meta) {
|
|
1254
|
+
console.log(`${SDK_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
console.log(`${SDK_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
|
|
1258
|
+
}
|
|
1000
1259
|
var OwneySDK = class {
|
|
1001
1260
|
agents = /* @__PURE__ */ new Map();
|
|
1002
1261
|
activeAgents = /* @__PURE__ */ new Set();
|
|
1003
1262
|
state = null;
|
|
1004
1263
|
apiKey;
|
|
1264
|
+
initializingAgentsPromise = null;
|
|
1005
1265
|
constructor(config) {
|
|
1006
1266
|
this.apiKey = config.apiKey;
|
|
1007
1267
|
}
|
|
@@ -1017,7 +1277,8 @@ var OwneySDK = class {
|
|
|
1017
1277
|
});
|
|
1018
1278
|
const walletAddress = accounts[0];
|
|
1019
1279
|
if (!walletAddress) {
|
|
1020
|
-
throw new
|
|
1280
|
+
throw new OwneyError(
|
|
1281
|
+
"WALLET_NO_ACCOUNTS",
|
|
1021
1282
|
"No accounts found. Ensure the wallet is unlocked and connected."
|
|
1022
1283
|
);
|
|
1023
1284
|
}
|
|
@@ -1056,7 +1317,8 @@ var OwneySDK = class {
|
|
|
1056
1317
|
requireChainId() {
|
|
1057
1318
|
const chainId = this.state?.chainId ?? null;
|
|
1058
1319
|
if (chainId === null) {
|
|
1059
|
-
throw new
|
|
1320
|
+
throw new OwneyError(
|
|
1321
|
+
"NO_ACTIVE_CHAIN",
|
|
1060
1322
|
"No active chain. Call sdk.activateAgent(chainId) first."
|
|
1061
1323
|
);
|
|
1062
1324
|
}
|
|
@@ -1078,6 +1340,33 @@ var OwneySDK = class {
|
|
|
1078
1340
|
}
|
|
1079
1341
|
return active;
|
|
1080
1342
|
}
|
|
1343
|
+
async ensureAgentsInitialized() {
|
|
1344
|
+
if (this.agents.size > 0)
|
|
1345
|
+
return;
|
|
1346
|
+
if (!this.initializingAgentsPromise) {
|
|
1347
|
+
this.initializingAgentsPromise = (async () => {
|
|
1348
|
+
const agentKeys = await fetchAgentKeys(this.apiKey);
|
|
1349
|
+
for (const { key, agent_type } of agentKeys) {
|
|
1350
|
+
const AgentClass = AGENT_CONSTRUCTORS[agent_type];
|
|
1351
|
+
if (!AgentClass)
|
|
1352
|
+
continue;
|
|
1353
|
+
const agent = new AgentClass(key);
|
|
1354
|
+
this.agents.set(agent_type, agent);
|
|
1355
|
+
}
|
|
1356
|
+
if (this.agents.size === 0) {
|
|
1357
|
+
throw new OwneyError(
|
|
1358
|
+
"API_NO_AGENTS",
|
|
1359
|
+
"No supported agents returned by the routing API. Check your API key and routing configuration."
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
})();
|
|
1363
|
+
}
|
|
1364
|
+
try {
|
|
1365
|
+
await this.initializingAgentsPromise;
|
|
1366
|
+
} finally {
|
|
1367
|
+
this.initializingAgentsPromise = null;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1081
1370
|
// --- Account lifecycle ---
|
|
1082
1371
|
/**
|
|
1083
1372
|
* Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
|
|
@@ -1090,25 +1379,13 @@ var OwneySDK = class {
|
|
|
1090
1379
|
* throws before activating any agent.
|
|
1091
1380
|
*/
|
|
1092
1381
|
async activateAgent(chainId, agentId) {
|
|
1382
|
+
const startedAt = Date.now();
|
|
1093
1383
|
const state = this.requireState();
|
|
1094
|
-
|
|
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
|
-
}
|
|
1384
|
+
await this.ensureAgentsInitialized();
|
|
1109
1385
|
if (agentId !== void 0) {
|
|
1110
1386
|
if (agentId.length === 0) {
|
|
1111
|
-
throw new
|
|
1387
|
+
throw new OwneyError(
|
|
1388
|
+
"AGENT_EMPTY_LIST",
|
|
1112
1389
|
"agentId array must not be empty. Omit the argument to activate all compatible agents."
|
|
1113
1390
|
);
|
|
1114
1391
|
}
|
|
@@ -1123,30 +1400,60 @@ var OwneySDK = class {
|
|
|
1123
1400
|
if (incompatibleAgents.length > 0) {
|
|
1124
1401
|
throw new AgentChainIncompatibleError(incompatibleAgents, chainId);
|
|
1125
1402
|
}
|
|
1126
|
-
for (const agent of agents) {
|
|
1127
|
-
await agent.activateAgent(state, chainId);
|
|
1128
|
-
}
|
|
1129
1403
|
this.activeAgents.clear();
|
|
1130
1404
|
for (const id of uniqueIds) {
|
|
1131
1405
|
this.activeAgents.add(id);
|
|
1132
1406
|
}
|
|
1133
1407
|
state.chainId = chainId;
|
|
1408
|
+
Promise.all(
|
|
1409
|
+
agents.map(async (agent) => {
|
|
1410
|
+
const agentStart = Date.now();
|
|
1411
|
+
await agent.activateAgent(state, chainId);
|
|
1412
|
+
logTiming3("activateAgent background-agent-ready", agentStart, {
|
|
1413
|
+
chainId,
|
|
1414
|
+
agentId: agent.id
|
|
1415
|
+
});
|
|
1416
|
+
})
|
|
1417
|
+
).catch((error) => {
|
|
1418
|
+
console.error("activateAgent background init failed:", error);
|
|
1419
|
+
});
|
|
1420
|
+
logTiming3("activateAgent scheduled", startedAt, {
|
|
1421
|
+
chainId,
|
|
1422
|
+
agentIds: uniqueIds
|
|
1423
|
+
});
|
|
1134
1424
|
return;
|
|
1135
1425
|
}
|
|
1136
1426
|
const compatible = [...this.agents.values()].filter(
|
|
1137
1427
|
(agent) => agent.supportedChainIds.includes(chainId)
|
|
1138
1428
|
);
|
|
1139
1429
|
if (compatible.length === 0) {
|
|
1140
|
-
throw new
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1430
|
+
throw new OwneyError(
|
|
1431
|
+
"CHAIN_NO_COMPATIBLE_AGENTS",
|
|
1432
|
+
`No agents support chain ${chainId}.`,
|
|
1433
|
+
{ chainId }
|
|
1434
|
+
);
|
|
1144
1435
|
}
|
|
1145
1436
|
this.activeAgents.clear();
|
|
1146
1437
|
for (const agent of compatible) {
|
|
1147
1438
|
this.activeAgents.add(agent.id);
|
|
1148
1439
|
}
|
|
1149
1440
|
state.chainId = chainId;
|
|
1441
|
+
Promise.all(
|
|
1442
|
+
compatible.map(async (agent) => {
|
|
1443
|
+
const agentStart = Date.now();
|
|
1444
|
+
await agent.activateAgent(state, chainId);
|
|
1445
|
+
logTiming3("activateAgent background-agent-ready", agentStart, {
|
|
1446
|
+
chainId,
|
|
1447
|
+
agentId: agent.id
|
|
1448
|
+
});
|
|
1449
|
+
})
|
|
1450
|
+
).catch((error) => {
|
|
1451
|
+
console.error("activateAgent background init failed:", error);
|
|
1452
|
+
});
|
|
1453
|
+
logTiming3("activateAgent scheduled", startedAt, {
|
|
1454
|
+
chainId,
|
|
1455
|
+
agentIds: compatible.map((agent) => agent.id)
|
|
1456
|
+
});
|
|
1150
1457
|
}
|
|
1151
1458
|
/**
|
|
1152
1459
|
* Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
|
|
@@ -1172,8 +1479,10 @@ var OwneySDK = class {
|
|
|
1172
1479
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
1173
1480
|
const totalAmount = BigInt(amount);
|
|
1174
1481
|
if (totalAmount < BigInt(eligibleAgents.length)) {
|
|
1175
|
-
throw new
|
|
1176
|
-
|
|
1482
|
+
throw new OwneyError(
|
|
1483
|
+
"DEPOSIT_AMOUNT_TOO_SMALL",
|
|
1484
|
+
`Amount "${amount}" is too small to split across ${eligibleAgents.length} agents.`,
|
|
1485
|
+
{ amount, agentCount: eligibleAgents.length }
|
|
1177
1486
|
);
|
|
1178
1487
|
}
|
|
1179
1488
|
const perAgent = totalAmount / BigInt(eligibleAgents.length);
|
|
@@ -1196,8 +1505,10 @@ var OwneySDK = class {
|
|
|
1196
1505
|
(sa) => sa.chainId === chainId
|
|
1197
1506
|
);
|
|
1198
1507
|
if (!chainAssets || !chainAssets.assets.includes(asset)) {
|
|
1199
|
-
throw new
|
|
1200
|
-
|
|
1508
|
+
throw new OwneyError(
|
|
1509
|
+
"ASSET_UNSUPPORTED",
|
|
1510
|
+
`Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
|
|
1511
|
+
{ agentId: agent.id, asset, chainId }
|
|
1201
1512
|
);
|
|
1202
1513
|
}
|
|
1203
1514
|
}
|
|
@@ -1209,8 +1520,10 @@ var OwneySDK = class {
|
|
|
1209
1520
|
return chainAssets && chainAssets.assets.includes(asset);
|
|
1210
1521
|
});
|
|
1211
1522
|
if (eligible.length === 0) {
|
|
1212
|
-
throw new
|
|
1213
|
-
|
|
1523
|
+
throw new OwneyError(
|
|
1524
|
+
"ASSET_NO_COMPATIBLE_AGENTS",
|
|
1525
|
+
`No agents support asset "${asset}" on chain ${chainId}.`,
|
|
1526
|
+
{ asset, chainId }
|
|
1214
1527
|
);
|
|
1215
1528
|
}
|
|
1216
1529
|
return eligible;
|
|
@@ -1235,10 +1548,25 @@ var OwneySDK = class {
|
|
|
1235
1548
|
return agent.withdraw(state, chainId, amount);
|
|
1236
1549
|
}
|
|
1237
1550
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
1551
|
+
if (!amount) {
|
|
1552
|
+
const results2 = {};
|
|
1553
|
+
for (const agent of eligibleAgents) {
|
|
1554
|
+
results2[agent.id] = await agent.withdraw(state, chainId);
|
|
1555
|
+
}
|
|
1556
|
+
return { agentResult: results2 };
|
|
1557
|
+
}
|
|
1238
1558
|
const results = {};
|
|
1559
|
+
let lastError;
|
|
1239
1560
|
for (const agent of eligibleAgents) {
|
|
1240
|
-
|
|
1561
|
+
try {
|
|
1562
|
+
results[agent.id] = await agent.withdraw(state, chainId, amount);
|
|
1563
|
+
return { agentResult: results };
|
|
1564
|
+
} catch (err) {
|
|
1565
|
+
lastError = err;
|
|
1566
|
+
}
|
|
1241
1567
|
}
|
|
1568
|
+
if (lastError)
|
|
1569
|
+
throw lastError;
|
|
1242
1570
|
return {
|
|
1243
1571
|
agentResult: results
|
|
1244
1572
|
};
|
|
@@ -1250,17 +1578,50 @@ var OwneySDK = class {
|
|
|
1250
1578
|
* @returns {AgentBalance} for a single agent, or {OwneyBalances} with totalBalance and per-agent breakdown
|
|
1251
1579
|
*/
|
|
1252
1580
|
async getBalances(agentId) {
|
|
1581
|
+
const startedAt = Date.now();
|
|
1253
1582
|
const state = this.requireState();
|
|
1254
1583
|
const chainId = this.requireChainId();
|
|
1255
1584
|
if (agentId) {
|
|
1256
|
-
|
|
1585
|
+
const result = await this.getAgent(agentId).getBalances(state, chainId);
|
|
1586
|
+
logTiming3("getBalances single-agent", startedAt, { chainId, agentId });
|
|
1587
|
+
return result;
|
|
1257
1588
|
}
|
|
1258
1589
|
let totalBalance = 0;
|
|
1259
1590
|
const results = {};
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1591
|
+
const entries = [...this.getActiveAgents().entries()];
|
|
1592
|
+
const balanceResults = await Promise.allSettled(
|
|
1593
|
+
entries.map(async ([id, agent]) => {
|
|
1594
|
+
const agentStartedAt = Date.now();
|
|
1595
|
+
const b = await agent.getBalances(state, chainId);
|
|
1596
|
+
logTiming3("getBalances per-agent", agentStartedAt, {
|
|
1597
|
+
chainId,
|
|
1598
|
+
agentId: id
|
|
1599
|
+
});
|
|
1600
|
+
return [id, b];
|
|
1601
|
+
})
|
|
1602
|
+
);
|
|
1603
|
+
let successCount = 0;
|
|
1604
|
+
for (const settledResult of balanceResults) {
|
|
1605
|
+
if (settledResult.status === "fulfilled") {
|
|
1606
|
+
const [id, b] = settledResult.value;
|
|
1607
|
+
results[id] = b;
|
|
1608
|
+
totalBalance += Number(b.totalBalance);
|
|
1609
|
+
successCount += 1;
|
|
1610
|
+
continue;
|
|
1611
|
+
}
|
|
1612
|
+
console.error("getBalances agent failed:", settledResult.reason);
|
|
1613
|
+
}
|
|
1614
|
+
if (successCount === 0) {
|
|
1615
|
+
throw new OwneyError(
|
|
1616
|
+
"BALANCE_ALL_FAILED",
|
|
1617
|
+
"Failed to fetch balances for all active agents."
|
|
1618
|
+
);
|
|
1263
1619
|
}
|
|
1620
|
+
logTiming3("getBalances aggregate", startedAt, {
|
|
1621
|
+
chainId,
|
|
1622
|
+
totalAgents: entries.length,
|
|
1623
|
+
successCount
|
|
1624
|
+
});
|
|
1264
1625
|
return {
|
|
1265
1626
|
totalBalance: String(totalBalance),
|
|
1266
1627
|
agentBalances: results
|
|
@@ -1279,9 +1640,16 @@ var OwneySDK = class {
|
|
|
1279
1640
|
}
|
|
1280
1641
|
let totalEarnings = 0;
|
|
1281
1642
|
const results = {};
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1643
|
+
const entries = [...this.getActiveAgents().entries()];
|
|
1644
|
+
const earningsResults = await Promise.all(
|
|
1645
|
+
entries.map(async ([id, agent]) => {
|
|
1646
|
+
const e = await agent.getEarnings(state, chainId);
|
|
1647
|
+
return [id, e];
|
|
1648
|
+
})
|
|
1649
|
+
);
|
|
1650
|
+
for (const [id, e] of earningsResults) {
|
|
1651
|
+
results[id] = e;
|
|
1652
|
+
totalEarnings += Number(e.lifetimeEarnings);
|
|
1285
1653
|
}
|
|
1286
1654
|
return {
|
|
1287
1655
|
totalEarnings: String(totalEarnings),
|
|
@@ -1306,16 +1674,30 @@ var OwneySDK = class {
|
|
|
1306
1674
|
return this.getAgent(agentId).getAccountApy(state, chainId, days);
|
|
1307
1675
|
}
|
|
1308
1676
|
const activeAgents = this.getActiveAgents();
|
|
1677
|
+
const entries = [...activeAgents.entries()];
|
|
1678
|
+
const [apyResults, balanceResults] = await Promise.all([
|
|
1679
|
+
Promise.all(
|
|
1680
|
+
entries.map(async ([id, agent]) => {
|
|
1681
|
+
const apy = await agent.getAccountApy(state, chainId, days);
|
|
1682
|
+
return [id, apy];
|
|
1683
|
+
})
|
|
1684
|
+
),
|
|
1685
|
+
Promise.all(
|
|
1686
|
+
entries.map(async ([id, agent]) => {
|
|
1687
|
+
const b = await agent.getBalances(state, chainId);
|
|
1688
|
+
return [id, Number(b.totalBalance)];
|
|
1689
|
+
})
|
|
1690
|
+
)
|
|
1691
|
+
]);
|
|
1309
1692
|
const results = {};
|
|
1310
|
-
for (const [id,
|
|
1311
|
-
results[id] =
|
|
1693
|
+
for (const [id, apy] of apyResults) {
|
|
1694
|
+
results[id] = apy;
|
|
1312
1695
|
}
|
|
1313
1696
|
const balances = {};
|
|
1314
1697
|
let totalBalance = 0;
|
|
1315
|
-
for (const [id,
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
totalBalance += balances[id];
|
|
1698
|
+
for (const [id, bal] of balanceResults) {
|
|
1699
|
+
balances[id] = bal;
|
|
1700
|
+
totalBalance += bal;
|
|
1319
1701
|
}
|
|
1320
1702
|
let totalApy = 0;
|
|
1321
1703
|
if (totalBalance > 0) {
|
|
@@ -1343,9 +1725,13 @@ var OwneySDK = class {
|
|
|
1343
1725
|
if (agentId) {
|
|
1344
1726
|
return this.getAgent(agentId).getHistory(state, chainId, filters);
|
|
1345
1727
|
}
|
|
1728
|
+
const historyResults = await Promise.all(
|
|
1729
|
+
[...this.getActiveAgents().values()].map(
|
|
1730
|
+
(agent) => agent.getHistory(state, chainId, filters)
|
|
1731
|
+
)
|
|
1732
|
+
);
|
|
1346
1733
|
let results = [];
|
|
1347
|
-
for (const
|
|
1348
|
-
const { data } = await agent.getHistory(state, chainId, filters);
|
|
1734
|
+
for (const { data } of historyResults) {
|
|
1349
1735
|
results = [...results, ...data];
|
|
1350
1736
|
}
|
|
1351
1737
|
results.sort(
|
|
@@ -1389,8 +1775,15 @@ var OwneySDK = class {
|
|
|
1389
1775
|
return this.getAgent(agentId).getUserProfile(state, chainId);
|
|
1390
1776
|
}
|
|
1391
1777
|
const results = {};
|
|
1392
|
-
|
|
1393
|
-
|
|
1778
|
+
const entries = [...this.getActiveAgents().entries()];
|
|
1779
|
+
const profileResults = await Promise.all(
|
|
1780
|
+
entries.map(async ([id, agent]) => {
|
|
1781
|
+
const p = await agent.getUserProfile(state, chainId);
|
|
1782
|
+
return [id, p];
|
|
1783
|
+
})
|
|
1784
|
+
);
|
|
1785
|
+
for (const [id, p] of profileResults) {
|
|
1786
|
+
results[id] = p;
|
|
1394
1787
|
}
|
|
1395
1788
|
return {
|
|
1396
1789
|
agentUserProfile: results
|
|
@@ -1402,18 +1795,34 @@ var OwneySDK = class {
|
|
|
1402
1795
|
* @param options - Contains agentId (optional) and days ("7D", "14D", or "30D")
|
|
1403
1796
|
* @param options.agentId - Optional. Agent to query. Omit for all agents.
|
|
1404
1797
|
* @param options.days - Lookback period: "7D", "14D", or "30D"
|
|
1798
|
+
* @param options.tokenSymbol - Optional asset symbol ("USDC" / "WETH") to
|
|
1799
|
+
* scope the APY to a specific asset+chain. Agents whose backends do not
|
|
1800
|
+
* support per-asset APY (currently Sail) ignore this.
|
|
1801
|
+
* @param options.chainId - Optional chain id for per-asset APY lookups.
|
|
1802
|
+
* Typically paired with `tokenSymbol`.
|
|
1405
1803
|
* @returns {AgentApy} for a single agent, or {OwneyAgentApy} with per-agent APY
|
|
1406
1804
|
*/
|
|
1407
1805
|
async getAgentApy({
|
|
1408
1806
|
agentId,
|
|
1409
|
-
days
|
|
1807
|
+
days,
|
|
1808
|
+
tokenSymbol,
|
|
1809
|
+
chainId
|
|
1410
1810
|
}) {
|
|
1811
|
+
await this.ensureAgentsInitialized();
|
|
1812
|
+
const agentOptions = { tokenSymbol, chainId };
|
|
1411
1813
|
if (agentId) {
|
|
1412
|
-
return this.getAgent(agentId).getAgentApy(days);
|
|
1814
|
+
return this.getAgent(agentId).getAgentApy(days, agentOptions);
|
|
1413
1815
|
}
|
|
1414
1816
|
const results = {};
|
|
1415
|
-
|
|
1416
|
-
|
|
1817
|
+
const agentEntries = [...this.agents.entries()];
|
|
1818
|
+
const apyResults = await Promise.all(
|
|
1819
|
+
agentEntries.map(async ([id, agent]) => {
|
|
1820
|
+
const apy = await agent.getAgentApy(days, agentOptions);
|
|
1821
|
+
return [id, apy];
|
|
1822
|
+
})
|
|
1823
|
+
);
|
|
1824
|
+
for (const [id, apy] of apyResults) {
|
|
1825
|
+
results[id] = apy;
|
|
1417
1826
|
}
|
|
1418
1827
|
return {
|
|
1419
1828
|
agentApy: results
|
|
@@ -1429,10 +1838,29 @@ var OwneySDK = class {
|
|
|
1429
1838
|
const activeAgents = this.getActiveAgents();
|
|
1430
1839
|
const balances = {};
|
|
1431
1840
|
let totalBalance = 0;
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1841
|
+
const entries = [...activeAgents.entries()];
|
|
1842
|
+
const balanceResults = await Promise.allSettled(
|
|
1843
|
+
entries.map(async ([id, agent]) => {
|
|
1844
|
+
const b = await agent.getBalances(state, chainId);
|
|
1845
|
+
return [id, Number(b.totalBalance)];
|
|
1846
|
+
})
|
|
1847
|
+
);
|
|
1848
|
+
let successCount = 0;
|
|
1849
|
+
for (const settledResult of balanceResults) {
|
|
1850
|
+
if (settledResult.status === "fulfilled") {
|
|
1851
|
+
const [id, bal] = settledResult.value;
|
|
1852
|
+
balances[id] = bal;
|
|
1853
|
+
totalBalance += bal;
|
|
1854
|
+
successCount += 1;
|
|
1855
|
+
} else {
|
|
1856
|
+
console.error("getAgentAllocation agent failed:", settledResult.reason);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
if (successCount === 0) {
|
|
1860
|
+
throw new OwneyError(
|
|
1861
|
+
"ALLOCATION_ALL_FAILED",
|
|
1862
|
+
"Failed to fetch allocation: all agents failed."
|
|
1863
|
+
);
|
|
1436
1864
|
}
|
|
1437
1865
|
const allocation = {};
|
|
1438
1866
|
if (totalBalance === 0) {
|
|
@@ -1461,5 +1889,6 @@ var OwneySDK = class {
|
|
|
1461
1889
|
AgentChainIncompatibleError,
|
|
1462
1890
|
AgentNotFoundError,
|
|
1463
1891
|
NotConnectedError,
|
|
1892
|
+
OwneyError,
|
|
1464
1893
|
OwneySDK
|
|
1465
1894
|
});
|