@owney/sdk 0.7.16-beta.0 → 0.7.16-beta.2
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 +2 -0
- package/dist/index.cjs +2269 -651
- package/dist/index.d.cts +137 -10
- package/dist/index.d.ts +137 -10
- package/dist/index.js +2279 -649
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
NotConnectedError: () => NotConnectedError,
|
|
27
27
|
OwneyError: () => OwneyError,
|
|
28
28
|
OwneySDK: () => OwneySDK,
|
|
29
|
+
YieldseekerAgent: () => YieldseekerAgent,
|
|
29
30
|
createOwneySIWX: () => createOwneySIWX,
|
|
30
31
|
setOwneyDebug: () => setOwneyDebug
|
|
31
32
|
});
|
|
@@ -83,23 +84,6 @@ var SupportedAssets = [
|
|
|
83
84
|
}
|
|
84
85
|
];
|
|
85
86
|
|
|
86
|
-
// src/lib/debug.ts
|
|
87
|
-
var configuredDebug = false;
|
|
88
|
-
function setOwneyDebug(enabled) {
|
|
89
|
-
configuredDebug = enabled;
|
|
90
|
-
}
|
|
91
|
-
function isOwneyDebug() {
|
|
92
|
-
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
93
|
-
}
|
|
94
|
-
function debugLog(scope, message, data) {
|
|
95
|
-
if (!isOwneyDebug()) return;
|
|
96
|
-
if (data === void 0) {
|
|
97
|
-
console.log(`[${scope}] ${message}`);
|
|
98
|
-
} else {
|
|
99
|
-
console.log(`[${scope}] ${message}`, data);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
87
|
// src/errors.ts
|
|
104
88
|
var OwneyError = class extends Error {
|
|
105
89
|
code;
|
|
@@ -197,18 +181,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
197
181
|
return explicit;
|
|
198
182
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
199
183
|
}
|
|
200
|
-
function mapDeposit(
|
|
184
|
+
function mapDeposit(raw2) {
|
|
201
185
|
return {
|
|
202
|
-
txHash:
|
|
203
|
-
smartWallet:
|
|
204
|
-
amount:
|
|
186
|
+
txHash: raw2.txHash,
|
|
187
|
+
smartWallet: raw2.smartWallet,
|
|
188
|
+
amount: raw2.amount
|
|
205
189
|
};
|
|
206
190
|
}
|
|
207
|
-
function mapWithdraw(
|
|
191
|
+
function mapWithdraw(raw2) {
|
|
208
192
|
return {
|
|
209
|
-
txHash:
|
|
210
|
-
type:
|
|
211
|
-
amount:
|
|
193
|
+
txHash: raw2.txHash,
|
|
194
|
+
type: raw2.type,
|
|
195
|
+
amount: raw2.amount
|
|
212
196
|
};
|
|
213
197
|
}
|
|
214
198
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -227,10 +211,10 @@ function resolveChainId(chain) {
|
|
|
227
211
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
228
212
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
229
213
|
}
|
|
230
|
-
function mapPendingAllocations(
|
|
231
|
-
if (!Array.isArray(
|
|
214
|
+
function mapPendingAllocations(raw2) {
|
|
215
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
232
216
|
const pending = [];
|
|
233
|
-
for (const entry of
|
|
217
|
+
for (const entry of raw2) {
|
|
234
218
|
if (typeof entry !== "object" || entry === null) continue;
|
|
235
219
|
const e = entry;
|
|
236
220
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -253,8 +237,8 @@ function mapPendingAllocations(raw) {
|
|
|
253
237
|
}
|
|
254
238
|
return pending.length > 0 ? pending : void 0;
|
|
255
239
|
}
|
|
256
|
-
function mapBalances(
|
|
257
|
-
const portfolio =
|
|
240
|
+
function mapBalances(raw2, _chainId, smartWallet) {
|
|
241
|
+
const portfolio = raw2.portfolio;
|
|
258
242
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
259
243
|
let totalBalance = 0;
|
|
260
244
|
const tokens = [];
|
|
@@ -299,7 +283,7 @@ function mapBalances(raw, _chainId, smartWallet) {
|
|
|
299
283
|
)
|
|
300
284
|
)
|
|
301
285
|
),
|
|
302
|
-
apy:
|
|
286
|
+
apy: p.pool_apy,
|
|
303
287
|
tvl: p.pool_tvl,
|
|
304
288
|
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
305
289
|
// structural read keeps it undefined today and auto-populates later.
|
|
@@ -323,8 +307,8 @@ function sumTokenValues(tokens) {
|
|
|
323
307
|
function sumTokenEarnings(tokens) {
|
|
324
308
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
325
309
|
}
|
|
326
|
-
function mapEarnings(
|
|
327
|
-
const totalEarningsByChain =
|
|
310
|
+
function mapEarnings(raw2, smartWallet) {
|
|
311
|
+
const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
|
|
328
312
|
const tokens = [];
|
|
329
313
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
330
314
|
totalEarningsByChain
|
|
@@ -343,15 +327,15 @@ function mapEarnings(raw, smartWallet) {
|
|
|
343
327
|
return {
|
|
344
328
|
smartWallet,
|
|
345
329
|
lifetimeEarnings: sumTokenEarnings(
|
|
346
|
-
|
|
330
|
+
raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
|
|
347
331
|
),
|
|
348
332
|
tokens
|
|
349
333
|
};
|
|
350
334
|
}
|
|
351
|
-
function mapWeightedApyByChain(
|
|
352
|
-
if (!
|
|
335
|
+
function mapWeightedApyByChain(raw2) {
|
|
336
|
+
if (!raw2) return void 0;
|
|
353
337
|
const out = {};
|
|
354
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
338
|
+
for (const [chainKey, tokenApy] of Object.entries(raw2)) {
|
|
355
339
|
const chainId = Number(chainKey);
|
|
356
340
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
357
341
|
const perAsset = {};
|
|
@@ -364,19 +348,6 @@ function mapWeightedApyByChain(raw) {
|
|
|
364
348
|
}
|
|
365
349
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
366
350
|
}
|
|
367
|
-
var warnedGrossApyFallbacks = /* @__PURE__ */ new Set();
|
|
368
|
-
function netApy(net, gross, field) {
|
|
369
|
-
if (net !== void 0 && net !== null) return net;
|
|
370
|
-
if (gross === void 0 || gross === null) return void 0;
|
|
371
|
-
if (!warnedGrossApyFallbacks.has(field)) {
|
|
372
|
-
warnedGrossApyFallbacks.add(field);
|
|
373
|
-
console.warn(
|
|
374
|
-
`[owney] @zyfai/sdk omitted "${field}"; falling back to the gross APY, which does not deduct Zyfai's performance fee and so reads high.`
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
debugLog("zyfai:apy", `gross fallback for ${field}`, { gross });
|
|
378
|
-
return gross;
|
|
379
|
-
}
|
|
380
351
|
function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
381
352
|
let weightedSum = 0;
|
|
382
353
|
let totalBalance = 0;
|
|
@@ -384,22 +355,21 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
384
355
|
if (p.chainId !== chainId) continue;
|
|
385
356
|
if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
|
|
386
357
|
const balance = p.balance ?? 0;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
weightedSum += apy * balance;
|
|
358
|
+
if (balance <= 0 || typeof p.apy !== "number") continue;
|
|
359
|
+
weightedSum += p.apy * balance;
|
|
390
360
|
totalBalance += balance;
|
|
391
361
|
}
|
|
392
362
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
393
363
|
}
|
|
394
|
-
function mapApyHistory(
|
|
395
|
-
const history = Object.entries(
|
|
364
|
+
function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
365
|
+
const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
|
|
396
366
|
date,
|
|
397
367
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
|
|
398
368
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
399
369
|
return {
|
|
400
|
-
walletAddress:
|
|
401
|
-
weightedApyAfterFee:
|
|
402
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
370
|
+
walletAddress: raw2.walletAddress,
|
|
371
|
+
weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
|
|
372
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
|
|
403
373
|
history
|
|
404
374
|
};
|
|
405
375
|
}
|
|
@@ -434,12 +404,8 @@ function mapEntries(rawEntries, chainId) {
|
|
|
434
404
|
const matched = positions.find(
|
|
435
405
|
(p) => p.protocol_name ? rawLog.newOpportunity.includes(`${p.protocol_name} (${p.pool})`) : false
|
|
436
406
|
);
|
|
437
|
-
oldApy = Number(
|
|
438
|
-
|
|
439
|
-
);
|
|
440
|
-
newApy = Number(
|
|
441
|
-
netApy(rawLog.newApy_withFee, rawLog.newApy, "newApy_withFee")
|
|
442
|
-
);
|
|
407
|
+
oldApy = Number(rawLog.oldApy);
|
|
408
|
+
newApy = Number(rawLog.newApy);
|
|
443
409
|
const from = splitOpportunity(rawLog.oldOpportunity);
|
|
444
410
|
const to = splitOpportunity(rawLog.newOpportunity);
|
|
445
411
|
rebalanceLog = [
|
|
@@ -495,23 +461,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
495
461
|
};
|
|
496
462
|
});
|
|
497
463
|
}
|
|
498
|
-
function mapUserProfile(
|
|
464
|
+
function mapUserProfile(raw2, userAddress) {
|
|
499
465
|
return {
|
|
500
466
|
address: userAddress,
|
|
501
|
-
smartWallet:
|
|
502
|
-
chains:
|
|
503
|
-
strategy:
|
|
504
|
-
hasActiveSessionKey:
|
|
505
|
-
protocols:
|
|
506
|
-
splitting:
|
|
507
|
-
minSplits:
|
|
467
|
+
smartWallet: raw2.smartWallet || "",
|
|
468
|
+
chains: raw2.chains || [],
|
|
469
|
+
strategy: raw2.strategy,
|
|
470
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey || false,
|
|
471
|
+
protocols: raw2.protocols || [],
|
|
472
|
+
splitting: raw2.splitting,
|
|
473
|
+
minSplits: raw2.minSplits
|
|
508
474
|
};
|
|
509
475
|
}
|
|
510
|
-
function mapApyByStrategy(
|
|
476
|
+
function mapApyByStrategy(raw2) {
|
|
511
477
|
const apyPerAsset = {};
|
|
512
478
|
let apySum = 0;
|
|
513
479
|
let apyCount = 0;
|
|
514
|
-
for (const entry of
|
|
480
|
+
for (const entry of raw2.data) {
|
|
515
481
|
const supported = SupportedAssets.find(
|
|
516
482
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
517
483
|
);
|
|
@@ -519,11 +485,7 @@ function mapApyByStrategy(raw) {
|
|
|
519
485
|
const chainKey = supported.chainId;
|
|
520
486
|
const symbolKey = supported.symbol;
|
|
521
487
|
const bucket = apyPerAsset[chainKey] ?? {};
|
|
522
|
-
const apy =
|
|
523
|
-
entry.average_apy_withFee,
|
|
524
|
-
entry.average_apy,
|
|
525
|
-
"average_apy_withFee"
|
|
526
|
-
) ?? entry.average_apy;
|
|
488
|
+
const apy = entry.average_apy_with_fee ?? entry.average_apy;
|
|
527
489
|
bucket[symbolKey] = apy;
|
|
528
490
|
apyPerAsset[chainKey] = bucket;
|
|
529
491
|
apySum += apy;
|
|
@@ -682,15 +644,15 @@ var readSession = (address, _chainId) => {
|
|
|
682
644
|
if (typeof window === "undefined") return null;
|
|
683
645
|
const key2 = buildKey(address);
|
|
684
646
|
const store = storage();
|
|
685
|
-
let
|
|
647
|
+
let raw2 = null;
|
|
686
648
|
try {
|
|
687
|
-
|
|
649
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
688
650
|
} catch {
|
|
689
|
-
|
|
651
|
+
raw2 = null;
|
|
690
652
|
}
|
|
691
|
-
if (
|
|
653
|
+
if (raw2) {
|
|
692
654
|
try {
|
|
693
|
-
const parsed = JSON.parse(
|
|
655
|
+
const parsed = JSON.parse(raw2);
|
|
694
656
|
if (isFreshSession(parsed)) return parsed;
|
|
695
657
|
} catch {
|
|
696
658
|
}
|
|
@@ -858,8 +820,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
858
820
|
}
|
|
859
821
|
return result;
|
|
860
822
|
}
|
|
861
|
-
function flattenAvailablePools(
|
|
862
|
-
const byChain =
|
|
823
|
+
function flattenAvailablePools(raw2) {
|
|
824
|
+
const byChain = raw2 ?? {};
|
|
863
825
|
const names = [];
|
|
864
826
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
865
827
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -883,6 +845,23 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
883
845
|
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
884
846
|
}
|
|
885
847
|
|
|
848
|
+
// src/lib/debug.ts
|
|
849
|
+
var configuredDebug = false;
|
|
850
|
+
function setOwneyDebug(enabled) {
|
|
851
|
+
configuredDebug = enabled;
|
|
852
|
+
}
|
|
853
|
+
function isOwneyDebug() {
|
|
854
|
+
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
855
|
+
}
|
|
856
|
+
function debugLog(scope, message, data) {
|
|
857
|
+
if (!isOwneyDebug()) return;
|
|
858
|
+
if (data === void 0) {
|
|
859
|
+
console.log(`[${scope}] ${message}`);
|
|
860
|
+
} else {
|
|
861
|
+
console.log(`[${scope}] ${message}`, data);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
886
865
|
// src/agents/zyfai/zyfai.agent.ts
|
|
887
866
|
var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem.parseAbi)([
|
|
888
867
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
@@ -1353,8 +1332,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1353
1332
|
const poolResults = await Promise.all(
|
|
1354
1333
|
universe.map(async (protocol) => {
|
|
1355
1334
|
try {
|
|
1356
|
-
const
|
|
1357
|
-
return [protocol.id, flattenAvailablePools(
|
|
1335
|
+
const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1336
|
+
return [protocol.id, flattenAvailablePools(raw2)];
|
|
1358
1337
|
} catch (error) {
|
|
1359
1338
|
console.warn(
|
|
1360
1339
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1420,14 +1399,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1420
1399
|
async readWalletState(ownerAddress) {
|
|
1421
1400
|
try {
|
|
1422
1401
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1423
|
-
const
|
|
1402
|
+
const raw2 = portfolio;
|
|
1424
1403
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1425
|
-
predeployed:
|
|
1426
|
-
hasActiveSessionKey:
|
|
1404
|
+
predeployed: raw2?.predeployed,
|
|
1405
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1427
1406
|
});
|
|
1428
1407
|
return {
|
|
1429
|
-
predeployed:
|
|
1430
|
-
hasActiveSessionKey:
|
|
1408
|
+
predeployed: raw2?.predeployed,
|
|
1409
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1431
1410
|
};
|
|
1432
1411
|
} catch (error) {
|
|
1433
1412
|
console.warn(
|
|
@@ -1707,14 +1686,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1707
1686
|
return { txHash, smartWallet, amount };
|
|
1708
1687
|
}
|
|
1709
1688
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1710
|
-
const
|
|
1689
|
+
const raw2 = await this.sdk.depositFunds(
|
|
1711
1690
|
this.getAddress(),
|
|
1712
1691
|
validChainId,
|
|
1713
1692
|
amount,
|
|
1714
1693
|
asset,
|
|
1715
1694
|
"aggressive"
|
|
1716
1695
|
);
|
|
1717
|
-
return mapDeposit(
|
|
1696
|
+
return mapDeposit(raw2);
|
|
1718
1697
|
} catch (error) {
|
|
1719
1698
|
throw error;
|
|
1720
1699
|
}
|
|
@@ -1723,32 +1702,32 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1723
1702
|
async withdraw(state, chainId, token, amount) {
|
|
1724
1703
|
const validChainId = isValidChainId(chainId);
|
|
1725
1704
|
await this.ensureConnected(state, validChainId);
|
|
1726
|
-
const
|
|
1705
|
+
const raw2 = await this.sdk.withdrawFunds(
|
|
1727
1706
|
this.getAddress(),
|
|
1728
1707
|
validChainId,
|
|
1729
1708
|
amount,
|
|
1730
1709
|
token
|
|
1731
1710
|
);
|
|
1732
|
-
if (!
|
|
1711
|
+
if (!raw2.success) {
|
|
1733
1712
|
throw new OwneyError(
|
|
1734
1713
|
"WITHDRAW_FAILED",
|
|
1735
|
-
|
|
1736
|
-
{ chainId: validChainId, token, amount, response:
|
|
1714
|
+
raw2.message || "Zyfai withdraw failed.",
|
|
1715
|
+
{ chainId: validChainId, token, amount, response: raw2 },
|
|
1737
1716
|
this.id
|
|
1738
1717
|
);
|
|
1739
1718
|
}
|
|
1740
|
-
return mapWithdraw(
|
|
1719
|
+
return mapWithdraw(raw2);
|
|
1741
1720
|
}
|
|
1742
1721
|
// --- IAgent: Portfolio reads ---
|
|
1743
1722
|
async getBalances(state, chainId) {
|
|
1744
1723
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1745
|
-
const
|
|
1746
|
-
return mapBalances(
|
|
1724
|
+
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
1725
|
+
return mapBalances(raw2, validChainId, smartWallet);
|
|
1747
1726
|
}
|
|
1748
1727
|
async getEarnings(state, chainId) {
|
|
1749
1728
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1750
|
-
const
|
|
1751
|
-
return mapEarnings(
|
|
1729
|
+
const raw2 = await this.sdk.getOnchainEarnings(smartWallet);
|
|
1730
|
+
return mapEarnings(raw2, smartWallet);
|
|
1752
1731
|
}
|
|
1753
1732
|
async refreshEarnings(state, chainId) {
|
|
1754
1733
|
if (this.earningsRefreshInFlight) return this.earningsRefreshInFlight;
|
|
@@ -1768,8 +1747,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1768
1747
|
}
|
|
1769
1748
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1770
1749
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1771
|
-
const
|
|
1772
|
-
return mapApyHistory(
|
|
1750
|
+
const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
1751
|
+
return mapApyHistory(raw2, chainId, tokenSymbol);
|
|
1773
1752
|
}
|
|
1774
1753
|
/**
|
|
1775
1754
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -1804,7 +1783,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1804
1783
|
const matched = [];
|
|
1805
1784
|
let backendExhausted = false;
|
|
1806
1785
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
1807
|
-
const
|
|
1786
|
+
const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
1808
1787
|
limit: backendPageSize,
|
|
1809
1788
|
offset,
|
|
1810
1789
|
fromDate: options?.fromDate,
|
|
@@ -1815,13 +1794,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1815
1794
|
// asset's rows and handing back a page that filters to nothing.
|
|
1816
1795
|
assetType
|
|
1817
1796
|
});
|
|
1818
|
-
|
|
1797
|
+
raw2.data.forEach((entry, idx) => {
|
|
1819
1798
|
if (entry.chainId === validChainId) {
|
|
1820
1799
|
matched.push({ entry, rawIdx: offset + idx });
|
|
1821
1800
|
}
|
|
1822
1801
|
});
|
|
1823
|
-
offset +=
|
|
1824
|
-
if (
|
|
1802
|
+
offset += raw2.data.length;
|
|
1803
|
+
if (raw2.data.length < backendPageSize) {
|
|
1825
1804
|
backendExhausted = true;
|
|
1826
1805
|
break;
|
|
1827
1806
|
}
|
|
@@ -1843,18 +1822,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1843
1822
|
}
|
|
1844
1823
|
async getUserProfile(state, chainId) {
|
|
1845
1824
|
await this.connectAuth(state, chainId);
|
|
1846
|
-
const
|
|
1825
|
+
const raw2 = await this.sdk.getUserDetails();
|
|
1847
1826
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
1848
1827
|
asset: "USDC (default \u2014 no asset passed)",
|
|
1849
|
-
splitting:
|
|
1850
|
-
minSplits:
|
|
1851
|
-
strategy:
|
|
1852
|
-
chains:
|
|
1853
|
-
protocolCount:
|
|
1854
|
-
hasActiveSessionKey:
|
|
1855
|
-
smartWallet:
|
|
1828
|
+
splitting: raw2.splitting,
|
|
1829
|
+
minSplits: raw2.minSplits,
|
|
1830
|
+
strategy: raw2.strategy,
|
|
1831
|
+
chains: raw2.chains,
|
|
1832
|
+
protocolCount: raw2.protocols?.length,
|
|
1833
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey,
|
|
1834
|
+
smartWallet: raw2.smartWallet
|
|
1856
1835
|
});
|
|
1857
|
-
return mapUserProfile(
|
|
1836
|
+
return mapUserProfile(raw2, this.connectedAddress);
|
|
1858
1837
|
}
|
|
1859
1838
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
1860
1839
|
await this.connectAuth(state, chainId);
|
|
@@ -1873,321 +1852,184 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1873
1852
|
}
|
|
1874
1853
|
// --- IAgent: Discovery (no wallet required) ---
|
|
1875
1854
|
async getAgentApy(days, options) {
|
|
1876
|
-
const
|
|
1855
|
+
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
1877
1856
|
false,
|
|
1878
1857
|
DayFilterMapping[days],
|
|
1879
1858
|
"aggressive",
|
|
1880
1859
|
options?.chainId,
|
|
1881
1860
|
options?.tokenSymbol
|
|
1882
1861
|
);
|
|
1883
|
-
return mapApyByStrategy(
|
|
1862
|
+
return mapApyByStrategy(raw2);
|
|
1884
1863
|
}
|
|
1885
1864
|
};
|
|
1886
1865
|
|
|
1887
|
-
// src/
|
|
1888
|
-
var
|
|
1889
|
-
|
|
1890
|
-
|
|
1866
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
1867
|
+
var import_viem6 = require("viem");
|
|
1868
|
+
var import_chains3 = require("viem/chains");
|
|
1869
|
+
|
|
1870
|
+
// src/lib/chain-guard.ts
|
|
1871
|
+
var CHAIN_NAMES = {
|
|
1872
|
+
1: "Ethereum",
|
|
1873
|
+
8453: "Base",
|
|
1874
|
+
42161: "Arbitrum"
|
|
1875
|
+
};
|
|
1876
|
+
function chainName(chainId) {
|
|
1877
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
1878
|
+
}
|
|
1879
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
1880
|
+
const actual = await pub.getChainId();
|
|
1881
|
+
if (actual === expected) return;
|
|
1891
1882
|
try {
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
console.warn(
|
|
1902
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
1903
|
-
);
|
|
1883
|
+
await wallet.switchChain({ id: expected });
|
|
1884
|
+
} catch (error) {
|
|
1885
|
+
throw new OwneyError(
|
|
1886
|
+
"CHAIN_MISMATCH",
|
|
1887
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
1888
|
+
{
|
|
1889
|
+
expectedChainId: expected,
|
|
1890
|
+
actualChainId: actual,
|
|
1891
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
1904
1892
|
}
|
|
1905
|
-
return null;
|
|
1906
|
-
}
|
|
1907
|
-
const json = await res.json();
|
|
1908
|
-
const policy = json.success ? json.data ?? null : null;
|
|
1909
|
-
debugLog(
|
|
1910
|
-
"owney-sdk",
|
|
1911
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
1912
|
-
policy ?? void 0
|
|
1913
1893
|
);
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1894
|
+
}
|
|
1895
|
+
const after = await pub.getChainId();
|
|
1896
|
+
if (after !== expected) {
|
|
1897
|
+
throw new OwneyError(
|
|
1898
|
+
"CHAIN_MISMATCH",
|
|
1899
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
1900
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
1919
1901
|
);
|
|
1920
|
-
return null;
|
|
1921
1902
|
}
|
|
1922
1903
|
}
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1904
|
+
|
|
1905
|
+
// src/lib/transfer-auth.ts
|
|
1906
|
+
var import_viem2 = require("viem");
|
|
1907
|
+
var ERC20_META_ABI = [
|
|
1908
|
+
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
1909
|
+
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
1910
|
+
];
|
|
1911
|
+
function buildTransferWithAuthorizationTypedData(input) {
|
|
1912
|
+
return {
|
|
1913
|
+
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
1914
|
+
types: {
|
|
1915
|
+
TransferWithAuthorization: [
|
|
1916
|
+
{ name: "from", type: "address" },
|
|
1917
|
+
{ name: "to", type: "address" },
|
|
1918
|
+
{ name: "value", type: "uint256" },
|
|
1919
|
+
{ name: "validAfter", type: "uint256" },
|
|
1920
|
+
{ name: "validBefore", type: "uint256" },
|
|
1921
|
+
{ name: "nonce", type: "bytes32" }
|
|
1922
|
+
]
|
|
1923
|
+
},
|
|
1924
|
+
primaryType: "TransferWithAuthorization",
|
|
1925
|
+
message: input.message
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
async function readTokenMeta(publicClient, token) {
|
|
1929
|
+
const [tokenName, tokenVersion] = await Promise.all([
|
|
1930
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
1931
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
1932
|
+
]);
|
|
1933
|
+
return { tokenName, tokenVersion };
|
|
1934
|
+
}
|
|
1935
|
+
function randomAuthNonce() {
|
|
1936
|
+
const bytes = new Uint8Array(32);
|
|
1937
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
1938
|
+
return (0, import_viem2.bytesToHex)(bytes);
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
// src/lib/sponsor-client.ts
|
|
1942
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1943
|
+
async function postPaymasterIntent(input) {
|
|
1944
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1945
|
+
let res;
|
|
1946
|
+
try {
|
|
1947
|
+
res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
|
|
1948
|
+
method: "POST",
|
|
1949
|
+
headers: {
|
|
1950
|
+
"content-type": "application/json",
|
|
1951
|
+
"x-owney-api-key": input.apiKey,
|
|
1952
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
1953
|
+
},
|
|
1954
|
+
body: JSON.stringify(input.body)
|
|
1955
|
+
});
|
|
1956
|
+
} catch (networkError) {
|
|
1934
1957
|
throw new OwneyError(
|
|
1935
|
-
"
|
|
1936
|
-
`
|
|
1937
|
-
{
|
|
1958
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1959
|
+
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1960
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
1938
1961
|
);
|
|
1939
1962
|
}
|
|
1940
|
-
const
|
|
1941
|
-
|
|
1963
|
+
const text = await res.text();
|
|
1964
|
+
let parsed = null;
|
|
1965
|
+
try {
|
|
1966
|
+
parsed = JSON.parse(text);
|
|
1967
|
+
} catch {
|
|
1968
|
+
}
|
|
1969
|
+
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
1942
1970
|
throw new OwneyError(
|
|
1943
|
-
"
|
|
1944
|
-
`
|
|
1945
|
-
{
|
|
1971
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1972
|
+
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1973
|
+
{
|
|
1974
|
+
statusCode: res.status,
|
|
1975
|
+
responseBody: text.slice(0, 500),
|
|
1976
|
+
safeToFallback: true
|
|
1977
|
+
}
|
|
1946
1978
|
);
|
|
1947
1979
|
}
|
|
1948
|
-
return
|
|
1980
|
+
return parsed.data;
|
|
1949
1981
|
}
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
async function reportAgentFailure(apiKey, agentType, errorCode) {
|
|
1982
|
+
async function postSponsorTransferAuth(input) {
|
|
1983
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1984
|
+
let res;
|
|
1954
1985
|
try {
|
|
1955
|
-
await fetch(`${
|
|
1986
|
+
res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
1956
1987
|
method: "POST",
|
|
1957
1988
|
headers: {
|
|
1958
|
-
"
|
|
1959
|
-
"x-owney-api-key": apiKey
|
|
1989
|
+
"content-type": "application/json",
|
|
1990
|
+
"x-owney-api-key": input.apiKey,
|
|
1991
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
1960
1992
|
},
|
|
1961
|
-
body: JSON.stringify(
|
|
1962
|
-
agent_type: agentType,
|
|
1963
|
-
error_code: errorCode,
|
|
1964
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1965
|
-
})
|
|
1993
|
+
body: JSON.stringify(input.body)
|
|
1966
1994
|
});
|
|
1967
|
-
} catch (
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1995
|
+
} catch (networkError) {
|
|
1996
|
+
throw new OwneyError(
|
|
1997
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1998
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1999
|
+
{ cause: String(networkError) }
|
|
1971
2000
|
);
|
|
1972
2001
|
}
|
|
1973
|
-
|
|
1974
|
-
|
|
2002
|
+
const text = await res.text();
|
|
2003
|
+
let parsed = null;
|
|
1975
2004
|
try {
|
|
1976
|
-
|
|
1977
|
-
} catch
|
|
1978
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
1979
|
-
void reportAgentFailure(apiKey, agentType, errorCode);
|
|
1980
|
-
throw err;
|
|
2005
|
+
parsed = JSON.parse(text);
|
|
2006
|
+
} catch {
|
|
1981
2007
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2008
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2009
|
+
throw new OwneyError(
|
|
2010
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2011
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2012
|
+
{
|
|
2013
|
+
statusCode: res.status,
|
|
2014
|
+
responseBody: text.slice(0, 500),
|
|
2015
|
+
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2016
|
+
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2017
|
+
safeToFallback: res.status === 503
|
|
2018
|
+
}
|
|
1992
2019
|
);
|
|
1993
|
-
if (!tokenBalance) return { agent, balance: 0n };
|
|
1994
|
-
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
1995
|
-
});
|
|
1996
|
-
}
|
|
1997
|
-
function planProportionalShares(balances, requested, totalAvailable) {
|
|
1998
|
-
const plans = balances.map(({ agent, balance }) => ({
|
|
1999
|
-
agent,
|
|
2000
|
-
balance,
|
|
2001
|
-
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
2002
|
-
}));
|
|
2003
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
2004
|
-
let remainder = requested - assigned;
|
|
2005
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
2006
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
2007
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2008
|
-
});
|
|
2009
|
-
for (const p of byHeadroom) {
|
|
2010
|
-
if (remainder === 0n) break;
|
|
2011
|
-
const headroom = p.balance - p.planned;
|
|
2012
|
-
if (headroom <= 0n) continue;
|
|
2013
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
2014
|
-
p.planned += take;
|
|
2015
|
-
remainder -= take;
|
|
2016
2020
|
}
|
|
2017
|
-
return
|
|
2018
|
-
}
|
|
2019
|
-
function planDisabledDrain(disabled, requested) {
|
|
2020
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
2021
|
-
const plans = [];
|
|
2022
|
-
let remaining = requested;
|
|
2023
|
-
for (const { agent, balance } of sorted) {
|
|
2024
|
-
if (remaining === 0n) {
|
|
2025
|
-
plans.push({ agent, balance, planned: 0n });
|
|
2026
|
-
continue;
|
|
2027
|
-
}
|
|
2028
|
-
const take = balance < remaining ? balance : remaining;
|
|
2029
|
-
plans.push({ agent, balance, planned: take });
|
|
2030
|
-
remaining -= take;
|
|
2031
|
-
}
|
|
2032
|
-
return { plans, remaining };
|
|
2033
|
-
}
|
|
2034
|
-
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
2035
|
-
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
2036
|
-
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
2037
|
-
const totalHeadroom = candidates.reduce(
|
|
2038
|
-
(s, c) => s + (c.balance - c.planned),
|
|
2039
|
-
0n
|
|
2040
|
-
);
|
|
2041
|
-
if (totalHeadroom === 0n) return;
|
|
2042
|
-
let distributed = 0n;
|
|
2043
|
-
for (const c of candidates) {
|
|
2044
|
-
const headroom = c.balance - c.planned;
|
|
2045
|
-
const proportional = headroom * amount / totalHeadroom;
|
|
2046
|
-
const give = proportional > headroom ? headroom : proportional;
|
|
2047
|
-
c.planned += give;
|
|
2048
|
-
distributed += give;
|
|
2049
|
-
}
|
|
2050
|
-
let leftover = amount - distributed;
|
|
2051
|
-
for (const c of candidates) {
|
|
2052
|
-
if (leftover === 0n) break;
|
|
2053
|
-
const headroom = c.balance - c.planned;
|
|
2054
|
-
if (headroom <= 0n) continue;
|
|
2055
|
-
const take = headroom < leftover ? headroom : leftover;
|
|
2056
|
-
c.planned += take;
|
|
2057
|
-
leftover -= take;
|
|
2058
|
-
}
|
|
2059
|
-
}
|
|
2060
|
-
function sumWithdrawnAmount(results) {
|
|
2061
|
-
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
2062
|
-
}
|
|
2063
|
-
|
|
2064
|
-
// src/lib/helpers/account-apy-helper.ts
|
|
2065
|
-
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
2066
|
-
const sums = {};
|
|
2067
|
-
const weights = {};
|
|
2068
|
-
for (const id of Object.keys(agentApys)) {
|
|
2069
|
-
const cells = agentApys[id].apyByChainAndAsset;
|
|
2070
|
-
const balance = agentBalances[id] ?? 0;
|
|
2071
|
-
if (!cells || balance <= 0) continue;
|
|
2072
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
2073
|
-
if (!perAsset) continue;
|
|
2074
|
-
const chainId = Number(chainKey);
|
|
2075
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
2076
|
-
const apy = Number(apyValue ?? 0);
|
|
2077
|
-
if (apy === 0) continue;
|
|
2078
|
-
sums[chainId] ??= {};
|
|
2079
|
-
weights[chainId] ??= {};
|
|
2080
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
2081
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
|
-
const out = {};
|
|
2086
|
-
for (const chainKey of Object.keys(sums)) {
|
|
2087
|
-
const chainId = Number(chainKey);
|
|
2088
|
-
const perAssetOut = {};
|
|
2089
|
-
for (const asset of Object.keys(sums[chainId])) {
|
|
2090
|
-
const w = weights[chainId][asset];
|
|
2091
|
-
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
2092
|
-
}
|
|
2093
|
-
if (Object.keys(perAssetOut).length > 0) {
|
|
2094
|
-
out[chainId] = perAssetOut;
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
return out;
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
// src/client.ts
|
|
2101
|
-
var import_viem6 = require("viem");
|
|
2102
|
-
var import_chains2 = require("viem/chains");
|
|
2103
|
-
|
|
2104
|
-
// src/lib/transfer-auth.ts
|
|
2105
|
-
var import_viem3 = require("viem");
|
|
2106
|
-
var ERC20_META_ABI = [
|
|
2107
|
-
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2108
|
-
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
2109
|
-
];
|
|
2110
|
-
function buildTransferWithAuthorizationTypedData(input) {
|
|
2111
|
-
return {
|
|
2112
|
-
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
2113
|
-
types: {
|
|
2114
|
-
TransferWithAuthorization: [
|
|
2115
|
-
{ name: "from", type: "address" },
|
|
2116
|
-
{ name: "to", type: "address" },
|
|
2117
|
-
{ name: "value", type: "uint256" },
|
|
2118
|
-
{ name: "validAfter", type: "uint256" },
|
|
2119
|
-
{ name: "validBefore", type: "uint256" },
|
|
2120
|
-
{ name: "nonce", type: "bytes32" }
|
|
2121
|
-
]
|
|
2122
|
-
},
|
|
2123
|
-
primaryType: "TransferWithAuthorization",
|
|
2124
|
-
message: input.message
|
|
2125
|
-
};
|
|
2126
|
-
}
|
|
2127
|
-
async function readTokenMeta(publicClient, token) {
|
|
2128
|
-
const [tokenName, tokenVersion] = await Promise.all([
|
|
2129
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
2130
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
2131
|
-
]);
|
|
2132
|
-
return { tokenName, tokenVersion };
|
|
2133
|
-
}
|
|
2134
|
-
function randomAuthNonce() {
|
|
2135
|
-
const bytes = new Uint8Array(32);
|
|
2136
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2137
|
-
return (0, import_viem3.bytesToHex)(bytes);
|
|
2138
|
-
}
|
|
2139
|
-
|
|
2140
|
-
// src/lib/sponsor-client.ts
|
|
2141
|
-
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2142
|
-
async function postSponsorTransferAuth(input) {
|
|
2143
|
-
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2144
|
-
let res;
|
|
2145
|
-
try {
|
|
2146
|
-
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2147
|
-
method: "POST",
|
|
2148
|
-
headers: {
|
|
2149
|
-
"content-type": "application/json",
|
|
2150
|
-
"x-owney-api-key": input.apiKey
|
|
2151
|
-
},
|
|
2152
|
-
body: JSON.stringify(input.body)
|
|
2153
|
-
});
|
|
2154
|
-
} catch (networkError) {
|
|
2155
|
-
throw new OwneyError(
|
|
2156
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2157
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2158
|
-
{ cause: String(networkError) }
|
|
2159
|
-
);
|
|
2160
|
-
}
|
|
2161
|
-
const text = await res.text();
|
|
2162
|
-
let parsed = null;
|
|
2163
|
-
try {
|
|
2164
|
-
parsed = JSON.parse(text);
|
|
2165
|
-
} catch {
|
|
2166
|
-
}
|
|
2167
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2168
|
-
throw new OwneyError(
|
|
2169
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2170
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2171
|
-
{
|
|
2172
|
-
statusCode: res.status,
|
|
2173
|
-
responseBody: text.slice(0, 500),
|
|
2174
|
-
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2175
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2176
|
-
safeToFallback: res.status === 503
|
|
2177
|
-
}
|
|
2178
|
-
);
|
|
2179
|
-
}
|
|
2180
|
-
return parsed.data;
|
|
2021
|
+
return parsed.data;
|
|
2181
2022
|
}
|
|
2182
2023
|
async function postSponsorPermit2Transfer(input) {
|
|
2183
|
-
const
|
|
2024
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2184
2025
|
let res;
|
|
2185
2026
|
try {
|
|
2186
|
-
res = await fetch(`${
|
|
2027
|
+
res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
|
|
2187
2028
|
method: "POST",
|
|
2188
2029
|
headers: {
|
|
2189
2030
|
"content-type": "application/json",
|
|
2190
|
-
"x-owney-api-key": input.apiKey
|
|
2031
|
+
"x-owney-api-key": input.apiKey,
|
|
2032
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2191
2033
|
},
|
|
2192
2034
|
body: JSON.stringify(input.body)
|
|
2193
2035
|
});
|
|
@@ -2218,11 +2060,11 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2218
2060
|
return parsed.data;
|
|
2219
2061
|
}
|
|
2220
2062
|
async function getSponsorRelayerAddress(input) {
|
|
2221
|
-
const
|
|
2063
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2222
2064
|
let res;
|
|
2223
2065
|
try {
|
|
2224
2066
|
res = await fetch(
|
|
2225
|
-
`${
|
|
2067
|
+
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2226
2068
|
{
|
|
2227
2069
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2228
2070
|
}
|
|
@@ -2255,7 +2097,7 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2255
2097
|
}
|
|
2256
2098
|
|
|
2257
2099
|
// src/lib/permit2.ts
|
|
2258
|
-
var
|
|
2100
|
+
var import_viem3 = require("viem");
|
|
2259
2101
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2260
2102
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2261
2103
|
var ERC20_ALLOWANCE_ABI = [
|
|
@@ -2313,7 +2155,7 @@ function buildPermitTransferFromTypedData(input) {
|
|
|
2313
2155
|
function randomPermit2Nonce() {
|
|
2314
2156
|
const bytes = new Uint8Array(32);
|
|
2315
2157
|
globalThis.crypto.getRandomValues(bytes);
|
|
2316
|
-
return BigInt((0,
|
|
2158
|
+
return BigInt((0, import_viem3.bytesToHex)(bytes));
|
|
2317
2159
|
}
|
|
2318
2160
|
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2319
2161
|
return publicClient.readContract({
|
|
@@ -2331,205 +2173,1864 @@ async function readErc20Balance(publicClient, token, owner) {
|
|
|
2331
2173
|
args: [owner]
|
|
2332
2174
|
});
|
|
2333
2175
|
}
|
|
2334
|
-
|
|
2335
|
-
// src/lib/
|
|
2336
|
-
var
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
}
|
|
2341
|
-
function
|
|
2342
|
-
|
|
2176
|
+
|
|
2177
|
+
// src/lib/sponsored-deposit.ts
|
|
2178
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2179
|
+
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2180
|
+
function provideDepositVerificationContext(callback, context) {
|
|
2181
|
+
callback[verificationSetter]?.(context);
|
|
2182
|
+
}
|
|
2183
|
+
function makeVerificationAwareDepositCallback(implementation) {
|
|
2184
|
+
let nextVerification;
|
|
2185
|
+
const callback = async (smartWallet, chainId, amount) => {
|
|
2186
|
+
const verification = nextVerification;
|
|
2187
|
+
nextVerification = void 0;
|
|
2188
|
+
return implementation(smartWallet, chainId, amount, verification);
|
|
2189
|
+
};
|
|
2190
|
+
Object.defineProperty(callback, verificationSetter, {
|
|
2191
|
+
value: (context) => {
|
|
2192
|
+
nextVerification = context;
|
|
2193
|
+
}
|
|
2194
|
+
});
|
|
2195
|
+
return callback;
|
|
2196
|
+
}
|
|
2197
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2198
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2199
|
+
return makeVerificationAwareDepositCallback(
|
|
2200
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
2201
|
+
const cid = chainId;
|
|
2202
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2203
|
+
if (!token) {
|
|
2204
|
+
throw new OwneyError(
|
|
2205
|
+
"CHAIN_UNSUPPORTED",
|
|
2206
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
const pub = deps.getPublicClient(cid);
|
|
2210
|
+
const wallet = deps.getWalletClient(cid);
|
|
2211
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2212
|
+
try {
|
|
2213
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2214
|
+
if (balance < BigInt(amount)) {
|
|
2215
|
+
throw new OwneyError(
|
|
2216
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2217
|
+
"Insufficient balance for this deposit.",
|
|
2218
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2219
|
+
);
|
|
2220
|
+
}
|
|
2221
|
+
} catch (err) {
|
|
2222
|
+
if (err instanceof OwneyError) throw err;
|
|
2223
|
+
console.warn(
|
|
2224
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2225
|
+
err instanceof Error ? err.message : String(err)
|
|
2226
|
+
);
|
|
2227
|
+
}
|
|
2228
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2229
|
+
const validAfter = 0n;
|
|
2230
|
+
const validBefore = BigInt(
|
|
2231
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2232
|
+
);
|
|
2233
|
+
const nonce = randomAuthNonce();
|
|
2234
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2235
|
+
token,
|
|
2236
|
+
chainId: cid,
|
|
2237
|
+
tokenName,
|
|
2238
|
+
tokenVersion,
|
|
2239
|
+
message: {
|
|
2240
|
+
from: deps.ownerAddress,
|
|
2241
|
+
to: smartWallet,
|
|
2242
|
+
value: BigInt(amount),
|
|
2243
|
+
validAfter,
|
|
2244
|
+
validBefore,
|
|
2245
|
+
nonce
|
|
2246
|
+
}
|
|
2247
|
+
});
|
|
2248
|
+
const authSignature = await wallet.signTypedData({
|
|
2249
|
+
account: deps.ownerAddress,
|
|
2250
|
+
...typedData
|
|
2251
|
+
});
|
|
2252
|
+
deps.onApproved?.();
|
|
2253
|
+
const result = await post({
|
|
2254
|
+
baseUrl: deps.baseUrl,
|
|
2255
|
+
apiKey: deps.apiKey,
|
|
2256
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2257
|
+
body: {
|
|
2258
|
+
chainId: cid,
|
|
2259
|
+
token,
|
|
2260
|
+
from: deps.ownerAddress,
|
|
2261
|
+
to: smartWallet,
|
|
2262
|
+
value: amount,
|
|
2263
|
+
validAfter: validAfter.toString(),
|
|
2264
|
+
validBefore: validBefore.toString(),
|
|
2265
|
+
nonce,
|
|
2266
|
+
authSignature,
|
|
2267
|
+
tokenName,
|
|
2268
|
+
tokenVersion,
|
|
2269
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
2270
|
+
yieldseekerUserId: verification.userId,
|
|
2271
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
2272
|
+
} : {}
|
|
2273
|
+
}
|
|
2274
|
+
});
|
|
2275
|
+
return result.txHash;
|
|
2276
|
+
}
|
|
2277
|
+
);
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2281
|
+
var import_siwe = require("siwe");
|
|
2282
|
+
var import_viem4 = require("viem");
|
|
2283
|
+
var import_chains2 = require("viem/chains");
|
|
2284
|
+
|
|
2285
|
+
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2286
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session.v4";
|
|
2287
|
+
var INVALIDATED_KEY_PREFIXES = [
|
|
2288
|
+
"owney.yieldseeker.session",
|
|
2289
|
+
"owney.yieldseeker.session.v3"
|
|
2290
|
+
];
|
|
2291
|
+
var storage2 = () => {
|
|
2292
|
+
if (typeof window === "undefined") return null;
|
|
2293
|
+
try {
|
|
2294
|
+
return window.localStorage;
|
|
2295
|
+
} catch {
|
|
2296
|
+
return null;
|
|
2297
|
+
}
|
|
2298
|
+
};
|
|
2299
|
+
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2300
|
+
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2301
|
+
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2302
|
+
);
|
|
2303
|
+
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2304
|
+
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2305
|
+
memorySessions2.delete(key2);
|
|
2306
|
+
try {
|
|
2307
|
+
store?.removeItem(key2);
|
|
2308
|
+
} catch {
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
};
|
|
2312
|
+
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2313
|
+
var isValidSession = (session) => {
|
|
2314
|
+
if (!session?.token) return false;
|
|
2315
|
+
try {
|
|
2316
|
+
const parsed = JSON.parse(atob(session.token));
|
|
2317
|
+
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2318
|
+
} catch {
|
|
2319
|
+
return false;
|
|
2320
|
+
}
|
|
2321
|
+
};
|
|
2322
|
+
var readYieldseekerSession = (address, chainId) => {
|
|
2323
|
+
if (typeof window === "undefined") return null;
|
|
2324
|
+
const key2 = buildKey2(address, chainId);
|
|
2325
|
+
const store = storage2();
|
|
2326
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2327
|
+
let raw2 = null;
|
|
2328
|
+
try {
|
|
2329
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
2330
|
+
} catch {
|
|
2331
|
+
raw2 = null;
|
|
2332
|
+
}
|
|
2333
|
+
if (raw2) {
|
|
2334
|
+
try {
|
|
2335
|
+
const parsed = JSON.parse(raw2);
|
|
2336
|
+
if (isValidSession(parsed)) return parsed.token;
|
|
2337
|
+
} catch {
|
|
2338
|
+
}
|
|
2339
|
+
memorySessions2.delete(key2);
|
|
2340
|
+
try {
|
|
2341
|
+
store?.removeItem(key2);
|
|
2342
|
+
} catch {
|
|
2343
|
+
}
|
|
2344
|
+
return null;
|
|
2345
|
+
}
|
|
2346
|
+
const cached = memorySessions2.get(key2);
|
|
2347
|
+
if (isValidSession(cached)) return cached.token;
|
|
2348
|
+
if (cached) memorySessions2.delete(key2);
|
|
2349
|
+
return null;
|
|
2350
|
+
};
|
|
2351
|
+
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2352
|
+
if (typeof window === "undefined") return;
|
|
2353
|
+
const session = { token };
|
|
2354
|
+
if (!isValidSession(session)) return;
|
|
2355
|
+
const key2 = buildKey2(address, chainId);
|
|
2356
|
+
memorySessions2.set(key2, session);
|
|
2357
|
+
const store = storage2();
|
|
2358
|
+
try {
|
|
2359
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2360
|
+
} catch {
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
var clearYieldseekerSession = (address, chainId) => {
|
|
2364
|
+
const key2 = buildKey2(address, chainId);
|
|
2365
|
+
memorySessions2.delete(key2);
|
|
2366
|
+
const store = storage2();
|
|
2367
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2368
|
+
try {
|
|
2369
|
+
store?.removeItem(key2);
|
|
2370
|
+
} catch {
|
|
2371
|
+
}
|
|
2372
|
+
};
|
|
2373
|
+
|
|
2374
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2375
|
+
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2376
|
+
return new import_siwe.SiweMessage({
|
|
2377
|
+
domain: "yieldseeker.xyz",
|
|
2378
|
+
address: (0, import_viem4.getAddress)(address),
|
|
2379
|
+
uri: "https://yieldseeker.xyz",
|
|
2380
|
+
version: "1",
|
|
2381
|
+
chainId,
|
|
2382
|
+
nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
|
|
2383
|
+
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2384
|
+
}).prepareMessage();
|
|
2385
|
+
}
|
|
2386
|
+
function encodeYieldseekerAuthToken(token) {
|
|
2387
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2388
|
+
let binary = "";
|
|
2389
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2390
|
+
return btoa(binary);
|
|
2391
|
+
}
|
|
2392
|
+
var YieldseekerAuth = class {
|
|
2393
|
+
constructor(dependencies = {}) {
|
|
2394
|
+
this.dependencies = dependencies;
|
|
2395
|
+
}
|
|
2396
|
+
dependencies;
|
|
2397
|
+
tokens = /* @__PURE__ */ new Map();
|
|
2398
|
+
pending = /* @__PURE__ */ new Map();
|
|
2399
|
+
scopes = /* @__PURE__ */ new Map();
|
|
2400
|
+
key(state, chainId) {
|
|
2401
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
2402
|
+
}
|
|
2403
|
+
async getToken(state, chainId) {
|
|
2404
|
+
const key2 = this.key(state, chainId);
|
|
2405
|
+
const scope = { address: state.walletAddress, chainId };
|
|
2406
|
+
this.scopes.set(key2, scope);
|
|
2407
|
+
const cached = this.tokens.get(key2);
|
|
2408
|
+
if (cached) return cached;
|
|
2409
|
+
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2410
|
+
if (persisted) {
|
|
2411
|
+
this.tokens.set(key2, persisted);
|
|
2412
|
+
return persisted;
|
|
2413
|
+
}
|
|
2414
|
+
const inFlight = this.pending.get(key2);
|
|
2415
|
+
if (inFlight) return inFlight;
|
|
2416
|
+
const request = this.sign(state, chainId).then((token) => {
|
|
2417
|
+
this.tokens.set(key2, token);
|
|
2418
|
+
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2419
|
+
return token;
|
|
2420
|
+
});
|
|
2421
|
+
this.pending.set(key2, request);
|
|
2422
|
+
try {
|
|
2423
|
+
return await request;
|
|
2424
|
+
} finally {
|
|
2425
|
+
this.pending.delete(key2);
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
clear(state, chainId) {
|
|
2429
|
+
if (!state || chainId === void 0) {
|
|
2430
|
+
for (const scope of this.scopes.values()) {
|
|
2431
|
+
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2432
|
+
}
|
|
2433
|
+
this.tokens.clear();
|
|
2434
|
+
this.pending.clear();
|
|
2435
|
+
this.scopes.clear();
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
const key2 = this.key(state, chainId);
|
|
2439
|
+
this.tokens.delete(key2);
|
|
2440
|
+
this.pending.delete(key2);
|
|
2441
|
+
this.scopes.delete(key2);
|
|
2442
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2443
|
+
}
|
|
2444
|
+
async sign(state, chainId) {
|
|
2445
|
+
const account = (0, import_viem4.getAddress)(state.walletAddress);
|
|
2446
|
+
const publicClient = (0, import_viem4.createPublicClient)({
|
|
2447
|
+
chain: import_chains2.base,
|
|
2448
|
+
transport: (0, import_viem4.custom)(state.provider)
|
|
2449
|
+
});
|
|
2450
|
+
const walletClient = (0, import_viem4.createWalletClient)({
|
|
2451
|
+
account,
|
|
2452
|
+
chain: import_chains2.base,
|
|
2453
|
+
transport: (0, import_viem4.custom)(state.provider)
|
|
2454
|
+
});
|
|
2455
|
+
await ensureWalletOnChain(
|
|
2456
|
+
publicClient,
|
|
2457
|
+
walletClient,
|
|
2458
|
+
8453
|
|
2459
|
+
);
|
|
2460
|
+
const message = createYieldseekerSiweMessage(
|
|
2461
|
+
account,
|
|
2462
|
+
chainId,
|
|
2463
|
+
this.dependencies
|
|
2464
|
+
);
|
|
2465
|
+
const signature = await walletClient.signMessage({ account, message });
|
|
2466
|
+
return encodeYieldseekerAuthToken({ message, signature });
|
|
2467
|
+
}
|
|
2468
|
+
};
|
|
2469
|
+
|
|
2470
|
+
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2471
|
+
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2472
|
+
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2473
|
+
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2474
|
+
var storage3 = () => {
|
|
2475
|
+
if (typeof window === "undefined") return null;
|
|
2476
|
+
try {
|
|
2477
|
+
return window.localStorage;
|
|
2478
|
+
} catch {
|
|
2479
|
+
return null;
|
|
2480
|
+
}
|
|
2481
|
+
};
|
|
2482
|
+
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2483
|
+
function valid(value, walletAddress, chainId, now) {
|
|
2484
|
+
return Boolean(
|
|
2485
|
+
value && typeof value.userId === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(value.userId) && value.walletAddress.toLowerCase() === walletAddress.toLowerCase() && value.chainId === chainId && Number.isSafeInteger(value.expiresAt) && value.expiresAt > now
|
|
2486
|
+
);
|
|
2487
|
+
}
|
|
2488
|
+
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2489
|
+
if (typeof window === "undefined") return null;
|
|
2490
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2491
|
+
const store = storage3();
|
|
2492
|
+
let parsed = null;
|
|
2493
|
+
try {
|
|
2494
|
+
const raw2 = store?.getItem(key2);
|
|
2495
|
+
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2496
|
+
} catch {
|
|
2497
|
+
parsed = null;
|
|
2498
|
+
}
|
|
2499
|
+
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2500
|
+
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2501
|
+
memoryIdentities.set(key2, candidate);
|
|
2502
|
+
return { userId: candidate.userId };
|
|
2503
|
+
}
|
|
2504
|
+
memoryIdentities.delete(key2);
|
|
2505
|
+
try {
|
|
2506
|
+
store?.removeItem(key2);
|
|
2507
|
+
} catch {
|
|
2508
|
+
}
|
|
2509
|
+
return null;
|
|
2510
|
+
}
|
|
2511
|
+
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2512
|
+
if (typeof window === "undefined") return;
|
|
2513
|
+
const identity = {
|
|
2514
|
+
userId,
|
|
2515
|
+
walletAddress,
|
|
2516
|
+
chainId,
|
|
2517
|
+
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2518
|
+
};
|
|
2519
|
+
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2520
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2521
|
+
memoryIdentities.set(key2, identity);
|
|
2522
|
+
try {
|
|
2523
|
+
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2524
|
+
} catch {
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2528
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2529
|
+
memoryIdentities.delete(key2);
|
|
2530
|
+
try {
|
|
2531
|
+
storage3()?.removeItem(key2);
|
|
2532
|
+
} catch {
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2537
|
+
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2538
|
+
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2539
|
+
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2540
|
+
}
|
|
2541
|
+
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2542
|
+
var YieldseekerApiError = class extends Error {
|
|
2543
|
+
constructor(status, providerCode, responseFields) {
|
|
2544
|
+
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2545
|
+
this.status = status;
|
|
2546
|
+
this.providerCode = providerCode;
|
|
2547
|
+
this.responseFields = responseFields;
|
|
2548
|
+
this.name = "YieldseekerApiError";
|
|
2549
|
+
}
|
|
2550
|
+
status;
|
|
2551
|
+
providerCode;
|
|
2552
|
+
responseFields;
|
|
2553
|
+
get isAuthenticationError() {
|
|
2554
|
+
return this.status === 401 || this.status === 403;
|
|
2555
|
+
}
|
|
2556
|
+
};
|
|
2557
|
+
function providerError(body, fallback) {
|
|
2558
|
+
if (!body || typeof body !== "object") return { code: fallback };
|
|
2559
|
+
const record = body;
|
|
2560
|
+
return {
|
|
2561
|
+
code: typeof record.message === "string" ? record.message : fallback,
|
|
2562
|
+
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
var YieldseekerApiClient = class {
|
|
2566
|
+
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
|
|
2567
|
+
this.owneyApiKey = owneyApiKey;
|
|
2568
|
+
this.baseUrl = baseUrl;
|
|
2569
|
+
this.fetchFn = fetchFn;
|
|
2570
|
+
}
|
|
2571
|
+
owneyApiKey;
|
|
2572
|
+
baseUrl;
|
|
2573
|
+
fetchFn;
|
|
2574
|
+
async request(path, options = {}) {
|
|
2575
|
+
const controller = new AbortController();
|
|
2576
|
+
const timer = setTimeout(
|
|
2577
|
+
() => controller.abort(),
|
|
2578
|
+
options.timeoutMs ?? 15e3
|
|
2579
|
+
);
|
|
2580
|
+
try {
|
|
2581
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2582
|
+
method: options.method ?? "GET",
|
|
2583
|
+
headers: {
|
|
2584
|
+
"Content-Type": "application/json",
|
|
2585
|
+
"x-owney-api-key": this.owneyApiKey,
|
|
2586
|
+
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2587
|
+
},
|
|
2588
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2589
|
+
signal: controller.signal
|
|
2590
|
+
});
|
|
2591
|
+
const payload = await response.json().catch(() => null);
|
|
2592
|
+
if (!response.ok) {
|
|
2593
|
+
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2594
|
+
throw new YieldseekerApiError(
|
|
2595
|
+
response.status,
|
|
2596
|
+
error.code,
|
|
2597
|
+
error.fields
|
|
2598
|
+
);
|
|
2599
|
+
}
|
|
2600
|
+
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2601
|
+
return payload.data;
|
|
2602
|
+
}
|
|
2603
|
+
return payload;
|
|
2604
|
+
} catch (error) {
|
|
2605
|
+
if (error instanceof YieldseekerApiError) throw error;
|
|
2606
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2607
|
+
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2608
|
+
}
|
|
2609
|
+
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2610
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2611
|
+
});
|
|
2612
|
+
} finally {
|
|
2613
|
+
clearTimeout(timer);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
};
|
|
2617
|
+
|
|
2618
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2619
|
+
var import_viem5 = require("viem");
|
|
2620
|
+
|
|
2621
|
+
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2622
|
+
var YIELDSEEKER_ASSET_METADATA = {
|
|
2623
|
+
USDC: {
|
|
2624
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2625
|
+
decimals: 6
|
|
2626
|
+
},
|
|
2627
|
+
WETH: {
|
|
2628
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
2629
|
+
decimals: 18
|
|
2630
|
+
}
|
|
2631
|
+
};
|
|
2632
|
+
|
|
2633
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2634
|
+
function invalid(endpoint, detail) {
|
|
2635
|
+
throw new OwneyError(
|
|
2636
|
+
"AGENT_INVALID_RESPONSE",
|
|
2637
|
+
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2638
|
+
{ endpoint, detail },
|
|
2639
|
+
"yieldseeker"
|
|
2640
|
+
);
|
|
2641
|
+
}
|
|
2642
|
+
function raw(value, endpoint) {
|
|
2643
|
+
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2644
|
+
return invalid(endpoint, "expected a base-10 integer string");
|
|
2645
|
+
}
|
|
2646
|
+
return BigInt(value);
|
|
2647
|
+
}
|
|
2648
|
+
function decimal(value, decimals, endpoint) {
|
|
2649
|
+
return (0, import_viem5.formatUnits)(raw(value, endpoint), decimals);
|
|
2650
|
+
}
|
|
2651
|
+
function usd(rawAmount, decimals, price) {
|
|
2652
|
+
return Number((0, import_viem5.formatUnits)(rawAmount, decimals)) * price;
|
|
2653
|
+
}
|
|
2654
|
+
function percent(value) {
|
|
2655
|
+
const result = Number(value);
|
|
2656
|
+
return Number.isFinite(result) ? result * 100 : 0;
|
|
2657
|
+
}
|
|
2658
|
+
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2659
|
+
function publicApyAfterYieldseekerFee(value) {
|
|
2660
|
+
const grossPercent = percent(value);
|
|
2661
|
+
if (grossPercent <= 0) return grossPercent;
|
|
2662
|
+
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2663
|
+
return Math.round(netPercent * 1e12) / 1e12;
|
|
2664
|
+
}
|
|
2665
|
+
function riskAdjustedApyForDays(option, days) {
|
|
2666
|
+
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2667
|
+
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2668
|
+
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2669
|
+
}
|
|
2670
|
+
function assetAddressValue(record, address) {
|
|
2671
|
+
const entry = Object.entries(record).find(
|
|
2672
|
+
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2673
|
+
);
|
|
2674
|
+
return entry?.[1] ?? "0";
|
|
2675
|
+
}
|
|
2676
|
+
function position(value, asset) {
|
|
2677
|
+
const option = value?.yieldOption;
|
|
2678
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem5.isAddress)(option.address)) {
|
|
2679
|
+
return invalid("yield positions", "missing vault metadata");
|
|
2680
|
+
}
|
|
2681
|
+
const decimals = YIELDSEEKER_ASSET_METADATA[asset].decimals;
|
|
2682
|
+
return {
|
|
2683
|
+
chain: "BASE",
|
|
2684
|
+
protocol: option.provider,
|
|
2685
|
+
protocolId: option.address,
|
|
2686
|
+
pool: option.name,
|
|
2687
|
+
asset,
|
|
2688
|
+
amount: decimal(value.assetsRaw, decimals, "yield positions"),
|
|
2689
|
+
amountRaw: String(value.assetsRaw),
|
|
2690
|
+
apy: percent(option.riskAdjustedApy),
|
|
2691
|
+
tvl: Number(option.totalDepositsUsd),
|
|
2692
|
+
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
function mapYieldseekerBalances(contexts) {
|
|
2696
|
+
const tokens = [];
|
|
2697
|
+
const positions = [];
|
|
2698
|
+
let totalUsd = 0;
|
|
2699
|
+
for (const context of contexts) {
|
|
2700
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2701
|
+
const idle = assetAddressValue(
|
|
2702
|
+
context.snapshot.tokenBalances,
|
|
2703
|
+
metadata.address
|
|
2704
|
+
);
|
|
2705
|
+
tokens.push({
|
|
2706
|
+
chain: "BASE",
|
|
2707
|
+
chainId: 8453,
|
|
2708
|
+
asset: context.asset,
|
|
2709
|
+
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2710
|
+
});
|
|
2711
|
+
positions.push(
|
|
2712
|
+
...context.positions.map((entry) => position(entry, context.asset))
|
|
2713
|
+
);
|
|
2714
|
+
totalUsd += usd(
|
|
2715
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2716
|
+
context.snapshot.baseAssetDecimals,
|
|
2717
|
+
context.snapshot.baseAssetPriceUsd
|
|
2718
|
+
);
|
|
2719
|
+
}
|
|
2720
|
+
return {
|
|
2721
|
+
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2722
|
+
totalBalance: String(totalUsd),
|
|
2723
|
+
totalBalanceAsset: "usdc",
|
|
2724
|
+
tokens,
|
|
2725
|
+
positions
|
|
2726
|
+
};
|
|
2727
|
+
}
|
|
2728
|
+
function mapYieldseekerEarnings(contexts) {
|
|
2729
|
+
const tokens = [];
|
|
2730
|
+
let lifetimeEarnings = 0;
|
|
2731
|
+
for (const context of contexts) {
|
|
2732
|
+
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
2733
|
+
tokens.push({
|
|
2734
|
+
chain: "BASE",
|
|
2735
|
+
chainId: 8453,
|
|
2736
|
+
asset: context.asset,
|
|
2737
|
+
amount: (0, import_viem5.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
2738
|
+
});
|
|
2739
|
+
lifetimeEarnings += usd(
|
|
2740
|
+
amount,
|
|
2741
|
+
context.snapshot.baseAssetDecimals,
|
|
2742
|
+
context.snapshot.baseAssetPriceUsd
|
|
2743
|
+
);
|
|
2744
|
+
}
|
|
2745
|
+
return {
|
|
2746
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2747
|
+
lifetimeEarnings,
|
|
2748
|
+
tokens
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
function apyForDays(snapshot, days) {
|
|
2752
|
+
if (days === "7D") return percent(snapshot.apy7d);
|
|
2753
|
+
if (days === "30D") return percent(snapshot.apy30d);
|
|
2754
|
+
return percent((snapshot.apy7d + snapshot.apy30d) / 2);
|
|
2755
|
+
}
|
|
2756
|
+
function dailyApy(point) {
|
|
2757
|
+
const total = raw(point.totalValueBase, "historic position");
|
|
2758
|
+
const earned = raw(point.dailyYieldBase, "historic position");
|
|
2759
|
+
const principal = total - earned;
|
|
2760
|
+
if (principal <= 0n || earned === 0n) return 0;
|
|
2761
|
+
return Number(earned) / Number(principal) * 365 * 100;
|
|
2762
|
+
}
|
|
2763
|
+
function aggregateHistory(contexts, dayCount) {
|
|
2764
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
2765
|
+
for (const context of contexts) {
|
|
2766
|
+
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
2767
|
+
for (const point of points.slice(-dayCount)) {
|
|
2768
|
+
const valueUsd = usd(
|
|
2769
|
+
raw(point.totalValueBase, "historic position"),
|
|
2770
|
+
point.baseAssetDecimals,
|
|
2771
|
+
point.baseAssetPriceUsd
|
|
2772
|
+
);
|
|
2773
|
+
const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
|
|
2774
|
+
current.weighted += dailyApy(point) * valueUsd;
|
|
2775
|
+
current.valueUsd += valueUsd;
|
|
2776
|
+
byDate.set(point.date, current);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
2780
|
+
date,
|
|
2781
|
+
apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
|
|
2782
|
+
}));
|
|
2783
|
+
}
|
|
2784
|
+
function mapYieldseekerApy(walletAddress, contexts, days) {
|
|
2785
|
+
let weighted = 0;
|
|
2786
|
+
let totalUsd = 0;
|
|
2787
|
+
const byAsset = {};
|
|
2788
|
+
for (const context of contexts) {
|
|
2789
|
+
const valueUsd = usd(
|
|
2790
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2791
|
+
context.snapshot.baseAssetDecimals,
|
|
2792
|
+
context.snapshot.baseAssetPriceUsd
|
|
2793
|
+
);
|
|
2794
|
+
const apy = apyForDays(context.snapshot, days);
|
|
2795
|
+
weighted += apy * valueUsd;
|
|
2796
|
+
totalUsd += valueUsd;
|
|
2797
|
+
byAsset[context.asset] = apy;
|
|
2798
|
+
}
|
|
2799
|
+
const dayCount = Number(days.slice(0, -1));
|
|
2800
|
+
return {
|
|
2801
|
+
walletAddress,
|
|
2802
|
+
weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
|
|
2803
|
+
apyByChainAndAsset: { 8453: byAsset },
|
|
2804
|
+
history: aggregateHistory(contexts, dayCount)
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2807
|
+
function actionType(value) {
|
|
2808
|
+
const normalized = value.toLowerCase();
|
|
2809
|
+
if (normalized.includes("deposit")) return "Deposit";
|
|
2810
|
+
if (normalized.includes("withdraw")) return "Withdraw";
|
|
2811
|
+
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
2812
|
+
return "Earned";
|
|
2813
|
+
return "Rebalance";
|
|
2814
|
+
}
|
|
2815
|
+
function transactionHashes(details) {
|
|
2816
|
+
if (!details) return [];
|
|
2817
|
+
const values = [
|
|
2818
|
+
details.transactionHash,
|
|
2819
|
+
details.txHash,
|
|
2820
|
+
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
2821
|
+
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
2822
|
+
];
|
|
2823
|
+
return values.filter(
|
|
2824
|
+
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
2825
|
+
).filter((value, index, all) => all.indexOf(value) === index);
|
|
2826
|
+
}
|
|
2827
|
+
function actionEntry(action) {
|
|
2828
|
+
return {
|
|
2829
|
+
agent: "yieldseeker",
|
|
2830
|
+
action: actionType(action.actionType),
|
|
2831
|
+
date: action.createdDate,
|
|
2832
|
+
oldApy: null,
|
|
2833
|
+
newApy: null,
|
|
2834
|
+
transactions: [
|
|
2835
|
+
{
|
|
2836
|
+
txHashes: transactionHashes(action.details),
|
|
2837
|
+
chainId: 8453
|
|
2838
|
+
}
|
|
2839
|
+
],
|
|
2840
|
+
rebalanceLog: []
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
function movementEntry(movement, wallet, agent, asset) {
|
|
2844
|
+
const isDeposit = movement.toAddress.toLowerCase() === wallet.walletAddress.toLowerCase();
|
|
2845
|
+
return {
|
|
2846
|
+
agent: "yieldseeker",
|
|
2847
|
+
action: isDeposit ? "Deposit" : "Withdraw",
|
|
2848
|
+
date: movement.blockDate,
|
|
2849
|
+
oldApy: null,
|
|
2850
|
+
newApy: null,
|
|
2851
|
+
transactions: [
|
|
2852
|
+
{
|
|
2853
|
+
txHashes: [movement.transactionHash],
|
|
2854
|
+
chainId: agent.chainId,
|
|
2855
|
+
tokenSymbol: asset,
|
|
2856
|
+
amount: decimal(
|
|
2857
|
+
movement.assetAmount,
|
|
2858
|
+
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
2859
|
+
"historic position"
|
|
2860
|
+
)
|
|
2861
|
+
}
|
|
2862
|
+
],
|
|
2863
|
+
rebalanceLog: []
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
function mapYieldseekerHistory(contexts, options) {
|
|
2867
|
+
const entries = contexts.flatMap((context) => [
|
|
2868
|
+
...(context.actions ?? []).map(actionEntry),
|
|
2869
|
+
...(context.historic?.movements ?? []).map(
|
|
2870
|
+
(movement) => movementEntry(movement, context.wallet, context.agent, context.asset)
|
|
2871
|
+
)
|
|
2872
|
+
]);
|
|
2873
|
+
const filtered = entries.filter(
|
|
2874
|
+
(entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
|
|
2875
|
+
).filter(
|
|
2876
|
+
(entry, index, all) => all.findIndex(
|
|
2877
|
+
(candidate) => candidate.action === entry.action && candidate.date === entry.date && candidate.transactions[0]?.txHashes[0] === entry.transactions[0]?.txHashes[0]
|
|
2878
|
+
) === index
|
|
2879
|
+
).sort((left, right) => right.date.localeCompare(left.date));
|
|
2880
|
+
return {
|
|
2881
|
+
data: filtered.slice(0, options.limit),
|
|
2882
|
+
// v1 returns the whole action/movement collection and defines no cursor.
|
|
2883
|
+
// Report a terminal page so callers never loop over the same prefix.
|
|
2884
|
+
hasMore: false
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
function mapYieldseekerProfile(address, contexts) {
|
|
2888
|
+
const protocols = /* @__PURE__ */ new Set();
|
|
2889
|
+
for (const context of contexts) {
|
|
2890
|
+
for (const current of context.positions) {
|
|
2891
|
+
if (current.yieldOption?.provider) {
|
|
2892
|
+
protocols.add(String(current.yieldOption.provider));
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
return {
|
|
2897
|
+
address,
|
|
2898
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2899
|
+
chains: contexts.length > 0 ? [8453] : [],
|
|
2900
|
+
hasActiveSessionKey: contexts.some(
|
|
2901
|
+
(context) => context.wallet.initializedDate != null
|
|
2902
|
+
),
|
|
2903
|
+
protocols: [...protocols]
|
|
2904
|
+
};
|
|
2905
|
+
}
|
|
2906
|
+
function mapYieldseekerAgentApy(options, days) {
|
|
2907
|
+
const perAsset = {};
|
|
2908
|
+
const all = [];
|
|
2909
|
+
for (const entry of options) {
|
|
2910
|
+
const apys = entry.yieldOptions.map(
|
|
2911
|
+
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
2912
|
+
).filter(Number.isFinite);
|
|
2913
|
+
if (apys.length === 0) continue;
|
|
2914
|
+
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
2915
|
+
perAsset[entry.asset] = average;
|
|
2916
|
+
all.push(average);
|
|
2917
|
+
}
|
|
2918
|
+
return {
|
|
2919
|
+
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
2920
|
+
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
2921
|
+
};
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2925
|
+
var OWNEY_AGENT_NAME = "owney";
|
|
2926
|
+
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
2927
|
+
{
|
|
2928
|
+
type: "function",
|
|
2929
|
+
name: "withdrawAssetToUser",
|
|
2930
|
+
stateMutability: "nonpayable",
|
|
2931
|
+
inputs: [
|
|
2932
|
+
{ name: "recipient", type: "address" },
|
|
2933
|
+
{ name: "asset", type: "address" },
|
|
2934
|
+
{ name: "amount", type: "uint256" }
|
|
2935
|
+
],
|
|
2936
|
+
outputs: []
|
|
2937
|
+
},
|
|
2938
|
+
{
|
|
2939
|
+
type: "function",
|
|
2940
|
+
name: "withdrawAllAssetToUser",
|
|
2941
|
+
stateMutability: "nonpayable",
|
|
2942
|
+
inputs: [
|
|
2943
|
+
{ name: "recipient", type: "address" },
|
|
2944
|
+
{ name: "asset", type: "address" }
|
|
2945
|
+
],
|
|
2946
|
+
outputs: []
|
|
2947
|
+
}
|
|
2948
|
+
];
|
|
2949
|
+
function query(params) {
|
|
2950
|
+
const search = new URLSearchParams();
|
|
2951
|
+
for (const [key2, value] of Object.entries(params)) {
|
|
2952
|
+
if (value !== void 0) search.set(key2, String(value));
|
|
2953
|
+
}
|
|
2954
|
+
const encoded = search.toString();
|
|
2955
|
+
return encoded ? `?${encoded}` : "";
|
|
2956
|
+
}
|
|
2957
|
+
var YieldseekerAgent = class {
|
|
2958
|
+
id = "yieldseeker";
|
|
2959
|
+
balanceComposition = "tokens-plus-positions";
|
|
2960
|
+
supportedChainIds = [8453];
|
|
2961
|
+
supportedAssets = [
|
|
2962
|
+
{
|
|
2963
|
+
chainId: 8453,
|
|
2964
|
+
chain: "BASE",
|
|
2965
|
+
assets: [
|
|
2966
|
+
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
2967
|
+
{ symbol: "WETH", minDepositAmount: "1" }
|
|
2968
|
+
]
|
|
2969
|
+
}
|
|
2970
|
+
];
|
|
2971
|
+
api;
|
|
2972
|
+
auth;
|
|
2973
|
+
transactionExecutor;
|
|
2974
|
+
unwindReceiptWaiter;
|
|
2975
|
+
agentContexts = /* @__PURE__ */ new Map();
|
|
2976
|
+
users = /* @__PURE__ */ new Map();
|
|
2977
|
+
pendingAgents = /* @__PURE__ */ new Map();
|
|
2978
|
+
constructor(owneyApiKey, options = {}) {
|
|
2979
|
+
this.api = new YieldseekerApiClient(
|
|
2980
|
+
owneyApiKey,
|
|
2981
|
+
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
2982
|
+
options.fetchFn
|
|
2983
|
+
);
|
|
2984
|
+
this.auth = new YieldseekerAuth(options.auth);
|
|
2985
|
+
this.transactionExecutor = options.transactionExecutor;
|
|
2986
|
+
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
2987
|
+
}
|
|
2988
|
+
async disconnect() {
|
|
2989
|
+
this.auth.clear();
|
|
2990
|
+
for (const key2 of this.users.keys()) {
|
|
2991
|
+
const [walletAddress, chainId] = key2.split(":");
|
|
2992
|
+
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
2993
|
+
}
|
|
2994
|
+
this.users.clear();
|
|
2995
|
+
this.agentContexts.clear();
|
|
2996
|
+
this.pendingAgents.clear();
|
|
2997
|
+
}
|
|
2998
|
+
async activateAgent(state, chainId, asset) {
|
|
2999
|
+
this.assertChain(chainId);
|
|
3000
|
+
const targetAsset = asset ?? "USDC";
|
|
3001
|
+
this.assertAsset(targetAsset);
|
|
3002
|
+
await this.ensureAgent(state, chainId, targetAsset);
|
|
3003
|
+
}
|
|
3004
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3005
|
+
this.assertChain(chainId);
|
|
3006
|
+
this.assertAsset(asset);
|
|
3007
|
+
if (BigInt(amount) <= 0n) {
|
|
3008
|
+
throw new OwneyError(
|
|
3009
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
3010
|
+
"Yieldseeker deposits must be greater than zero.",
|
|
3011
|
+
{ amount, minDepositAmount: "1" },
|
|
3012
|
+
this.id
|
|
3013
|
+
);
|
|
3014
|
+
}
|
|
3015
|
+
const context = await this.ensureAgent(state, chainId, asset);
|
|
3016
|
+
let txHash;
|
|
3017
|
+
try {
|
|
3018
|
+
if (depositCallback) {
|
|
3019
|
+
provideDepositVerificationContext(depositCallback, {
|
|
3020
|
+
agentId: "yieldseeker",
|
|
3021
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3022
|
+
userId: context.user.userId,
|
|
3023
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3024
|
+
});
|
|
3025
|
+
txHash = await depositCallback(
|
|
3026
|
+
context.wallet.walletAddress,
|
|
3027
|
+
chainId,
|
|
3028
|
+
amount
|
|
3029
|
+
);
|
|
3030
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3031
|
+
} else {
|
|
3032
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3033
|
+
from: (0, import_viem6.getAddress)(state.walletAddress),
|
|
3034
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3035
|
+
data: (0, import_viem6.encodeFunctionData)({
|
|
3036
|
+
abi: import_viem6.erc20Abi,
|
|
3037
|
+
functionName: "transfer",
|
|
3038
|
+
args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
3039
|
+
}),
|
|
3040
|
+
value: "0",
|
|
3041
|
+
chainId
|
|
3042
|
+
});
|
|
3043
|
+
}
|
|
3044
|
+
await this.deployAfterFunding(state, chainId, context);
|
|
3045
|
+
} finally {
|
|
3046
|
+
await this.refreshSnapshotAfterMovement(
|
|
3047
|
+
state,
|
|
3048
|
+
chainId,
|
|
3049
|
+
context,
|
|
3050
|
+
"deposit"
|
|
3051
|
+
);
|
|
3052
|
+
}
|
|
3053
|
+
return {
|
|
3054
|
+
txHash,
|
|
3055
|
+
smartWallet: context.wallet.walletAddress,
|
|
3056
|
+
amount
|
|
3057
|
+
};
|
|
3058
|
+
}
|
|
3059
|
+
async withdraw(state, chainId, asset, amount) {
|
|
3060
|
+
this.assertChain(chainId);
|
|
3061
|
+
this.assertAsset(asset);
|
|
3062
|
+
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
3063
|
+
throw new OwneyError(
|
|
3064
|
+
"WITHDRAW_FAILED",
|
|
3065
|
+
"Yieldseeker withdrawals must be greater than zero.",
|
|
3066
|
+
{ amount },
|
|
3067
|
+
this.id
|
|
3068
|
+
);
|
|
3069
|
+
}
|
|
3070
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
3071
|
+
if (!context) {
|
|
3072
|
+
throw new OwneyError(
|
|
3073
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3074
|
+
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3075
|
+
{ asset, available: "0" },
|
|
3076
|
+
this.id
|
|
3077
|
+
);
|
|
3078
|
+
}
|
|
3079
|
+
try {
|
|
3080
|
+
const portfolio = await this.loadPortfolioContext(
|
|
3081
|
+
state,
|
|
3082
|
+
chainId,
|
|
3083
|
+
context
|
|
3084
|
+
);
|
|
3085
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3086
|
+
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3087
|
+
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3088
|
+
);
|
|
3089
|
+
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3090
|
+
const deployed = portfolio.positions.reduce(
|
|
3091
|
+
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3092
|
+
0n
|
|
3093
|
+
);
|
|
3094
|
+
const totalAvailable = idle + deployed;
|
|
3095
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3096
|
+
if (requested > totalAvailable) {
|
|
3097
|
+
throw new OwneyError(
|
|
3098
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3099
|
+
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3100
|
+
{
|
|
3101
|
+
asset,
|
|
3102
|
+
requested: requested.toString(),
|
|
3103
|
+
available: totalAvailable.toString()
|
|
3104
|
+
},
|
|
3105
|
+
this.id
|
|
3106
|
+
);
|
|
3107
|
+
}
|
|
3108
|
+
let remaining = requested > idle ? requested - idle : 0n;
|
|
3109
|
+
for (const position2 of portfolio.positions) {
|
|
3110
|
+
if (remaining === 0n) break;
|
|
3111
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3112
|
+
if (available <= 0n) continue;
|
|
3113
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
3114
|
+
const response = await this.walletRequest(
|
|
3115
|
+
state,
|
|
3116
|
+
chainId,
|
|
3117
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
3118
|
+
{
|
|
3119
|
+
method: "POST",
|
|
3120
|
+
body: {
|
|
3121
|
+
chainId,
|
|
3122
|
+
vaultAddress: position2.yieldOption.address,
|
|
3123
|
+
assetsRaw: assetsRaw.toString()
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
);
|
|
3127
|
+
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3128
|
+
throw this.invalidResponse("position withdrawal");
|
|
3129
|
+
}
|
|
3130
|
+
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3131
|
+
remaining -= assetsRaw;
|
|
3132
|
+
}
|
|
3133
|
+
if (remaining > 0n) {
|
|
3134
|
+
throw this.invalidResponse("yield positions", {
|
|
3135
|
+
reason: "Withdrawable positions could not cover the request.",
|
|
3136
|
+
remaining: remaining.toString()
|
|
3137
|
+
});
|
|
3138
|
+
}
|
|
3139
|
+
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3140
|
+
const txHash = await this.submitTransaction(state, chainId, {
|
|
3141
|
+
from: account,
|
|
3142
|
+
to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
|
|
3143
|
+
data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
|
|
3144
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3145
|
+
functionName: "withdrawAllAssetToUser",
|
|
3146
|
+
args: [account, metadata.address]
|
|
3147
|
+
}) : (0, import_viem6.encodeFunctionData)({
|
|
3148
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3149
|
+
functionName: "withdrawAssetToUser",
|
|
3150
|
+
args: [account, metadata.address, requested]
|
|
3151
|
+
}),
|
|
3152
|
+
value: "0",
|
|
3153
|
+
chainId
|
|
3154
|
+
});
|
|
3155
|
+
return {
|
|
3156
|
+
txHash,
|
|
3157
|
+
type: amount === void 0 ? "full" : "partial",
|
|
3158
|
+
amount: requested.toString()
|
|
3159
|
+
};
|
|
3160
|
+
} finally {
|
|
3161
|
+
await this.refreshSnapshotAfterMovement(
|
|
3162
|
+
state,
|
|
3163
|
+
chainId,
|
|
3164
|
+
context,
|
|
3165
|
+
"withdrawal"
|
|
3166
|
+
);
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
async getBalances(state, chainId) {
|
|
3170
|
+
this.assertChain(chainId);
|
|
3171
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3172
|
+
}
|
|
3173
|
+
async getEarnings(state, chainId) {
|
|
3174
|
+
this.assertChain(chainId);
|
|
3175
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3176
|
+
}
|
|
3177
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3178
|
+
this.assertChain(chainId);
|
|
3179
|
+
const asset = tokenSymbol?.toUpperCase();
|
|
3180
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3181
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3182
|
+
...asset ? { asset } : {},
|
|
3183
|
+
historic: true
|
|
3184
|
+
});
|
|
3185
|
+
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3186
|
+
}
|
|
3187
|
+
async getHistory(state, chainId, options) {
|
|
3188
|
+
this.assertChain(chainId);
|
|
3189
|
+
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3190
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3191
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3192
|
+
...asset ? { asset } : {},
|
|
3193
|
+
historic: true,
|
|
3194
|
+
actions: true
|
|
3195
|
+
});
|
|
3196
|
+
return mapYieldseekerHistory(contexts, {
|
|
3197
|
+
limit: options?.limit ?? 10,
|
|
3198
|
+
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3199
|
+
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3200
|
+
});
|
|
3201
|
+
}
|
|
3202
|
+
async getUserProfile(state, chainId) {
|
|
3203
|
+
this.assertChain(chainId);
|
|
3204
|
+
return mapYieldseekerProfile(
|
|
3205
|
+
state.walletAddress,
|
|
3206
|
+
await this.loadPortfolio(state, chainId, {})
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
async getAgentApy(days, options) {
|
|
3210
|
+
this.assertOptionalChain(options?.chainId);
|
|
3211
|
+
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3212
|
+
if (requested !== void 0) this.assertAsset(requested);
|
|
3213
|
+
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3214
|
+
const values = await Promise.all(
|
|
3215
|
+
assets.map(async (asset) => {
|
|
3216
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3217
|
+
const response = await this.api.request(
|
|
3218
|
+
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3219
|
+
);
|
|
3220
|
+
if (!Array.isArray(response?.yieldOptions)) {
|
|
3221
|
+
throw this.invalidResponse("yield options");
|
|
3222
|
+
}
|
|
3223
|
+
return { asset, yieldOptions: response.yieldOptions };
|
|
3224
|
+
})
|
|
3225
|
+
);
|
|
3226
|
+
return mapYieldseekerAgentApy(values, days);
|
|
3227
|
+
}
|
|
3228
|
+
userKey(state, chainId) {
|
|
3229
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3230
|
+
}
|
|
3231
|
+
contextKey(state, chainId, asset) {
|
|
3232
|
+
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3233
|
+
}
|
|
3234
|
+
async resolveUser(state, chainId) {
|
|
3235
|
+
const key2 = this.userKey(state, chainId);
|
|
3236
|
+
const inMemory = this.users.get(key2);
|
|
3237
|
+
if (inMemory) return inMemory;
|
|
3238
|
+
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3239
|
+
if (persisted) {
|
|
3240
|
+
this.users.set(key2, persisted);
|
|
3241
|
+
return persisted;
|
|
3242
|
+
}
|
|
3243
|
+
const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3244
|
+
let user = null;
|
|
3245
|
+
try {
|
|
3246
|
+
const login = await this.providerRequest(
|
|
3247
|
+
state,
|
|
3248
|
+
chainId,
|
|
3249
|
+
"/users/login-with-wallet",
|
|
3250
|
+
{ method: "POST", body: { walletAddress } }
|
|
3251
|
+
);
|
|
3252
|
+
user = login?.user ?? null;
|
|
3253
|
+
if (!user) {
|
|
3254
|
+
throw this.invalidResponse("wallet login", {
|
|
3255
|
+
reason: "A successful login returned no user."
|
|
3256
|
+
});
|
|
3257
|
+
}
|
|
3258
|
+
} catch (error) {
|
|
3259
|
+
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3260
|
+
if (error instanceof OwneyError) throw error;
|
|
3261
|
+
throw this.mapApiError(error);
|
|
3262
|
+
}
|
|
3263
|
+
let created;
|
|
3264
|
+
try {
|
|
3265
|
+
created = await this.providerRequest(
|
|
3266
|
+
state,
|
|
3267
|
+
chainId,
|
|
3268
|
+
"/users",
|
|
3269
|
+
{
|
|
3270
|
+
method: "POST",
|
|
3271
|
+
body: {
|
|
3272
|
+
walletAddress,
|
|
3273
|
+
username: `owney-${walletAddress.slice(2).toLowerCase()}`
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
);
|
|
3277
|
+
} catch (createError) {
|
|
3278
|
+
throw this.mapApiError(createError);
|
|
3279
|
+
}
|
|
3280
|
+
user = created?.user ?? null;
|
|
3281
|
+
}
|
|
3282
|
+
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3283
|
+
throw this.invalidResponse("wallet identity");
|
|
3284
|
+
}
|
|
3285
|
+
const resolved = { userId: user.userId };
|
|
3286
|
+
this.users.set(key2, resolved);
|
|
3287
|
+
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3288
|
+
return resolved;
|
|
3289
|
+
}
|
|
3290
|
+
forgetUser(state, chainId) {
|
|
3291
|
+
this.users.delete(this.userKey(state, chainId));
|
|
3292
|
+
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3293
|
+
}
|
|
3294
|
+
async ensureAgent(state, chainId, asset) {
|
|
3295
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3296
|
+
const cached = this.agentContexts.get(key2);
|
|
3297
|
+
if (cached) return cached;
|
|
3298
|
+
const pending = this.pendingAgents.get(key2);
|
|
3299
|
+
if (pending) return pending;
|
|
3300
|
+
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3301
|
+
(context) => {
|
|
3302
|
+
if (!context) throw this.invalidResponse("agent creation");
|
|
3303
|
+
this.agentContexts.set(key2, context);
|
|
3304
|
+
return context;
|
|
3305
|
+
}
|
|
3306
|
+
);
|
|
3307
|
+
this.pendingAgents.set(key2, request);
|
|
3308
|
+
try {
|
|
3309
|
+
return await request;
|
|
3310
|
+
} finally {
|
|
3311
|
+
this.pendingAgents.delete(key2);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
async findAgent(state, chainId, asset) {
|
|
3315
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3316
|
+
const cached = this.agentContexts.get(key2);
|
|
3317
|
+
if (cached) return cached;
|
|
3318
|
+
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3319
|
+
if (context) this.agentContexts.set(key2, context);
|
|
3320
|
+
return context;
|
|
3321
|
+
}
|
|
3322
|
+
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3323
|
+
const user = await this.resolveUser(state, chainId);
|
|
3324
|
+
const response = await this.walletRequest(
|
|
3325
|
+
state,
|
|
3326
|
+
chainId,
|
|
3327
|
+
`/users/${user.userId}/agents`
|
|
3328
|
+
);
|
|
3329
|
+
if (!Array.isArray(response?.agents)) {
|
|
3330
|
+
throw this.invalidResponse("agent list");
|
|
3331
|
+
}
|
|
3332
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3333
|
+
let agent = response.agents.find(
|
|
3334
|
+
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3335
|
+
);
|
|
3336
|
+
if (!agent && createIfMissing) {
|
|
3337
|
+
const created = await this.walletRequest(
|
|
3338
|
+
state,
|
|
3339
|
+
chainId,
|
|
3340
|
+
`/users/${user.userId}/agents`,
|
|
3341
|
+
{
|
|
3342
|
+
method: "POST",
|
|
3343
|
+
body: {
|
|
3344
|
+
name: OWNEY_AGENT_NAME,
|
|
3345
|
+
emoji: "\u{1F989}",
|
|
3346
|
+
chainId,
|
|
3347
|
+
assetAddress: metadata.address,
|
|
3348
|
+
type: "vault",
|
|
3349
|
+
rulePreset: null
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
);
|
|
3353
|
+
agent = created?.agent;
|
|
3354
|
+
}
|
|
3355
|
+
if (!agent) return null;
|
|
3356
|
+
this.assertAgent(agent);
|
|
3357
|
+
const walletResponse = await this.walletRequest(
|
|
3358
|
+
state,
|
|
3359
|
+
chainId,
|
|
3360
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3361
|
+
);
|
|
3362
|
+
if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3363
|
+
throw this.invalidResponse("agent wallet");
|
|
3364
|
+
}
|
|
3365
|
+
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3366
|
+
}
|
|
3367
|
+
async loadPortfolio(state, chainId, options) {
|
|
3368
|
+
const user = await this.resolveUser(state, chainId);
|
|
3369
|
+
const response = await this.walletRequest(
|
|
3370
|
+
state,
|
|
3371
|
+
chainId,
|
|
3372
|
+
`/users/${user.userId}/agents`
|
|
3373
|
+
);
|
|
3374
|
+
if (!Array.isArray(response?.agents)) {
|
|
3375
|
+
throw this.invalidResponse("agent list");
|
|
3376
|
+
}
|
|
3377
|
+
const contexts = [];
|
|
3378
|
+
for (const agent of response.agents) {
|
|
3379
|
+
const asset = this.assetForAgent(agent);
|
|
3380
|
+
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3381
|
+
continue;
|
|
3382
|
+
}
|
|
3383
|
+
this.assertAgent(agent);
|
|
3384
|
+
const walletResponse = await this.walletRequest(
|
|
3385
|
+
state,
|
|
3386
|
+
chainId,
|
|
3387
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3388
|
+
);
|
|
3389
|
+
if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3390
|
+
throw this.invalidResponse("agent wallet");
|
|
3391
|
+
}
|
|
3392
|
+
const context = {
|
|
3393
|
+
user,
|
|
3394
|
+
agent,
|
|
3395
|
+
wallet: walletResponse.agentWallet,
|
|
3396
|
+
asset
|
|
3397
|
+
};
|
|
3398
|
+
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3399
|
+
contexts.push(context);
|
|
3400
|
+
}
|
|
3401
|
+
return Promise.all(
|
|
3402
|
+
contexts.map(
|
|
3403
|
+
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3404
|
+
)
|
|
3405
|
+
);
|
|
3406
|
+
}
|
|
3407
|
+
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3408
|
+
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3409
|
+
this.walletRequest(
|
|
3410
|
+
state,
|
|
3411
|
+
chainId,
|
|
3412
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3413
|
+
shouldOnlyUseRecentValue: true,
|
|
3414
|
+
shouldAllowStaleOnError: true
|
|
3415
|
+
})}`
|
|
3416
|
+
),
|
|
3417
|
+
this.walletRequest(
|
|
3418
|
+
state,
|
|
3419
|
+
chainId,
|
|
3420
|
+
this.agentPath(context, "yield-positions")
|
|
3421
|
+
),
|
|
3422
|
+
options.historic ? this.walletRequest(
|
|
3423
|
+
state,
|
|
3424
|
+
chainId,
|
|
3425
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3426
|
+
) : Promise.resolve(void 0),
|
|
3427
|
+
options.actions ? this.walletRequest(
|
|
3428
|
+
state,
|
|
3429
|
+
chainId,
|
|
3430
|
+
this.agentPath(context, "actions")
|
|
3431
|
+
) : Promise.resolve(void 0)
|
|
3432
|
+
]);
|
|
3433
|
+
if (!snapshot?.agentSnapshot) {
|
|
3434
|
+
throw this.invalidResponse("agent snapshot");
|
|
3435
|
+
}
|
|
3436
|
+
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3437
|
+
throw this.invalidResponse("yield positions");
|
|
3438
|
+
}
|
|
3439
|
+
return {
|
|
3440
|
+
...context,
|
|
3441
|
+
snapshot: snapshot.agentSnapshot,
|
|
3442
|
+
positions: positions.yieldPositions,
|
|
3443
|
+
...historic?.position ? { historic: historic.position } : {},
|
|
3444
|
+
...actions?.actions ? { actions: actions.actions } : {}
|
|
3445
|
+
};
|
|
3446
|
+
}
|
|
3447
|
+
async deployAfterFunding(state, chainId, context) {
|
|
3448
|
+
if (context.wallet.initializedDate != null) return;
|
|
3449
|
+
try {
|
|
3450
|
+
const deployed = await this.walletRequest(
|
|
3451
|
+
state,
|
|
3452
|
+
chainId,
|
|
3453
|
+
this.agentPath(context, "deploy"),
|
|
3454
|
+
{ method: "POST", body: {} }
|
|
3455
|
+
);
|
|
3456
|
+
if (deployed?.agentWallet) {
|
|
3457
|
+
context.wallet = deployed.agentWallet;
|
|
3458
|
+
}
|
|
3459
|
+
} catch (error) {
|
|
3460
|
+
console.warn(
|
|
3461
|
+
"[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
|
|
3462
|
+
error
|
|
3463
|
+
);
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3467
|
+
try {
|
|
3468
|
+
const response = await this.walletRequest(
|
|
3469
|
+
state,
|
|
3470
|
+
chainId,
|
|
3471
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3472
|
+
shouldForceRefresh: true
|
|
3473
|
+
})}`
|
|
3474
|
+
);
|
|
3475
|
+
if (!response?.agentSnapshot) {
|
|
3476
|
+
throw this.invalidResponse("agent snapshot refresh");
|
|
3477
|
+
}
|
|
3478
|
+
} catch (error) {
|
|
3479
|
+
console.warn(
|
|
3480
|
+
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3481
|
+
error
|
|
3482
|
+
);
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
agentPath(context, suffix) {
|
|
3486
|
+
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
3487
|
+
}
|
|
3488
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
3489
|
+
try {
|
|
3490
|
+
return await this.providerRequest(state, chainId, path, options);
|
|
3491
|
+
} catch (error) {
|
|
3492
|
+
throw this.mapApiError(error);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
async providerRequest(state, chainId, path, options = {}) {
|
|
3496
|
+
this.assertChain(chainId);
|
|
3497
|
+
const request = (signature2) => this.api.request(path, {
|
|
3498
|
+
...options,
|
|
3499
|
+
signature: signature2
|
|
3500
|
+
});
|
|
3501
|
+
let signature = await this.auth.getToken(state, chainId);
|
|
3502
|
+
try {
|
|
3503
|
+
return await request(signature);
|
|
3504
|
+
} catch (error) {
|
|
3505
|
+
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
3506
|
+
if (error.providerCode === "NO_USER") throw error;
|
|
3507
|
+
if (!error.isAuthenticationError) throw error;
|
|
3508
|
+
this.auth.clear(state, chainId);
|
|
3509
|
+
signature = await this.auth.getToken(state, chainId);
|
|
3510
|
+
try {
|
|
3511
|
+
return await request(signature);
|
|
3512
|
+
} catch (retryError) {
|
|
3513
|
+
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3514
|
+
this.forgetUser(state, chainId);
|
|
3515
|
+
}
|
|
3516
|
+
throw retryError;
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
mapApiError(error) {
|
|
3521
|
+
if (!(error instanceof YieldseekerApiError)) {
|
|
3522
|
+
return new OwneyError(
|
|
3523
|
+
"AGENT_API_ERROR",
|
|
3524
|
+
"Yieldseeker request failed.",
|
|
3525
|
+
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3526
|
+
this.id
|
|
3527
|
+
);
|
|
3528
|
+
}
|
|
3529
|
+
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3530
|
+
return new OwneyError(
|
|
3531
|
+
code,
|
|
3532
|
+
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3533
|
+
{
|
|
3534
|
+
statusCode: error.status,
|
|
3535
|
+
providerCode: error.providerCode,
|
|
3536
|
+
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3537
|
+
},
|
|
3538
|
+
this.id
|
|
3539
|
+
);
|
|
3540
|
+
}
|
|
3541
|
+
async submitTransaction(state, chainId, transaction) {
|
|
3542
|
+
if (this.transactionExecutor) {
|
|
3543
|
+
return this.transactionExecutor(state, chainId, transaction);
|
|
3544
|
+
}
|
|
3545
|
+
this.assertTransaction(transaction, state, chainId);
|
|
3546
|
+
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3547
|
+
const walletClient = (0, import_viem6.createWalletClient)({
|
|
3548
|
+
account,
|
|
3549
|
+
chain: import_chains3.base,
|
|
3550
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3551
|
+
});
|
|
3552
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3553
|
+
chain: import_chains3.base,
|
|
3554
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3555
|
+
});
|
|
3556
|
+
await ensureWalletOnChain(
|
|
3557
|
+
publicClient,
|
|
3558
|
+
walletClient,
|
|
3559
|
+
8453
|
|
3560
|
+
);
|
|
3561
|
+
const hash = await walletClient.sendTransaction({
|
|
3562
|
+
account,
|
|
3563
|
+
chain: import_chains3.base,
|
|
3564
|
+
to: (0, import_viem6.getAddress)(transaction.to),
|
|
3565
|
+
data: transaction.data,
|
|
3566
|
+
value: BigInt(transaction.value)
|
|
3567
|
+
});
|
|
3568
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3569
|
+
hash,
|
|
3570
|
+
confirmations: 1
|
|
3571
|
+
});
|
|
3572
|
+
if (receipt.status !== "success") {
|
|
3573
|
+
throw new OwneyError(
|
|
3574
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3575
|
+
`Yieldseeker transaction reverted (${hash}).`,
|
|
3576
|
+
{ transactionHash: hash },
|
|
3577
|
+
this.id
|
|
3578
|
+
);
|
|
3579
|
+
}
|
|
3580
|
+
return hash;
|
|
3581
|
+
}
|
|
3582
|
+
async waitForReceipt(state, chainId, transactionHash) {
|
|
3583
|
+
if (this.unwindReceiptWaiter) {
|
|
3584
|
+
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3585
|
+
return;
|
|
3586
|
+
}
|
|
3587
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3588
|
+
chain: import_chains3.base,
|
|
3589
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3590
|
+
});
|
|
3591
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3592
|
+
hash: transactionHash,
|
|
3593
|
+
confirmations: 1
|
|
3594
|
+
});
|
|
3595
|
+
if (receipt.status !== "success") {
|
|
3596
|
+
throw new OwneyError(
|
|
3597
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3598
|
+
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3599
|
+
{ transactionHash },
|
|
3600
|
+
this.id
|
|
3601
|
+
);
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
assertTransaction(transaction, state, chainId) {
|
|
3605
|
+
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem6.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem6.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem6.getAddress)(transaction.from) !== (0, import_viem6.getAddress)(state.walletAddress)) {
|
|
3606
|
+
throw this.invalidResponse("transaction");
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
assertAgent(agent) {
|
|
3610
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3611
|
+
throw this.invalidResponse("agent");
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
isOwneyAgent(agent) {
|
|
3615
|
+
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3616
|
+
}
|
|
3617
|
+
assetForAgent(agent) {
|
|
3618
|
+
for (const asset of ["USDC", "WETH"]) {
|
|
3619
|
+
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3620
|
+
return asset;
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
return null;
|
|
3624
|
+
}
|
|
3625
|
+
isTransactionHash(value) {
|
|
3626
|
+
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3627
|
+
}
|
|
3628
|
+
assertChain(chainId) {
|
|
3629
|
+
if (chainId !== 8453) {
|
|
3630
|
+
throw new OwneyError(
|
|
3631
|
+
"CHAIN_UNSUPPORTED",
|
|
3632
|
+
`Yieldseeker does not support chain ${chainId}.`,
|
|
3633
|
+
{ chainId, supportedChainIds: [8453] },
|
|
3634
|
+
this.id
|
|
3635
|
+
);
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
assertOptionalChain(chainId) {
|
|
3639
|
+
if (chainId !== void 0) this.assertChain(chainId);
|
|
3640
|
+
}
|
|
3641
|
+
assertAsset(asset) {
|
|
3642
|
+
if (asset !== "USDC" && asset !== "WETH") {
|
|
3643
|
+
throw new OwneyError(
|
|
3644
|
+
"ASSET_UNSUPPORTED",
|
|
3645
|
+
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
3646
|
+
{
|
|
3647
|
+
asset,
|
|
3648
|
+
supportedAssets: ["USDC", "WETH"],
|
|
3649
|
+
providerAlsoAdvertises: ["cbBTC"]
|
|
3650
|
+
},
|
|
3651
|
+
this.id
|
|
3652
|
+
);
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
invalidResponse(operation, details = {}) {
|
|
3656
|
+
return new OwneyError(
|
|
3657
|
+
"AGENT_INVALID_RESPONSE",
|
|
3658
|
+
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3659
|
+
details,
|
|
3660
|
+
this.id
|
|
3661
|
+
);
|
|
3662
|
+
}
|
|
3663
|
+
};
|
|
3664
|
+
|
|
3665
|
+
// src/lib/routing-api.ts
|
|
3666
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3667
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3668
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3669
|
+
try {
|
|
3670
|
+
const res = await fetch(url, {
|
|
3671
|
+
method: "GET",
|
|
3672
|
+
headers: {
|
|
3673
|
+
"Content-Type": "application/json",
|
|
3674
|
+
"x-owney-api-key": `${apiKey}`
|
|
3675
|
+
}
|
|
3676
|
+
});
|
|
3677
|
+
if (!res.ok) {
|
|
3678
|
+
if (res.status !== 404) {
|
|
3679
|
+
console.warn(
|
|
3680
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
3681
|
+
);
|
|
3682
|
+
}
|
|
3683
|
+
return null;
|
|
3684
|
+
}
|
|
3685
|
+
const json = await res.json();
|
|
3686
|
+
const policy = json.success ? json.data ?? null : null;
|
|
3687
|
+
debugLog(
|
|
3688
|
+
"owney-sdk",
|
|
3689
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
3690
|
+
policy ?? void 0
|
|
3691
|
+
);
|
|
3692
|
+
return policy;
|
|
3693
|
+
} catch (error) {
|
|
3694
|
+
console.warn(
|
|
3695
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
3696
|
+
error instanceof Error ? error.message : String(error)
|
|
3697
|
+
);
|
|
3698
|
+
return null;
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3702
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
3703
|
+
const res = await fetch(url, {
|
|
3704
|
+
method: "GET",
|
|
3705
|
+
headers: {
|
|
3706
|
+
"Content-Type": "application/json",
|
|
3707
|
+
"x-owney-api-key": `${apiKey}`
|
|
3708
|
+
}
|
|
3709
|
+
});
|
|
3710
|
+
if (!res.ok) {
|
|
3711
|
+
const text = await res.text().catch(() => "");
|
|
3712
|
+
throw new OwneyError(
|
|
3713
|
+
"API_ROUTING_ERROR",
|
|
3714
|
+
`Routing API error ${res.status}: ${text}`,
|
|
3715
|
+
{ statusCode: res.status, responseBody: text }
|
|
3716
|
+
);
|
|
3717
|
+
}
|
|
3718
|
+
const json = await res.json();
|
|
3719
|
+
if (!json.success) {
|
|
3720
|
+
throw new OwneyError(
|
|
3721
|
+
"API_ROUTING_FAILED",
|
|
3722
|
+
`Routing API request failed: ${json.message}`,
|
|
3723
|
+
{ message: json.message }
|
|
3724
|
+
);
|
|
3725
|
+
}
|
|
3726
|
+
return json.data;
|
|
3727
|
+
}
|
|
3728
|
+
|
|
3729
|
+
// src/lib/health-report.ts
|
|
3730
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3731
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
3732
|
+
try {
|
|
3733
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
3734
|
+
method: "POST",
|
|
3735
|
+
headers: {
|
|
3736
|
+
"Content-Type": "application/json",
|
|
3737
|
+
"x-owney-api-key": apiKey
|
|
3738
|
+
},
|
|
3739
|
+
body: JSON.stringify({
|
|
3740
|
+
agent_type: agentType,
|
|
3741
|
+
error_code: errorCode,
|
|
3742
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3743
|
+
})
|
|
3744
|
+
});
|
|
3745
|
+
} catch (err) {
|
|
3746
|
+
console.warn(
|
|
3747
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
3748
|
+
err instanceof Error ? err.message : err
|
|
3749
|
+
);
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
3753
|
+
try {
|
|
3754
|
+
return await fn();
|
|
3755
|
+
} catch (err) {
|
|
3756
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3757
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
3758
|
+
throw err;
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
|
|
3762
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
3763
|
+
var import_viem7 = require("viem");
|
|
3764
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
3765
|
+
const target = asset.toUpperCase();
|
|
3766
|
+
return agents.map((agent) => {
|
|
3767
|
+
const agentBalance = aggregated[agent.id];
|
|
3768
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
3769
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
3770
|
+
);
|
|
3771
|
+
let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
3772
|
+
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
3773
|
+
const chainNameById = {
|
|
3774
|
+
1: "ETHEREUM",
|
|
3775
|
+
8453: "BASE",
|
|
3776
|
+
42161: "ARBITRUM"
|
|
3777
|
+
};
|
|
3778
|
+
const targetChain = chainNameById[chainId];
|
|
3779
|
+
for (const position2 of agentBalance?.positions ?? []) {
|
|
3780
|
+
const positionChain = position2.chain.trim().toUpperCase();
|
|
3781
|
+
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
3782
|
+
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
3783
|
+
if (position2.amountRaw !== void 0) {
|
|
3784
|
+
try {
|
|
3785
|
+
balance += BigInt(position2.amountRaw);
|
|
3786
|
+
continue;
|
|
3787
|
+
} catch {
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
return { agent, balance };
|
|
3794
|
+
});
|
|
3795
|
+
}
|
|
3796
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
3797
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
3798
|
+
agent,
|
|
3799
|
+
balance,
|
|
3800
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
3801
|
+
}));
|
|
3802
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
3803
|
+
let remainder = requested - assigned;
|
|
3804
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
3805
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
3806
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
3807
|
+
});
|
|
3808
|
+
for (const p of byHeadroom) {
|
|
3809
|
+
if (remainder === 0n) break;
|
|
3810
|
+
const headroom = p.balance - p.planned;
|
|
3811
|
+
if (headroom <= 0n) continue;
|
|
3812
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
3813
|
+
p.planned += take;
|
|
3814
|
+
remainder -= take;
|
|
3815
|
+
}
|
|
3816
|
+
return plans;
|
|
3817
|
+
}
|
|
3818
|
+
function planDisabledDrain(disabled, requested) {
|
|
3819
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
3820
|
+
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
3821
|
+
);
|
|
3822
|
+
const plans = [];
|
|
3823
|
+
let remaining = requested;
|
|
3824
|
+
for (const { agent, balance } of sorted) {
|
|
3825
|
+
if (remaining === 0n) {
|
|
3826
|
+
plans.push({ agent, balance, planned: 0n });
|
|
3827
|
+
continue;
|
|
3828
|
+
}
|
|
3829
|
+
const take = balance < remaining ? balance : remaining;
|
|
3830
|
+
plans.push({ agent, balance, planned: take });
|
|
3831
|
+
remaining -= take;
|
|
3832
|
+
}
|
|
3833
|
+
return { plans, remaining };
|
|
2343
3834
|
}
|
|
2344
|
-
|
|
2345
|
-
const
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
3835
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
3836
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
3837
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
3838
|
+
const totalHeadroom = candidates.reduce(
|
|
3839
|
+
(s, c) => s + (c.balance - c.planned),
|
|
3840
|
+
0n
|
|
3841
|
+
);
|
|
3842
|
+
if (totalHeadroom === 0n) return;
|
|
3843
|
+
let distributed = 0n;
|
|
3844
|
+
for (const c of candidates) {
|
|
3845
|
+
const headroom = c.balance - c.planned;
|
|
3846
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
3847
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
3848
|
+
c.planned += give;
|
|
3849
|
+
distributed += give;
|
|
2359
3850
|
}
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
3851
|
+
let leftover = amount - distributed;
|
|
3852
|
+
for (const c of candidates) {
|
|
3853
|
+
if (leftover === 0n) break;
|
|
3854
|
+
const headroom = c.balance - c.planned;
|
|
3855
|
+
if (headroom <= 0n) continue;
|
|
3856
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
3857
|
+
c.planned += take;
|
|
3858
|
+
leftover -= take;
|
|
2367
3859
|
}
|
|
2368
3860
|
}
|
|
3861
|
+
function sumWithdrawnAmount(results) {
|
|
3862
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
3863
|
+
}
|
|
2369
3864
|
|
|
2370
|
-
// src/lib/
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
`No sponsored token configured for chain ${chainId}`
|
|
2381
|
-
);
|
|
3865
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
3866
|
+
function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
3867
|
+
if (!tokenSymbol) {
|
|
3868
|
+
const total = Number(balance.totalBalance);
|
|
3869
|
+
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
3870
|
+
}
|
|
3871
|
+
const normalizedToken = tokenSymbol.toUpperCase();
|
|
3872
|
+
return balance.tokens.reduce((total, token) => {
|
|
3873
|
+
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
3874
|
+
return total;
|
|
2382
3875
|
}
|
|
2383
|
-
const
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
account: deps.ownerAddress,
|
|
2424
|
-
...typedData
|
|
2425
|
-
});
|
|
2426
|
-
deps.onApproved?.();
|
|
2427
|
-
const result = await post({
|
|
2428
|
-
baseUrl: deps.baseUrl,
|
|
2429
|
-
apiKey: deps.apiKey,
|
|
2430
|
-
body: {
|
|
2431
|
-
chainId: cid,
|
|
2432
|
-
token,
|
|
2433
|
-
from: deps.ownerAddress,
|
|
2434
|
-
to: smartWallet,
|
|
2435
|
-
value: amount,
|
|
2436
|
-
validAfter: validAfter.toString(),
|
|
2437
|
-
validBefore: validBefore.toString(),
|
|
2438
|
-
nonce,
|
|
2439
|
-
authSignature,
|
|
2440
|
-
tokenName,
|
|
2441
|
-
tokenVersion
|
|
3876
|
+
const amount = Number(token.amount);
|
|
3877
|
+
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
3878
|
+
}, 0);
|
|
3879
|
+
}
|
|
3880
|
+
function aggregateApyHistory(agentApys, agentBalances) {
|
|
3881
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
3882
|
+
for (const [id, accountApy] of Object.entries(agentApys)) {
|
|
3883
|
+
const balance = agentBalances[id] ?? 0;
|
|
3884
|
+
if (!Number.isFinite(balance) || balance <= 0) continue;
|
|
3885
|
+
for (const point of accountApy.history ?? []) {
|
|
3886
|
+
const apy = Number(point.apy);
|
|
3887
|
+
if (!point.date || !Number.isFinite(apy)) continue;
|
|
3888
|
+
const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
|
|
3889
|
+
current.weightedSum += apy * balance;
|
|
3890
|
+
current.weight += balance;
|
|
3891
|
+
byDate.set(point.date, current);
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
3895
|
+
date,
|
|
3896
|
+
apy: value.weightedSum / value.weight
|
|
3897
|
+
}));
|
|
3898
|
+
}
|
|
3899
|
+
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
3900
|
+
const sums = {};
|
|
3901
|
+
const weights = {};
|
|
3902
|
+
for (const id of Object.keys(agentApys)) {
|
|
3903
|
+
const cells = agentApys[id].apyByChainAndAsset;
|
|
3904
|
+
const balance = agentBalances[id] ?? 0;
|
|
3905
|
+
if (!cells || balance <= 0) continue;
|
|
3906
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
3907
|
+
if (!perAsset) continue;
|
|
3908
|
+
const chainId = Number(chainKey);
|
|
3909
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
3910
|
+
const apy = Number(apyValue ?? 0);
|
|
3911
|
+
if (apy === 0) continue;
|
|
3912
|
+
sums[chainId] ??= {};
|
|
3913
|
+
weights[chainId] ??= {};
|
|
3914
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
3915
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2442
3916
|
}
|
|
2443
|
-
}
|
|
2444
|
-
|
|
2445
|
-
};
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
const out = {};
|
|
3920
|
+
for (const chainKey of Object.keys(sums)) {
|
|
3921
|
+
const chainId = Number(chainKey);
|
|
3922
|
+
const perAssetOut = {};
|
|
3923
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
3924
|
+
const w = weights[chainId][asset];
|
|
3925
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
3926
|
+
}
|
|
3927
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
3928
|
+
out[chainId] = perAssetOut;
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
return out;
|
|
2446
3932
|
}
|
|
2447
3933
|
|
|
3934
|
+
// src/client.ts
|
|
3935
|
+
var import_viem9 = require("viem");
|
|
3936
|
+
var import_chains4 = require("viem/chains");
|
|
3937
|
+
|
|
2448
3938
|
// src/lib/sponsored-weth-deposit.ts
|
|
2449
3939
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2450
3940
|
function makeSponsoredWethCallback(deps) {
|
|
2451
3941
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
2452
3942
|
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
2453
|
-
return
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
"CHAIN_UNSUPPORTED",
|
|
2459
|
-
`No sponsored WETH configured for chain ${chainId}`
|
|
2460
|
-
);
|
|
2461
|
-
}
|
|
2462
|
-
const amountWei = BigInt(amount);
|
|
2463
|
-
const pub = deps.getPublicClient(cid);
|
|
2464
|
-
const wallet = deps.getWalletClient(cid);
|
|
2465
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2466
|
-
try {
|
|
2467
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2468
|
-
if (balance < amountWei) {
|
|
3943
|
+
return makeVerificationAwareDepositCallback(
|
|
3944
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
3945
|
+
const cid = chainId;
|
|
3946
|
+
const token = deps.tokenAddressByChain[cid];
|
|
3947
|
+
if (!token) {
|
|
2469
3948
|
throw new OwneyError(
|
|
2470
|
-
"
|
|
2471
|
-
|
|
2472
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3949
|
+
"CHAIN_UNSUPPORTED",
|
|
3950
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
2473
3951
|
);
|
|
2474
3952
|
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
)
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
});
|
|
2495
|
-
const nonce = randomPermit2Nonce();
|
|
2496
|
-
const deadline = BigInt(
|
|
2497
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2498
|
-
);
|
|
2499
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
2500
|
-
chainId: cid,
|
|
2501
|
-
message: {
|
|
2502
|
-
permitted: { token, amount: amountWei },
|
|
2503
|
-
spender: relayer,
|
|
2504
|
-
nonce,
|
|
2505
|
-
deadline
|
|
3953
|
+
const amountWei = BigInt(amount);
|
|
3954
|
+
const pub = deps.getPublicClient(cid);
|
|
3955
|
+
const wallet = deps.getWalletClient(cid);
|
|
3956
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
3957
|
+
try {
|
|
3958
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
3959
|
+
if (balance < amountWei) {
|
|
3960
|
+
throw new OwneyError(
|
|
3961
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
3962
|
+
"Insufficient WETH balance for this deposit.",
|
|
3963
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3964
|
+
);
|
|
3965
|
+
}
|
|
3966
|
+
} catch (err) {
|
|
3967
|
+
if (err instanceof OwneyError) throw err;
|
|
3968
|
+
console.warn(
|
|
3969
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
3970
|
+
err instanceof Error ? err.message : String(err)
|
|
3971
|
+
);
|
|
2506
3972
|
}
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
account: deps.ownerAddress,
|
|
2510
|
-
...typedData
|
|
2511
|
-
});
|
|
2512
|
-
deps.onApproved?.();
|
|
2513
|
-
const result = await post({
|
|
2514
|
-
baseUrl: deps.baseUrl,
|
|
2515
|
-
apiKey: deps.apiKey,
|
|
2516
|
-
body: {
|
|
2517
|
-
chainId: cid,
|
|
3973
|
+
const allowance = await readPermit2Allowance(
|
|
3974
|
+
pub,
|
|
2518
3975
|
token,
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
3976
|
+
deps.ownerAddress
|
|
3977
|
+
);
|
|
3978
|
+
if (allowance < amountWei) {
|
|
3979
|
+
throw new OwneyError(
|
|
3980
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
3981
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
3982
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
3983
|
+
);
|
|
2525
3984
|
}
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
3985
|
+
const relayer = await get({
|
|
3986
|
+
baseUrl: deps.baseUrl,
|
|
3987
|
+
apiKey: deps.apiKey,
|
|
3988
|
+
chainId: cid
|
|
3989
|
+
});
|
|
3990
|
+
const nonce = randomPermit2Nonce();
|
|
3991
|
+
const deadline = BigInt(
|
|
3992
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
3993
|
+
);
|
|
3994
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
3995
|
+
chainId: cid,
|
|
3996
|
+
message: {
|
|
3997
|
+
permitted: { token, amount: amountWei },
|
|
3998
|
+
spender: relayer,
|
|
3999
|
+
nonce,
|
|
4000
|
+
deadline
|
|
4001
|
+
}
|
|
4002
|
+
});
|
|
4003
|
+
const signature = await wallet.signTypedData({
|
|
4004
|
+
account: deps.ownerAddress,
|
|
4005
|
+
...typedData
|
|
4006
|
+
});
|
|
4007
|
+
deps.onApproved?.();
|
|
4008
|
+
const result = await post({
|
|
4009
|
+
baseUrl: deps.baseUrl,
|
|
4010
|
+
apiKey: deps.apiKey,
|
|
4011
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
4012
|
+
body: {
|
|
4013
|
+
chainId: cid,
|
|
4014
|
+
token,
|
|
4015
|
+
from: deps.ownerAddress,
|
|
4016
|
+
to: smartWallet,
|
|
4017
|
+
amount,
|
|
4018
|
+
nonce: nonce.toString(),
|
|
4019
|
+
deadline: deadline.toString(),
|
|
4020
|
+
signature,
|
|
4021
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
4022
|
+
yieldseekerUserId: verification.userId,
|
|
4023
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4024
|
+
} : {}
|
|
4025
|
+
}
|
|
4026
|
+
});
|
|
4027
|
+
return result.txHash;
|
|
4028
|
+
}
|
|
4029
|
+
);
|
|
2529
4030
|
}
|
|
2530
4031
|
|
|
2531
4032
|
// src/lib/sponsored-calls-deposit.ts
|
|
2532
|
-
var
|
|
4033
|
+
var import_viem8 = require("viem");
|
|
2533
4034
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2534
4035
|
var DEFAULT_MAX_POLLS = 30;
|
|
2535
4036
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -2537,7 +4038,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
2537
4038
|
method: "wallet_getCapabilities",
|
|
2538
4039
|
params: [owner]
|
|
2539
4040
|
});
|
|
2540
|
-
const forChain = caps?.[(0,
|
|
4041
|
+
const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
2541
4042
|
return Boolean(forChain?.paymasterService?.supported);
|
|
2542
4043
|
}
|
|
2543
4044
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -2555,7 +4056,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2555
4056
|
}
|
|
2556
4057
|
return new URL(configured, origin).toString();
|
|
2557
4058
|
};
|
|
2558
|
-
return async (smartWallet, chainId, amount) => {
|
|
4059
|
+
return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
|
|
2559
4060
|
const cid = chainId;
|
|
2560
4061
|
const token = deps.tokenAddressByChain[cid];
|
|
2561
4062
|
if (!token) {
|
|
@@ -2571,22 +4072,48 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2571
4072
|
{ chainId }
|
|
2572
4073
|
);
|
|
2573
4074
|
}
|
|
2574
|
-
const data = (0,
|
|
2575
|
-
abi:
|
|
4075
|
+
const data = (0, import_viem8.encodeFunctionData)({
|
|
4076
|
+
abi: import_viem8.erc20Abi,
|
|
2576
4077
|
functionName: "transfer",
|
|
2577
4078
|
args: [smartWallet, BigInt(amount)]
|
|
2578
4079
|
});
|
|
4080
|
+
let paymasterUrl = absolutePaymasterUrl();
|
|
4081
|
+
if (verification?.agentId === "yieldseeker") {
|
|
4082
|
+
if (chainId !== 8453) {
|
|
4083
|
+
throw new OwneyError(
|
|
4084
|
+
"CHAIN_UNSUPPORTED",
|
|
4085
|
+
`Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
|
|
4086
|
+
);
|
|
4087
|
+
}
|
|
4088
|
+
const { intent } = await postPaymasterIntent({
|
|
4089
|
+
baseUrl: deps.routingApiBaseUrl,
|
|
4090
|
+
apiKey: deps.apiKey,
|
|
4091
|
+
yieldseekerSignature: verification.signature,
|
|
4092
|
+
body: {
|
|
4093
|
+
chainId,
|
|
4094
|
+
token,
|
|
4095
|
+
from: deps.ownerAddress,
|
|
4096
|
+
to: smartWallet,
|
|
4097
|
+
amount,
|
|
4098
|
+
yieldseekerUserId: verification.userId,
|
|
4099
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4100
|
+
}
|
|
4101
|
+
});
|
|
4102
|
+
const url = new URL(paymasterUrl);
|
|
4103
|
+
url.searchParams.set("owneyIntent", intent);
|
|
4104
|
+
paymasterUrl = url.toString();
|
|
4105
|
+
}
|
|
2579
4106
|
const sendResult = await deps.provider.request({
|
|
2580
4107
|
method: "wallet_sendCalls",
|
|
2581
4108
|
params: [
|
|
2582
4109
|
{
|
|
2583
4110
|
version: "2.0.0",
|
|
2584
4111
|
from: deps.ownerAddress,
|
|
2585
|
-
chainId: (0,
|
|
4112
|
+
chainId: (0, import_viem8.toHex)(chainId),
|
|
2586
4113
|
atomicRequired: false,
|
|
2587
4114
|
calls: [{ to: token, value: "0x0", data }],
|
|
2588
4115
|
capabilities: {
|
|
2589
|
-
paymasterService: { url:
|
|
4116
|
+
paymasterService: { url: paymasterUrl }
|
|
2590
4117
|
}
|
|
2591
4118
|
}
|
|
2592
4119
|
]
|
|
@@ -2616,7 +4143,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2616
4143
|
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
2617
4144
|
{ chainId, callsId }
|
|
2618
4145
|
);
|
|
2619
|
-
};
|
|
4146
|
+
});
|
|
2620
4147
|
}
|
|
2621
4148
|
|
|
2622
4149
|
// src/client.ts
|
|
@@ -2646,9 +4173,9 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2646
4173
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2647
4174
|
};
|
|
2648
4175
|
var VIEM_CHAIN2 = {
|
|
2649
|
-
8453:
|
|
2650
|
-
42161:
|
|
2651
|
-
1:
|
|
4176
|
+
8453: import_chains4.base,
|
|
4177
|
+
42161: import_chains4.arbitrum,
|
|
4178
|
+
1: import_chains4.mainnet
|
|
2652
4179
|
};
|
|
2653
4180
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
2654
4181
|
8453: "0x4200000000000000000000000000000000000006",
|
|
@@ -2676,6 +4203,7 @@ var OwneySDK = class {
|
|
|
2676
4203
|
orgAgentConfig;
|
|
2677
4204
|
orgAgentConfigPromise = null;
|
|
2678
4205
|
zyfaiRpcUrls;
|
|
4206
|
+
yieldseekerApiBaseUrl;
|
|
2679
4207
|
routingApiBaseUrl;
|
|
2680
4208
|
referralSource;
|
|
2681
4209
|
cachedSponsoredCallback = null;
|
|
@@ -2687,6 +4215,7 @@ var OwneySDK = class {
|
|
|
2687
4215
|
this.apiKey = config.apiKey;
|
|
2688
4216
|
if (config.debug) setOwneyDebug(true);
|
|
2689
4217
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
4218
|
+
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
2690
4219
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2691
4220
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2692
4221
|
this.referralSource = config.referralSource;
|
|
@@ -2789,14 +4318,14 @@ var OwneySDK = class {
|
|
|
2789
4318
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2790
4319
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2791
4320
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2792
|
-
getPublicClient: (cid) => (0,
|
|
4321
|
+
getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
|
|
2793
4322
|
chain: VIEM_CHAIN2[cid],
|
|
2794
|
-
transport: (0,
|
|
4323
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2795
4324
|
}),
|
|
2796
|
-
getWalletClient: (cid) => (0,
|
|
4325
|
+
getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
|
|
2797
4326
|
account: owner,
|
|
2798
4327
|
chain: VIEM_CHAIN2[cid],
|
|
2799
|
-
transport: (0,
|
|
4328
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2800
4329
|
})
|
|
2801
4330
|
});
|
|
2802
4331
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -2813,6 +4342,8 @@ var OwneySDK = class {
|
|
|
2813
4342
|
if (!onApproved && cached) return cached;
|
|
2814
4343
|
const provider = this.requireConnectedProvider();
|
|
2815
4344
|
const callback = makeSponsoredCallsCallback({
|
|
4345
|
+
apiKey: this.apiKey,
|
|
4346
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
2816
4347
|
provider,
|
|
2817
4348
|
ownerAddress: this.state.walletAddress,
|
|
2818
4349
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
@@ -2842,14 +4373,14 @@ var OwneySDK = class {
|
|
|
2842
4373
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2843
4374
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2844
4375
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2845
|
-
getPublicClient: (cid) => (0,
|
|
4376
|
+
getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
|
|
2846
4377
|
chain: VIEM_CHAIN2[cid],
|
|
2847
|
-
transport: (0,
|
|
4378
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2848
4379
|
}),
|
|
2849
|
-
getWalletClient: (cid) => (0,
|
|
4380
|
+
getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
|
|
2850
4381
|
account: owner,
|
|
2851
4382
|
chain: VIEM_CHAIN2[cid],
|
|
2852
|
-
transport: (0,
|
|
4383
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2853
4384
|
})
|
|
2854
4385
|
});
|
|
2855
4386
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -2877,12 +4408,13 @@ var OwneySDK = class {
|
|
|
2877
4408
|
async getOrgAgentConfig() {
|
|
2878
4409
|
if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
|
|
2879
4410
|
if (!this.orgAgentConfigPromise) {
|
|
2880
|
-
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
4411
|
+
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
4412
|
+
this.apiKey,
|
|
4413
|
+
this.routingApiBaseUrl
|
|
4414
|
+
).then((config) => {
|
|
4415
|
+
this.orgAgentConfig = config;
|
|
4416
|
+
return config;
|
|
4417
|
+
});
|
|
2886
4418
|
}
|
|
2887
4419
|
return this.orgAgentConfigPromise;
|
|
2888
4420
|
}
|
|
@@ -2917,9 +4449,19 @@ var OwneySDK = class {
|
|
|
2917
4449
|
if (this.agents.size > 0) return;
|
|
2918
4450
|
if (!this.initializingAgentsPromise) {
|
|
2919
4451
|
this.initializingAgentsPromise = (async () => {
|
|
2920
|
-
const agentKeys = await fetchAgentKeys(
|
|
4452
|
+
const agentKeys = await fetchAgentKeys(
|
|
4453
|
+
this.apiKey,
|
|
4454
|
+
this.routingApiBaseUrl
|
|
4455
|
+
);
|
|
2921
4456
|
this.disabledAgents.clear();
|
|
2922
|
-
for (const {
|
|
4457
|
+
for (const {
|
|
4458
|
+
key: key2,
|
|
4459
|
+
agent_type,
|
|
4460
|
+
is_enabled,
|
|
4461
|
+
is_configured
|
|
4462
|
+
} of agentKeys) {
|
|
4463
|
+
const configured = is_configured ?? Boolean(key2);
|
|
4464
|
+
if (!configured) continue;
|
|
2923
4465
|
const agent = this.createAgent(agent_type, key2);
|
|
2924
4466
|
if (!agent) continue;
|
|
2925
4467
|
this.agents.set(agent_type, agent);
|
|
@@ -2943,10 +4485,47 @@ var OwneySDK = class {
|
|
|
2943
4485
|
}
|
|
2944
4486
|
createAgent(agentId, key2) {
|
|
2945
4487
|
if (agentId === "zyfai") {
|
|
4488
|
+
if (!key2) return null;
|
|
2946
4489
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
2947
4490
|
}
|
|
4491
|
+
if (agentId === "yieldseeker") {
|
|
4492
|
+
return new YieldseekerAgent(this.apiKey, {
|
|
4493
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
4494
|
+
});
|
|
4495
|
+
}
|
|
2948
4496
|
return null;
|
|
2949
4497
|
}
|
|
4498
|
+
/**
|
|
4499
|
+
* Discover the agents actually returned by routing for this organization.
|
|
4500
|
+
* Consumers should use this instead of hardcoding a global agent roster.
|
|
4501
|
+
*/
|
|
4502
|
+
async getAvailableAgents(options = {}) {
|
|
4503
|
+
await this.ensureAgentsInitialized();
|
|
4504
|
+
const { chainId, asset, includeDisabled = false } = options;
|
|
4505
|
+
const available = [];
|
|
4506
|
+
for (const [id, agent] of this.agents) {
|
|
4507
|
+
const isEnabled = !this.isAgentDisabled(id);
|
|
4508
|
+
if (!includeDisabled && !isEnabled) continue;
|
|
4509
|
+
if (chainId !== void 0 && !agent.supportedChainIds.includes(chainId)) {
|
|
4510
|
+
continue;
|
|
4511
|
+
}
|
|
4512
|
+
if (asset !== void 0) {
|
|
4513
|
+
const supportsAsset = agent.supportedAssets.some(
|
|
4514
|
+
(entry) => (chainId === void 0 || entry.chainId === chainId) && entry.assets.some((candidate) => candidate.symbol === asset)
|
|
4515
|
+
);
|
|
4516
|
+
if (!supportsAsset) {
|
|
4517
|
+
continue;
|
|
4518
|
+
}
|
|
4519
|
+
}
|
|
4520
|
+
available.push({
|
|
4521
|
+
id,
|
|
4522
|
+
isEnabled,
|
|
4523
|
+
supportedChainIds: agent.supportedChainIds,
|
|
4524
|
+
supportedAssets: agent.supportedAssets
|
|
4525
|
+
});
|
|
4526
|
+
}
|
|
4527
|
+
return available;
|
|
4528
|
+
}
|
|
2950
4529
|
// --- Account lifecycle ---
|
|
2951
4530
|
/**
|
|
2952
4531
|
* Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
|
|
@@ -2958,7 +4537,7 @@ var OwneySDK = class {
|
|
|
2958
4537
|
* If provided, ALL specified agents must support the chainId or the call
|
|
2959
4538
|
* throws before activating any agent.
|
|
2960
4539
|
*/
|
|
2961
|
-
async activateAgent(chainId, agentId) {
|
|
4540
|
+
async activateAgent(chainId, agentId, asset) {
|
|
2962
4541
|
const state = this.requireState();
|
|
2963
4542
|
await this.ensureAgentsInitialized();
|
|
2964
4543
|
if (agentId !== void 0) {
|
|
@@ -2994,9 +4573,7 @@ var OwneySDK = class {
|
|
|
2994
4573
|
this.activeAgents.add(id);
|
|
2995
4574
|
}
|
|
2996
4575
|
state.chainId = chainId;
|
|
2997
|
-
this.activateAgentsInTurn(agents, state, chainId)
|
|
2998
|
-
console.error("activateAgent background init failed:", error);
|
|
2999
|
-
});
|
|
4576
|
+
await this.activateAgentsInTurn(agents, state, chainId, asset);
|
|
3000
4577
|
return;
|
|
3001
4578
|
}
|
|
3002
4579
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -3017,11 +4594,7 @@ var OwneySDK = class {
|
|
|
3017
4594
|
const enabledCompatible = compatible.filter(
|
|
3018
4595
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
3019
4596
|
);
|
|
3020
|
-
this.activateAgentsInTurn(enabledCompatible, state, chainId)
|
|
3021
|
-
(error) => {
|
|
3022
|
-
console.error("activateAgent background init failed:", error);
|
|
3023
|
-
}
|
|
3024
|
-
);
|
|
4597
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
|
|
3025
4598
|
}
|
|
3026
4599
|
/**
|
|
3027
4600
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -3041,11 +4614,11 @@ var OwneySDK = class {
|
|
|
3041
4614
|
* rethrown (matching the previous `Promise.all` rejection) once all agents
|
|
3042
4615
|
* have had a chance to activate.
|
|
3043
4616
|
*/
|
|
3044
|
-
async activateAgentsInTurn(agents, state, chainId) {
|
|
4617
|
+
async activateAgentsInTurn(agents, state, chainId, asset) {
|
|
3045
4618
|
let firstError = null;
|
|
3046
4619
|
for (const agent of agents) {
|
|
3047
4620
|
try {
|
|
3048
|
-
await agent.activateAgent(state, chainId);
|
|
4621
|
+
await agent.activateAgent(state, chainId, asset);
|
|
3049
4622
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
3050
4623
|
} catch (error) {
|
|
3051
4624
|
if (firstError === null) {
|
|
@@ -3173,7 +4746,8 @@ var OwneySDK = class {
|
|
|
3173
4746
|
amount,
|
|
3174
4747
|
asset,
|
|
3175
4748
|
callback
|
|
3176
|
-
)
|
|
4749
|
+
),
|
|
4750
|
+
this.routingApiBaseUrl
|
|
3177
4751
|
);
|
|
3178
4752
|
let approvalAttempted = false;
|
|
3179
4753
|
for (; ; ) {
|
|
@@ -3212,15 +4786,15 @@ var OwneySDK = class {
|
|
|
3212
4786
|
agent,
|
|
3213
4787
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
3214
4788
|
}));
|
|
3215
|
-
const
|
|
4789
|
+
const valid2 = splits.filter(
|
|
3216
4790
|
(s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
|
|
3217
4791
|
);
|
|
3218
|
-
if (
|
|
4792
|
+
if (valid2.length === agents.length) {
|
|
3219
4793
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
3220
4794
|
}
|
|
3221
4795
|
return this.splitDepositAmount(
|
|
3222
4796
|
totalAmount,
|
|
3223
|
-
|
|
4797
|
+
valid2.map((s) => s.agent),
|
|
3224
4798
|
chainId,
|
|
3225
4799
|
asset,
|
|
3226
4800
|
exempt
|
|
@@ -3323,7 +4897,8 @@ var OwneySDK = class {
|
|
|
3323
4897
|
return withFailureReporting(
|
|
3324
4898
|
this.apiKey,
|
|
3325
4899
|
agent.id,
|
|
3326
|
-
() => agent.withdraw(state, chainId, token, amount)
|
|
4900
|
+
() => agent.withdraw(state, chainId, token, amount),
|
|
4901
|
+
this.routingApiBaseUrl
|
|
3327
4902
|
);
|
|
3328
4903
|
}
|
|
3329
4904
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
@@ -3337,7 +4912,12 @@ var OwneySDK = class {
|
|
|
3337
4912
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
3338
4913
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
3339
4914
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3340
|
-
void reportAgentFailure(
|
|
4915
|
+
void reportAgentFailure(
|
|
4916
|
+
this.apiKey,
|
|
4917
|
+
agent.id,
|
|
4918
|
+
code,
|
|
4919
|
+
this.routingApiBaseUrl
|
|
4920
|
+
);
|
|
3341
4921
|
}
|
|
3342
4922
|
}
|
|
3343
4923
|
if (Object.keys(results2).length === 0) {
|
|
@@ -3354,6 +4934,10 @@ var OwneySDK = class {
|
|
|
3354
4934
|
}
|
|
3355
4935
|
const requested = BigInt(amount);
|
|
3356
4936
|
const aggregated = await this.getBalances();
|
|
4937
|
+
const unavailableAgents = eligibleAgents.filter(
|
|
4938
|
+
(agent) => !(agent.id in aggregated.agentBalances)
|
|
4939
|
+
);
|
|
4940
|
+
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
3357
4941
|
const balances = projectAgentBalancesForAsset(
|
|
3358
4942
|
eligibleAgents,
|
|
3359
4943
|
aggregated.agentBalances,
|
|
@@ -3362,7 +4946,18 @@ var OwneySDK = class {
|
|
|
3362
4946
|
assetInfo.decimals
|
|
3363
4947
|
);
|
|
3364
4948
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
3365
|
-
if (totalAvailable
|
|
4949
|
+
if (totalAvailable === 0n && unavailableAgents.length > 0) {
|
|
4950
|
+
throw new OwneyError(
|
|
4951
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
4952
|
+
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
4953
|
+
{
|
|
4954
|
+
asset,
|
|
4955
|
+
unavailableAgents: unavailableAgentIds,
|
|
4956
|
+
agentErrors: aggregated.agentErrors
|
|
4957
|
+
}
|
|
4958
|
+
);
|
|
4959
|
+
}
|
|
4960
|
+
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3366
4961
|
throw new OwneyError(
|
|
3367
4962
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3368
4963
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -3373,6 +4968,7 @@ var OwneySDK = class {
|
|
|
3373
4968
|
}
|
|
3374
4969
|
);
|
|
3375
4970
|
}
|
|
4971
|
+
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
3376
4972
|
const disabledBalances = balances.filter(
|
|
3377
4973
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
3378
4974
|
);
|
|
@@ -3381,7 +4977,7 @@ var OwneySDK = class {
|
|
|
3381
4977
|
);
|
|
3382
4978
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
3383
4979
|
disabledBalances,
|
|
3384
|
-
|
|
4980
|
+
plannedTarget
|
|
3385
4981
|
);
|
|
3386
4982
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
3387
4983
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -3391,7 +4987,9 @@ var OwneySDK = class {
|
|
|
3391
4987
|
}));
|
|
3392
4988
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
3393
4989
|
const results = {};
|
|
3394
|
-
const agentErrors = {
|
|
4990
|
+
const agentErrors = {
|
|
4991
|
+
...aggregated.agentErrors ?? {}
|
|
4992
|
+
};
|
|
3395
4993
|
for (let i = 0; i < plans.length; i++) {
|
|
3396
4994
|
const p = plans[i];
|
|
3397
4995
|
if (p.planned === 0n) continue;
|
|
@@ -3406,7 +5004,12 @@ var OwneySDK = class {
|
|
|
3406
5004
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
3407
5005
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
3408
5006
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3409
|
-
void reportAgentFailure(
|
|
5007
|
+
void reportAgentFailure(
|
|
5008
|
+
this.apiKey,
|
|
5009
|
+
p.agent.id,
|
|
5010
|
+
code,
|
|
5011
|
+
this.routingApiBaseUrl
|
|
5012
|
+
);
|
|
3410
5013
|
const failedAmount = p.planned;
|
|
3411
5014
|
p.planned = 0n;
|
|
3412
5015
|
redistributeShare(plans, i, failedAmount);
|
|
@@ -3433,7 +5036,8 @@ var OwneySDK = class {
|
|
|
3433
5036
|
requested: amount,
|
|
3434
5037
|
withdrawn: withdrawn.toString(),
|
|
3435
5038
|
partialResults: results,
|
|
3436
|
-
agentErrors
|
|
5039
|
+
agentErrors,
|
|
5040
|
+
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3437
5041
|
}
|
|
3438
5042
|
);
|
|
3439
5043
|
}
|
|
@@ -3474,7 +5078,12 @@ var OwneySDK = class {
|
|
|
3474
5078
|
continue;
|
|
3475
5079
|
}
|
|
3476
5080
|
const reason = settledResult.reason;
|
|
3477
|
-
|
|
5081
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
5082
|
+
agentErrors[agentId2] = message;
|
|
5083
|
+
console.error(
|
|
5084
|
+
`[owney-sdk] Balance fetch failed for agent "${agentId2}":`,
|
|
5085
|
+
reason
|
|
5086
|
+
);
|
|
3478
5087
|
}
|
|
3479
5088
|
if (successCount === 0) {
|
|
3480
5089
|
throw new OwneyError(
|
|
@@ -3486,7 +5095,8 @@ var OwneySDK = class {
|
|
|
3486
5095
|
return {
|
|
3487
5096
|
totalBalance: String(totalBalance),
|
|
3488
5097
|
totalBalanceAsset: "usdc",
|
|
3489
|
-
agentBalances: results
|
|
5098
|
+
agentBalances: results,
|
|
5099
|
+
...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
|
|
3490
5100
|
};
|
|
3491
5101
|
}
|
|
3492
5102
|
/**
|
|
@@ -3580,7 +5190,10 @@ var OwneySDK = class {
|
|
|
3580
5190
|
Promise.all(
|
|
3581
5191
|
entries.map(async ([id, agent]) => {
|
|
3582
5192
|
const b = await agent.getBalances(state, chainId);
|
|
3583
|
-
return [
|
|
5193
|
+
return [
|
|
5194
|
+
id,
|
|
5195
|
+
balanceForApyScope(b, chainId, tokenSymbol)
|
|
5196
|
+
];
|
|
3584
5197
|
})
|
|
3585
5198
|
)
|
|
3586
5199
|
]);
|
|
@@ -3608,10 +5221,12 @@ var OwneySDK = class {
|
|
|
3608
5221
|
}
|
|
3609
5222
|
}
|
|
3610
5223
|
const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
|
|
5224
|
+
const history = aggregateApyHistory(results, balances);
|
|
3611
5225
|
return {
|
|
3612
5226
|
totalApy: String(totalApy),
|
|
3613
5227
|
agentApy: results,
|
|
3614
|
-
apyByChainAndAsset
|
|
5228
|
+
apyByChainAndAsset,
|
|
5229
|
+
history
|
|
3615
5230
|
};
|
|
3616
5231
|
}
|
|
3617
5232
|
/**
|
|
@@ -3741,10 +5356,10 @@ var OwneySDK = class {
|
|
|
3741
5356
|
);
|
|
3742
5357
|
}
|
|
3743
5358
|
const provider = this.requireConnectedProvider();
|
|
3744
|
-
const wallet = (0,
|
|
5359
|
+
const wallet = (0, import_viem9.createWalletClient)({
|
|
3745
5360
|
account: state.walletAddress,
|
|
3746
5361
|
chain: VIEM_CHAIN2[chainId],
|
|
3747
|
-
transport: (0,
|
|
5362
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3748
5363
|
});
|
|
3749
5364
|
const hash = await wallet.writeContract({
|
|
3750
5365
|
address: token,
|
|
@@ -3754,9 +5369,9 @@ var OwneySDK = class {
|
|
|
3754
5369
|
account: state.walletAddress,
|
|
3755
5370
|
chain: VIEM_CHAIN2[chainId]
|
|
3756
5371
|
});
|
|
3757
|
-
const publicClient = (0,
|
|
5372
|
+
const publicClient = (0, import_viem9.createPublicClient)({
|
|
3758
5373
|
chain: VIEM_CHAIN2[chainId],
|
|
3759
|
-
transport: (0,
|
|
5374
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3760
5375
|
});
|
|
3761
5376
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3762
5377
|
hash,
|
|
@@ -3792,7 +5407,9 @@ var OwneySDK = class {
|
|
|
3792
5407
|
return this.getAgent(agentId).getAgentApy(days, agentOptions);
|
|
3793
5408
|
}
|
|
3794
5409
|
const results = {};
|
|
3795
|
-
const agentEntries = [...this.agents.entries()]
|
|
5410
|
+
const agentEntries = [...this.agents.entries()].filter(
|
|
5411
|
+
([id]) => !this.isAgentDisabled(id)
|
|
5412
|
+
);
|
|
3796
5413
|
const apyResults = await Promise.all(
|
|
3797
5414
|
agentEntries.map(async ([id, agent]) => {
|
|
3798
5415
|
const apy = await agent.getAgentApy(days, agentOptions);
|
|
@@ -3865,13 +5482,13 @@ var OwneySDK = class {
|
|
|
3865
5482
|
};
|
|
3866
5483
|
|
|
3867
5484
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
3868
|
-
var
|
|
3869
|
-
var
|
|
5485
|
+
var import_viem10 = require("viem");
|
|
5486
|
+
var import_siwe2 = require("siwe");
|
|
3870
5487
|
var import_sdk2 = require("@zyfai/sdk");
|
|
3871
5488
|
|
|
3872
5489
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
3873
|
-
var
|
|
3874
|
-
var
|
|
5490
|
+
var KEY_PREFIX4 = "owney.siwx.session";
|
|
5491
|
+
var storage4 = () => {
|
|
3875
5492
|
if (typeof window === "undefined") return null;
|
|
3876
5493
|
try {
|
|
3877
5494
|
return window.localStorage;
|
|
@@ -3879,8 +5496,8 @@ var storage2 = () => {
|
|
|
3879
5496
|
return null;
|
|
3880
5497
|
}
|
|
3881
5498
|
};
|
|
3882
|
-
var
|
|
3883
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
5499
|
+
var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
|
|
5500
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
|
|
3884
5501
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
3885
5502
|
var readLegacySiwxSession = (store, address) => {
|
|
3886
5503
|
if (!store) return null;
|
|
@@ -3911,17 +5528,17 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
3911
5528
|
};
|
|
3912
5529
|
var readSiwxSession = (address, chainId) => {
|
|
3913
5530
|
if (typeof window === "undefined") return null;
|
|
3914
|
-
const key2 =
|
|
3915
|
-
const store =
|
|
3916
|
-
let
|
|
5531
|
+
const key2 = buildKey3(address);
|
|
5532
|
+
const store = storage4();
|
|
5533
|
+
let raw2 = null;
|
|
3917
5534
|
try {
|
|
3918
|
-
|
|
5535
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
3919
5536
|
} catch {
|
|
3920
|
-
|
|
5537
|
+
raw2 = null;
|
|
3921
5538
|
}
|
|
3922
|
-
if (
|
|
5539
|
+
if (raw2) {
|
|
3923
5540
|
try {
|
|
3924
|
-
return JSON.parse(
|
|
5541
|
+
return JSON.parse(raw2);
|
|
3925
5542
|
} catch {
|
|
3926
5543
|
memorySiwxSessions.delete(key2);
|
|
3927
5544
|
try {
|
|
@@ -3940,18 +5557,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
3940
5557
|
};
|
|
3941
5558
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
3942
5559
|
if (typeof window === "undefined") return;
|
|
3943
|
-
const key2 =
|
|
5560
|
+
const key2 = buildKey3(address);
|
|
3944
5561
|
memorySiwxSessions.set(key2, session);
|
|
3945
|
-
const store =
|
|
5562
|
+
const store = storage4();
|
|
3946
5563
|
try {
|
|
3947
5564
|
store?.setItem(key2, JSON.stringify(session));
|
|
3948
5565
|
} catch {
|
|
3949
5566
|
}
|
|
3950
5567
|
};
|
|
3951
5568
|
var clearSiwxSession = (address, _chainId) => {
|
|
3952
|
-
const key2 =
|
|
5569
|
+
const key2 = buildKey3(address);
|
|
3953
5570
|
memorySiwxSessions.delete(key2);
|
|
3954
|
-
const store =
|
|
5571
|
+
const store = storage4();
|
|
3955
5572
|
try {
|
|
3956
5573
|
store?.removeItem(key2);
|
|
3957
5574
|
} catch {
|
|
@@ -3991,8 +5608,8 @@ function buildSIWXConfig(deps) {
|
|
|
3991
5608
|
statement: STATEMENT,
|
|
3992
5609
|
issuedAt,
|
|
3993
5610
|
toString() {
|
|
3994
|
-
return new
|
|
3995
|
-
address: (0,
|
|
5611
|
+
return new import_siwe2.SiweMessage({
|
|
5612
|
+
address: (0, import_viem10.getAddress)(accountAddress),
|
|
3996
5613
|
chainId: numericChainId(chainId),
|
|
3997
5614
|
domain,
|
|
3998
5615
|
uri,
|
|
@@ -4034,7 +5651,7 @@ function buildSIWXConfig(deps) {
|
|
|
4034
5651
|
const persistSession = async (session) => {
|
|
4035
5652
|
const address = session.data.accountAddress;
|
|
4036
5653
|
const id = numericChainId(session.data.chainId);
|
|
4037
|
-
const message = new
|
|
5654
|
+
const message = new import_siwe2.SiweMessage(session.message);
|
|
4038
5655
|
const login = await post("/auth/login", {
|
|
4039
5656
|
message,
|
|
4040
5657
|
signature: session.signature,
|
|
@@ -4083,6 +5700,7 @@ function createOwneySIWX(config) {
|
|
|
4083
5700
|
NotConnectedError,
|
|
4084
5701
|
OwneyError,
|
|
4085
5702
|
OwneySDK,
|
|
5703
|
+
YieldseekerAgent,
|
|
4086
5704
|
createOwneySIWX,
|
|
4087
5705
|
setOwneyDebug
|
|
4088
5706
|
});
|