@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.js
CHANGED
|
@@ -8,12 +8,76 @@ 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
|
+
];
|
|
28
|
+
|
|
29
|
+
// src/errors.ts
|
|
30
|
+
var OwneyError = class extends Error {
|
|
31
|
+
code;
|
|
32
|
+
details;
|
|
33
|
+
constructor(code, message, details) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = "OwneyError";
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.details = details;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var AgentNotFoundError = class extends OwneyError {
|
|
41
|
+
constructor(agentId, available) {
|
|
42
|
+
super(
|
|
43
|
+
"AGENT_NOT_FOUND",
|
|
44
|
+
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
45
|
+
{ agentId, available }
|
|
46
|
+
);
|
|
47
|
+
this.name = "AgentNotFoundError";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var NotConnectedError = class extends OwneyError {
|
|
51
|
+
constructor() {
|
|
52
|
+
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
53
|
+
this.name = "NotConnectedError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var AgentChainIncompatibleError = class extends OwneyError {
|
|
57
|
+
incompatibleAgents;
|
|
58
|
+
connectedChainId;
|
|
59
|
+
constructor(incompatibleAgents, connectedChainId) {
|
|
60
|
+
const details = incompatibleAgents.map(
|
|
61
|
+
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
62
|
+
).join("; ");
|
|
63
|
+
super(
|
|
64
|
+
"AGENT_CHAIN_INCOMPATIBLE",
|
|
65
|
+
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
66
|
+
{ incompatibleAgents, connectedChainId }
|
|
67
|
+
);
|
|
68
|
+
this.name = "AgentChainIncompatibleError";
|
|
69
|
+
this.incompatibleAgents = incompatibleAgents;
|
|
70
|
+
this.connectedChainId = connectedChainId;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
11
73
|
|
|
12
74
|
// src/lib/utils.ts
|
|
13
75
|
var isValidChainId = (chainId) => {
|
|
14
76
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
|
|
15
|
-
throw new
|
|
16
|
-
|
|
77
|
+
throw new OwneyError(
|
|
78
|
+
"CHAIN_UNSUPPORTED",
|
|
79
|
+
`Unsupported chain ID: ${chainId}. Supported chains: ${SUPPORTED_CHAIN_IDS.join(", ")}`,
|
|
80
|
+
{ chainId, supportedChainIds: [...SUPPORTED_CHAIN_IDS] }
|
|
17
81
|
);
|
|
18
82
|
}
|
|
19
83
|
return chainId;
|
|
@@ -28,6 +92,15 @@ function hexToDecimal(hex, decimals = 6) {
|
|
|
28
92
|
const remainder = parsed % divisor;
|
|
29
93
|
return Number(whole) + Number(remainder) / Number(divisor);
|
|
30
94
|
}
|
|
95
|
+
function normalizeDecimalPlace(value, decimals = 6) {
|
|
96
|
+
const parsed = BigInt(value);
|
|
97
|
+
if (decimals <= 0)
|
|
98
|
+
return Number(parsed);
|
|
99
|
+
const divisor = BigInt(10 ** decimals);
|
|
100
|
+
const whole = parsed / divisor;
|
|
101
|
+
const remainder = parsed % divisor;
|
|
102
|
+
return Number(whole) + Number(remainder) / Number(divisor);
|
|
103
|
+
}
|
|
31
104
|
var DayFilterMapping = {
|
|
32
105
|
"30D": 30,
|
|
33
106
|
"14D": 14,
|
|
@@ -57,7 +130,7 @@ var CHAIN_NAME_TO_ID = {
|
|
|
57
130
|
BASE: 8453,
|
|
58
131
|
ARBITRUM: 42161
|
|
59
132
|
};
|
|
60
|
-
function mapBalances(raw, chainId) {
|
|
133
|
+
function mapBalances(raw, chainId, smartWallet) {
|
|
61
134
|
const portfolio = raw.portfolio;
|
|
62
135
|
const tokenMap = /* @__PURE__ */ new Map();
|
|
63
136
|
for (const p of portfolio.positions || []) {
|
|
@@ -68,18 +141,7 @@ function mapBalances(raw, chainId) {
|
|
|
68
141
|
tokenMap.set(key, {
|
|
69
142
|
symbol,
|
|
70
143
|
chainId: positionChainId,
|
|
71
|
-
amount: (current?.amount ?? 0) +
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
for (const s of portfolio.staleBalances || []) {
|
|
75
|
-
const symbol = s.tokenSymbol || "USDC";
|
|
76
|
-
const staleChainId = s.chainId ?? chainId;
|
|
77
|
-
const key = `${symbol}:${staleChainId}`;
|
|
78
|
-
const current = tokenMap.get(key);
|
|
79
|
-
tokenMap.set(key, {
|
|
80
|
-
symbol,
|
|
81
|
-
chainId: staleChainId,
|
|
82
|
-
amount: (current?.amount ?? 0) + hexToDecimal(s.balance || "0")
|
|
144
|
+
amount: (current?.amount ?? 0) + normalizeDecimalPlace(p.underlyingAmount || "0")
|
|
83
145
|
});
|
|
84
146
|
}
|
|
85
147
|
let totalBalance = 0;
|
|
@@ -94,7 +156,7 @@ function mapBalances(raw, chainId) {
|
|
|
94
156
|
});
|
|
95
157
|
}
|
|
96
158
|
return {
|
|
97
|
-
smartWallet
|
|
159
|
+
smartWallet,
|
|
98
160
|
totalBalance: String(totalBalance),
|
|
99
161
|
tokens
|
|
100
162
|
};
|
|
@@ -185,13 +247,41 @@ function mapUserProfile(raw, userAddress) {
|
|
|
185
247
|
};
|
|
186
248
|
}
|
|
187
249
|
function mapApyByStrategy(raw) {
|
|
188
|
-
const
|
|
250
|
+
const apyPerAsset = {};
|
|
251
|
+
let apySum = 0;
|
|
252
|
+
let apyCount = 0;
|
|
253
|
+
for (const entry of raw.data) {
|
|
254
|
+
const supported = SupportedAssets.find(
|
|
255
|
+
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
256
|
+
);
|
|
257
|
+
if (!supported)
|
|
258
|
+
continue;
|
|
259
|
+
const chainKey = supported.chainId;
|
|
260
|
+
const symbolKey = supported.symbol;
|
|
261
|
+
const bucket = apyPerAsset[chainKey] ?? {};
|
|
262
|
+
bucket[symbolKey] = entry.average_apy;
|
|
263
|
+
apyPerAsset[chainKey] = bucket;
|
|
264
|
+
apySum += entry.average_apy;
|
|
265
|
+
apyCount += 1;
|
|
266
|
+
}
|
|
267
|
+
const averageApy = apyCount > 0 ? apySum / apyCount : 0;
|
|
268
|
+
const hasDetails = Object.keys(apyPerAsset).length > 0;
|
|
189
269
|
return {
|
|
190
|
-
averageApy
|
|
270
|
+
averageApy,
|
|
271
|
+
detailedApys: hasDetails ? { apyPerAsset } : void 0
|
|
191
272
|
};
|
|
192
273
|
}
|
|
193
274
|
|
|
194
275
|
// src/agents/zyfai/zyfai.agent.ts
|
|
276
|
+
var ZYFAI_TIMING_PREFIX = "[AgentTiming][zyfai]";
|
|
277
|
+
function logTiming(label, startedAt, meta) {
|
|
278
|
+
const elapsedMs = Date.now() - startedAt;
|
|
279
|
+
if (meta) {
|
|
280
|
+
console.log(`${ZYFAI_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
console.log(`${ZYFAI_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
|
|
284
|
+
}
|
|
195
285
|
var ZyfaiAgent = class {
|
|
196
286
|
id = "zyfai";
|
|
197
287
|
supportedChainIds = [8453, 42161];
|
|
@@ -210,62 +300,122 @@ var ZyfaiAgent = class {
|
|
|
210
300
|
sdk;
|
|
211
301
|
connectedAddress = null;
|
|
212
302
|
connectedChainId = null;
|
|
303
|
+
smartWalletCache = /* @__PURE__ */ new Map();
|
|
304
|
+
connectAuthPromise = null;
|
|
213
305
|
constructor(apiKey) {
|
|
214
306
|
this.sdk = new ZyfaiSDK({ apiKey });
|
|
215
307
|
}
|
|
216
308
|
// --- Private helpers ---
|
|
217
309
|
getAddress() {
|
|
218
310
|
if (!this.connectedAddress) {
|
|
219
|
-
throw new
|
|
311
|
+
throw new OwneyError(
|
|
312
|
+
"NOT_CONNECTED",
|
|
313
|
+
"Wallet not connected. Call sdk.connect() first."
|
|
314
|
+
);
|
|
220
315
|
}
|
|
221
316
|
return this.connectedAddress;
|
|
222
317
|
}
|
|
223
318
|
/** Light connect: SIWE auth only. Re-auths if chain changed. */
|
|
224
319
|
async connectAuth(state, chainId) {
|
|
320
|
+
const startedAt = Date.now();
|
|
225
321
|
const validChainId = isValidChainId(chainId);
|
|
226
|
-
if (
|
|
322
|
+
if (this.connectedAddress && this.connectedChainId === validChainId) {
|
|
323
|
+
logTiming("connectAuth cache-hit", startedAt, { chainId: validChainId });
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (this.connectAuthPromise) {
|
|
327
|
+
await this.connectAuthPromise;
|
|
328
|
+
logTiming("connectAuth awaited-inflight", startedAt, {
|
|
329
|
+
chainId: validChainId
|
|
330
|
+
});
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
this.connectAuthPromise = (async () => {
|
|
227
334
|
const address = await this.sdk.connectAccount(
|
|
228
335
|
state.provider,
|
|
229
336
|
validChainId
|
|
230
337
|
);
|
|
231
338
|
this.connectedAddress = address;
|
|
232
339
|
this.connectedChainId = validChainId;
|
|
340
|
+
})();
|
|
341
|
+
try {
|
|
342
|
+
await this.connectAuthPromise;
|
|
343
|
+
logTiming("connectAuth success", startedAt, { chainId: validChainId });
|
|
344
|
+
} finally {
|
|
345
|
+
this.connectAuthPromise = null;
|
|
233
346
|
}
|
|
234
347
|
}
|
|
235
348
|
/** Full connect: SIWE auth + Safe deploy check + session key. */
|
|
236
349
|
async connectFull(state, chainId) {
|
|
350
|
+
const startedAt = Date.now();
|
|
237
351
|
await this.connectAuth(state, chainId);
|
|
238
352
|
const validChainId = isValidChainId(chainId);
|
|
239
353
|
const address = this.getAddress();
|
|
354
|
+
const walletLookupStart = Date.now();
|
|
240
355
|
const wallet = await this.sdk.getSmartWalletAddress(address, validChainId);
|
|
356
|
+
logTiming("connectFull getSmartWalletAddress", walletLookupStart, {
|
|
357
|
+
chainId: validChainId
|
|
358
|
+
});
|
|
359
|
+
this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
|
|
241
360
|
if (!wallet.isDeployed) {
|
|
361
|
+
const deployStart = Date.now();
|
|
242
362
|
await this.sdk.deploySafe(address, validChainId, "aggressive", true);
|
|
363
|
+
logTiming("connectFull deploySafe", deployStart, {
|
|
364
|
+
chainId: validChainId
|
|
365
|
+
});
|
|
243
366
|
}
|
|
367
|
+
const userDetailsStart = Date.now();
|
|
244
368
|
const user = await this.sdk.getUserDetails();
|
|
369
|
+
logTiming("connectFull getUserDetails", userDetailsStart, {
|
|
370
|
+
chainId: validChainId
|
|
371
|
+
});
|
|
245
372
|
if (!user.hasActiveSessionKey) {
|
|
373
|
+
const sessionKeyStart = Date.now();
|
|
246
374
|
await this.sdk.createSessionKey(address, validChainId);
|
|
375
|
+
logTiming("connectFull createSessionKey", sessionKeyStart, {
|
|
376
|
+
chainId: validChainId
|
|
377
|
+
});
|
|
247
378
|
}
|
|
379
|
+
logTiming("connectFull success", startedAt, { chainId: validChainId });
|
|
248
380
|
}
|
|
249
381
|
/** Connect + resolve smart wallet address for read operations. */
|
|
250
382
|
async resolveSmartWallet(state, chainId) {
|
|
383
|
+
const startedAt = Date.now();
|
|
251
384
|
await this.connectAuth(state, chainId);
|
|
252
385
|
const validChainId = isValidChainId(chainId);
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
)
|
|
257
|
-
|
|
386
|
+
const ownerAddress = this.getAddress();
|
|
387
|
+
const cacheKey = `${ownerAddress}_${validChainId}`;
|
|
388
|
+
let smartWallet = this.smartWalletCache.get(cacheKey);
|
|
389
|
+
if (!smartWallet) {
|
|
390
|
+
const lookupStart = Date.now();
|
|
391
|
+
const { address } = await this.sdk.getSmartWalletAddress(
|
|
392
|
+
ownerAddress,
|
|
393
|
+
validChainId
|
|
394
|
+
);
|
|
395
|
+
logTiming("resolveSmartWallet remote-lookup", lookupStart, {
|
|
396
|
+
chainId: validChainId
|
|
397
|
+
});
|
|
398
|
+
smartWallet = address;
|
|
399
|
+
this.smartWalletCache.set(cacheKey, smartWallet);
|
|
400
|
+
} else {
|
|
401
|
+
logTiming("resolveSmartWallet cache-hit", startedAt, {
|
|
402
|
+
chainId: validChainId
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
logTiming("resolveSmartWallet ready", startedAt, { chainId: validChainId });
|
|
406
|
+
return { smartWallet, chainId: validChainId };
|
|
258
407
|
}
|
|
259
408
|
// --- IAgent: Connection lifecycle ---
|
|
260
409
|
async disconnect() {
|
|
261
410
|
await this.sdk.disconnectAccount();
|
|
262
411
|
this.connectedAddress = null;
|
|
263
412
|
this.connectedChainId = null;
|
|
413
|
+
this.smartWalletCache.clear();
|
|
264
414
|
}
|
|
265
415
|
// --- IAgent: Account lifecycle ---
|
|
266
416
|
async activateAgent(state, chainId) {
|
|
267
417
|
try {
|
|
268
|
-
await this.
|
|
418
|
+
await this.connectAuth(state, chainId);
|
|
269
419
|
} catch (error) {
|
|
270
420
|
await this.disconnect();
|
|
271
421
|
throw error;
|
|
@@ -312,10 +462,15 @@ var ZyfaiAgent = class {
|
|
|
312
462
|
}
|
|
313
463
|
// --- IAgent: Portfolio reads ---
|
|
314
464
|
async getBalances(state, chainId) {
|
|
315
|
-
|
|
316
|
-
const validChainId =
|
|
317
|
-
const
|
|
318
|
-
|
|
465
|
+
const startedAt = Date.now();
|
|
466
|
+
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
467
|
+
const portfolioStart = Date.now();
|
|
468
|
+
const raw = await this.sdk.getPortfolio(this.getAddress());
|
|
469
|
+
logTiming("getBalances getPortfolio", portfolioStart, {
|
|
470
|
+
chainId: validChainId
|
|
471
|
+
});
|
|
472
|
+
logTiming("getBalances success", startedAt, { chainId: validChainId });
|
|
473
|
+
return mapBalances(raw, validChainId, smartWallet);
|
|
319
474
|
}
|
|
320
475
|
async getEarnings(state, chainId) {
|
|
321
476
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -349,11 +504,13 @@ var ZyfaiAgent = class {
|
|
|
349
504
|
return mapUserProfile(raw, this.connectedAddress);
|
|
350
505
|
}
|
|
351
506
|
// --- IAgent: Discovery (no wallet required) ---
|
|
352
|
-
async getAgentApy(days) {
|
|
507
|
+
async getAgentApy(days, options) {
|
|
353
508
|
const raw = await this.sdk.getAPYPerStrategy(
|
|
354
509
|
false,
|
|
355
510
|
DayFilterMapping[days],
|
|
356
|
-
"aggressive"
|
|
511
|
+
"aggressive",
|
|
512
|
+
options?.chainId,
|
|
513
|
+
options?.tokenSymbol
|
|
357
514
|
);
|
|
358
515
|
return mapApyByStrategy(raw);
|
|
359
516
|
}
|
|
@@ -399,9 +556,11 @@ function mapEarnings2(raw, smartWallet) {
|
|
|
399
556
|
};
|
|
400
557
|
}
|
|
401
558
|
function mapApyHistory2(raw, walletAddress) {
|
|
559
|
+
const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
|
|
402
560
|
return {
|
|
403
561
|
walletAddress,
|
|
404
|
-
weightedApyAfterFee: raw.result
|
|
562
|
+
weightedApyAfterFee: raw.result.apy,
|
|
563
|
+
weightedApyAfterFeeDetails: apyPerAsset ? { apyPerAsset } : void 0
|
|
405
564
|
};
|
|
406
565
|
}
|
|
407
566
|
function mapSailTransaction(tx) {
|
|
@@ -535,15 +694,45 @@ function mapUserProfile2(raw) {
|
|
|
535
694
|
};
|
|
536
695
|
}
|
|
537
696
|
function mapApyByStrategy2(raw) {
|
|
697
|
+
const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
|
|
538
698
|
return {
|
|
539
|
-
averageApy: raw.result
|
|
699
|
+
averageApy: raw.result.apy,
|
|
700
|
+
detailedApys: apyPerAsset ? { apyPerAsset } : void 0
|
|
540
701
|
};
|
|
541
702
|
}
|
|
703
|
+
function remapApyPerAssetToSymbols(raw) {
|
|
704
|
+
if (!raw)
|
|
705
|
+
return void 0;
|
|
706
|
+
const remapped = {};
|
|
707
|
+
for (const asset of SupportedAssets) {
|
|
708
|
+
const chainKey = asset.chainId;
|
|
709
|
+
const apyByAddress = raw[String(chainKey)];
|
|
710
|
+
if (!apyByAddress)
|
|
711
|
+
continue;
|
|
712
|
+
const apy = apyByAddress[asset.address] ?? apyByAddress[asset.address.toLowerCase()];
|
|
713
|
+
if (apy === void 0)
|
|
714
|
+
continue;
|
|
715
|
+
const bucket = remapped[chainKey] ?? {};
|
|
716
|
+
bucket[asset.symbol] = apy;
|
|
717
|
+
remapped[chainKey] = bucket;
|
|
718
|
+
}
|
|
719
|
+
return Object.keys(remapped).length > 0 ? remapped : void 0;
|
|
720
|
+
}
|
|
542
721
|
|
|
543
722
|
// src/agents/sail/sail.agent.ts
|
|
544
723
|
var SAIL_API_BASE = "https://app.sail.money";
|
|
545
724
|
var PROJECT_ID = "sail";
|
|
546
725
|
var PAGE_ID = "home";
|
|
726
|
+
var SAIL_TIMING_PREFIX = "[AgentTiming][sail]";
|
|
727
|
+
var SAIL_PORTFOLIO_TIMEOUT_MS = 8e3;
|
|
728
|
+
function logTiming2(label, startedAt, meta) {
|
|
729
|
+
const elapsedMs = Date.now() - startedAt;
|
|
730
|
+
if (meta) {
|
|
731
|
+
console.log(`${SAIL_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
console.log(`${SAIL_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
|
|
735
|
+
}
|
|
547
736
|
var SailAgent = class {
|
|
548
737
|
id = "sail";
|
|
549
738
|
supportedChainIds = [8453, 42161];
|
|
@@ -565,11 +754,12 @@ var SailAgent = class {
|
|
|
565
754
|
connectedAddress = null;
|
|
566
755
|
smartWalletAddress = null;
|
|
567
756
|
connectedChainId = null;
|
|
757
|
+
connectPromise = null;
|
|
568
758
|
constructor(apiKey) {
|
|
569
759
|
this.apiKey = apiKey;
|
|
570
760
|
}
|
|
571
761
|
// --- Private helpers ---
|
|
572
|
-
async request(method, path, body) {
|
|
762
|
+
async request(method, path, body, timeoutMs) {
|
|
573
763
|
const headers = {
|
|
574
764
|
"Content-Type": "application/json",
|
|
575
765
|
"X-Business-API-Key": this.apiKey
|
|
@@ -580,32 +770,57 @@ var SailAgent = class {
|
|
|
580
770
|
if (this.accountToken) {
|
|
581
771
|
headers["X-Account-Token"] = this.accountToken;
|
|
582
772
|
}
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
773
|
+
const controller = new AbortController();
|
|
774
|
+
const timeoutId = typeof timeoutMs === "number" && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
775
|
+
let res;
|
|
776
|
+
try {
|
|
777
|
+
res = await fetch(`${SAIL_API_BASE}${path}`, {
|
|
778
|
+
method,
|
|
779
|
+
headers,
|
|
780
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
781
|
+
signal: controller.signal
|
|
782
|
+
});
|
|
783
|
+
} catch (error) {
|
|
784
|
+
if (error instanceof Error && error.name === "AbortError" && timeoutMs) {
|
|
785
|
+
throw new OwneyError(
|
|
786
|
+
"API_SAIL_TIMEOUT",
|
|
787
|
+
`Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
|
|
788
|
+
{ timeoutMs, method, path }
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
throw error;
|
|
792
|
+
} finally {
|
|
793
|
+
if (timeoutId)
|
|
794
|
+
clearTimeout(timeoutId);
|
|
795
|
+
}
|
|
588
796
|
if (!res.ok) {
|
|
589
797
|
const text = await res.text().catch(() => "");
|
|
590
|
-
throw new
|
|
798
|
+
throw new OwneyError(
|
|
799
|
+
"API_SAIL_ERROR",
|
|
800
|
+
`Sail API error ${res.status}: ${text}`,
|
|
801
|
+
{ statusCode: res.status, method, path, responseBody: text }
|
|
802
|
+
);
|
|
591
803
|
}
|
|
592
804
|
return res.json();
|
|
593
805
|
}
|
|
594
806
|
/** Initiate SIWE and sign with the user's wallet. */
|
|
595
807
|
async signSiwe(state, chainId) {
|
|
808
|
+
const startedAt = Date.now();
|
|
596
809
|
const initiate = await this.request(
|
|
597
810
|
"POST",
|
|
598
811
|
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/initiate`,
|
|
599
812
|
{
|
|
600
813
|
method: "siwe",
|
|
601
814
|
address: state.walletAddress,
|
|
602
|
-
chainId
|
|
815
|
+
chainId,
|
|
816
|
+
identityMode: "byWallet"
|
|
603
817
|
}
|
|
604
818
|
);
|
|
605
819
|
const signature = await state.provider.request({
|
|
606
820
|
method: "personal_sign",
|
|
607
821
|
params: [initiate.message, state.walletAddress]
|
|
608
822
|
});
|
|
823
|
+
logTiming2("signSiwe success", startedAt, { chainId });
|
|
609
824
|
return {
|
|
610
825
|
sessionId: initiate.sessionId,
|
|
611
826
|
message: initiate.message,
|
|
@@ -614,38 +829,62 @@ var SailAgent = class {
|
|
|
614
829
|
}
|
|
615
830
|
/** Full connect: auth handles smart account creation via SIWE flow. */
|
|
616
831
|
async connectFull(state, chainId) {
|
|
832
|
+
const startedAt = Date.now();
|
|
617
833
|
if (this.connectedAddress && this.connectedChainId === chainId) {
|
|
834
|
+
logTiming2("connectFull cache-hit", startedAt, { chainId });
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
if (this.connectPromise) {
|
|
838
|
+
await this.connectPromise;
|
|
839
|
+
logTiming2("connectFull awaited-inflight", startedAt, { chainId });
|
|
618
840
|
return;
|
|
619
841
|
}
|
|
620
842
|
if (!state.walletAddress) {
|
|
621
|
-
throw new
|
|
843
|
+
throw new OwneyError(
|
|
844
|
+
"WALLET_ADDRESS_REQUIRED",
|
|
845
|
+
"Wallet address is required. Call sdk.connect(provider) first."
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
this.connectPromise = (async () => {
|
|
849
|
+
const { sessionId, signature, message } = await this.signSiwe(
|
|
850
|
+
state,
|
|
851
|
+
chainId
|
|
852
|
+
);
|
|
853
|
+
const complete = await this.request(
|
|
854
|
+
"POST",
|
|
855
|
+
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/complete`,
|
|
856
|
+
{
|
|
857
|
+
method: "custom",
|
|
858
|
+
sessionId,
|
|
859
|
+
signature,
|
|
860
|
+
walletAddress: state.walletAddress,
|
|
861
|
+
identityMode: "byWallet"
|
|
862
|
+
}
|
|
863
|
+
);
|
|
864
|
+
this.authToken = complete.token;
|
|
865
|
+
this.accountToken = complete.accountToken;
|
|
866
|
+
this.connectedAddress = state.walletAddress;
|
|
867
|
+
this.smartWalletAddress = complete.walletAddress;
|
|
868
|
+
this.connectedChainId = chainId;
|
|
869
|
+
})();
|
|
870
|
+
try {
|
|
871
|
+
await this.connectPromise;
|
|
872
|
+
logTiming2("connectFull success", startedAt, { chainId });
|
|
873
|
+
} finally {
|
|
874
|
+
this.connectPromise = null;
|
|
622
875
|
}
|
|
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;
|
|
642
876
|
}
|
|
643
877
|
async resolveSmartWallet(state, chainId) {
|
|
878
|
+
const startedAt = Date.now();
|
|
644
879
|
await this.connectFull(state, chainId);
|
|
645
880
|
const validChainId = isValidChainId(chainId);
|
|
646
881
|
if (!this.smartWalletAddress) {
|
|
647
|
-
throw new
|
|
882
|
+
throw new OwneyError(
|
|
883
|
+
"NOT_CONNECTED",
|
|
884
|
+
"Wallet not connected. Call sdk.connect() first."
|
|
885
|
+
);
|
|
648
886
|
}
|
|
887
|
+
logTiming2("resolveSmartWallet ready", startedAt, { chainId: validChainId });
|
|
649
888
|
return {
|
|
650
889
|
smartWallet: this.smartWalletAddress,
|
|
651
890
|
chainId: validChainId
|
|
@@ -670,7 +909,8 @@ var SailAgent = class {
|
|
|
670
909
|
}
|
|
671
910
|
async deposit(state, chainId, amount, depositCallback) {
|
|
672
911
|
if (!depositCallback) {
|
|
673
|
-
throw new
|
|
912
|
+
throw new OwneyError(
|
|
913
|
+
"DEPOSIT_CALLBACK_REQUIRED",
|
|
674
914
|
"depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
|
|
675
915
|
);
|
|
676
916
|
}
|
|
@@ -679,12 +919,16 @@ var SailAgent = class {
|
|
|
679
919
|
const depositInfo = await this.getDepositInfo();
|
|
680
920
|
const resolvedTokenAddress = depositInfo.permittedTokens[0]?.address;
|
|
681
921
|
if (!resolvedTokenAddress) {
|
|
682
|
-
throw new
|
|
922
|
+
throw new OwneyError(
|
|
923
|
+
"DEPOSIT_NO_PERMITTED_TOKENS",
|
|
924
|
+
"No permitted tokens available for deposit."
|
|
925
|
+
);
|
|
683
926
|
}
|
|
684
927
|
await this.executePreHooks(resolvedTokenAddress, amount, chainId);
|
|
685
928
|
const txHash = await depositCallback(smartWallet, chainId, amount);
|
|
686
929
|
if (!txHash) {
|
|
687
|
-
throw new
|
|
930
|
+
throw new OwneyError(
|
|
931
|
+
"DEPOSIT_CALLBACK_INVALID",
|
|
688
932
|
"depositCallback must return a transaction hash (txHash) for Sail deposits."
|
|
689
933
|
);
|
|
690
934
|
}
|
|
@@ -738,7 +982,10 @@ var SailAgent = class {
|
|
|
738
982
|
const withdrawInfo = await this.getWithdrawInfo();
|
|
739
983
|
const tokenAddress = withdrawInfo.permittedTokens[0]?.address;
|
|
740
984
|
if (!tokenAddress) {
|
|
741
|
-
throw new
|
|
985
|
+
throw new OwneyError(
|
|
986
|
+
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
987
|
+
"No permitted tokens available for withdrawal."
|
|
988
|
+
);
|
|
742
989
|
}
|
|
743
990
|
const resolvedAmount = amount ?? withdrawInfo.currentBalance;
|
|
744
991
|
const { signature: adminWalletSignature, message: adminWalletPayload } = await this.signSiwe(state, chainId);
|
|
@@ -762,9 +1009,30 @@ var SailAgent = class {
|
|
|
762
1009
|
}
|
|
763
1010
|
// --- IAgent: Portfolio reads ---
|
|
764
1011
|
async getBalances(state, chainId) {
|
|
1012
|
+
const startedAt = Date.now();
|
|
765
1013
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
766
|
-
const
|
|
767
|
-
|
|
1014
|
+
const portfolioStart = Date.now();
|
|
1015
|
+
try {
|
|
1016
|
+
const raw = await this.getMetricsPortfolio(state, chainId);
|
|
1017
|
+
logTiming2("getBalances getMetricsPortfolio", portfolioStart, { chainId });
|
|
1018
|
+
logTiming2("getBalances success", startedAt, { chainId });
|
|
1019
|
+
return mapPortfolioBalances(raw, smartWallet);
|
|
1020
|
+
} catch (error) {
|
|
1021
|
+
if (error instanceof OwneyError && error.code === "API_SAIL_TIMEOUT") {
|
|
1022
|
+
console.warn(
|
|
1023
|
+
"Sail portfolio request timed out; using empty snapshot.",
|
|
1024
|
+
{
|
|
1025
|
+
chainId
|
|
1026
|
+
}
|
|
1027
|
+
);
|
|
1028
|
+
logTiming2("getBalances timeout-fallback", startedAt, { chainId });
|
|
1029
|
+
return mapPortfolioBalances(
|
|
1030
|
+
{ message: "timeout", result: [] },
|
|
1031
|
+
smartWallet
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
throw error;
|
|
1035
|
+
}
|
|
768
1036
|
}
|
|
769
1037
|
async getEarnings(state, chainId) {
|
|
770
1038
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -785,7 +1053,9 @@ var SailAgent = class {
|
|
|
785
1053
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
786
1054
|
return this.request(
|
|
787
1055
|
"GET",
|
|
788
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}
|
|
1056
|
+
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}`,
|
|
1057
|
+
void 0,
|
|
1058
|
+
SAIL_PORTFOLIO_TIMEOUT_MS
|
|
789
1059
|
);
|
|
790
1060
|
}
|
|
791
1061
|
async getMetricsEarnings(state, chainId) {
|
|
@@ -832,8 +1102,10 @@ var SailAgent = class {
|
|
|
832
1102
|
}
|
|
833
1103
|
const dayCount = DayFilterMapping[days];
|
|
834
1104
|
if (dayCount === void 0) {
|
|
835
|
-
throw new
|
|
836
|
-
|
|
1105
|
+
throw new OwneyError(
|
|
1106
|
+
"VALIDATION_INVALID_DAYS",
|
|
1107
|
+
`Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
|
|
1108
|
+
{ days }
|
|
837
1109
|
);
|
|
838
1110
|
}
|
|
839
1111
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -904,43 +1176,12 @@ var SailAgent = class {
|
|
|
904
1176
|
return mapUserProfile2(raw);
|
|
905
1177
|
}
|
|
906
1178
|
// --- IAgent: Discovery (no wallet required) ---
|
|
907
|
-
async getAgentApy(days) {
|
|
1179
|
+
async getAgentApy(days, _options) {
|
|
908
1180
|
const raw = await this.getMetricsYield(days);
|
|
909
1181
|
return mapApyByStrategy2(raw);
|
|
910
1182
|
}
|
|
911
1183
|
};
|
|
912
1184
|
|
|
913
|
-
// src/errors.ts
|
|
914
|
-
var AgentNotFoundError = class extends Error {
|
|
915
|
-
constructor(agentId, available) {
|
|
916
|
-
super(
|
|
917
|
-
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`
|
|
918
|
-
);
|
|
919
|
-
this.name = "AgentNotFoundError";
|
|
920
|
-
}
|
|
921
|
-
};
|
|
922
|
-
var NotConnectedError = class extends Error {
|
|
923
|
-
constructor() {
|
|
924
|
-
super("Not connected. Call sdk.connect(provider) first.");
|
|
925
|
-
this.name = "NotConnectedError";
|
|
926
|
-
}
|
|
927
|
-
};
|
|
928
|
-
var AgentChainIncompatibleError = class extends Error {
|
|
929
|
-
incompatibleAgents;
|
|
930
|
-
connectedChainId;
|
|
931
|
-
constructor(incompatibleAgents, connectedChainId) {
|
|
932
|
-
const details = incompatibleAgents.map(
|
|
933
|
-
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
934
|
-
).join("; ");
|
|
935
|
-
super(
|
|
936
|
-
`Chain ${connectedChainId} is not supported by the following agents: ${details}`
|
|
937
|
-
);
|
|
938
|
-
this.name = "AgentChainIncompatibleError";
|
|
939
|
-
this.incompatibleAgents = incompatibleAgents;
|
|
940
|
-
this.connectedChainId = connectedChainId;
|
|
941
|
-
}
|
|
942
|
-
};
|
|
943
|
-
|
|
944
1185
|
// src/lib/routing-api.ts
|
|
945
1186
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
946
1187
|
async function fetchAgentKeys(apiKey) {
|
|
@@ -954,11 +1195,19 @@ async function fetchAgentKeys(apiKey) {
|
|
|
954
1195
|
});
|
|
955
1196
|
if (!res.ok) {
|
|
956
1197
|
const text = await res.text().catch(() => "");
|
|
957
|
-
throw new
|
|
1198
|
+
throw new OwneyError(
|
|
1199
|
+
"API_ROUTING_ERROR",
|
|
1200
|
+
`Routing API error ${res.status}: ${text}`,
|
|
1201
|
+
{ statusCode: res.status, responseBody: text }
|
|
1202
|
+
);
|
|
958
1203
|
}
|
|
959
1204
|
const json = await res.json();
|
|
960
1205
|
if (!json.success) {
|
|
961
|
-
throw new
|
|
1206
|
+
throw new OwneyError(
|
|
1207
|
+
"API_ROUTING_FAILED",
|
|
1208
|
+
`Routing API request failed: ${json.message}`,
|
|
1209
|
+
{ message: json.message }
|
|
1210
|
+
);
|
|
962
1211
|
}
|
|
963
1212
|
return json.data;
|
|
964
1213
|
}
|
|
@@ -968,11 +1217,21 @@ var AGENT_CONSTRUCTORS = {
|
|
|
968
1217
|
zyfai: ZyfaiAgent,
|
|
969
1218
|
sail: SailAgent
|
|
970
1219
|
};
|
|
1220
|
+
var SDK_TIMING_PREFIX = "[OwneySDKTiming]";
|
|
1221
|
+
function logTiming3(label, startedAt, meta) {
|
|
1222
|
+
const elapsedMs = Date.now() - startedAt;
|
|
1223
|
+
if (meta) {
|
|
1224
|
+
console.log(`${SDK_TIMING_PREFIX} ${label} ${elapsedMs}ms`, meta);
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
console.log(`${SDK_TIMING_PREFIX} ${label} ${elapsedMs}ms`);
|
|
1228
|
+
}
|
|
971
1229
|
var OwneySDK = class {
|
|
972
1230
|
agents = /* @__PURE__ */ new Map();
|
|
973
1231
|
activeAgents = /* @__PURE__ */ new Set();
|
|
974
1232
|
state = null;
|
|
975
1233
|
apiKey;
|
|
1234
|
+
initializingAgentsPromise = null;
|
|
976
1235
|
constructor(config) {
|
|
977
1236
|
this.apiKey = config.apiKey;
|
|
978
1237
|
}
|
|
@@ -988,7 +1247,8 @@ var OwneySDK = class {
|
|
|
988
1247
|
});
|
|
989
1248
|
const walletAddress = accounts[0];
|
|
990
1249
|
if (!walletAddress) {
|
|
991
|
-
throw new
|
|
1250
|
+
throw new OwneyError(
|
|
1251
|
+
"WALLET_NO_ACCOUNTS",
|
|
992
1252
|
"No accounts found. Ensure the wallet is unlocked and connected."
|
|
993
1253
|
);
|
|
994
1254
|
}
|
|
@@ -1027,7 +1287,8 @@ var OwneySDK = class {
|
|
|
1027
1287
|
requireChainId() {
|
|
1028
1288
|
const chainId = this.state?.chainId ?? null;
|
|
1029
1289
|
if (chainId === null) {
|
|
1030
|
-
throw new
|
|
1290
|
+
throw new OwneyError(
|
|
1291
|
+
"NO_ACTIVE_CHAIN",
|
|
1031
1292
|
"No active chain. Call sdk.activateAgent(chainId) first."
|
|
1032
1293
|
);
|
|
1033
1294
|
}
|
|
@@ -1049,6 +1310,33 @@ var OwneySDK = class {
|
|
|
1049
1310
|
}
|
|
1050
1311
|
return active;
|
|
1051
1312
|
}
|
|
1313
|
+
async ensureAgentsInitialized() {
|
|
1314
|
+
if (this.agents.size > 0)
|
|
1315
|
+
return;
|
|
1316
|
+
if (!this.initializingAgentsPromise) {
|
|
1317
|
+
this.initializingAgentsPromise = (async () => {
|
|
1318
|
+
const agentKeys = await fetchAgentKeys(this.apiKey);
|
|
1319
|
+
for (const { key, agent_type } of agentKeys) {
|
|
1320
|
+
const AgentClass = AGENT_CONSTRUCTORS[agent_type];
|
|
1321
|
+
if (!AgentClass)
|
|
1322
|
+
continue;
|
|
1323
|
+
const agent = new AgentClass(key);
|
|
1324
|
+
this.agents.set(agent_type, agent);
|
|
1325
|
+
}
|
|
1326
|
+
if (this.agents.size === 0) {
|
|
1327
|
+
throw new OwneyError(
|
|
1328
|
+
"API_NO_AGENTS",
|
|
1329
|
+
"No supported agents returned by the routing API. Check your API key and routing configuration."
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
})();
|
|
1333
|
+
}
|
|
1334
|
+
try {
|
|
1335
|
+
await this.initializingAgentsPromise;
|
|
1336
|
+
} finally {
|
|
1337
|
+
this.initializingAgentsPromise = null;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1052
1340
|
// --- Account lifecycle ---
|
|
1053
1341
|
/**
|
|
1054
1342
|
* Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
|
|
@@ -1061,25 +1349,13 @@ var OwneySDK = class {
|
|
|
1061
1349
|
* throws before activating any agent.
|
|
1062
1350
|
*/
|
|
1063
1351
|
async activateAgent(chainId, agentId) {
|
|
1352
|
+
const startedAt = Date.now();
|
|
1064
1353
|
const state = this.requireState();
|
|
1065
|
-
|
|
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
|
-
}
|
|
1354
|
+
await this.ensureAgentsInitialized();
|
|
1080
1355
|
if (agentId !== void 0) {
|
|
1081
1356
|
if (agentId.length === 0) {
|
|
1082
|
-
throw new
|
|
1357
|
+
throw new OwneyError(
|
|
1358
|
+
"AGENT_EMPTY_LIST",
|
|
1083
1359
|
"agentId array must not be empty. Omit the argument to activate all compatible agents."
|
|
1084
1360
|
);
|
|
1085
1361
|
}
|
|
@@ -1094,30 +1370,60 @@ var OwneySDK = class {
|
|
|
1094
1370
|
if (incompatibleAgents.length > 0) {
|
|
1095
1371
|
throw new AgentChainIncompatibleError(incompatibleAgents, chainId);
|
|
1096
1372
|
}
|
|
1097
|
-
for (const agent of agents) {
|
|
1098
|
-
await agent.activateAgent(state, chainId);
|
|
1099
|
-
}
|
|
1100
1373
|
this.activeAgents.clear();
|
|
1101
1374
|
for (const id of uniqueIds) {
|
|
1102
1375
|
this.activeAgents.add(id);
|
|
1103
1376
|
}
|
|
1104
1377
|
state.chainId = chainId;
|
|
1378
|
+
Promise.all(
|
|
1379
|
+
agents.map(async (agent) => {
|
|
1380
|
+
const agentStart = Date.now();
|
|
1381
|
+
await agent.activateAgent(state, chainId);
|
|
1382
|
+
logTiming3("activateAgent background-agent-ready", agentStart, {
|
|
1383
|
+
chainId,
|
|
1384
|
+
agentId: agent.id
|
|
1385
|
+
});
|
|
1386
|
+
})
|
|
1387
|
+
).catch((error) => {
|
|
1388
|
+
console.error("activateAgent background init failed:", error);
|
|
1389
|
+
});
|
|
1390
|
+
logTiming3("activateAgent scheduled", startedAt, {
|
|
1391
|
+
chainId,
|
|
1392
|
+
agentIds: uniqueIds
|
|
1393
|
+
});
|
|
1105
1394
|
return;
|
|
1106
1395
|
}
|
|
1107
1396
|
const compatible = [...this.agents.values()].filter(
|
|
1108
1397
|
(agent) => agent.supportedChainIds.includes(chainId)
|
|
1109
1398
|
);
|
|
1110
1399
|
if (compatible.length === 0) {
|
|
1111
|
-
throw new
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1400
|
+
throw new OwneyError(
|
|
1401
|
+
"CHAIN_NO_COMPATIBLE_AGENTS",
|
|
1402
|
+
`No agents support chain ${chainId}.`,
|
|
1403
|
+
{ chainId }
|
|
1404
|
+
);
|
|
1115
1405
|
}
|
|
1116
1406
|
this.activeAgents.clear();
|
|
1117
1407
|
for (const agent of compatible) {
|
|
1118
1408
|
this.activeAgents.add(agent.id);
|
|
1119
1409
|
}
|
|
1120
1410
|
state.chainId = chainId;
|
|
1411
|
+
Promise.all(
|
|
1412
|
+
compatible.map(async (agent) => {
|
|
1413
|
+
const agentStart = Date.now();
|
|
1414
|
+
await agent.activateAgent(state, chainId);
|
|
1415
|
+
logTiming3("activateAgent background-agent-ready", agentStart, {
|
|
1416
|
+
chainId,
|
|
1417
|
+
agentId: agent.id
|
|
1418
|
+
});
|
|
1419
|
+
})
|
|
1420
|
+
).catch((error) => {
|
|
1421
|
+
console.error("activateAgent background init failed:", error);
|
|
1422
|
+
});
|
|
1423
|
+
logTiming3("activateAgent scheduled", startedAt, {
|
|
1424
|
+
chainId,
|
|
1425
|
+
agentIds: compatible.map((agent) => agent.id)
|
|
1426
|
+
});
|
|
1121
1427
|
}
|
|
1122
1428
|
/**
|
|
1123
1429
|
* Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
|
|
@@ -1143,8 +1449,10 @@ var OwneySDK = class {
|
|
|
1143
1449
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
1144
1450
|
const totalAmount = BigInt(amount);
|
|
1145
1451
|
if (totalAmount < BigInt(eligibleAgents.length)) {
|
|
1146
|
-
throw new
|
|
1147
|
-
|
|
1452
|
+
throw new OwneyError(
|
|
1453
|
+
"DEPOSIT_AMOUNT_TOO_SMALL",
|
|
1454
|
+
`Amount "${amount}" is too small to split across ${eligibleAgents.length} agents.`,
|
|
1455
|
+
{ amount, agentCount: eligibleAgents.length }
|
|
1148
1456
|
);
|
|
1149
1457
|
}
|
|
1150
1458
|
const perAgent = totalAmount / BigInt(eligibleAgents.length);
|
|
@@ -1167,8 +1475,10 @@ var OwneySDK = class {
|
|
|
1167
1475
|
(sa) => sa.chainId === chainId
|
|
1168
1476
|
);
|
|
1169
1477
|
if (!chainAssets || !chainAssets.assets.includes(asset)) {
|
|
1170
|
-
throw new
|
|
1171
|
-
|
|
1478
|
+
throw new OwneyError(
|
|
1479
|
+
"ASSET_UNSUPPORTED",
|
|
1480
|
+
`Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
|
|
1481
|
+
{ agentId: agent.id, asset, chainId }
|
|
1172
1482
|
);
|
|
1173
1483
|
}
|
|
1174
1484
|
}
|
|
@@ -1180,8 +1490,10 @@ var OwneySDK = class {
|
|
|
1180
1490
|
return chainAssets && chainAssets.assets.includes(asset);
|
|
1181
1491
|
});
|
|
1182
1492
|
if (eligible.length === 0) {
|
|
1183
|
-
throw new
|
|
1184
|
-
|
|
1493
|
+
throw new OwneyError(
|
|
1494
|
+
"ASSET_NO_COMPATIBLE_AGENTS",
|
|
1495
|
+
`No agents support asset "${asset}" on chain ${chainId}.`,
|
|
1496
|
+
{ asset, chainId }
|
|
1185
1497
|
);
|
|
1186
1498
|
}
|
|
1187
1499
|
return eligible;
|
|
@@ -1206,10 +1518,25 @@ var OwneySDK = class {
|
|
|
1206
1518
|
return agent.withdraw(state, chainId, amount);
|
|
1207
1519
|
}
|
|
1208
1520
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
1521
|
+
if (!amount) {
|
|
1522
|
+
const results2 = {};
|
|
1523
|
+
for (const agent of eligibleAgents) {
|
|
1524
|
+
results2[agent.id] = await agent.withdraw(state, chainId);
|
|
1525
|
+
}
|
|
1526
|
+
return { agentResult: results2 };
|
|
1527
|
+
}
|
|
1209
1528
|
const results = {};
|
|
1529
|
+
let lastError;
|
|
1210
1530
|
for (const agent of eligibleAgents) {
|
|
1211
|
-
|
|
1531
|
+
try {
|
|
1532
|
+
results[agent.id] = await agent.withdraw(state, chainId, amount);
|
|
1533
|
+
return { agentResult: results };
|
|
1534
|
+
} catch (err) {
|
|
1535
|
+
lastError = err;
|
|
1536
|
+
}
|
|
1212
1537
|
}
|
|
1538
|
+
if (lastError)
|
|
1539
|
+
throw lastError;
|
|
1213
1540
|
return {
|
|
1214
1541
|
agentResult: results
|
|
1215
1542
|
};
|
|
@@ -1221,17 +1548,50 @@ var OwneySDK = class {
|
|
|
1221
1548
|
* @returns {AgentBalance} for a single agent, or {OwneyBalances} with totalBalance and per-agent breakdown
|
|
1222
1549
|
*/
|
|
1223
1550
|
async getBalances(agentId) {
|
|
1551
|
+
const startedAt = Date.now();
|
|
1224
1552
|
const state = this.requireState();
|
|
1225
1553
|
const chainId = this.requireChainId();
|
|
1226
1554
|
if (agentId) {
|
|
1227
|
-
|
|
1555
|
+
const result = await this.getAgent(agentId).getBalances(state, chainId);
|
|
1556
|
+
logTiming3("getBalances single-agent", startedAt, { chainId, agentId });
|
|
1557
|
+
return result;
|
|
1228
1558
|
}
|
|
1229
1559
|
let totalBalance = 0;
|
|
1230
1560
|
const results = {};
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1561
|
+
const entries = [...this.getActiveAgents().entries()];
|
|
1562
|
+
const balanceResults = await Promise.allSettled(
|
|
1563
|
+
entries.map(async ([id, agent]) => {
|
|
1564
|
+
const agentStartedAt = Date.now();
|
|
1565
|
+
const b = await agent.getBalances(state, chainId);
|
|
1566
|
+
logTiming3("getBalances per-agent", agentStartedAt, {
|
|
1567
|
+
chainId,
|
|
1568
|
+
agentId: id
|
|
1569
|
+
});
|
|
1570
|
+
return [id, b];
|
|
1571
|
+
})
|
|
1572
|
+
);
|
|
1573
|
+
let successCount = 0;
|
|
1574
|
+
for (const settledResult of balanceResults) {
|
|
1575
|
+
if (settledResult.status === "fulfilled") {
|
|
1576
|
+
const [id, b] = settledResult.value;
|
|
1577
|
+
results[id] = b;
|
|
1578
|
+
totalBalance += Number(b.totalBalance);
|
|
1579
|
+
successCount += 1;
|
|
1580
|
+
continue;
|
|
1581
|
+
}
|
|
1582
|
+
console.error("getBalances agent failed:", settledResult.reason);
|
|
1583
|
+
}
|
|
1584
|
+
if (successCount === 0) {
|
|
1585
|
+
throw new OwneyError(
|
|
1586
|
+
"BALANCE_ALL_FAILED",
|
|
1587
|
+
"Failed to fetch balances for all active agents."
|
|
1588
|
+
);
|
|
1234
1589
|
}
|
|
1590
|
+
logTiming3("getBalances aggregate", startedAt, {
|
|
1591
|
+
chainId,
|
|
1592
|
+
totalAgents: entries.length,
|
|
1593
|
+
successCount
|
|
1594
|
+
});
|
|
1235
1595
|
return {
|
|
1236
1596
|
totalBalance: String(totalBalance),
|
|
1237
1597
|
agentBalances: results
|
|
@@ -1250,9 +1610,16 @@ var OwneySDK = class {
|
|
|
1250
1610
|
}
|
|
1251
1611
|
let totalEarnings = 0;
|
|
1252
1612
|
const results = {};
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1613
|
+
const entries = [...this.getActiveAgents().entries()];
|
|
1614
|
+
const earningsResults = await Promise.all(
|
|
1615
|
+
entries.map(async ([id, agent]) => {
|
|
1616
|
+
const e = await agent.getEarnings(state, chainId);
|
|
1617
|
+
return [id, e];
|
|
1618
|
+
})
|
|
1619
|
+
);
|
|
1620
|
+
for (const [id, e] of earningsResults) {
|
|
1621
|
+
results[id] = e;
|
|
1622
|
+
totalEarnings += Number(e.lifetimeEarnings);
|
|
1256
1623
|
}
|
|
1257
1624
|
return {
|
|
1258
1625
|
totalEarnings: String(totalEarnings),
|
|
@@ -1277,16 +1644,30 @@ var OwneySDK = class {
|
|
|
1277
1644
|
return this.getAgent(agentId).getAccountApy(state, chainId, days);
|
|
1278
1645
|
}
|
|
1279
1646
|
const activeAgents = this.getActiveAgents();
|
|
1647
|
+
const entries = [...activeAgents.entries()];
|
|
1648
|
+
const [apyResults, balanceResults] = await Promise.all([
|
|
1649
|
+
Promise.all(
|
|
1650
|
+
entries.map(async ([id, agent]) => {
|
|
1651
|
+
const apy = await agent.getAccountApy(state, chainId, days);
|
|
1652
|
+
return [id, apy];
|
|
1653
|
+
})
|
|
1654
|
+
),
|
|
1655
|
+
Promise.all(
|
|
1656
|
+
entries.map(async ([id, agent]) => {
|
|
1657
|
+
const b = await agent.getBalances(state, chainId);
|
|
1658
|
+
return [id, Number(b.totalBalance)];
|
|
1659
|
+
})
|
|
1660
|
+
)
|
|
1661
|
+
]);
|
|
1280
1662
|
const results = {};
|
|
1281
|
-
for (const [id,
|
|
1282
|
-
results[id] =
|
|
1663
|
+
for (const [id, apy] of apyResults) {
|
|
1664
|
+
results[id] = apy;
|
|
1283
1665
|
}
|
|
1284
1666
|
const balances = {};
|
|
1285
1667
|
let totalBalance = 0;
|
|
1286
|
-
for (const [id,
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
totalBalance += balances[id];
|
|
1668
|
+
for (const [id, bal] of balanceResults) {
|
|
1669
|
+
balances[id] = bal;
|
|
1670
|
+
totalBalance += bal;
|
|
1290
1671
|
}
|
|
1291
1672
|
let totalApy = 0;
|
|
1292
1673
|
if (totalBalance > 0) {
|
|
@@ -1314,9 +1695,13 @@ var OwneySDK = class {
|
|
|
1314
1695
|
if (agentId) {
|
|
1315
1696
|
return this.getAgent(agentId).getHistory(state, chainId, filters);
|
|
1316
1697
|
}
|
|
1698
|
+
const historyResults = await Promise.all(
|
|
1699
|
+
[...this.getActiveAgents().values()].map(
|
|
1700
|
+
(agent) => agent.getHistory(state, chainId, filters)
|
|
1701
|
+
)
|
|
1702
|
+
);
|
|
1317
1703
|
let results = [];
|
|
1318
|
-
for (const
|
|
1319
|
-
const { data } = await agent.getHistory(state, chainId, filters);
|
|
1704
|
+
for (const { data } of historyResults) {
|
|
1320
1705
|
results = [...results, ...data];
|
|
1321
1706
|
}
|
|
1322
1707
|
results.sort(
|
|
@@ -1360,8 +1745,15 @@ var OwneySDK = class {
|
|
|
1360
1745
|
return this.getAgent(agentId).getUserProfile(state, chainId);
|
|
1361
1746
|
}
|
|
1362
1747
|
const results = {};
|
|
1363
|
-
|
|
1364
|
-
|
|
1748
|
+
const entries = [...this.getActiveAgents().entries()];
|
|
1749
|
+
const profileResults = await Promise.all(
|
|
1750
|
+
entries.map(async ([id, agent]) => {
|
|
1751
|
+
const p = await agent.getUserProfile(state, chainId);
|
|
1752
|
+
return [id, p];
|
|
1753
|
+
})
|
|
1754
|
+
);
|
|
1755
|
+
for (const [id, p] of profileResults) {
|
|
1756
|
+
results[id] = p;
|
|
1365
1757
|
}
|
|
1366
1758
|
return {
|
|
1367
1759
|
agentUserProfile: results
|
|
@@ -1373,18 +1765,34 @@ var OwneySDK = class {
|
|
|
1373
1765
|
* @param options - Contains agentId (optional) and days ("7D", "14D", or "30D")
|
|
1374
1766
|
* @param options.agentId - Optional. Agent to query. Omit for all agents.
|
|
1375
1767
|
* @param options.days - Lookback period: "7D", "14D", or "30D"
|
|
1768
|
+
* @param options.tokenSymbol - Optional asset symbol ("USDC" / "WETH") to
|
|
1769
|
+
* scope the APY to a specific asset+chain. Agents whose backends do not
|
|
1770
|
+
* support per-asset APY (currently Sail) ignore this.
|
|
1771
|
+
* @param options.chainId - Optional chain id for per-asset APY lookups.
|
|
1772
|
+
* Typically paired with `tokenSymbol`.
|
|
1376
1773
|
* @returns {AgentApy} for a single agent, or {OwneyAgentApy} with per-agent APY
|
|
1377
1774
|
*/
|
|
1378
1775
|
async getAgentApy({
|
|
1379
1776
|
agentId,
|
|
1380
|
-
days
|
|
1777
|
+
days,
|
|
1778
|
+
tokenSymbol,
|
|
1779
|
+
chainId
|
|
1381
1780
|
}) {
|
|
1781
|
+
await this.ensureAgentsInitialized();
|
|
1782
|
+
const agentOptions = { tokenSymbol, chainId };
|
|
1382
1783
|
if (agentId) {
|
|
1383
|
-
return this.getAgent(agentId).getAgentApy(days);
|
|
1784
|
+
return this.getAgent(agentId).getAgentApy(days, agentOptions);
|
|
1384
1785
|
}
|
|
1385
1786
|
const results = {};
|
|
1386
|
-
|
|
1387
|
-
|
|
1787
|
+
const agentEntries = [...this.agents.entries()];
|
|
1788
|
+
const apyResults = await Promise.all(
|
|
1789
|
+
agentEntries.map(async ([id, agent]) => {
|
|
1790
|
+
const apy = await agent.getAgentApy(days, agentOptions);
|
|
1791
|
+
return [id, apy];
|
|
1792
|
+
})
|
|
1793
|
+
);
|
|
1794
|
+
for (const [id, apy] of apyResults) {
|
|
1795
|
+
results[id] = apy;
|
|
1388
1796
|
}
|
|
1389
1797
|
return {
|
|
1390
1798
|
agentApy: results
|
|
@@ -1400,10 +1808,29 @@ var OwneySDK = class {
|
|
|
1400
1808
|
const activeAgents = this.getActiveAgents();
|
|
1401
1809
|
const balances = {};
|
|
1402
1810
|
let totalBalance = 0;
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1811
|
+
const entries = [...activeAgents.entries()];
|
|
1812
|
+
const balanceResults = await Promise.allSettled(
|
|
1813
|
+
entries.map(async ([id, agent]) => {
|
|
1814
|
+
const b = await agent.getBalances(state, chainId);
|
|
1815
|
+
return [id, Number(b.totalBalance)];
|
|
1816
|
+
})
|
|
1817
|
+
);
|
|
1818
|
+
let successCount = 0;
|
|
1819
|
+
for (const settledResult of balanceResults) {
|
|
1820
|
+
if (settledResult.status === "fulfilled") {
|
|
1821
|
+
const [id, bal] = settledResult.value;
|
|
1822
|
+
balances[id] = bal;
|
|
1823
|
+
totalBalance += bal;
|
|
1824
|
+
successCount += 1;
|
|
1825
|
+
} else {
|
|
1826
|
+
console.error("getAgentAllocation agent failed:", settledResult.reason);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
if (successCount === 0) {
|
|
1830
|
+
throw new OwneyError(
|
|
1831
|
+
"ALLOCATION_ALL_FAILED",
|
|
1832
|
+
"Failed to fetch allocation: all agents failed."
|
|
1833
|
+
);
|
|
1407
1834
|
}
|
|
1408
1835
|
const allocation = {};
|
|
1409
1836
|
if (totalBalance === 0) {
|
|
@@ -1431,5 +1858,6 @@ export {
|
|
|
1431
1858
|
AgentChainIncompatibleError,
|
|
1432
1859
|
AgentNotFoundError,
|
|
1433
1860
|
NotConnectedError,
|
|
1861
|
+
OwneyError,
|
|
1434
1862
|
OwneySDK
|
|
1435
1863
|
};
|