@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.js
CHANGED
|
@@ -50,23 +50,6 @@ var SupportedAssets = [
|
|
|
50
50
|
}
|
|
51
51
|
];
|
|
52
52
|
|
|
53
|
-
// src/lib/debug.ts
|
|
54
|
-
var configuredDebug = false;
|
|
55
|
-
function setOwneyDebug(enabled) {
|
|
56
|
-
configuredDebug = enabled;
|
|
57
|
-
}
|
|
58
|
-
function isOwneyDebug() {
|
|
59
|
-
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
60
|
-
}
|
|
61
|
-
function debugLog(scope, message, data) {
|
|
62
|
-
if (!isOwneyDebug()) return;
|
|
63
|
-
if (data === void 0) {
|
|
64
|
-
console.log(`[${scope}] ${message}`);
|
|
65
|
-
} else {
|
|
66
|
-
console.log(`[${scope}] ${message}`, data);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
53
|
// src/errors.ts
|
|
71
54
|
var OwneyError = class extends Error {
|
|
72
55
|
code;
|
|
@@ -164,18 +147,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
164
147
|
return explicit;
|
|
165
148
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
166
149
|
}
|
|
167
|
-
function mapDeposit(
|
|
150
|
+
function mapDeposit(raw2) {
|
|
168
151
|
return {
|
|
169
|
-
txHash:
|
|
170
|
-
smartWallet:
|
|
171
|
-
amount:
|
|
152
|
+
txHash: raw2.txHash,
|
|
153
|
+
smartWallet: raw2.smartWallet,
|
|
154
|
+
amount: raw2.amount
|
|
172
155
|
};
|
|
173
156
|
}
|
|
174
|
-
function mapWithdraw(
|
|
157
|
+
function mapWithdraw(raw2) {
|
|
175
158
|
return {
|
|
176
|
-
txHash:
|
|
177
|
-
type:
|
|
178
|
-
amount:
|
|
159
|
+
txHash: raw2.txHash,
|
|
160
|
+
type: raw2.type,
|
|
161
|
+
amount: raw2.amount
|
|
179
162
|
};
|
|
180
163
|
}
|
|
181
164
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -194,10 +177,10 @@ function resolveChainId(chain) {
|
|
|
194
177
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
195
178
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
196
179
|
}
|
|
197
|
-
function mapPendingAllocations(
|
|
198
|
-
if (!Array.isArray(
|
|
180
|
+
function mapPendingAllocations(raw2) {
|
|
181
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
199
182
|
const pending = [];
|
|
200
|
-
for (const entry of
|
|
183
|
+
for (const entry of raw2) {
|
|
201
184
|
if (typeof entry !== "object" || entry === null) continue;
|
|
202
185
|
const e = entry;
|
|
203
186
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -220,8 +203,8 @@ function mapPendingAllocations(raw) {
|
|
|
220
203
|
}
|
|
221
204
|
return pending.length > 0 ? pending : void 0;
|
|
222
205
|
}
|
|
223
|
-
function mapBalances(
|
|
224
|
-
const portfolio =
|
|
206
|
+
function mapBalances(raw2, _chainId, smartWallet) {
|
|
207
|
+
const portfolio = raw2.portfolio;
|
|
225
208
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
226
209
|
let totalBalance = 0;
|
|
227
210
|
const tokens = [];
|
|
@@ -266,7 +249,7 @@ function mapBalances(raw, _chainId, smartWallet) {
|
|
|
266
249
|
)
|
|
267
250
|
)
|
|
268
251
|
),
|
|
269
|
-
apy:
|
|
252
|
+
apy: p.pool_apy,
|
|
270
253
|
tvl: p.pool_tvl,
|
|
271
254
|
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
272
255
|
// structural read keeps it undefined today and auto-populates later.
|
|
@@ -290,8 +273,8 @@ function sumTokenValues(tokens) {
|
|
|
290
273
|
function sumTokenEarnings(tokens) {
|
|
291
274
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
292
275
|
}
|
|
293
|
-
function mapEarnings(
|
|
294
|
-
const totalEarningsByChain =
|
|
276
|
+
function mapEarnings(raw2, smartWallet) {
|
|
277
|
+
const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
|
|
295
278
|
const tokens = [];
|
|
296
279
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
297
280
|
totalEarningsByChain
|
|
@@ -310,15 +293,15 @@ function mapEarnings(raw, smartWallet) {
|
|
|
310
293
|
return {
|
|
311
294
|
smartWallet,
|
|
312
295
|
lifetimeEarnings: sumTokenEarnings(
|
|
313
|
-
|
|
296
|
+
raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
|
|
314
297
|
),
|
|
315
298
|
tokens
|
|
316
299
|
};
|
|
317
300
|
}
|
|
318
|
-
function mapWeightedApyByChain(
|
|
319
|
-
if (!
|
|
301
|
+
function mapWeightedApyByChain(raw2) {
|
|
302
|
+
if (!raw2) return void 0;
|
|
320
303
|
const out = {};
|
|
321
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
304
|
+
for (const [chainKey, tokenApy] of Object.entries(raw2)) {
|
|
322
305
|
const chainId = Number(chainKey);
|
|
323
306
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
324
307
|
const perAsset = {};
|
|
@@ -331,19 +314,6 @@ function mapWeightedApyByChain(raw) {
|
|
|
331
314
|
}
|
|
332
315
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
333
316
|
}
|
|
334
|
-
var warnedGrossApyFallbacks = /* @__PURE__ */ new Set();
|
|
335
|
-
function netApy(net, gross, field) {
|
|
336
|
-
if (net !== void 0 && net !== null) return net;
|
|
337
|
-
if (gross === void 0 || gross === null) return void 0;
|
|
338
|
-
if (!warnedGrossApyFallbacks.has(field)) {
|
|
339
|
-
warnedGrossApyFallbacks.add(field);
|
|
340
|
-
console.warn(
|
|
341
|
-
`[owney] @zyfai/sdk omitted "${field}"; falling back to the gross APY, which does not deduct Zyfai's performance fee and so reads high.`
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
debugLog("zyfai:apy", `gross fallback for ${field}`, { gross });
|
|
345
|
-
return gross;
|
|
346
|
-
}
|
|
347
317
|
function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
348
318
|
let weightedSum = 0;
|
|
349
319
|
let totalBalance = 0;
|
|
@@ -351,22 +321,21 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
351
321
|
if (p.chainId !== chainId) continue;
|
|
352
322
|
if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
|
|
353
323
|
const balance = p.balance ?? 0;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
weightedSum += apy * balance;
|
|
324
|
+
if (balance <= 0 || typeof p.apy !== "number") continue;
|
|
325
|
+
weightedSum += p.apy * balance;
|
|
357
326
|
totalBalance += balance;
|
|
358
327
|
}
|
|
359
328
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
360
329
|
}
|
|
361
|
-
function mapApyHistory(
|
|
362
|
-
const history = Object.entries(
|
|
330
|
+
function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
331
|
+
const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
|
|
363
332
|
date,
|
|
364
333
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
|
|
365
334
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
366
335
|
return {
|
|
367
|
-
walletAddress:
|
|
368
|
-
weightedApyAfterFee:
|
|
369
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
336
|
+
walletAddress: raw2.walletAddress,
|
|
337
|
+
weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
|
|
338
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
|
|
370
339
|
history
|
|
371
340
|
};
|
|
372
341
|
}
|
|
@@ -401,12 +370,8 @@ function mapEntries(rawEntries, chainId) {
|
|
|
401
370
|
const matched = positions.find(
|
|
402
371
|
(p) => p.protocol_name ? rawLog.newOpportunity.includes(`${p.protocol_name} (${p.pool})`) : false
|
|
403
372
|
);
|
|
404
|
-
oldApy = Number(
|
|
405
|
-
|
|
406
|
-
);
|
|
407
|
-
newApy = Number(
|
|
408
|
-
netApy(rawLog.newApy_withFee, rawLog.newApy, "newApy_withFee")
|
|
409
|
-
);
|
|
373
|
+
oldApy = Number(rawLog.oldApy);
|
|
374
|
+
newApy = Number(rawLog.newApy);
|
|
410
375
|
const from = splitOpportunity(rawLog.oldOpportunity);
|
|
411
376
|
const to = splitOpportunity(rawLog.newOpportunity);
|
|
412
377
|
rebalanceLog = [
|
|
@@ -462,23 +427,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
462
427
|
};
|
|
463
428
|
});
|
|
464
429
|
}
|
|
465
|
-
function mapUserProfile(
|
|
430
|
+
function mapUserProfile(raw2, userAddress) {
|
|
466
431
|
return {
|
|
467
432
|
address: userAddress,
|
|
468
|
-
smartWallet:
|
|
469
|
-
chains:
|
|
470
|
-
strategy:
|
|
471
|
-
hasActiveSessionKey:
|
|
472
|
-
protocols:
|
|
473
|
-
splitting:
|
|
474
|
-
minSplits:
|
|
433
|
+
smartWallet: raw2.smartWallet || "",
|
|
434
|
+
chains: raw2.chains || [],
|
|
435
|
+
strategy: raw2.strategy,
|
|
436
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey || false,
|
|
437
|
+
protocols: raw2.protocols || [],
|
|
438
|
+
splitting: raw2.splitting,
|
|
439
|
+
minSplits: raw2.minSplits
|
|
475
440
|
};
|
|
476
441
|
}
|
|
477
|
-
function mapApyByStrategy(
|
|
442
|
+
function mapApyByStrategy(raw2) {
|
|
478
443
|
const apyPerAsset = {};
|
|
479
444
|
let apySum = 0;
|
|
480
445
|
let apyCount = 0;
|
|
481
|
-
for (const entry of
|
|
446
|
+
for (const entry of raw2.data) {
|
|
482
447
|
const supported = SupportedAssets.find(
|
|
483
448
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
484
449
|
);
|
|
@@ -486,11 +451,7 @@ function mapApyByStrategy(raw) {
|
|
|
486
451
|
const chainKey = supported.chainId;
|
|
487
452
|
const symbolKey = supported.symbol;
|
|
488
453
|
const bucket = apyPerAsset[chainKey] ?? {};
|
|
489
|
-
const apy =
|
|
490
|
-
entry.average_apy_withFee,
|
|
491
|
-
entry.average_apy,
|
|
492
|
-
"average_apy_withFee"
|
|
493
|
-
) ?? entry.average_apy;
|
|
454
|
+
const apy = entry.average_apy_with_fee ?? entry.average_apy;
|
|
494
455
|
bucket[symbolKey] = apy;
|
|
495
456
|
apyPerAsset[chainKey] = bucket;
|
|
496
457
|
apySum += apy;
|
|
@@ -649,15 +610,15 @@ var readSession = (address, _chainId) => {
|
|
|
649
610
|
if (typeof window === "undefined") return null;
|
|
650
611
|
const key2 = buildKey(address);
|
|
651
612
|
const store = storage();
|
|
652
|
-
let
|
|
613
|
+
let raw2 = null;
|
|
653
614
|
try {
|
|
654
|
-
|
|
615
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
655
616
|
} catch {
|
|
656
|
-
|
|
617
|
+
raw2 = null;
|
|
657
618
|
}
|
|
658
|
-
if (
|
|
619
|
+
if (raw2) {
|
|
659
620
|
try {
|
|
660
|
-
const parsed = JSON.parse(
|
|
621
|
+
const parsed = JSON.parse(raw2);
|
|
661
622
|
if (isFreshSession(parsed)) return parsed;
|
|
662
623
|
} catch {
|
|
663
624
|
}
|
|
@@ -825,8 +786,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
825
786
|
}
|
|
826
787
|
return result;
|
|
827
788
|
}
|
|
828
|
-
function flattenAvailablePools(
|
|
829
|
-
const byChain =
|
|
789
|
+
function flattenAvailablePools(raw2) {
|
|
790
|
+
const byChain = raw2 ?? {};
|
|
830
791
|
const names = [];
|
|
831
792
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
832
793
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -850,6 +811,23 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
850
811
|
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
851
812
|
}
|
|
852
813
|
|
|
814
|
+
// src/lib/debug.ts
|
|
815
|
+
var configuredDebug = false;
|
|
816
|
+
function setOwneyDebug(enabled) {
|
|
817
|
+
configuredDebug = enabled;
|
|
818
|
+
}
|
|
819
|
+
function isOwneyDebug() {
|
|
820
|
+
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
821
|
+
}
|
|
822
|
+
function debugLog(scope, message, data) {
|
|
823
|
+
if (!isOwneyDebug()) return;
|
|
824
|
+
if (data === void 0) {
|
|
825
|
+
console.log(`[${scope}] ${message}`);
|
|
826
|
+
} else {
|
|
827
|
+
console.log(`[${scope}] ${message}`, data);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
853
831
|
// src/agents/zyfai/zyfai.agent.ts
|
|
854
832
|
var ERC7579_IS_MODULE_INSTALLED_ABI = parseAbi([
|
|
855
833
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
@@ -1320,8 +1298,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1320
1298
|
const poolResults = await Promise.all(
|
|
1321
1299
|
universe.map(async (protocol) => {
|
|
1322
1300
|
try {
|
|
1323
|
-
const
|
|
1324
|
-
return [protocol.id, flattenAvailablePools(
|
|
1301
|
+
const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1302
|
+
return [protocol.id, flattenAvailablePools(raw2)];
|
|
1325
1303
|
} catch (error) {
|
|
1326
1304
|
console.warn(
|
|
1327
1305
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1387,14 +1365,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1387
1365
|
async readWalletState(ownerAddress) {
|
|
1388
1366
|
try {
|
|
1389
1367
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1390
|
-
const
|
|
1368
|
+
const raw2 = portfolio;
|
|
1391
1369
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1392
|
-
predeployed:
|
|
1393
|
-
hasActiveSessionKey:
|
|
1370
|
+
predeployed: raw2?.predeployed,
|
|
1371
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1394
1372
|
});
|
|
1395
1373
|
return {
|
|
1396
|
-
predeployed:
|
|
1397
|
-
hasActiveSessionKey:
|
|
1374
|
+
predeployed: raw2?.predeployed,
|
|
1375
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1398
1376
|
};
|
|
1399
1377
|
} catch (error) {
|
|
1400
1378
|
console.warn(
|
|
@@ -1674,14 +1652,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1674
1652
|
return { txHash, smartWallet, amount };
|
|
1675
1653
|
}
|
|
1676
1654
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1677
|
-
const
|
|
1655
|
+
const raw2 = await this.sdk.depositFunds(
|
|
1678
1656
|
this.getAddress(),
|
|
1679
1657
|
validChainId,
|
|
1680
1658
|
amount,
|
|
1681
1659
|
asset,
|
|
1682
1660
|
"aggressive"
|
|
1683
1661
|
);
|
|
1684
|
-
return mapDeposit(
|
|
1662
|
+
return mapDeposit(raw2);
|
|
1685
1663
|
} catch (error) {
|
|
1686
1664
|
throw error;
|
|
1687
1665
|
}
|
|
@@ -1690,32 +1668,32 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1690
1668
|
async withdraw(state, chainId, token, amount) {
|
|
1691
1669
|
const validChainId = isValidChainId(chainId);
|
|
1692
1670
|
await this.ensureConnected(state, validChainId);
|
|
1693
|
-
const
|
|
1671
|
+
const raw2 = await this.sdk.withdrawFunds(
|
|
1694
1672
|
this.getAddress(),
|
|
1695
1673
|
validChainId,
|
|
1696
1674
|
amount,
|
|
1697
1675
|
token
|
|
1698
1676
|
);
|
|
1699
|
-
if (!
|
|
1677
|
+
if (!raw2.success) {
|
|
1700
1678
|
throw new OwneyError(
|
|
1701
1679
|
"WITHDRAW_FAILED",
|
|
1702
|
-
|
|
1703
|
-
{ chainId: validChainId, token, amount, response:
|
|
1680
|
+
raw2.message || "Zyfai withdraw failed.",
|
|
1681
|
+
{ chainId: validChainId, token, amount, response: raw2 },
|
|
1704
1682
|
this.id
|
|
1705
1683
|
);
|
|
1706
1684
|
}
|
|
1707
|
-
return mapWithdraw(
|
|
1685
|
+
return mapWithdraw(raw2);
|
|
1708
1686
|
}
|
|
1709
1687
|
// --- IAgent: Portfolio reads ---
|
|
1710
1688
|
async getBalances(state, chainId) {
|
|
1711
1689
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1712
|
-
const
|
|
1713
|
-
return mapBalances(
|
|
1690
|
+
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
1691
|
+
return mapBalances(raw2, validChainId, smartWallet);
|
|
1714
1692
|
}
|
|
1715
1693
|
async getEarnings(state, chainId) {
|
|
1716
1694
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1717
|
-
const
|
|
1718
|
-
return mapEarnings(
|
|
1695
|
+
const raw2 = await this.sdk.getOnchainEarnings(smartWallet);
|
|
1696
|
+
return mapEarnings(raw2, smartWallet);
|
|
1719
1697
|
}
|
|
1720
1698
|
async refreshEarnings(state, chainId) {
|
|
1721
1699
|
if (this.earningsRefreshInFlight) return this.earningsRefreshInFlight;
|
|
@@ -1735,8 +1713,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1735
1713
|
}
|
|
1736
1714
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1737
1715
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1738
|
-
const
|
|
1739
|
-
return mapApyHistory(
|
|
1716
|
+
const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
1717
|
+
return mapApyHistory(raw2, chainId, tokenSymbol);
|
|
1740
1718
|
}
|
|
1741
1719
|
/**
|
|
1742
1720
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -1771,7 +1749,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1771
1749
|
const matched = [];
|
|
1772
1750
|
let backendExhausted = false;
|
|
1773
1751
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
1774
|
-
const
|
|
1752
|
+
const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
1775
1753
|
limit: backendPageSize,
|
|
1776
1754
|
offset,
|
|
1777
1755
|
fromDate: options?.fromDate,
|
|
@@ -1782,13 +1760,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1782
1760
|
// asset's rows and handing back a page that filters to nothing.
|
|
1783
1761
|
assetType
|
|
1784
1762
|
});
|
|
1785
|
-
|
|
1763
|
+
raw2.data.forEach((entry, idx) => {
|
|
1786
1764
|
if (entry.chainId === validChainId) {
|
|
1787
1765
|
matched.push({ entry, rawIdx: offset + idx });
|
|
1788
1766
|
}
|
|
1789
1767
|
});
|
|
1790
|
-
offset +=
|
|
1791
|
-
if (
|
|
1768
|
+
offset += raw2.data.length;
|
|
1769
|
+
if (raw2.data.length < backendPageSize) {
|
|
1792
1770
|
backendExhausted = true;
|
|
1793
1771
|
break;
|
|
1794
1772
|
}
|
|
@@ -1810,18 +1788,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1810
1788
|
}
|
|
1811
1789
|
async getUserProfile(state, chainId) {
|
|
1812
1790
|
await this.connectAuth(state, chainId);
|
|
1813
|
-
const
|
|
1791
|
+
const raw2 = await this.sdk.getUserDetails();
|
|
1814
1792
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
1815
1793
|
asset: "USDC (default \u2014 no asset passed)",
|
|
1816
|
-
splitting:
|
|
1817
|
-
minSplits:
|
|
1818
|
-
strategy:
|
|
1819
|
-
chains:
|
|
1820
|
-
protocolCount:
|
|
1821
|
-
hasActiveSessionKey:
|
|
1822
|
-
smartWallet:
|
|
1794
|
+
splitting: raw2.splitting,
|
|
1795
|
+
minSplits: raw2.minSplits,
|
|
1796
|
+
strategy: raw2.strategy,
|
|
1797
|
+
chains: raw2.chains,
|
|
1798
|
+
protocolCount: raw2.protocols?.length,
|
|
1799
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey,
|
|
1800
|
+
smartWallet: raw2.smartWallet
|
|
1823
1801
|
});
|
|
1824
|
-
return mapUserProfile(
|
|
1802
|
+
return mapUserProfile(raw2, this.connectedAddress);
|
|
1825
1803
|
}
|
|
1826
1804
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
1827
1805
|
await this.connectAuth(state, chainId);
|
|
@@ -1840,325 +1818,192 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1840
1818
|
}
|
|
1841
1819
|
// --- IAgent: Discovery (no wallet required) ---
|
|
1842
1820
|
async getAgentApy(days, options) {
|
|
1843
|
-
const
|
|
1821
|
+
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
1844
1822
|
false,
|
|
1845
1823
|
DayFilterMapping[days],
|
|
1846
1824
|
"aggressive",
|
|
1847
1825
|
options?.chainId,
|
|
1848
1826
|
options?.tokenSymbol
|
|
1849
1827
|
);
|
|
1850
|
-
return mapApyByStrategy(
|
|
1828
|
+
return mapApyByStrategy(raw2);
|
|
1851
1829
|
}
|
|
1852
1830
|
};
|
|
1853
1831
|
|
|
1854
|
-
// src/
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1832
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
1833
|
+
import {
|
|
1834
|
+
createPublicClient as createPublicClient3,
|
|
1835
|
+
createWalletClient as createWalletClient2,
|
|
1836
|
+
custom as custom2,
|
|
1837
|
+
encodeFunctionData,
|
|
1838
|
+
erc20Abi,
|
|
1839
|
+
getAddress as getAddress2,
|
|
1840
|
+
isAddress as isAddress2
|
|
1841
|
+
} from "viem";
|
|
1842
|
+
import { base as base3 } from "viem/chains";
|
|
1843
|
+
|
|
1844
|
+
// src/lib/chain-guard.ts
|
|
1845
|
+
var CHAIN_NAMES = {
|
|
1846
|
+
1: "Ethereum",
|
|
1847
|
+
8453: "Base",
|
|
1848
|
+
42161: "Arbitrum"
|
|
1849
|
+
};
|
|
1850
|
+
function chainName(chainId) {
|
|
1851
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
1852
|
+
}
|
|
1853
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
1854
|
+
const actual = await pub.getChainId();
|
|
1855
|
+
if (actual === expected) return;
|
|
1858
1856
|
try {
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
console.warn(
|
|
1869
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
1870
|
-
);
|
|
1857
|
+
await wallet.switchChain({ id: expected });
|
|
1858
|
+
} catch (error) {
|
|
1859
|
+
throw new OwneyError(
|
|
1860
|
+
"CHAIN_MISMATCH",
|
|
1861
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
1862
|
+
{
|
|
1863
|
+
expectedChainId: expected,
|
|
1864
|
+
actualChainId: actual,
|
|
1865
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
1871
1866
|
}
|
|
1872
|
-
return null;
|
|
1873
|
-
}
|
|
1874
|
-
const json = await res.json();
|
|
1875
|
-
const policy = json.success ? json.data ?? null : null;
|
|
1876
|
-
debugLog(
|
|
1877
|
-
"owney-sdk",
|
|
1878
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
1879
|
-
policy ?? void 0
|
|
1880
1867
|
);
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1868
|
+
}
|
|
1869
|
+
const after = await pub.getChainId();
|
|
1870
|
+
if (after !== expected) {
|
|
1871
|
+
throw new OwneyError(
|
|
1872
|
+
"CHAIN_MISMATCH",
|
|
1873
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
1874
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
1886
1875
|
);
|
|
1887
|
-
return null;
|
|
1888
1876
|
}
|
|
1889
1877
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1878
|
+
|
|
1879
|
+
// src/lib/transfer-auth.ts
|
|
1880
|
+
import { bytesToHex } from "viem";
|
|
1881
|
+
var ERC20_META_ABI = [
|
|
1882
|
+
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
1883
|
+
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
1884
|
+
];
|
|
1885
|
+
function buildTransferWithAuthorizationTypedData(input) {
|
|
1886
|
+
return {
|
|
1887
|
+
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
1888
|
+
types: {
|
|
1889
|
+
TransferWithAuthorization: [
|
|
1890
|
+
{ name: "from", type: "address" },
|
|
1891
|
+
{ name: "to", type: "address" },
|
|
1892
|
+
{ name: "value", type: "uint256" },
|
|
1893
|
+
{ name: "validAfter", type: "uint256" },
|
|
1894
|
+
{ name: "validBefore", type: "uint256" },
|
|
1895
|
+
{ name: "nonce", type: "bytes32" }
|
|
1896
|
+
]
|
|
1897
|
+
},
|
|
1898
|
+
primaryType: "TransferWithAuthorization",
|
|
1899
|
+
message: input.message
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
async function readTokenMeta(publicClient, token) {
|
|
1903
|
+
const [tokenName, tokenVersion] = await Promise.all([
|
|
1904
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
1905
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
1906
|
+
]);
|
|
1907
|
+
return { tokenName, tokenVersion };
|
|
1908
|
+
}
|
|
1909
|
+
function randomAuthNonce() {
|
|
1910
|
+
const bytes = new Uint8Array(32);
|
|
1911
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
1912
|
+
return bytesToHex(bytes);
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// src/lib/sponsor-client.ts
|
|
1916
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1917
|
+
async function postPaymasterIntent(input) {
|
|
1918
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1919
|
+
let res;
|
|
1920
|
+
try {
|
|
1921
|
+
res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
|
|
1922
|
+
method: "POST",
|
|
1923
|
+
headers: {
|
|
1924
|
+
"content-type": "application/json",
|
|
1925
|
+
"x-owney-api-key": input.apiKey,
|
|
1926
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
1927
|
+
},
|
|
1928
|
+
body: JSON.stringify(input.body)
|
|
1929
|
+
});
|
|
1930
|
+
} catch (networkError) {
|
|
1901
1931
|
throw new OwneyError(
|
|
1902
|
-
"
|
|
1903
|
-
`
|
|
1904
|
-
{
|
|
1932
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1933
|
+
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1934
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
1905
1935
|
);
|
|
1906
1936
|
}
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1937
|
+
const text = await res.text();
|
|
1938
|
+
let parsed = null;
|
|
1939
|
+
try {
|
|
1940
|
+
parsed = JSON.parse(text);
|
|
1941
|
+
} catch {
|
|
1942
|
+
}
|
|
1943
|
+
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
1909
1944
|
throw new OwneyError(
|
|
1910
|
-
"
|
|
1911
|
-
`
|
|
1912
|
-
{
|
|
1945
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1946
|
+
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1947
|
+
{
|
|
1948
|
+
statusCode: res.status,
|
|
1949
|
+
responseBody: text.slice(0, 500),
|
|
1950
|
+
safeToFallback: true
|
|
1951
|
+
}
|
|
1913
1952
|
);
|
|
1914
1953
|
}
|
|
1915
|
-
return
|
|
1954
|
+
return parsed.data;
|
|
1916
1955
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
async function reportAgentFailure(apiKey, agentType, errorCode) {
|
|
1956
|
+
async function postSponsorTransferAuth(input) {
|
|
1957
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1958
|
+
let res;
|
|
1921
1959
|
try {
|
|
1922
|
-
await fetch(`${
|
|
1960
|
+
res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
1923
1961
|
method: "POST",
|
|
1924
1962
|
headers: {
|
|
1925
|
-
"
|
|
1926
|
-
"x-owney-api-key": apiKey
|
|
1963
|
+
"content-type": "application/json",
|
|
1964
|
+
"x-owney-api-key": input.apiKey,
|
|
1965
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
1927
1966
|
},
|
|
1928
|
-
body: JSON.stringify(
|
|
1929
|
-
agent_type: agentType,
|
|
1930
|
-
error_code: errorCode,
|
|
1931
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1932
|
-
})
|
|
1967
|
+
body: JSON.stringify(input.body)
|
|
1933
1968
|
});
|
|
1934
|
-
} catch (
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1969
|
+
} catch (networkError) {
|
|
1970
|
+
throw new OwneyError(
|
|
1971
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1972
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1973
|
+
{ cause: String(networkError) }
|
|
1938
1974
|
);
|
|
1939
1975
|
}
|
|
1940
|
-
|
|
1941
|
-
|
|
1976
|
+
const text = await res.text();
|
|
1977
|
+
let parsed = null;
|
|
1942
1978
|
try {
|
|
1943
|
-
|
|
1944
|
-
} catch
|
|
1945
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
1946
|
-
void reportAgentFailure(apiKey, agentType, errorCode);
|
|
1947
|
-
throw err;
|
|
1979
|
+
parsed = JSON.parse(text);
|
|
1980
|
+
} catch {
|
|
1948
1981
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1982
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
1983
|
+
throw new OwneyError(
|
|
1984
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1985
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1986
|
+
{
|
|
1987
|
+
statusCode: res.status,
|
|
1988
|
+
responseBody: text.slice(0, 500),
|
|
1989
|
+
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
1990
|
+
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
1991
|
+
safeToFallback: res.status === 503
|
|
1992
|
+
}
|
|
1959
1993
|
);
|
|
1960
|
-
if (!tokenBalance) return { agent, balance: 0n };
|
|
1961
|
-
return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
|
|
1962
|
-
});
|
|
1963
|
-
}
|
|
1964
|
-
function planProportionalShares(balances, requested, totalAvailable) {
|
|
1965
|
-
const plans = balances.map(({ agent, balance }) => ({
|
|
1966
|
-
agent,
|
|
1967
|
-
balance,
|
|
1968
|
-
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
1969
|
-
}));
|
|
1970
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
1971
|
-
let remainder = requested - assigned;
|
|
1972
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
1973
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
1974
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
1975
|
-
});
|
|
1976
|
-
for (const p of byHeadroom) {
|
|
1977
|
-
if (remainder === 0n) break;
|
|
1978
|
-
const headroom = p.balance - p.planned;
|
|
1979
|
-
if (headroom <= 0n) continue;
|
|
1980
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
1981
|
-
p.planned += take;
|
|
1982
|
-
remainder -= take;
|
|
1983
1994
|
}
|
|
1984
|
-
return
|
|
1985
|
-
}
|
|
1986
|
-
function planDisabledDrain(disabled, requested) {
|
|
1987
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
1988
|
-
const plans = [];
|
|
1989
|
-
let remaining = requested;
|
|
1990
|
-
for (const { agent, balance } of sorted) {
|
|
1991
|
-
if (remaining === 0n) {
|
|
1992
|
-
plans.push({ agent, balance, planned: 0n });
|
|
1993
|
-
continue;
|
|
1994
|
-
}
|
|
1995
|
-
const take = balance < remaining ? balance : remaining;
|
|
1996
|
-
plans.push({ agent, balance, planned: take });
|
|
1997
|
-
remaining -= take;
|
|
1998
|
-
}
|
|
1999
|
-
return { plans, remaining };
|
|
2000
|
-
}
|
|
2001
|
-
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
2002
|
-
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
2003
|
-
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
2004
|
-
const totalHeadroom = candidates.reduce(
|
|
2005
|
-
(s, c) => s + (c.balance - c.planned),
|
|
2006
|
-
0n
|
|
2007
|
-
);
|
|
2008
|
-
if (totalHeadroom === 0n) return;
|
|
2009
|
-
let distributed = 0n;
|
|
2010
|
-
for (const c of candidates) {
|
|
2011
|
-
const headroom = c.balance - c.planned;
|
|
2012
|
-
const proportional = headroom * amount / totalHeadroom;
|
|
2013
|
-
const give = proportional > headroom ? headroom : proportional;
|
|
2014
|
-
c.planned += give;
|
|
2015
|
-
distributed += give;
|
|
2016
|
-
}
|
|
2017
|
-
let leftover = amount - distributed;
|
|
2018
|
-
for (const c of candidates) {
|
|
2019
|
-
if (leftover === 0n) break;
|
|
2020
|
-
const headroom = c.balance - c.planned;
|
|
2021
|
-
if (headroom <= 0n) continue;
|
|
2022
|
-
const take = headroom < leftover ? headroom : leftover;
|
|
2023
|
-
c.planned += take;
|
|
2024
|
-
leftover -= take;
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
function sumWithdrawnAmount(results) {
|
|
2028
|
-
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
// src/lib/helpers/account-apy-helper.ts
|
|
2032
|
-
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
2033
|
-
const sums = {};
|
|
2034
|
-
const weights = {};
|
|
2035
|
-
for (const id of Object.keys(agentApys)) {
|
|
2036
|
-
const cells = agentApys[id].apyByChainAndAsset;
|
|
2037
|
-
const balance = agentBalances[id] ?? 0;
|
|
2038
|
-
if (!cells || balance <= 0) continue;
|
|
2039
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
2040
|
-
if (!perAsset) continue;
|
|
2041
|
-
const chainId = Number(chainKey);
|
|
2042
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
2043
|
-
const apy = Number(apyValue ?? 0);
|
|
2044
|
-
if (apy === 0) continue;
|
|
2045
|
-
sums[chainId] ??= {};
|
|
2046
|
-
weights[chainId] ??= {};
|
|
2047
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
2048
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
const out = {};
|
|
2053
|
-
for (const chainKey of Object.keys(sums)) {
|
|
2054
|
-
const chainId = Number(chainKey);
|
|
2055
|
-
const perAssetOut = {};
|
|
2056
|
-
for (const asset of Object.keys(sums[chainId])) {
|
|
2057
|
-
const w = weights[chainId][asset];
|
|
2058
|
-
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
2059
|
-
}
|
|
2060
|
-
if (Object.keys(perAssetOut).length > 0) {
|
|
2061
|
-
out[chainId] = perAssetOut;
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
return out;
|
|
2065
|
-
}
|
|
2066
|
-
|
|
2067
|
-
// src/client.ts
|
|
2068
|
-
import {
|
|
2069
|
-
createPublicClient as createPublicClient2,
|
|
2070
|
-
createWalletClient,
|
|
2071
|
-
custom
|
|
2072
|
-
} from "viem";
|
|
2073
|
-
import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
2074
|
-
|
|
2075
|
-
// src/lib/transfer-auth.ts
|
|
2076
|
-
import { bytesToHex } from "viem";
|
|
2077
|
-
var ERC20_META_ABI = [
|
|
2078
|
-
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2079
|
-
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
2080
|
-
];
|
|
2081
|
-
function buildTransferWithAuthorizationTypedData(input) {
|
|
2082
|
-
return {
|
|
2083
|
-
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
2084
|
-
types: {
|
|
2085
|
-
TransferWithAuthorization: [
|
|
2086
|
-
{ name: "from", type: "address" },
|
|
2087
|
-
{ name: "to", type: "address" },
|
|
2088
|
-
{ name: "value", type: "uint256" },
|
|
2089
|
-
{ name: "validAfter", type: "uint256" },
|
|
2090
|
-
{ name: "validBefore", type: "uint256" },
|
|
2091
|
-
{ name: "nonce", type: "bytes32" }
|
|
2092
|
-
]
|
|
2093
|
-
},
|
|
2094
|
-
primaryType: "TransferWithAuthorization",
|
|
2095
|
-
message: input.message
|
|
2096
|
-
};
|
|
2097
|
-
}
|
|
2098
|
-
async function readTokenMeta(publicClient, token) {
|
|
2099
|
-
const [tokenName, tokenVersion] = await Promise.all([
|
|
2100
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
2101
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
2102
|
-
]);
|
|
2103
|
-
return { tokenName, tokenVersion };
|
|
2104
|
-
}
|
|
2105
|
-
function randomAuthNonce() {
|
|
2106
|
-
const bytes = new Uint8Array(32);
|
|
2107
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2108
|
-
return bytesToHex(bytes);
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
// src/lib/sponsor-client.ts
|
|
2112
|
-
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2113
|
-
async function postSponsorTransferAuth(input) {
|
|
2114
|
-
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2115
|
-
let res;
|
|
2116
|
-
try {
|
|
2117
|
-
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2118
|
-
method: "POST",
|
|
2119
|
-
headers: {
|
|
2120
|
-
"content-type": "application/json",
|
|
2121
|
-
"x-owney-api-key": input.apiKey
|
|
2122
|
-
},
|
|
2123
|
-
body: JSON.stringify(input.body)
|
|
2124
|
-
});
|
|
2125
|
-
} catch (networkError) {
|
|
2126
|
-
throw new OwneyError(
|
|
2127
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2128
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2129
|
-
{ cause: String(networkError) }
|
|
2130
|
-
);
|
|
2131
|
-
}
|
|
2132
|
-
const text = await res.text();
|
|
2133
|
-
let parsed = null;
|
|
2134
|
-
try {
|
|
2135
|
-
parsed = JSON.parse(text);
|
|
2136
|
-
} catch {
|
|
2137
|
-
}
|
|
2138
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2139
|
-
throw new OwneyError(
|
|
2140
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2141
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2142
|
-
{
|
|
2143
|
-
statusCode: res.status,
|
|
2144
|
-
responseBody: text.slice(0, 500),
|
|
2145
|
-
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2146
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2147
|
-
safeToFallback: res.status === 503
|
|
2148
|
-
}
|
|
2149
|
-
);
|
|
2150
|
-
}
|
|
2151
|
-
return parsed.data;
|
|
1995
|
+
return parsed.data;
|
|
2152
1996
|
}
|
|
2153
1997
|
async function postSponsorPermit2Transfer(input) {
|
|
2154
|
-
const
|
|
1998
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2155
1999
|
let res;
|
|
2156
2000
|
try {
|
|
2157
|
-
res = await fetch(`${
|
|
2001
|
+
res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
|
|
2158
2002
|
method: "POST",
|
|
2159
2003
|
headers: {
|
|
2160
2004
|
"content-type": "application/json",
|
|
2161
|
-
"x-owney-api-key": input.apiKey
|
|
2005
|
+
"x-owney-api-key": input.apiKey,
|
|
2006
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2162
2007
|
},
|
|
2163
2008
|
body: JSON.stringify(input.body)
|
|
2164
2009
|
});
|
|
@@ -2189,11 +2034,11 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2189
2034
|
return parsed.data;
|
|
2190
2035
|
}
|
|
2191
2036
|
async function getSponsorRelayerAddress(input) {
|
|
2192
|
-
const
|
|
2037
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2193
2038
|
let res;
|
|
2194
2039
|
try {
|
|
2195
2040
|
res = await fetch(
|
|
2196
|
-
`${
|
|
2041
|
+
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2197
2042
|
{
|
|
2198
2043
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2199
2044
|
}
|
|
@@ -2302,205 +2147,1873 @@ async function readErc20Balance(publicClient, token, owner) {
|
|
|
2302
2147
|
args: [owner]
|
|
2303
2148
|
});
|
|
2304
2149
|
}
|
|
2305
|
-
|
|
2306
|
-
// src/lib/
|
|
2307
|
-
var
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
}
|
|
2312
|
-
function
|
|
2313
|
-
|
|
2150
|
+
|
|
2151
|
+
// src/lib/sponsored-deposit.ts
|
|
2152
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2153
|
+
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2154
|
+
function provideDepositVerificationContext(callback, context) {
|
|
2155
|
+
callback[verificationSetter]?.(context);
|
|
2156
|
+
}
|
|
2157
|
+
function makeVerificationAwareDepositCallback(implementation) {
|
|
2158
|
+
let nextVerification;
|
|
2159
|
+
const callback = async (smartWallet, chainId, amount) => {
|
|
2160
|
+
const verification = nextVerification;
|
|
2161
|
+
nextVerification = void 0;
|
|
2162
|
+
return implementation(smartWallet, chainId, amount, verification);
|
|
2163
|
+
};
|
|
2164
|
+
Object.defineProperty(callback, verificationSetter, {
|
|
2165
|
+
value: (context) => {
|
|
2166
|
+
nextVerification = context;
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
return callback;
|
|
2170
|
+
}
|
|
2171
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2172
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2173
|
+
return makeVerificationAwareDepositCallback(
|
|
2174
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
2175
|
+
const cid = chainId;
|
|
2176
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2177
|
+
if (!token) {
|
|
2178
|
+
throw new OwneyError(
|
|
2179
|
+
"CHAIN_UNSUPPORTED",
|
|
2180
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
const pub = deps.getPublicClient(cid);
|
|
2184
|
+
const wallet = deps.getWalletClient(cid);
|
|
2185
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2186
|
+
try {
|
|
2187
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2188
|
+
if (balance < BigInt(amount)) {
|
|
2189
|
+
throw new OwneyError(
|
|
2190
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2191
|
+
"Insufficient balance for this deposit.",
|
|
2192
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
2195
|
+
} catch (err) {
|
|
2196
|
+
if (err instanceof OwneyError) throw err;
|
|
2197
|
+
console.warn(
|
|
2198
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2199
|
+
err instanceof Error ? err.message : String(err)
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2202
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2203
|
+
const validAfter = 0n;
|
|
2204
|
+
const validBefore = BigInt(
|
|
2205
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2206
|
+
);
|
|
2207
|
+
const nonce = randomAuthNonce();
|
|
2208
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2209
|
+
token,
|
|
2210
|
+
chainId: cid,
|
|
2211
|
+
tokenName,
|
|
2212
|
+
tokenVersion,
|
|
2213
|
+
message: {
|
|
2214
|
+
from: deps.ownerAddress,
|
|
2215
|
+
to: smartWallet,
|
|
2216
|
+
value: BigInt(amount),
|
|
2217
|
+
validAfter,
|
|
2218
|
+
validBefore,
|
|
2219
|
+
nonce
|
|
2220
|
+
}
|
|
2221
|
+
});
|
|
2222
|
+
const authSignature = await wallet.signTypedData({
|
|
2223
|
+
account: deps.ownerAddress,
|
|
2224
|
+
...typedData
|
|
2225
|
+
});
|
|
2226
|
+
deps.onApproved?.();
|
|
2227
|
+
const result = await post({
|
|
2228
|
+
baseUrl: deps.baseUrl,
|
|
2229
|
+
apiKey: deps.apiKey,
|
|
2230
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2231
|
+
body: {
|
|
2232
|
+
chainId: cid,
|
|
2233
|
+
token,
|
|
2234
|
+
from: deps.ownerAddress,
|
|
2235
|
+
to: smartWallet,
|
|
2236
|
+
value: amount,
|
|
2237
|
+
validAfter: validAfter.toString(),
|
|
2238
|
+
validBefore: validBefore.toString(),
|
|
2239
|
+
nonce,
|
|
2240
|
+
authSignature,
|
|
2241
|
+
tokenName,
|
|
2242
|
+
tokenVersion,
|
|
2243
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
2244
|
+
yieldseekerUserId: verification.userId,
|
|
2245
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
2246
|
+
} : {}
|
|
2247
|
+
}
|
|
2248
|
+
});
|
|
2249
|
+
return result.txHash;
|
|
2250
|
+
}
|
|
2251
|
+
);
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2255
|
+
import { SiweMessage, generateNonce } from "siwe";
|
|
2256
|
+
import {
|
|
2257
|
+
createPublicClient as createPublicClient2,
|
|
2258
|
+
createWalletClient,
|
|
2259
|
+
custom,
|
|
2260
|
+
getAddress
|
|
2261
|
+
} from "viem";
|
|
2262
|
+
import { base as base2 } from "viem/chains";
|
|
2263
|
+
|
|
2264
|
+
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2265
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session.v4";
|
|
2266
|
+
var INVALIDATED_KEY_PREFIXES = [
|
|
2267
|
+
"owney.yieldseeker.session",
|
|
2268
|
+
"owney.yieldseeker.session.v3"
|
|
2269
|
+
];
|
|
2270
|
+
var storage2 = () => {
|
|
2271
|
+
if (typeof window === "undefined") return null;
|
|
2272
|
+
try {
|
|
2273
|
+
return window.localStorage;
|
|
2274
|
+
} catch {
|
|
2275
|
+
return null;
|
|
2276
|
+
}
|
|
2277
|
+
};
|
|
2278
|
+
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2279
|
+
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2280
|
+
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2281
|
+
);
|
|
2282
|
+
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2283
|
+
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2284
|
+
memorySessions2.delete(key2);
|
|
2285
|
+
try {
|
|
2286
|
+
store?.removeItem(key2);
|
|
2287
|
+
} catch {
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
};
|
|
2291
|
+
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2292
|
+
var isValidSession = (session) => {
|
|
2293
|
+
if (!session?.token) return false;
|
|
2294
|
+
try {
|
|
2295
|
+
const parsed = JSON.parse(atob(session.token));
|
|
2296
|
+
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2297
|
+
} catch {
|
|
2298
|
+
return false;
|
|
2299
|
+
}
|
|
2300
|
+
};
|
|
2301
|
+
var readYieldseekerSession = (address, chainId) => {
|
|
2302
|
+
if (typeof window === "undefined") return null;
|
|
2303
|
+
const key2 = buildKey2(address, chainId);
|
|
2304
|
+
const store = storage2();
|
|
2305
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2306
|
+
let raw2 = null;
|
|
2307
|
+
try {
|
|
2308
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
2309
|
+
} catch {
|
|
2310
|
+
raw2 = null;
|
|
2311
|
+
}
|
|
2312
|
+
if (raw2) {
|
|
2313
|
+
try {
|
|
2314
|
+
const parsed = JSON.parse(raw2);
|
|
2315
|
+
if (isValidSession(parsed)) return parsed.token;
|
|
2316
|
+
} catch {
|
|
2317
|
+
}
|
|
2318
|
+
memorySessions2.delete(key2);
|
|
2319
|
+
try {
|
|
2320
|
+
store?.removeItem(key2);
|
|
2321
|
+
} catch {
|
|
2322
|
+
}
|
|
2323
|
+
return null;
|
|
2324
|
+
}
|
|
2325
|
+
const cached = memorySessions2.get(key2);
|
|
2326
|
+
if (isValidSession(cached)) return cached.token;
|
|
2327
|
+
if (cached) memorySessions2.delete(key2);
|
|
2328
|
+
return null;
|
|
2329
|
+
};
|
|
2330
|
+
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2331
|
+
if (typeof window === "undefined") return;
|
|
2332
|
+
const session = { token };
|
|
2333
|
+
if (!isValidSession(session)) return;
|
|
2334
|
+
const key2 = buildKey2(address, chainId);
|
|
2335
|
+
memorySessions2.set(key2, session);
|
|
2336
|
+
const store = storage2();
|
|
2337
|
+
try {
|
|
2338
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2339
|
+
} catch {
|
|
2340
|
+
}
|
|
2341
|
+
};
|
|
2342
|
+
var clearYieldseekerSession = (address, chainId) => {
|
|
2343
|
+
const key2 = buildKey2(address, chainId);
|
|
2344
|
+
memorySessions2.delete(key2);
|
|
2345
|
+
const store = storage2();
|
|
2346
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2347
|
+
try {
|
|
2348
|
+
store?.removeItem(key2);
|
|
2349
|
+
} catch {
|
|
2350
|
+
}
|
|
2351
|
+
};
|
|
2352
|
+
|
|
2353
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2354
|
+
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2355
|
+
return new SiweMessage({
|
|
2356
|
+
domain: "yieldseeker.xyz",
|
|
2357
|
+
address: getAddress(address),
|
|
2358
|
+
uri: "https://yieldseeker.xyz",
|
|
2359
|
+
version: "1",
|
|
2360
|
+
chainId,
|
|
2361
|
+
nonce: (dependencies.nonce ?? generateNonce)(),
|
|
2362
|
+
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2363
|
+
}).prepareMessage();
|
|
2364
|
+
}
|
|
2365
|
+
function encodeYieldseekerAuthToken(token) {
|
|
2366
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2367
|
+
let binary = "";
|
|
2368
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2369
|
+
return btoa(binary);
|
|
2370
|
+
}
|
|
2371
|
+
var YieldseekerAuth = class {
|
|
2372
|
+
constructor(dependencies = {}) {
|
|
2373
|
+
this.dependencies = dependencies;
|
|
2374
|
+
}
|
|
2375
|
+
dependencies;
|
|
2376
|
+
tokens = /* @__PURE__ */ new Map();
|
|
2377
|
+
pending = /* @__PURE__ */ new Map();
|
|
2378
|
+
scopes = /* @__PURE__ */ new Map();
|
|
2379
|
+
key(state, chainId) {
|
|
2380
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
2381
|
+
}
|
|
2382
|
+
async getToken(state, chainId) {
|
|
2383
|
+
const key2 = this.key(state, chainId);
|
|
2384
|
+
const scope = { address: state.walletAddress, chainId };
|
|
2385
|
+
this.scopes.set(key2, scope);
|
|
2386
|
+
const cached = this.tokens.get(key2);
|
|
2387
|
+
if (cached) return cached;
|
|
2388
|
+
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2389
|
+
if (persisted) {
|
|
2390
|
+
this.tokens.set(key2, persisted);
|
|
2391
|
+
return persisted;
|
|
2392
|
+
}
|
|
2393
|
+
const inFlight = this.pending.get(key2);
|
|
2394
|
+
if (inFlight) return inFlight;
|
|
2395
|
+
const request = this.sign(state, chainId).then((token) => {
|
|
2396
|
+
this.tokens.set(key2, token);
|
|
2397
|
+
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2398
|
+
return token;
|
|
2399
|
+
});
|
|
2400
|
+
this.pending.set(key2, request);
|
|
2401
|
+
try {
|
|
2402
|
+
return await request;
|
|
2403
|
+
} finally {
|
|
2404
|
+
this.pending.delete(key2);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
clear(state, chainId) {
|
|
2408
|
+
if (!state || chainId === void 0) {
|
|
2409
|
+
for (const scope of this.scopes.values()) {
|
|
2410
|
+
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2411
|
+
}
|
|
2412
|
+
this.tokens.clear();
|
|
2413
|
+
this.pending.clear();
|
|
2414
|
+
this.scopes.clear();
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2417
|
+
const key2 = this.key(state, chainId);
|
|
2418
|
+
this.tokens.delete(key2);
|
|
2419
|
+
this.pending.delete(key2);
|
|
2420
|
+
this.scopes.delete(key2);
|
|
2421
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2422
|
+
}
|
|
2423
|
+
async sign(state, chainId) {
|
|
2424
|
+
const account = getAddress(state.walletAddress);
|
|
2425
|
+
const publicClient = createPublicClient2({
|
|
2426
|
+
chain: base2,
|
|
2427
|
+
transport: custom(state.provider)
|
|
2428
|
+
});
|
|
2429
|
+
const walletClient = createWalletClient({
|
|
2430
|
+
account,
|
|
2431
|
+
chain: base2,
|
|
2432
|
+
transport: custom(state.provider)
|
|
2433
|
+
});
|
|
2434
|
+
await ensureWalletOnChain(
|
|
2435
|
+
publicClient,
|
|
2436
|
+
walletClient,
|
|
2437
|
+
8453
|
|
2438
|
+
);
|
|
2439
|
+
const message = createYieldseekerSiweMessage(
|
|
2440
|
+
account,
|
|
2441
|
+
chainId,
|
|
2442
|
+
this.dependencies
|
|
2443
|
+
);
|
|
2444
|
+
const signature = await walletClient.signMessage({ account, message });
|
|
2445
|
+
return encodeYieldseekerAuthToken({ message, signature });
|
|
2446
|
+
}
|
|
2447
|
+
};
|
|
2448
|
+
|
|
2449
|
+
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2450
|
+
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2451
|
+
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2452
|
+
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2453
|
+
var storage3 = () => {
|
|
2454
|
+
if (typeof window === "undefined") return null;
|
|
2455
|
+
try {
|
|
2456
|
+
return window.localStorage;
|
|
2457
|
+
} catch {
|
|
2458
|
+
return null;
|
|
2459
|
+
}
|
|
2460
|
+
};
|
|
2461
|
+
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2462
|
+
function valid(value, walletAddress, chainId, now) {
|
|
2463
|
+
return Boolean(
|
|
2464
|
+
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
|
|
2465
|
+
);
|
|
2466
|
+
}
|
|
2467
|
+
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2468
|
+
if (typeof window === "undefined") return null;
|
|
2469
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2470
|
+
const store = storage3();
|
|
2471
|
+
let parsed = null;
|
|
2472
|
+
try {
|
|
2473
|
+
const raw2 = store?.getItem(key2);
|
|
2474
|
+
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2475
|
+
} catch {
|
|
2476
|
+
parsed = null;
|
|
2477
|
+
}
|
|
2478
|
+
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2479
|
+
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2480
|
+
memoryIdentities.set(key2, candidate);
|
|
2481
|
+
return { userId: candidate.userId };
|
|
2482
|
+
}
|
|
2483
|
+
memoryIdentities.delete(key2);
|
|
2484
|
+
try {
|
|
2485
|
+
store?.removeItem(key2);
|
|
2486
|
+
} catch {
|
|
2487
|
+
}
|
|
2488
|
+
return null;
|
|
2489
|
+
}
|
|
2490
|
+
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2491
|
+
if (typeof window === "undefined") return;
|
|
2492
|
+
const identity = {
|
|
2493
|
+
userId,
|
|
2494
|
+
walletAddress,
|
|
2495
|
+
chainId,
|
|
2496
|
+
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2497
|
+
};
|
|
2498
|
+
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2499
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2500
|
+
memoryIdentities.set(key2, identity);
|
|
2501
|
+
try {
|
|
2502
|
+
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2503
|
+
} catch {
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2507
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2508
|
+
memoryIdentities.delete(key2);
|
|
2509
|
+
try {
|
|
2510
|
+
storage3()?.removeItem(key2);
|
|
2511
|
+
} catch {
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2516
|
+
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2517
|
+
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2518
|
+
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2519
|
+
}
|
|
2520
|
+
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2521
|
+
var YieldseekerApiError = class extends Error {
|
|
2522
|
+
constructor(status, providerCode, responseFields) {
|
|
2523
|
+
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2524
|
+
this.status = status;
|
|
2525
|
+
this.providerCode = providerCode;
|
|
2526
|
+
this.responseFields = responseFields;
|
|
2527
|
+
this.name = "YieldseekerApiError";
|
|
2528
|
+
}
|
|
2529
|
+
status;
|
|
2530
|
+
providerCode;
|
|
2531
|
+
responseFields;
|
|
2532
|
+
get isAuthenticationError() {
|
|
2533
|
+
return this.status === 401 || this.status === 403;
|
|
2534
|
+
}
|
|
2535
|
+
};
|
|
2536
|
+
function providerError(body, fallback) {
|
|
2537
|
+
if (!body || typeof body !== "object") return { code: fallback };
|
|
2538
|
+
const record = body;
|
|
2539
|
+
return {
|
|
2540
|
+
code: typeof record.message === "string" ? record.message : fallback,
|
|
2541
|
+
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
var YieldseekerApiClient = class {
|
|
2545
|
+
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
|
|
2546
|
+
this.owneyApiKey = owneyApiKey;
|
|
2547
|
+
this.baseUrl = baseUrl;
|
|
2548
|
+
this.fetchFn = fetchFn;
|
|
2549
|
+
}
|
|
2550
|
+
owneyApiKey;
|
|
2551
|
+
baseUrl;
|
|
2552
|
+
fetchFn;
|
|
2553
|
+
async request(path, options = {}) {
|
|
2554
|
+
const controller = new AbortController();
|
|
2555
|
+
const timer = setTimeout(
|
|
2556
|
+
() => controller.abort(),
|
|
2557
|
+
options.timeoutMs ?? 15e3
|
|
2558
|
+
);
|
|
2559
|
+
try {
|
|
2560
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2561
|
+
method: options.method ?? "GET",
|
|
2562
|
+
headers: {
|
|
2563
|
+
"Content-Type": "application/json",
|
|
2564
|
+
"x-owney-api-key": this.owneyApiKey,
|
|
2565
|
+
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2566
|
+
},
|
|
2567
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2568
|
+
signal: controller.signal
|
|
2569
|
+
});
|
|
2570
|
+
const payload = await response.json().catch(() => null);
|
|
2571
|
+
if (!response.ok) {
|
|
2572
|
+
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2573
|
+
throw new YieldseekerApiError(
|
|
2574
|
+
response.status,
|
|
2575
|
+
error.code,
|
|
2576
|
+
error.fields
|
|
2577
|
+
);
|
|
2578
|
+
}
|
|
2579
|
+
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2580
|
+
return payload.data;
|
|
2581
|
+
}
|
|
2582
|
+
return payload;
|
|
2583
|
+
} catch (error) {
|
|
2584
|
+
if (error instanceof YieldseekerApiError) throw error;
|
|
2585
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2586
|
+
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2587
|
+
}
|
|
2588
|
+
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2589
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2590
|
+
});
|
|
2591
|
+
} finally {
|
|
2592
|
+
clearTimeout(timer);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
};
|
|
2596
|
+
|
|
2597
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2598
|
+
import { formatUnits, isAddress } from "viem";
|
|
2599
|
+
|
|
2600
|
+
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2601
|
+
var YIELDSEEKER_ASSET_METADATA = {
|
|
2602
|
+
USDC: {
|
|
2603
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2604
|
+
decimals: 6
|
|
2605
|
+
},
|
|
2606
|
+
WETH: {
|
|
2607
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
2608
|
+
decimals: 18
|
|
2609
|
+
}
|
|
2610
|
+
};
|
|
2611
|
+
|
|
2612
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2613
|
+
function invalid(endpoint, detail) {
|
|
2614
|
+
throw new OwneyError(
|
|
2615
|
+
"AGENT_INVALID_RESPONSE",
|
|
2616
|
+
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2617
|
+
{ endpoint, detail },
|
|
2618
|
+
"yieldseeker"
|
|
2619
|
+
);
|
|
2620
|
+
}
|
|
2621
|
+
function raw(value, endpoint) {
|
|
2622
|
+
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2623
|
+
return invalid(endpoint, "expected a base-10 integer string");
|
|
2624
|
+
}
|
|
2625
|
+
return BigInt(value);
|
|
2626
|
+
}
|
|
2627
|
+
function decimal(value, decimals, endpoint) {
|
|
2628
|
+
return formatUnits(raw(value, endpoint), decimals);
|
|
2629
|
+
}
|
|
2630
|
+
function usd(rawAmount, decimals, price) {
|
|
2631
|
+
return Number(formatUnits(rawAmount, decimals)) * price;
|
|
2632
|
+
}
|
|
2633
|
+
function percent(value) {
|
|
2634
|
+
const result = Number(value);
|
|
2635
|
+
return Number.isFinite(result) ? result * 100 : 0;
|
|
2636
|
+
}
|
|
2637
|
+
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2638
|
+
function publicApyAfterYieldseekerFee(value) {
|
|
2639
|
+
const grossPercent = percent(value);
|
|
2640
|
+
if (grossPercent <= 0) return grossPercent;
|
|
2641
|
+
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2642
|
+
return Math.round(netPercent * 1e12) / 1e12;
|
|
2643
|
+
}
|
|
2644
|
+
function riskAdjustedApyForDays(option, days) {
|
|
2645
|
+
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2646
|
+
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2647
|
+
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2648
|
+
}
|
|
2649
|
+
function assetAddressValue(record, address) {
|
|
2650
|
+
const entry = Object.entries(record).find(
|
|
2651
|
+
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2652
|
+
);
|
|
2653
|
+
return entry?.[1] ?? "0";
|
|
2654
|
+
}
|
|
2655
|
+
function position(value, asset) {
|
|
2656
|
+
const option = value?.yieldOption;
|
|
2657
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress(option.address)) {
|
|
2658
|
+
return invalid("yield positions", "missing vault metadata");
|
|
2659
|
+
}
|
|
2660
|
+
const decimals = YIELDSEEKER_ASSET_METADATA[asset].decimals;
|
|
2661
|
+
return {
|
|
2662
|
+
chain: "BASE",
|
|
2663
|
+
protocol: option.provider,
|
|
2664
|
+
protocolId: option.address,
|
|
2665
|
+
pool: option.name,
|
|
2666
|
+
asset,
|
|
2667
|
+
amount: decimal(value.assetsRaw, decimals, "yield positions"),
|
|
2668
|
+
amountRaw: String(value.assetsRaw),
|
|
2669
|
+
apy: percent(option.riskAdjustedApy),
|
|
2670
|
+
tvl: Number(option.totalDepositsUsd),
|
|
2671
|
+
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2672
|
+
};
|
|
2673
|
+
}
|
|
2674
|
+
function mapYieldseekerBalances(contexts) {
|
|
2675
|
+
const tokens = [];
|
|
2676
|
+
const positions = [];
|
|
2677
|
+
let totalUsd = 0;
|
|
2678
|
+
for (const context of contexts) {
|
|
2679
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2680
|
+
const idle = assetAddressValue(
|
|
2681
|
+
context.snapshot.tokenBalances,
|
|
2682
|
+
metadata.address
|
|
2683
|
+
);
|
|
2684
|
+
tokens.push({
|
|
2685
|
+
chain: "BASE",
|
|
2686
|
+
chainId: 8453,
|
|
2687
|
+
asset: context.asset,
|
|
2688
|
+
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2689
|
+
});
|
|
2690
|
+
positions.push(
|
|
2691
|
+
...context.positions.map((entry) => position(entry, context.asset))
|
|
2692
|
+
);
|
|
2693
|
+
totalUsd += usd(
|
|
2694
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2695
|
+
context.snapshot.baseAssetDecimals,
|
|
2696
|
+
context.snapshot.baseAssetPriceUsd
|
|
2697
|
+
);
|
|
2698
|
+
}
|
|
2699
|
+
return {
|
|
2700
|
+
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2701
|
+
totalBalance: String(totalUsd),
|
|
2702
|
+
totalBalanceAsset: "usdc",
|
|
2703
|
+
tokens,
|
|
2704
|
+
positions
|
|
2705
|
+
};
|
|
2706
|
+
}
|
|
2707
|
+
function mapYieldseekerEarnings(contexts) {
|
|
2708
|
+
const tokens = [];
|
|
2709
|
+
let lifetimeEarnings = 0;
|
|
2710
|
+
for (const context of contexts) {
|
|
2711
|
+
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
2712
|
+
tokens.push({
|
|
2713
|
+
chain: "BASE",
|
|
2714
|
+
chainId: 8453,
|
|
2715
|
+
asset: context.asset,
|
|
2716
|
+
amount: formatUnits(amount, context.snapshot.baseAssetDecimals)
|
|
2717
|
+
});
|
|
2718
|
+
lifetimeEarnings += usd(
|
|
2719
|
+
amount,
|
|
2720
|
+
context.snapshot.baseAssetDecimals,
|
|
2721
|
+
context.snapshot.baseAssetPriceUsd
|
|
2722
|
+
);
|
|
2723
|
+
}
|
|
2724
|
+
return {
|
|
2725
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2726
|
+
lifetimeEarnings,
|
|
2727
|
+
tokens
|
|
2728
|
+
};
|
|
2729
|
+
}
|
|
2730
|
+
function apyForDays(snapshot, days) {
|
|
2731
|
+
if (days === "7D") return percent(snapshot.apy7d);
|
|
2732
|
+
if (days === "30D") return percent(snapshot.apy30d);
|
|
2733
|
+
return percent((snapshot.apy7d + snapshot.apy30d) / 2);
|
|
2734
|
+
}
|
|
2735
|
+
function dailyApy(point) {
|
|
2736
|
+
const total = raw(point.totalValueBase, "historic position");
|
|
2737
|
+
const earned = raw(point.dailyYieldBase, "historic position");
|
|
2738
|
+
const principal = total - earned;
|
|
2739
|
+
if (principal <= 0n || earned === 0n) return 0;
|
|
2740
|
+
return Number(earned) / Number(principal) * 365 * 100;
|
|
2741
|
+
}
|
|
2742
|
+
function aggregateHistory(contexts, dayCount) {
|
|
2743
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
2744
|
+
for (const context of contexts) {
|
|
2745
|
+
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
2746
|
+
for (const point of points.slice(-dayCount)) {
|
|
2747
|
+
const valueUsd = usd(
|
|
2748
|
+
raw(point.totalValueBase, "historic position"),
|
|
2749
|
+
point.baseAssetDecimals,
|
|
2750
|
+
point.baseAssetPriceUsd
|
|
2751
|
+
);
|
|
2752
|
+
const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
|
|
2753
|
+
current.weighted += dailyApy(point) * valueUsd;
|
|
2754
|
+
current.valueUsd += valueUsd;
|
|
2755
|
+
byDate.set(point.date, current);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
2759
|
+
date,
|
|
2760
|
+
apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
|
|
2761
|
+
}));
|
|
2762
|
+
}
|
|
2763
|
+
function mapYieldseekerApy(walletAddress, contexts, days) {
|
|
2764
|
+
let weighted = 0;
|
|
2765
|
+
let totalUsd = 0;
|
|
2766
|
+
const byAsset = {};
|
|
2767
|
+
for (const context of contexts) {
|
|
2768
|
+
const valueUsd = usd(
|
|
2769
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2770
|
+
context.snapshot.baseAssetDecimals,
|
|
2771
|
+
context.snapshot.baseAssetPriceUsd
|
|
2772
|
+
);
|
|
2773
|
+
const apy = apyForDays(context.snapshot, days);
|
|
2774
|
+
weighted += apy * valueUsd;
|
|
2775
|
+
totalUsd += valueUsd;
|
|
2776
|
+
byAsset[context.asset] = apy;
|
|
2777
|
+
}
|
|
2778
|
+
const dayCount = Number(days.slice(0, -1));
|
|
2779
|
+
return {
|
|
2780
|
+
walletAddress,
|
|
2781
|
+
weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
|
|
2782
|
+
apyByChainAndAsset: { 8453: byAsset },
|
|
2783
|
+
history: aggregateHistory(contexts, dayCount)
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
function actionType(value) {
|
|
2787
|
+
const normalized = value.toLowerCase();
|
|
2788
|
+
if (normalized.includes("deposit")) return "Deposit";
|
|
2789
|
+
if (normalized.includes("withdraw")) return "Withdraw";
|
|
2790
|
+
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
2791
|
+
return "Earned";
|
|
2792
|
+
return "Rebalance";
|
|
2793
|
+
}
|
|
2794
|
+
function transactionHashes(details) {
|
|
2795
|
+
if (!details) return [];
|
|
2796
|
+
const values = [
|
|
2797
|
+
details.transactionHash,
|
|
2798
|
+
details.txHash,
|
|
2799
|
+
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
2800
|
+
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
2801
|
+
];
|
|
2802
|
+
return values.filter(
|
|
2803
|
+
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
2804
|
+
).filter((value, index, all) => all.indexOf(value) === index);
|
|
2805
|
+
}
|
|
2806
|
+
function actionEntry(action) {
|
|
2807
|
+
return {
|
|
2808
|
+
agent: "yieldseeker",
|
|
2809
|
+
action: actionType(action.actionType),
|
|
2810
|
+
date: action.createdDate,
|
|
2811
|
+
oldApy: null,
|
|
2812
|
+
newApy: null,
|
|
2813
|
+
transactions: [
|
|
2814
|
+
{
|
|
2815
|
+
txHashes: transactionHashes(action.details),
|
|
2816
|
+
chainId: 8453
|
|
2817
|
+
}
|
|
2818
|
+
],
|
|
2819
|
+
rebalanceLog: []
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
function movementEntry(movement, wallet, agent, asset) {
|
|
2823
|
+
const isDeposit = movement.toAddress.toLowerCase() === wallet.walletAddress.toLowerCase();
|
|
2824
|
+
return {
|
|
2825
|
+
agent: "yieldseeker",
|
|
2826
|
+
action: isDeposit ? "Deposit" : "Withdraw",
|
|
2827
|
+
date: movement.blockDate,
|
|
2828
|
+
oldApy: null,
|
|
2829
|
+
newApy: null,
|
|
2830
|
+
transactions: [
|
|
2831
|
+
{
|
|
2832
|
+
txHashes: [movement.transactionHash],
|
|
2833
|
+
chainId: agent.chainId,
|
|
2834
|
+
tokenSymbol: asset,
|
|
2835
|
+
amount: decimal(
|
|
2836
|
+
movement.assetAmount,
|
|
2837
|
+
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
2838
|
+
"historic position"
|
|
2839
|
+
)
|
|
2840
|
+
}
|
|
2841
|
+
],
|
|
2842
|
+
rebalanceLog: []
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
function mapYieldseekerHistory(contexts, options) {
|
|
2846
|
+
const entries = contexts.flatMap((context) => [
|
|
2847
|
+
...(context.actions ?? []).map(actionEntry),
|
|
2848
|
+
...(context.historic?.movements ?? []).map(
|
|
2849
|
+
(movement) => movementEntry(movement, context.wallet, context.agent, context.asset)
|
|
2850
|
+
)
|
|
2851
|
+
]);
|
|
2852
|
+
const filtered = entries.filter(
|
|
2853
|
+
(entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
|
|
2854
|
+
).filter(
|
|
2855
|
+
(entry, index, all) => all.findIndex(
|
|
2856
|
+
(candidate) => candidate.action === entry.action && candidate.date === entry.date && candidate.transactions[0]?.txHashes[0] === entry.transactions[0]?.txHashes[0]
|
|
2857
|
+
) === index
|
|
2858
|
+
).sort((left, right) => right.date.localeCompare(left.date));
|
|
2859
|
+
return {
|
|
2860
|
+
data: filtered.slice(0, options.limit),
|
|
2861
|
+
// v1 returns the whole action/movement collection and defines no cursor.
|
|
2862
|
+
// Report a terminal page so callers never loop over the same prefix.
|
|
2863
|
+
hasMore: false
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
function mapYieldseekerProfile(address, contexts) {
|
|
2867
|
+
const protocols = /* @__PURE__ */ new Set();
|
|
2868
|
+
for (const context of contexts) {
|
|
2869
|
+
for (const current of context.positions) {
|
|
2870
|
+
if (current.yieldOption?.provider) {
|
|
2871
|
+
protocols.add(String(current.yieldOption.provider));
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
return {
|
|
2876
|
+
address,
|
|
2877
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2878
|
+
chains: contexts.length > 0 ? [8453] : [],
|
|
2879
|
+
hasActiveSessionKey: contexts.some(
|
|
2880
|
+
(context) => context.wallet.initializedDate != null
|
|
2881
|
+
),
|
|
2882
|
+
protocols: [...protocols]
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
function mapYieldseekerAgentApy(options, days) {
|
|
2886
|
+
const perAsset = {};
|
|
2887
|
+
const all = [];
|
|
2888
|
+
for (const entry of options) {
|
|
2889
|
+
const apys = entry.yieldOptions.map(
|
|
2890
|
+
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
2891
|
+
).filter(Number.isFinite);
|
|
2892
|
+
if (apys.length === 0) continue;
|
|
2893
|
+
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
2894
|
+
perAsset[entry.asset] = average;
|
|
2895
|
+
all.push(average);
|
|
2896
|
+
}
|
|
2897
|
+
return {
|
|
2898
|
+
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
2899
|
+
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2904
|
+
var OWNEY_AGENT_NAME = "owney";
|
|
2905
|
+
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
2906
|
+
{
|
|
2907
|
+
type: "function",
|
|
2908
|
+
name: "withdrawAssetToUser",
|
|
2909
|
+
stateMutability: "nonpayable",
|
|
2910
|
+
inputs: [
|
|
2911
|
+
{ name: "recipient", type: "address" },
|
|
2912
|
+
{ name: "asset", type: "address" },
|
|
2913
|
+
{ name: "amount", type: "uint256" }
|
|
2914
|
+
],
|
|
2915
|
+
outputs: []
|
|
2916
|
+
},
|
|
2917
|
+
{
|
|
2918
|
+
type: "function",
|
|
2919
|
+
name: "withdrawAllAssetToUser",
|
|
2920
|
+
stateMutability: "nonpayable",
|
|
2921
|
+
inputs: [
|
|
2922
|
+
{ name: "recipient", type: "address" },
|
|
2923
|
+
{ name: "asset", type: "address" }
|
|
2924
|
+
],
|
|
2925
|
+
outputs: []
|
|
2926
|
+
}
|
|
2927
|
+
];
|
|
2928
|
+
function query(params) {
|
|
2929
|
+
const search = new URLSearchParams();
|
|
2930
|
+
for (const [key2, value] of Object.entries(params)) {
|
|
2931
|
+
if (value !== void 0) search.set(key2, String(value));
|
|
2932
|
+
}
|
|
2933
|
+
const encoded = search.toString();
|
|
2934
|
+
return encoded ? `?${encoded}` : "";
|
|
2935
|
+
}
|
|
2936
|
+
var YieldseekerAgent = class {
|
|
2937
|
+
id = "yieldseeker";
|
|
2938
|
+
balanceComposition = "tokens-plus-positions";
|
|
2939
|
+
supportedChainIds = [8453];
|
|
2940
|
+
supportedAssets = [
|
|
2941
|
+
{
|
|
2942
|
+
chainId: 8453,
|
|
2943
|
+
chain: "BASE",
|
|
2944
|
+
assets: [
|
|
2945
|
+
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
2946
|
+
{ symbol: "WETH", minDepositAmount: "1" }
|
|
2947
|
+
]
|
|
2948
|
+
}
|
|
2949
|
+
];
|
|
2950
|
+
api;
|
|
2951
|
+
auth;
|
|
2952
|
+
transactionExecutor;
|
|
2953
|
+
unwindReceiptWaiter;
|
|
2954
|
+
agentContexts = /* @__PURE__ */ new Map();
|
|
2955
|
+
users = /* @__PURE__ */ new Map();
|
|
2956
|
+
pendingAgents = /* @__PURE__ */ new Map();
|
|
2957
|
+
constructor(owneyApiKey, options = {}) {
|
|
2958
|
+
this.api = new YieldseekerApiClient(
|
|
2959
|
+
owneyApiKey,
|
|
2960
|
+
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
2961
|
+
options.fetchFn
|
|
2962
|
+
);
|
|
2963
|
+
this.auth = new YieldseekerAuth(options.auth);
|
|
2964
|
+
this.transactionExecutor = options.transactionExecutor;
|
|
2965
|
+
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
2966
|
+
}
|
|
2967
|
+
async disconnect() {
|
|
2968
|
+
this.auth.clear();
|
|
2969
|
+
for (const key2 of this.users.keys()) {
|
|
2970
|
+
const [walletAddress, chainId] = key2.split(":");
|
|
2971
|
+
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
2972
|
+
}
|
|
2973
|
+
this.users.clear();
|
|
2974
|
+
this.agentContexts.clear();
|
|
2975
|
+
this.pendingAgents.clear();
|
|
2976
|
+
}
|
|
2977
|
+
async activateAgent(state, chainId, asset) {
|
|
2978
|
+
this.assertChain(chainId);
|
|
2979
|
+
const targetAsset = asset ?? "USDC";
|
|
2980
|
+
this.assertAsset(targetAsset);
|
|
2981
|
+
await this.ensureAgent(state, chainId, targetAsset);
|
|
2982
|
+
}
|
|
2983
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
2984
|
+
this.assertChain(chainId);
|
|
2985
|
+
this.assertAsset(asset);
|
|
2986
|
+
if (BigInt(amount) <= 0n) {
|
|
2987
|
+
throw new OwneyError(
|
|
2988
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
2989
|
+
"Yieldseeker deposits must be greater than zero.",
|
|
2990
|
+
{ amount, minDepositAmount: "1" },
|
|
2991
|
+
this.id
|
|
2992
|
+
);
|
|
2993
|
+
}
|
|
2994
|
+
const context = await this.ensureAgent(state, chainId, asset);
|
|
2995
|
+
let txHash;
|
|
2996
|
+
try {
|
|
2997
|
+
if (depositCallback) {
|
|
2998
|
+
provideDepositVerificationContext(depositCallback, {
|
|
2999
|
+
agentId: "yieldseeker",
|
|
3000
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3001
|
+
userId: context.user.userId,
|
|
3002
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3003
|
+
});
|
|
3004
|
+
txHash = await depositCallback(
|
|
3005
|
+
context.wallet.walletAddress,
|
|
3006
|
+
chainId,
|
|
3007
|
+
amount
|
|
3008
|
+
);
|
|
3009
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3010
|
+
} else {
|
|
3011
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3012
|
+
from: getAddress2(state.walletAddress),
|
|
3013
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3014
|
+
data: encodeFunctionData({
|
|
3015
|
+
abi: erc20Abi,
|
|
3016
|
+
functionName: "transfer",
|
|
3017
|
+
args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
|
|
3018
|
+
}),
|
|
3019
|
+
value: "0",
|
|
3020
|
+
chainId
|
|
3021
|
+
});
|
|
3022
|
+
}
|
|
3023
|
+
await this.deployAfterFunding(state, chainId, context);
|
|
3024
|
+
} finally {
|
|
3025
|
+
await this.refreshSnapshotAfterMovement(
|
|
3026
|
+
state,
|
|
3027
|
+
chainId,
|
|
3028
|
+
context,
|
|
3029
|
+
"deposit"
|
|
3030
|
+
);
|
|
3031
|
+
}
|
|
3032
|
+
return {
|
|
3033
|
+
txHash,
|
|
3034
|
+
smartWallet: context.wallet.walletAddress,
|
|
3035
|
+
amount
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
3038
|
+
async withdraw(state, chainId, asset, amount) {
|
|
3039
|
+
this.assertChain(chainId);
|
|
3040
|
+
this.assertAsset(asset);
|
|
3041
|
+
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
3042
|
+
throw new OwneyError(
|
|
3043
|
+
"WITHDRAW_FAILED",
|
|
3044
|
+
"Yieldseeker withdrawals must be greater than zero.",
|
|
3045
|
+
{ amount },
|
|
3046
|
+
this.id
|
|
3047
|
+
);
|
|
3048
|
+
}
|
|
3049
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
3050
|
+
if (!context) {
|
|
3051
|
+
throw new OwneyError(
|
|
3052
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3053
|
+
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3054
|
+
{ asset, available: "0" },
|
|
3055
|
+
this.id
|
|
3056
|
+
);
|
|
3057
|
+
}
|
|
3058
|
+
try {
|
|
3059
|
+
const portfolio = await this.loadPortfolioContext(
|
|
3060
|
+
state,
|
|
3061
|
+
chainId,
|
|
3062
|
+
context
|
|
3063
|
+
);
|
|
3064
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3065
|
+
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3066
|
+
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3067
|
+
);
|
|
3068
|
+
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3069
|
+
const deployed = portfolio.positions.reduce(
|
|
3070
|
+
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3071
|
+
0n
|
|
3072
|
+
);
|
|
3073
|
+
const totalAvailable = idle + deployed;
|
|
3074
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3075
|
+
if (requested > totalAvailable) {
|
|
3076
|
+
throw new OwneyError(
|
|
3077
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3078
|
+
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3079
|
+
{
|
|
3080
|
+
asset,
|
|
3081
|
+
requested: requested.toString(),
|
|
3082
|
+
available: totalAvailable.toString()
|
|
3083
|
+
},
|
|
3084
|
+
this.id
|
|
3085
|
+
);
|
|
3086
|
+
}
|
|
3087
|
+
let remaining = requested > idle ? requested - idle : 0n;
|
|
3088
|
+
for (const position2 of portfolio.positions) {
|
|
3089
|
+
if (remaining === 0n) break;
|
|
3090
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3091
|
+
if (available <= 0n) continue;
|
|
3092
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
3093
|
+
const response = await this.walletRequest(
|
|
3094
|
+
state,
|
|
3095
|
+
chainId,
|
|
3096
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
3097
|
+
{
|
|
3098
|
+
method: "POST",
|
|
3099
|
+
body: {
|
|
3100
|
+
chainId,
|
|
3101
|
+
vaultAddress: position2.yieldOption.address,
|
|
3102
|
+
assetsRaw: assetsRaw.toString()
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
);
|
|
3106
|
+
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3107
|
+
throw this.invalidResponse("position withdrawal");
|
|
3108
|
+
}
|
|
3109
|
+
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3110
|
+
remaining -= assetsRaw;
|
|
3111
|
+
}
|
|
3112
|
+
if (remaining > 0n) {
|
|
3113
|
+
throw this.invalidResponse("yield positions", {
|
|
3114
|
+
reason: "Withdrawable positions could not cover the request.",
|
|
3115
|
+
remaining: remaining.toString()
|
|
3116
|
+
});
|
|
3117
|
+
}
|
|
3118
|
+
const account = getAddress2(state.walletAddress);
|
|
3119
|
+
const txHash = await this.submitTransaction(state, chainId, {
|
|
3120
|
+
from: account,
|
|
3121
|
+
to: getAddress2(context.wallet.walletAddress),
|
|
3122
|
+
data: amount === void 0 ? encodeFunctionData({
|
|
3123
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3124
|
+
functionName: "withdrawAllAssetToUser",
|
|
3125
|
+
args: [account, metadata.address]
|
|
3126
|
+
}) : encodeFunctionData({
|
|
3127
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3128
|
+
functionName: "withdrawAssetToUser",
|
|
3129
|
+
args: [account, metadata.address, requested]
|
|
3130
|
+
}),
|
|
3131
|
+
value: "0",
|
|
3132
|
+
chainId
|
|
3133
|
+
});
|
|
3134
|
+
return {
|
|
3135
|
+
txHash,
|
|
3136
|
+
type: amount === void 0 ? "full" : "partial",
|
|
3137
|
+
amount: requested.toString()
|
|
3138
|
+
};
|
|
3139
|
+
} finally {
|
|
3140
|
+
await this.refreshSnapshotAfterMovement(
|
|
3141
|
+
state,
|
|
3142
|
+
chainId,
|
|
3143
|
+
context,
|
|
3144
|
+
"withdrawal"
|
|
3145
|
+
);
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
async getBalances(state, chainId) {
|
|
3149
|
+
this.assertChain(chainId);
|
|
3150
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3151
|
+
}
|
|
3152
|
+
async getEarnings(state, chainId) {
|
|
3153
|
+
this.assertChain(chainId);
|
|
3154
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3155
|
+
}
|
|
3156
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3157
|
+
this.assertChain(chainId);
|
|
3158
|
+
const asset = tokenSymbol?.toUpperCase();
|
|
3159
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3160
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3161
|
+
...asset ? { asset } : {},
|
|
3162
|
+
historic: true
|
|
3163
|
+
});
|
|
3164
|
+
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3165
|
+
}
|
|
3166
|
+
async getHistory(state, chainId, options) {
|
|
3167
|
+
this.assertChain(chainId);
|
|
3168
|
+
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3169
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3170
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3171
|
+
...asset ? { asset } : {},
|
|
3172
|
+
historic: true,
|
|
3173
|
+
actions: true
|
|
3174
|
+
});
|
|
3175
|
+
return mapYieldseekerHistory(contexts, {
|
|
3176
|
+
limit: options?.limit ?? 10,
|
|
3177
|
+
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3178
|
+
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3181
|
+
async getUserProfile(state, chainId) {
|
|
3182
|
+
this.assertChain(chainId);
|
|
3183
|
+
return mapYieldseekerProfile(
|
|
3184
|
+
state.walletAddress,
|
|
3185
|
+
await this.loadPortfolio(state, chainId, {})
|
|
3186
|
+
);
|
|
3187
|
+
}
|
|
3188
|
+
async getAgentApy(days, options) {
|
|
3189
|
+
this.assertOptionalChain(options?.chainId);
|
|
3190
|
+
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3191
|
+
if (requested !== void 0) this.assertAsset(requested);
|
|
3192
|
+
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3193
|
+
const values = await Promise.all(
|
|
3194
|
+
assets.map(async (asset) => {
|
|
3195
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3196
|
+
const response = await this.api.request(
|
|
3197
|
+
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3198
|
+
);
|
|
3199
|
+
if (!Array.isArray(response?.yieldOptions)) {
|
|
3200
|
+
throw this.invalidResponse("yield options");
|
|
3201
|
+
}
|
|
3202
|
+
return { asset, yieldOptions: response.yieldOptions };
|
|
3203
|
+
})
|
|
3204
|
+
);
|
|
3205
|
+
return mapYieldseekerAgentApy(values, days);
|
|
3206
|
+
}
|
|
3207
|
+
userKey(state, chainId) {
|
|
3208
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3209
|
+
}
|
|
3210
|
+
contextKey(state, chainId, asset) {
|
|
3211
|
+
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3212
|
+
}
|
|
3213
|
+
async resolveUser(state, chainId) {
|
|
3214
|
+
const key2 = this.userKey(state, chainId);
|
|
3215
|
+
const inMemory = this.users.get(key2);
|
|
3216
|
+
if (inMemory) return inMemory;
|
|
3217
|
+
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3218
|
+
if (persisted) {
|
|
3219
|
+
this.users.set(key2, persisted);
|
|
3220
|
+
return persisted;
|
|
3221
|
+
}
|
|
3222
|
+
const walletAddress = getAddress2(state.walletAddress);
|
|
3223
|
+
let user = null;
|
|
3224
|
+
try {
|
|
3225
|
+
const login = await this.providerRequest(
|
|
3226
|
+
state,
|
|
3227
|
+
chainId,
|
|
3228
|
+
"/users/login-with-wallet",
|
|
3229
|
+
{ method: "POST", body: { walletAddress } }
|
|
3230
|
+
);
|
|
3231
|
+
user = login?.user ?? null;
|
|
3232
|
+
if (!user) {
|
|
3233
|
+
throw this.invalidResponse("wallet login", {
|
|
3234
|
+
reason: "A successful login returned no user."
|
|
3235
|
+
});
|
|
3236
|
+
}
|
|
3237
|
+
} catch (error) {
|
|
3238
|
+
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3239
|
+
if (error instanceof OwneyError) throw error;
|
|
3240
|
+
throw this.mapApiError(error);
|
|
3241
|
+
}
|
|
3242
|
+
let created;
|
|
3243
|
+
try {
|
|
3244
|
+
created = await this.providerRequest(
|
|
3245
|
+
state,
|
|
3246
|
+
chainId,
|
|
3247
|
+
"/users",
|
|
3248
|
+
{
|
|
3249
|
+
method: "POST",
|
|
3250
|
+
body: {
|
|
3251
|
+
walletAddress,
|
|
3252
|
+
username: `owney-${walletAddress.slice(2).toLowerCase()}`
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
);
|
|
3256
|
+
} catch (createError) {
|
|
3257
|
+
throw this.mapApiError(createError);
|
|
3258
|
+
}
|
|
3259
|
+
user = created?.user ?? null;
|
|
3260
|
+
}
|
|
3261
|
+
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3262
|
+
throw this.invalidResponse("wallet identity");
|
|
3263
|
+
}
|
|
3264
|
+
const resolved = { userId: user.userId };
|
|
3265
|
+
this.users.set(key2, resolved);
|
|
3266
|
+
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3267
|
+
return resolved;
|
|
3268
|
+
}
|
|
3269
|
+
forgetUser(state, chainId) {
|
|
3270
|
+
this.users.delete(this.userKey(state, chainId));
|
|
3271
|
+
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3272
|
+
}
|
|
3273
|
+
async ensureAgent(state, chainId, asset) {
|
|
3274
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3275
|
+
const cached = this.agentContexts.get(key2);
|
|
3276
|
+
if (cached) return cached;
|
|
3277
|
+
const pending = this.pendingAgents.get(key2);
|
|
3278
|
+
if (pending) return pending;
|
|
3279
|
+
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3280
|
+
(context) => {
|
|
3281
|
+
if (!context) throw this.invalidResponse("agent creation");
|
|
3282
|
+
this.agentContexts.set(key2, context);
|
|
3283
|
+
return context;
|
|
3284
|
+
}
|
|
3285
|
+
);
|
|
3286
|
+
this.pendingAgents.set(key2, request);
|
|
3287
|
+
try {
|
|
3288
|
+
return await request;
|
|
3289
|
+
} finally {
|
|
3290
|
+
this.pendingAgents.delete(key2);
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
async findAgent(state, chainId, asset) {
|
|
3294
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3295
|
+
const cached = this.agentContexts.get(key2);
|
|
3296
|
+
if (cached) return cached;
|
|
3297
|
+
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3298
|
+
if (context) this.agentContexts.set(key2, context);
|
|
3299
|
+
return context;
|
|
3300
|
+
}
|
|
3301
|
+
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3302
|
+
const user = await this.resolveUser(state, chainId);
|
|
3303
|
+
const response = await this.walletRequest(
|
|
3304
|
+
state,
|
|
3305
|
+
chainId,
|
|
3306
|
+
`/users/${user.userId}/agents`
|
|
3307
|
+
);
|
|
3308
|
+
if (!Array.isArray(response?.agents)) {
|
|
3309
|
+
throw this.invalidResponse("agent list");
|
|
3310
|
+
}
|
|
3311
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3312
|
+
let agent = response.agents.find(
|
|
3313
|
+
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3314
|
+
);
|
|
3315
|
+
if (!agent && createIfMissing) {
|
|
3316
|
+
const created = await this.walletRequest(
|
|
3317
|
+
state,
|
|
3318
|
+
chainId,
|
|
3319
|
+
`/users/${user.userId}/agents`,
|
|
3320
|
+
{
|
|
3321
|
+
method: "POST",
|
|
3322
|
+
body: {
|
|
3323
|
+
name: OWNEY_AGENT_NAME,
|
|
3324
|
+
emoji: "\u{1F989}",
|
|
3325
|
+
chainId,
|
|
3326
|
+
assetAddress: metadata.address,
|
|
3327
|
+
type: "vault",
|
|
3328
|
+
rulePreset: null
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
);
|
|
3332
|
+
agent = created?.agent;
|
|
3333
|
+
}
|
|
3334
|
+
if (!agent) return null;
|
|
3335
|
+
this.assertAgent(agent);
|
|
3336
|
+
const walletResponse = await this.walletRequest(
|
|
3337
|
+
state,
|
|
3338
|
+
chainId,
|
|
3339
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3340
|
+
);
|
|
3341
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3342
|
+
throw this.invalidResponse("agent wallet");
|
|
3343
|
+
}
|
|
3344
|
+
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3345
|
+
}
|
|
3346
|
+
async loadPortfolio(state, chainId, options) {
|
|
3347
|
+
const user = await this.resolveUser(state, chainId);
|
|
3348
|
+
const response = await this.walletRequest(
|
|
3349
|
+
state,
|
|
3350
|
+
chainId,
|
|
3351
|
+
`/users/${user.userId}/agents`
|
|
3352
|
+
);
|
|
3353
|
+
if (!Array.isArray(response?.agents)) {
|
|
3354
|
+
throw this.invalidResponse("agent list");
|
|
3355
|
+
}
|
|
3356
|
+
const contexts = [];
|
|
3357
|
+
for (const agent of response.agents) {
|
|
3358
|
+
const asset = this.assetForAgent(agent);
|
|
3359
|
+
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3360
|
+
continue;
|
|
3361
|
+
}
|
|
3362
|
+
this.assertAgent(agent);
|
|
3363
|
+
const walletResponse = await this.walletRequest(
|
|
3364
|
+
state,
|
|
3365
|
+
chainId,
|
|
3366
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3367
|
+
);
|
|
3368
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3369
|
+
throw this.invalidResponse("agent wallet");
|
|
3370
|
+
}
|
|
3371
|
+
const context = {
|
|
3372
|
+
user,
|
|
3373
|
+
agent,
|
|
3374
|
+
wallet: walletResponse.agentWallet,
|
|
3375
|
+
asset
|
|
3376
|
+
};
|
|
3377
|
+
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3378
|
+
contexts.push(context);
|
|
3379
|
+
}
|
|
3380
|
+
return Promise.all(
|
|
3381
|
+
contexts.map(
|
|
3382
|
+
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3383
|
+
)
|
|
3384
|
+
);
|
|
3385
|
+
}
|
|
3386
|
+
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3387
|
+
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3388
|
+
this.walletRequest(
|
|
3389
|
+
state,
|
|
3390
|
+
chainId,
|
|
3391
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3392
|
+
shouldOnlyUseRecentValue: true,
|
|
3393
|
+
shouldAllowStaleOnError: true
|
|
3394
|
+
})}`
|
|
3395
|
+
),
|
|
3396
|
+
this.walletRequest(
|
|
3397
|
+
state,
|
|
3398
|
+
chainId,
|
|
3399
|
+
this.agentPath(context, "yield-positions")
|
|
3400
|
+
),
|
|
3401
|
+
options.historic ? this.walletRequest(
|
|
3402
|
+
state,
|
|
3403
|
+
chainId,
|
|
3404
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3405
|
+
) : Promise.resolve(void 0),
|
|
3406
|
+
options.actions ? this.walletRequest(
|
|
3407
|
+
state,
|
|
3408
|
+
chainId,
|
|
3409
|
+
this.agentPath(context, "actions")
|
|
3410
|
+
) : Promise.resolve(void 0)
|
|
3411
|
+
]);
|
|
3412
|
+
if (!snapshot?.agentSnapshot) {
|
|
3413
|
+
throw this.invalidResponse("agent snapshot");
|
|
3414
|
+
}
|
|
3415
|
+
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3416
|
+
throw this.invalidResponse("yield positions");
|
|
3417
|
+
}
|
|
3418
|
+
return {
|
|
3419
|
+
...context,
|
|
3420
|
+
snapshot: snapshot.agentSnapshot,
|
|
3421
|
+
positions: positions.yieldPositions,
|
|
3422
|
+
...historic?.position ? { historic: historic.position } : {},
|
|
3423
|
+
...actions?.actions ? { actions: actions.actions } : {}
|
|
3424
|
+
};
|
|
3425
|
+
}
|
|
3426
|
+
async deployAfterFunding(state, chainId, context) {
|
|
3427
|
+
if (context.wallet.initializedDate != null) return;
|
|
3428
|
+
try {
|
|
3429
|
+
const deployed = await this.walletRequest(
|
|
3430
|
+
state,
|
|
3431
|
+
chainId,
|
|
3432
|
+
this.agentPath(context, "deploy"),
|
|
3433
|
+
{ method: "POST", body: {} }
|
|
3434
|
+
);
|
|
3435
|
+
if (deployed?.agentWallet) {
|
|
3436
|
+
context.wallet = deployed.agentWallet;
|
|
3437
|
+
}
|
|
3438
|
+
} catch (error) {
|
|
3439
|
+
console.warn(
|
|
3440
|
+
"[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
|
|
3441
|
+
error
|
|
3442
|
+
);
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3446
|
+
try {
|
|
3447
|
+
const response = await this.walletRequest(
|
|
3448
|
+
state,
|
|
3449
|
+
chainId,
|
|
3450
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3451
|
+
shouldForceRefresh: true
|
|
3452
|
+
})}`
|
|
3453
|
+
);
|
|
3454
|
+
if (!response?.agentSnapshot) {
|
|
3455
|
+
throw this.invalidResponse("agent snapshot refresh");
|
|
3456
|
+
}
|
|
3457
|
+
} catch (error) {
|
|
3458
|
+
console.warn(
|
|
3459
|
+
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3460
|
+
error
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
agentPath(context, suffix) {
|
|
3465
|
+
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
3466
|
+
}
|
|
3467
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
3468
|
+
try {
|
|
3469
|
+
return await this.providerRequest(state, chainId, path, options);
|
|
3470
|
+
} catch (error) {
|
|
3471
|
+
throw this.mapApiError(error);
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
async providerRequest(state, chainId, path, options = {}) {
|
|
3475
|
+
this.assertChain(chainId);
|
|
3476
|
+
const request = (signature2) => this.api.request(path, {
|
|
3477
|
+
...options,
|
|
3478
|
+
signature: signature2
|
|
3479
|
+
});
|
|
3480
|
+
let signature = await this.auth.getToken(state, chainId);
|
|
3481
|
+
try {
|
|
3482
|
+
return await request(signature);
|
|
3483
|
+
} catch (error) {
|
|
3484
|
+
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
3485
|
+
if (error.providerCode === "NO_USER") throw error;
|
|
3486
|
+
if (!error.isAuthenticationError) throw error;
|
|
3487
|
+
this.auth.clear(state, chainId);
|
|
3488
|
+
signature = await this.auth.getToken(state, chainId);
|
|
3489
|
+
try {
|
|
3490
|
+
return await request(signature);
|
|
3491
|
+
} catch (retryError) {
|
|
3492
|
+
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3493
|
+
this.forgetUser(state, chainId);
|
|
3494
|
+
}
|
|
3495
|
+
throw retryError;
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
mapApiError(error) {
|
|
3500
|
+
if (!(error instanceof YieldseekerApiError)) {
|
|
3501
|
+
return new OwneyError(
|
|
3502
|
+
"AGENT_API_ERROR",
|
|
3503
|
+
"Yieldseeker request failed.",
|
|
3504
|
+
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3505
|
+
this.id
|
|
3506
|
+
);
|
|
3507
|
+
}
|
|
3508
|
+
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3509
|
+
return new OwneyError(
|
|
3510
|
+
code,
|
|
3511
|
+
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3512
|
+
{
|
|
3513
|
+
statusCode: error.status,
|
|
3514
|
+
providerCode: error.providerCode,
|
|
3515
|
+
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3516
|
+
},
|
|
3517
|
+
this.id
|
|
3518
|
+
);
|
|
3519
|
+
}
|
|
3520
|
+
async submitTransaction(state, chainId, transaction) {
|
|
3521
|
+
if (this.transactionExecutor) {
|
|
3522
|
+
return this.transactionExecutor(state, chainId, transaction);
|
|
3523
|
+
}
|
|
3524
|
+
this.assertTransaction(transaction, state, chainId);
|
|
3525
|
+
const account = getAddress2(state.walletAddress);
|
|
3526
|
+
const walletClient = createWalletClient2({
|
|
3527
|
+
account,
|
|
3528
|
+
chain: base3,
|
|
3529
|
+
transport: custom2(state.provider)
|
|
3530
|
+
});
|
|
3531
|
+
const publicClient = createPublicClient3({
|
|
3532
|
+
chain: base3,
|
|
3533
|
+
transport: custom2(state.provider)
|
|
3534
|
+
});
|
|
3535
|
+
await ensureWalletOnChain(
|
|
3536
|
+
publicClient,
|
|
3537
|
+
walletClient,
|
|
3538
|
+
8453
|
|
3539
|
+
);
|
|
3540
|
+
const hash = await walletClient.sendTransaction({
|
|
3541
|
+
account,
|
|
3542
|
+
chain: base3,
|
|
3543
|
+
to: getAddress2(transaction.to),
|
|
3544
|
+
data: transaction.data,
|
|
3545
|
+
value: BigInt(transaction.value)
|
|
3546
|
+
});
|
|
3547
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3548
|
+
hash,
|
|
3549
|
+
confirmations: 1
|
|
3550
|
+
});
|
|
3551
|
+
if (receipt.status !== "success") {
|
|
3552
|
+
throw new OwneyError(
|
|
3553
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3554
|
+
`Yieldseeker transaction reverted (${hash}).`,
|
|
3555
|
+
{ transactionHash: hash },
|
|
3556
|
+
this.id
|
|
3557
|
+
);
|
|
3558
|
+
}
|
|
3559
|
+
return hash;
|
|
3560
|
+
}
|
|
3561
|
+
async waitForReceipt(state, chainId, transactionHash) {
|
|
3562
|
+
if (this.unwindReceiptWaiter) {
|
|
3563
|
+
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
const publicClient = createPublicClient3({
|
|
3567
|
+
chain: base3,
|
|
3568
|
+
transport: custom2(state.provider)
|
|
3569
|
+
});
|
|
3570
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3571
|
+
hash: transactionHash,
|
|
3572
|
+
confirmations: 1
|
|
3573
|
+
});
|
|
3574
|
+
if (receipt.status !== "success") {
|
|
3575
|
+
throw new OwneyError(
|
|
3576
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3577
|
+
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3578
|
+
{ transactionHash },
|
|
3579
|
+
this.id
|
|
3580
|
+
);
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
assertTransaction(transaction, state, chainId) {
|
|
3584
|
+
if (!transaction || typeof transaction.from !== "string" || !isAddress2(transaction.from) || typeof transaction.to !== "string" || !isAddress2(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 || getAddress2(transaction.from) !== getAddress2(state.walletAddress)) {
|
|
3585
|
+
throw this.invalidResponse("transaction");
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
assertAgent(agent) {
|
|
3589
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress2(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3590
|
+
throw this.invalidResponse("agent");
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
isOwneyAgent(agent) {
|
|
3594
|
+
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3595
|
+
}
|
|
3596
|
+
assetForAgent(agent) {
|
|
3597
|
+
for (const asset of ["USDC", "WETH"]) {
|
|
3598
|
+
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3599
|
+
return asset;
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
return null;
|
|
3603
|
+
}
|
|
3604
|
+
isTransactionHash(value) {
|
|
3605
|
+
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3606
|
+
}
|
|
3607
|
+
assertChain(chainId) {
|
|
3608
|
+
if (chainId !== 8453) {
|
|
3609
|
+
throw new OwneyError(
|
|
3610
|
+
"CHAIN_UNSUPPORTED",
|
|
3611
|
+
`Yieldseeker does not support chain ${chainId}.`,
|
|
3612
|
+
{ chainId, supportedChainIds: [8453] },
|
|
3613
|
+
this.id
|
|
3614
|
+
);
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
assertOptionalChain(chainId) {
|
|
3618
|
+
if (chainId !== void 0) this.assertChain(chainId);
|
|
3619
|
+
}
|
|
3620
|
+
assertAsset(asset) {
|
|
3621
|
+
if (asset !== "USDC" && asset !== "WETH") {
|
|
3622
|
+
throw new OwneyError(
|
|
3623
|
+
"ASSET_UNSUPPORTED",
|
|
3624
|
+
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
3625
|
+
{
|
|
3626
|
+
asset,
|
|
3627
|
+
supportedAssets: ["USDC", "WETH"],
|
|
3628
|
+
providerAlsoAdvertises: ["cbBTC"]
|
|
3629
|
+
},
|
|
3630
|
+
this.id
|
|
3631
|
+
);
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
invalidResponse(operation, details = {}) {
|
|
3635
|
+
return new OwneyError(
|
|
3636
|
+
"AGENT_INVALID_RESPONSE",
|
|
3637
|
+
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3638
|
+
details,
|
|
3639
|
+
this.id
|
|
3640
|
+
);
|
|
3641
|
+
}
|
|
3642
|
+
};
|
|
3643
|
+
|
|
3644
|
+
// src/lib/routing-api.ts
|
|
3645
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3646
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3647
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3648
|
+
try {
|
|
3649
|
+
const res = await fetch(url, {
|
|
3650
|
+
method: "GET",
|
|
3651
|
+
headers: {
|
|
3652
|
+
"Content-Type": "application/json",
|
|
3653
|
+
"x-owney-api-key": `${apiKey}`
|
|
3654
|
+
}
|
|
3655
|
+
});
|
|
3656
|
+
if (!res.ok) {
|
|
3657
|
+
if (res.status !== 404) {
|
|
3658
|
+
console.warn(
|
|
3659
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
3660
|
+
);
|
|
3661
|
+
}
|
|
3662
|
+
return null;
|
|
3663
|
+
}
|
|
3664
|
+
const json = await res.json();
|
|
3665
|
+
const policy = json.success ? json.data ?? null : null;
|
|
3666
|
+
debugLog(
|
|
3667
|
+
"owney-sdk",
|
|
3668
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
3669
|
+
policy ?? void 0
|
|
3670
|
+
);
|
|
3671
|
+
return policy;
|
|
3672
|
+
} catch (error) {
|
|
3673
|
+
console.warn(
|
|
3674
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
3675
|
+
error instanceof Error ? error.message : String(error)
|
|
3676
|
+
);
|
|
3677
|
+
return null;
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3681
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
3682
|
+
const res = await fetch(url, {
|
|
3683
|
+
method: "GET",
|
|
3684
|
+
headers: {
|
|
3685
|
+
"Content-Type": "application/json",
|
|
3686
|
+
"x-owney-api-key": `${apiKey}`
|
|
3687
|
+
}
|
|
3688
|
+
});
|
|
3689
|
+
if (!res.ok) {
|
|
3690
|
+
const text = await res.text().catch(() => "");
|
|
3691
|
+
throw new OwneyError(
|
|
3692
|
+
"API_ROUTING_ERROR",
|
|
3693
|
+
`Routing API error ${res.status}: ${text}`,
|
|
3694
|
+
{ statusCode: res.status, responseBody: text }
|
|
3695
|
+
);
|
|
3696
|
+
}
|
|
3697
|
+
const json = await res.json();
|
|
3698
|
+
if (!json.success) {
|
|
3699
|
+
throw new OwneyError(
|
|
3700
|
+
"API_ROUTING_FAILED",
|
|
3701
|
+
`Routing API request failed: ${json.message}`,
|
|
3702
|
+
{ message: json.message }
|
|
3703
|
+
);
|
|
3704
|
+
}
|
|
3705
|
+
return json.data;
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
// src/lib/health-report.ts
|
|
3709
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3710
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
3711
|
+
try {
|
|
3712
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
3713
|
+
method: "POST",
|
|
3714
|
+
headers: {
|
|
3715
|
+
"Content-Type": "application/json",
|
|
3716
|
+
"x-owney-api-key": apiKey
|
|
3717
|
+
},
|
|
3718
|
+
body: JSON.stringify({
|
|
3719
|
+
agent_type: agentType,
|
|
3720
|
+
error_code: errorCode,
|
|
3721
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3722
|
+
})
|
|
3723
|
+
});
|
|
3724
|
+
} catch (err) {
|
|
3725
|
+
console.warn(
|
|
3726
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
3727
|
+
err instanceof Error ? err.message : err
|
|
3728
|
+
);
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
3732
|
+
try {
|
|
3733
|
+
return await fn();
|
|
3734
|
+
} catch (err) {
|
|
3735
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3736
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
3737
|
+
throw err;
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3741
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
3742
|
+
import { parseUnits } from "viem";
|
|
3743
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
3744
|
+
const target = asset.toUpperCase();
|
|
3745
|
+
return agents.map((agent) => {
|
|
3746
|
+
const agentBalance = aggregated[agent.id];
|
|
3747
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
3748
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
3749
|
+
);
|
|
3750
|
+
let balance = tokenBalance ? parseUnits(tokenBalance.amount, decimals) : 0n;
|
|
3751
|
+
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
3752
|
+
const chainNameById = {
|
|
3753
|
+
1: "ETHEREUM",
|
|
3754
|
+
8453: "BASE",
|
|
3755
|
+
42161: "ARBITRUM"
|
|
3756
|
+
};
|
|
3757
|
+
const targetChain = chainNameById[chainId];
|
|
3758
|
+
for (const position2 of agentBalance?.positions ?? []) {
|
|
3759
|
+
const positionChain = position2.chain.trim().toUpperCase();
|
|
3760
|
+
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
3761
|
+
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
3762
|
+
if (position2.amountRaw !== void 0) {
|
|
3763
|
+
try {
|
|
3764
|
+
balance += BigInt(position2.amountRaw);
|
|
3765
|
+
continue;
|
|
3766
|
+
} catch {
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
balance += parseUnits(position2.amount, decimals);
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
return { agent, balance };
|
|
3773
|
+
});
|
|
3774
|
+
}
|
|
3775
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
3776
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
3777
|
+
agent,
|
|
3778
|
+
balance,
|
|
3779
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
3780
|
+
}));
|
|
3781
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
3782
|
+
let remainder = requested - assigned;
|
|
3783
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
3784
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
3785
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
3786
|
+
});
|
|
3787
|
+
for (const p of byHeadroom) {
|
|
3788
|
+
if (remainder === 0n) break;
|
|
3789
|
+
const headroom = p.balance - p.planned;
|
|
3790
|
+
if (headroom <= 0n) continue;
|
|
3791
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
3792
|
+
p.planned += take;
|
|
3793
|
+
remainder -= take;
|
|
3794
|
+
}
|
|
3795
|
+
return plans;
|
|
3796
|
+
}
|
|
3797
|
+
function planDisabledDrain(disabled, requested) {
|
|
3798
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
3799
|
+
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
3800
|
+
);
|
|
3801
|
+
const plans = [];
|
|
3802
|
+
let remaining = requested;
|
|
3803
|
+
for (const { agent, balance } of sorted) {
|
|
3804
|
+
if (remaining === 0n) {
|
|
3805
|
+
plans.push({ agent, balance, planned: 0n });
|
|
3806
|
+
continue;
|
|
3807
|
+
}
|
|
3808
|
+
const take = balance < remaining ? balance : remaining;
|
|
3809
|
+
plans.push({ agent, balance, planned: take });
|
|
3810
|
+
remaining -= take;
|
|
3811
|
+
}
|
|
3812
|
+
return { plans, remaining };
|
|
2314
3813
|
}
|
|
2315
|
-
|
|
2316
|
-
const
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
3814
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
3815
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
3816
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
3817
|
+
const totalHeadroom = candidates.reduce(
|
|
3818
|
+
(s, c) => s + (c.balance - c.planned),
|
|
3819
|
+
0n
|
|
3820
|
+
);
|
|
3821
|
+
if (totalHeadroom === 0n) return;
|
|
3822
|
+
let distributed = 0n;
|
|
3823
|
+
for (const c of candidates) {
|
|
3824
|
+
const headroom = c.balance - c.planned;
|
|
3825
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
3826
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
3827
|
+
c.planned += give;
|
|
3828
|
+
distributed += give;
|
|
2330
3829
|
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
3830
|
+
let leftover = amount - distributed;
|
|
3831
|
+
for (const c of candidates) {
|
|
3832
|
+
if (leftover === 0n) break;
|
|
3833
|
+
const headroom = c.balance - c.planned;
|
|
3834
|
+
if (headroom <= 0n) continue;
|
|
3835
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
3836
|
+
c.planned += take;
|
|
3837
|
+
leftover -= take;
|
|
2338
3838
|
}
|
|
2339
3839
|
}
|
|
3840
|
+
function sumWithdrawnAmount(results) {
|
|
3841
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
3842
|
+
}
|
|
2340
3843
|
|
|
2341
|
-
// src/lib/
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
`No sponsored token configured for chain ${chainId}`
|
|
2352
|
-
);
|
|
3844
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
3845
|
+
function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
3846
|
+
if (!tokenSymbol) {
|
|
3847
|
+
const total = Number(balance.totalBalance);
|
|
3848
|
+
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
3849
|
+
}
|
|
3850
|
+
const normalizedToken = tokenSymbol.toUpperCase();
|
|
3851
|
+
return balance.tokens.reduce((total, token) => {
|
|
3852
|
+
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
3853
|
+
return total;
|
|
2353
3854
|
}
|
|
2354
|
-
const
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
account: deps.ownerAddress,
|
|
2395
|
-
...typedData
|
|
2396
|
-
});
|
|
2397
|
-
deps.onApproved?.();
|
|
2398
|
-
const result = await post({
|
|
2399
|
-
baseUrl: deps.baseUrl,
|
|
2400
|
-
apiKey: deps.apiKey,
|
|
2401
|
-
body: {
|
|
2402
|
-
chainId: cid,
|
|
2403
|
-
token,
|
|
2404
|
-
from: deps.ownerAddress,
|
|
2405
|
-
to: smartWallet,
|
|
2406
|
-
value: amount,
|
|
2407
|
-
validAfter: validAfter.toString(),
|
|
2408
|
-
validBefore: validBefore.toString(),
|
|
2409
|
-
nonce,
|
|
2410
|
-
authSignature,
|
|
2411
|
-
tokenName,
|
|
2412
|
-
tokenVersion
|
|
3855
|
+
const amount = Number(token.amount);
|
|
3856
|
+
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
3857
|
+
}, 0);
|
|
3858
|
+
}
|
|
3859
|
+
function aggregateApyHistory(agentApys, agentBalances) {
|
|
3860
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
3861
|
+
for (const [id, accountApy] of Object.entries(agentApys)) {
|
|
3862
|
+
const balance = agentBalances[id] ?? 0;
|
|
3863
|
+
if (!Number.isFinite(balance) || balance <= 0) continue;
|
|
3864
|
+
for (const point of accountApy.history ?? []) {
|
|
3865
|
+
const apy = Number(point.apy);
|
|
3866
|
+
if (!point.date || !Number.isFinite(apy)) continue;
|
|
3867
|
+
const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
|
|
3868
|
+
current.weightedSum += apy * balance;
|
|
3869
|
+
current.weight += balance;
|
|
3870
|
+
byDate.set(point.date, current);
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
3874
|
+
date,
|
|
3875
|
+
apy: value.weightedSum / value.weight
|
|
3876
|
+
}));
|
|
3877
|
+
}
|
|
3878
|
+
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
3879
|
+
const sums = {};
|
|
3880
|
+
const weights = {};
|
|
3881
|
+
for (const id of Object.keys(agentApys)) {
|
|
3882
|
+
const cells = agentApys[id].apyByChainAndAsset;
|
|
3883
|
+
const balance = agentBalances[id] ?? 0;
|
|
3884
|
+
if (!cells || balance <= 0) continue;
|
|
3885
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
3886
|
+
if (!perAsset) continue;
|
|
3887
|
+
const chainId = Number(chainKey);
|
|
3888
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
3889
|
+
const apy = Number(apyValue ?? 0);
|
|
3890
|
+
if (apy === 0) continue;
|
|
3891
|
+
sums[chainId] ??= {};
|
|
3892
|
+
weights[chainId] ??= {};
|
|
3893
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
3894
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2413
3895
|
}
|
|
2414
|
-
}
|
|
2415
|
-
|
|
2416
|
-
};
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
const out = {};
|
|
3899
|
+
for (const chainKey of Object.keys(sums)) {
|
|
3900
|
+
const chainId = Number(chainKey);
|
|
3901
|
+
const perAssetOut = {};
|
|
3902
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
3903
|
+
const w = weights[chainId][asset];
|
|
3904
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
3905
|
+
}
|
|
3906
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
3907
|
+
out[chainId] = perAssetOut;
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
return out;
|
|
2417
3911
|
}
|
|
2418
3912
|
|
|
3913
|
+
// src/client.ts
|
|
3914
|
+
import {
|
|
3915
|
+
createPublicClient as createPublicClient4,
|
|
3916
|
+
createWalletClient as createWalletClient3,
|
|
3917
|
+
custom as custom3
|
|
3918
|
+
} from "viem";
|
|
3919
|
+
import { base as base4, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
3920
|
+
|
|
2419
3921
|
// src/lib/sponsored-weth-deposit.ts
|
|
2420
3922
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2421
3923
|
function makeSponsoredWethCallback(deps) {
|
|
2422
3924
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
2423
3925
|
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
2424
|
-
return
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
"CHAIN_UNSUPPORTED",
|
|
2430
|
-
`No sponsored WETH configured for chain ${chainId}`
|
|
2431
|
-
);
|
|
2432
|
-
}
|
|
2433
|
-
const amountWei = BigInt(amount);
|
|
2434
|
-
const pub = deps.getPublicClient(cid);
|
|
2435
|
-
const wallet = deps.getWalletClient(cid);
|
|
2436
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2437
|
-
try {
|
|
2438
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2439
|
-
if (balance < amountWei) {
|
|
3926
|
+
return makeVerificationAwareDepositCallback(
|
|
3927
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
3928
|
+
const cid = chainId;
|
|
3929
|
+
const token = deps.tokenAddressByChain[cid];
|
|
3930
|
+
if (!token) {
|
|
2440
3931
|
throw new OwneyError(
|
|
2441
|
-
"
|
|
2442
|
-
|
|
2443
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3932
|
+
"CHAIN_UNSUPPORTED",
|
|
3933
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
2444
3934
|
);
|
|
2445
3935
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
)
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
});
|
|
2466
|
-
const nonce = randomPermit2Nonce();
|
|
2467
|
-
const deadline = BigInt(
|
|
2468
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2469
|
-
);
|
|
2470
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
2471
|
-
chainId: cid,
|
|
2472
|
-
message: {
|
|
2473
|
-
permitted: { token, amount: amountWei },
|
|
2474
|
-
spender: relayer,
|
|
2475
|
-
nonce,
|
|
2476
|
-
deadline
|
|
3936
|
+
const amountWei = BigInt(amount);
|
|
3937
|
+
const pub = deps.getPublicClient(cid);
|
|
3938
|
+
const wallet = deps.getWalletClient(cid);
|
|
3939
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
3940
|
+
try {
|
|
3941
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
3942
|
+
if (balance < amountWei) {
|
|
3943
|
+
throw new OwneyError(
|
|
3944
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
3945
|
+
"Insufficient WETH balance for this deposit.",
|
|
3946
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3947
|
+
);
|
|
3948
|
+
}
|
|
3949
|
+
} catch (err) {
|
|
3950
|
+
if (err instanceof OwneyError) throw err;
|
|
3951
|
+
console.warn(
|
|
3952
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
3953
|
+
err instanceof Error ? err.message : String(err)
|
|
3954
|
+
);
|
|
2477
3955
|
}
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
account: deps.ownerAddress,
|
|
2481
|
-
...typedData
|
|
2482
|
-
});
|
|
2483
|
-
deps.onApproved?.();
|
|
2484
|
-
const result = await post({
|
|
2485
|
-
baseUrl: deps.baseUrl,
|
|
2486
|
-
apiKey: deps.apiKey,
|
|
2487
|
-
body: {
|
|
2488
|
-
chainId: cid,
|
|
3956
|
+
const allowance = await readPermit2Allowance(
|
|
3957
|
+
pub,
|
|
2489
3958
|
token,
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
3959
|
+
deps.ownerAddress
|
|
3960
|
+
);
|
|
3961
|
+
if (allowance < amountWei) {
|
|
3962
|
+
throw new OwneyError(
|
|
3963
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
3964
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
3965
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
3966
|
+
);
|
|
2496
3967
|
}
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
3968
|
+
const relayer = await get({
|
|
3969
|
+
baseUrl: deps.baseUrl,
|
|
3970
|
+
apiKey: deps.apiKey,
|
|
3971
|
+
chainId: cid
|
|
3972
|
+
});
|
|
3973
|
+
const nonce = randomPermit2Nonce();
|
|
3974
|
+
const deadline = BigInt(
|
|
3975
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
3976
|
+
);
|
|
3977
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
3978
|
+
chainId: cid,
|
|
3979
|
+
message: {
|
|
3980
|
+
permitted: { token, amount: amountWei },
|
|
3981
|
+
spender: relayer,
|
|
3982
|
+
nonce,
|
|
3983
|
+
deadline
|
|
3984
|
+
}
|
|
3985
|
+
});
|
|
3986
|
+
const signature = await wallet.signTypedData({
|
|
3987
|
+
account: deps.ownerAddress,
|
|
3988
|
+
...typedData
|
|
3989
|
+
});
|
|
3990
|
+
deps.onApproved?.();
|
|
3991
|
+
const result = await post({
|
|
3992
|
+
baseUrl: deps.baseUrl,
|
|
3993
|
+
apiKey: deps.apiKey,
|
|
3994
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
3995
|
+
body: {
|
|
3996
|
+
chainId: cid,
|
|
3997
|
+
token,
|
|
3998
|
+
from: deps.ownerAddress,
|
|
3999
|
+
to: smartWallet,
|
|
4000
|
+
amount,
|
|
4001
|
+
nonce: nonce.toString(),
|
|
4002
|
+
deadline: deadline.toString(),
|
|
4003
|
+
signature,
|
|
4004
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
4005
|
+
yieldseekerUserId: verification.userId,
|
|
4006
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4007
|
+
} : {}
|
|
4008
|
+
}
|
|
4009
|
+
});
|
|
4010
|
+
return result.txHash;
|
|
4011
|
+
}
|
|
4012
|
+
);
|
|
2500
4013
|
}
|
|
2501
4014
|
|
|
2502
4015
|
// src/lib/sponsored-calls-deposit.ts
|
|
2503
|
-
import { encodeFunctionData, erc20Abi, toHex } from "viem";
|
|
4016
|
+
import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi2, toHex } from "viem";
|
|
2504
4017
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2505
4018
|
var DEFAULT_MAX_POLLS = 30;
|
|
2506
4019
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -2526,7 +4039,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2526
4039
|
}
|
|
2527
4040
|
return new URL(configured, origin).toString();
|
|
2528
4041
|
};
|
|
2529
|
-
return async (smartWallet, chainId, amount) => {
|
|
4042
|
+
return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
|
|
2530
4043
|
const cid = chainId;
|
|
2531
4044
|
const token = deps.tokenAddressByChain[cid];
|
|
2532
4045
|
if (!token) {
|
|
@@ -2542,11 +4055,37 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2542
4055
|
{ chainId }
|
|
2543
4056
|
);
|
|
2544
4057
|
}
|
|
2545
|
-
const data =
|
|
2546
|
-
abi:
|
|
4058
|
+
const data = encodeFunctionData2({
|
|
4059
|
+
abi: erc20Abi2,
|
|
2547
4060
|
functionName: "transfer",
|
|
2548
4061
|
args: [smartWallet, BigInt(amount)]
|
|
2549
4062
|
});
|
|
4063
|
+
let paymasterUrl = absolutePaymasterUrl();
|
|
4064
|
+
if (verification?.agentId === "yieldseeker") {
|
|
4065
|
+
if (chainId !== 8453) {
|
|
4066
|
+
throw new OwneyError(
|
|
4067
|
+
"CHAIN_UNSUPPORTED",
|
|
4068
|
+
`Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
|
|
4069
|
+
);
|
|
4070
|
+
}
|
|
4071
|
+
const { intent } = await postPaymasterIntent({
|
|
4072
|
+
baseUrl: deps.routingApiBaseUrl,
|
|
4073
|
+
apiKey: deps.apiKey,
|
|
4074
|
+
yieldseekerSignature: verification.signature,
|
|
4075
|
+
body: {
|
|
4076
|
+
chainId,
|
|
4077
|
+
token,
|
|
4078
|
+
from: deps.ownerAddress,
|
|
4079
|
+
to: smartWallet,
|
|
4080
|
+
amount,
|
|
4081
|
+
yieldseekerUserId: verification.userId,
|
|
4082
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4083
|
+
}
|
|
4084
|
+
});
|
|
4085
|
+
const url = new URL(paymasterUrl);
|
|
4086
|
+
url.searchParams.set("owneyIntent", intent);
|
|
4087
|
+
paymasterUrl = url.toString();
|
|
4088
|
+
}
|
|
2550
4089
|
const sendResult = await deps.provider.request({
|
|
2551
4090
|
method: "wallet_sendCalls",
|
|
2552
4091
|
params: [
|
|
@@ -2557,7 +4096,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2557
4096
|
atomicRequired: false,
|
|
2558
4097
|
calls: [{ to: token, value: "0x0", data }],
|
|
2559
4098
|
capabilities: {
|
|
2560
|
-
paymasterService: { url:
|
|
4099
|
+
paymasterService: { url: paymasterUrl }
|
|
2561
4100
|
}
|
|
2562
4101
|
}
|
|
2563
4102
|
]
|
|
@@ -2587,7 +4126,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2587
4126
|
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
2588
4127
|
{ chainId, callsId }
|
|
2589
4128
|
);
|
|
2590
|
-
};
|
|
4129
|
+
});
|
|
2591
4130
|
}
|
|
2592
4131
|
|
|
2593
4132
|
// src/client.ts
|
|
@@ -2617,7 +4156,7 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2617
4156
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2618
4157
|
};
|
|
2619
4158
|
var VIEM_CHAIN2 = {
|
|
2620
|
-
8453:
|
|
4159
|
+
8453: base4,
|
|
2621
4160
|
42161: arbitrum2,
|
|
2622
4161
|
1: mainnet2
|
|
2623
4162
|
};
|
|
@@ -2647,6 +4186,7 @@ var OwneySDK = class {
|
|
|
2647
4186
|
orgAgentConfig;
|
|
2648
4187
|
orgAgentConfigPromise = null;
|
|
2649
4188
|
zyfaiRpcUrls;
|
|
4189
|
+
yieldseekerApiBaseUrl;
|
|
2650
4190
|
routingApiBaseUrl;
|
|
2651
4191
|
referralSource;
|
|
2652
4192
|
cachedSponsoredCallback = null;
|
|
@@ -2658,6 +4198,7 @@ var OwneySDK = class {
|
|
|
2658
4198
|
this.apiKey = config.apiKey;
|
|
2659
4199
|
if (config.debug) setOwneyDebug(true);
|
|
2660
4200
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
4201
|
+
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
2661
4202
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2662
4203
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2663
4204
|
this.referralSource = config.referralSource;
|
|
@@ -2760,14 +4301,14 @@ var OwneySDK = class {
|
|
|
2760
4301
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2761
4302
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2762
4303
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2763
|
-
getPublicClient: (cid) =>
|
|
4304
|
+
getPublicClient: (cid) => createPublicClient4({
|
|
2764
4305
|
chain: VIEM_CHAIN2[cid],
|
|
2765
|
-
transport:
|
|
4306
|
+
transport: custom3(provider)
|
|
2766
4307
|
}),
|
|
2767
|
-
getWalletClient: (cid) =>
|
|
4308
|
+
getWalletClient: (cid) => createWalletClient3({
|
|
2768
4309
|
account: owner,
|
|
2769
4310
|
chain: VIEM_CHAIN2[cid],
|
|
2770
|
-
transport:
|
|
4311
|
+
transport: custom3(provider)
|
|
2771
4312
|
})
|
|
2772
4313
|
});
|
|
2773
4314
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -2784,6 +4325,8 @@ var OwneySDK = class {
|
|
|
2784
4325
|
if (!onApproved && cached) return cached;
|
|
2785
4326
|
const provider = this.requireConnectedProvider();
|
|
2786
4327
|
const callback = makeSponsoredCallsCallback({
|
|
4328
|
+
apiKey: this.apiKey,
|
|
4329
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
2787
4330
|
provider,
|
|
2788
4331
|
ownerAddress: this.state.walletAddress,
|
|
2789
4332
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
@@ -2813,14 +4356,14 @@ var OwneySDK = class {
|
|
|
2813
4356
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2814
4357
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2815
4358
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2816
|
-
getPublicClient: (cid) =>
|
|
4359
|
+
getPublicClient: (cid) => createPublicClient4({
|
|
2817
4360
|
chain: VIEM_CHAIN2[cid],
|
|
2818
|
-
transport:
|
|
4361
|
+
transport: custom3(provider)
|
|
2819
4362
|
}),
|
|
2820
|
-
getWalletClient: (cid) =>
|
|
4363
|
+
getWalletClient: (cid) => createWalletClient3({
|
|
2821
4364
|
account: owner,
|
|
2822
4365
|
chain: VIEM_CHAIN2[cid],
|
|
2823
|
-
transport:
|
|
4366
|
+
transport: custom3(provider)
|
|
2824
4367
|
})
|
|
2825
4368
|
});
|
|
2826
4369
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -2848,12 +4391,13 @@ var OwneySDK = class {
|
|
|
2848
4391
|
async getOrgAgentConfig() {
|
|
2849
4392
|
if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
|
|
2850
4393
|
if (!this.orgAgentConfigPromise) {
|
|
2851
|
-
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
4394
|
+
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
4395
|
+
this.apiKey,
|
|
4396
|
+
this.routingApiBaseUrl
|
|
4397
|
+
).then((config) => {
|
|
4398
|
+
this.orgAgentConfig = config;
|
|
4399
|
+
return config;
|
|
4400
|
+
});
|
|
2857
4401
|
}
|
|
2858
4402
|
return this.orgAgentConfigPromise;
|
|
2859
4403
|
}
|
|
@@ -2888,9 +4432,19 @@ var OwneySDK = class {
|
|
|
2888
4432
|
if (this.agents.size > 0) return;
|
|
2889
4433
|
if (!this.initializingAgentsPromise) {
|
|
2890
4434
|
this.initializingAgentsPromise = (async () => {
|
|
2891
|
-
const agentKeys = await fetchAgentKeys(
|
|
4435
|
+
const agentKeys = await fetchAgentKeys(
|
|
4436
|
+
this.apiKey,
|
|
4437
|
+
this.routingApiBaseUrl
|
|
4438
|
+
);
|
|
2892
4439
|
this.disabledAgents.clear();
|
|
2893
|
-
for (const {
|
|
4440
|
+
for (const {
|
|
4441
|
+
key: key2,
|
|
4442
|
+
agent_type,
|
|
4443
|
+
is_enabled,
|
|
4444
|
+
is_configured
|
|
4445
|
+
} of agentKeys) {
|
|
4446
|
+
const configured = is_configured ?? Boolean(key2);
|
|
4447
|
+
if (!configured) continue;
|
|
2894
4448
|
const agent = this.createAgent(agent_type, key2);
|
|
2895
4449
|
if (!agent) continue;
|
|
2896
4450
|
this.agents.set(agent_type, agent);
|
|
@@ -2914,10 +4468,47 @@ var OwneySDK = class {
|
|
|
2914
4468
|
}
|
|
2915
4469
|
createAgent(agentId, key2) {
|
|
2916
4470
|
if (agentId === "zyfai") {
|
|
4471
|
+
if (!key2) return null;
|
|
2917
4472
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
2918
4473
|
}
|
|
4474
|
+
if (agentId === "yieldseeker") {
|
|
4475
|
+
return new YieldseekerAgent(this.apiKey, {
|
|
4476
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
4477
|
+
});
|
|
4478
|
+
}
|
|
2919
4479
|
return null;
|
|
2920
4480
|
}
|
|
4481
|
+
/**
|
|
4482
|
+
* Discover the agents actually returned by routing for this organization.
|
|
4483
|
+
* Consumers should use this instead of hardcoding a global agent roster.
|
|
4484
|
+
*/
|
|
4485
|
+
async getAvailableAgents(options = {}) {
|
|
4486
|
+
await this.ensureAgentsInitialized();
|
|
4487
|
+
const { chainId, asset, includeDisabled = false } = options;
|
|
4488
|
+
const available = [];
|
|
4489
|
+
for (const [id, agent] of this.agents) {
|
|
4490
|
+
const isEnabled = !this.isAgentDisabled(id);
|
|
4491
|
+
if (!includeDisabled && !isEnabled) continue;
|
|
4492
|
+
if (chainId !== void 0 && !agent.supportedChainIds.includes(chainId)) {
|
|
4493
|
+
continue;
|
|
4494
|
+
}
|
|
4495
|
+
if (asset !== void 0) {
|
|
4496
|
+
const supportsAsset = agent.supportedAssets.some(
|
|
4497
|
+
(entry) => (chainId === void 0 || entry.chainId === chainId) && entry.assets.some((candidate) => candidate.symbol === asset)
|
|
4498
|
+
);
|
|
4499
|
+
if (!supportsAsset) {
|
|
4500
|
+
continue;
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4503
|
+
available.push({
|
|
4504
|
+
id,
|
|
4505
|
+
isEnabled,
|
|
4506
|
+
supportedChainIds: agent.supportedChainIds,
|
|
4507
|
+
supportedAssets: agent.supportedAssets
|
|
4508
|
+
});
|
|
4509
|
+
}
|
|
4510
|
+
return available;
|
|
4511
|
+
}
|
|
2921
4512
|
// --- Account lifecycle ---
|
|
2922
4513
|
/**
|
|
2923
4514
|
* Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
|
|
@@ -2929,7 +4520,7 @@ var OwneySDK = class {
|
|
|
2929
4520
|
* If provided, ALL specified agents must support the chainId or the call
|
|
2930
4521
|
* throws before activating any agent.
|
|
2931
4522
|
*/
|
|
2932
|
-
async activateAgent(chainId, agentId) {
|
|
4523
|
+
async activateAgent(chainId, agentId, asset) {
|
|
2933
4524
|
const state = this.requireState();
|
|
2934
4525
|
await this.ensureAgentsInitialized();
|
|
2935
4526
|
if (agentId !== void 0) {
|
|
@@ -2965,9 +4556,7 @@ var OwneySDK = class {
|
|
|
2965
4556
|
this.activeAgents.add(id);
|
|
2966
4557
|
}
|
|
2967
4558
|
state.chainId = chainId;
|
|
2968
|
-
this.activateAgentsInTurn(agents, state, chainId)
|
|
2969
|
-
console.error("activateAgent background init failed:", error);
|
|
2970
|
-
});
|
|
4559
|
+
await this.activateAgentsInTurn(agents, state, chainId, asset);
|
|
2971
4560
|
return;
|
|
2972
4561
|
}
|
|
2973
4562
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -2988,11 +4577,7 @@ var OwneySDK = class {
|
|
|
2988
4577
|
const enabledCompatible = compatible.filter(
|
|
2989
4578
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
2990
4579
|
);
|
|
2991
|
-
this.activateAgentsInTurn(enabledCompatible, state, chainId)
|
|
2992
|
-
(error) => {
|
|
2993
|
-
console.error("activateAgent background init failed:", error);
|
|
2994
|
-
}
|
|
2995
|
-
);
|
|
4580
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
|
|
2996
4581
|
}
|
|
2997
4582
|
/**
|
|
2998
4583
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -3012,11 +4597,11 @@ var OwneySDK = class {
|
|
|
3012
4597
|
* rethrown (matching the previous `Promise.all` rejection) once all agents
|
|
3013
4598
|
* have had a chance to activate.
|
|
3014
4599
|
*/
|
|
3015
|
-
async activateAgentsInTurn(agents, state, chainId) {
|
|
4600
|
+
async activateAgentsInTurn(agents, state, chainId, asset) {
|
|
3016
4601
|
let firstError = null;
|
|
3017
4602
|
for (const agent of agents) {
|
|
3018
4603
|
try {
|
|
3019
|
-
await agent.activateAgent(state, chainId);
|
|
4604
|
+
await agent.activateAgent(state, chainId, asset);
|
|
3020
4605
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
3021
4606
|
} catch (error) {
|
|
3022
4607
|
if (firstError === null) {
|
|
@@ -3144,7 +4729,8 @@ var OwneySDK = class {
|
|
|
3144
4729
|
amount,
|
|
3145
4730
|
asset,
|
|
3146
4731
|
callback
|
|
3147
|
-
)
|
|
4732
|
+
),
|
|
4733
|
+
this.routingApiBaseUrl
|
|
3148
4734
|
);
|
|
3149
4735
|
let approvalAttempted = false;
|
|
3150
4736
|
for (; ; ) {
|
|
@@ -3183,15 +4769,15 @@ var OwneySDK = class {
|
|
|
3183
4769
|
agent,
|
|
3184
4770
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
3185
4771
|
}));
|
|
3186
|
-
const
|
|
4772
|
+
const valid2 = splits.filter(
|
|
3187
4773
|
(s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
|
|
3188
4774
|
);
|
|
3189
|
-
if (
|
|
4775
|
+
if (valid2.length === agents.length) {
|
|
3190
4776
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
3191
4777
|
}
|
|
3192
4778
|
return this.splitDepositAmount(
|
|
3193
4779
|
totalAmount,
|
|
3194
|
-
|
|
4780
|
+
valid2.map((s) => s.agent),
|
|
3195
4781
|
chainId,
|
|
3196
4782
|
asset,
|
|
3197
4783
|
exempt
|
|
@@ -3294,7 +4880,8 @@ var OwneySDK = class {
|
|
|
3294
4880
|
return withFailureReporting(
|
|
3295
4881
|
this.apiKey,
|
|
3296
4882
|
agent.id,
|
|
3297
|
-
() => agent.withdraw(state, chainId, token, amount)
|
|
4883
|
+
() => agent.withdraw(state, chainId, token, amount),
|
|
4884
|
+
this.routingApiBaseUrl
|
|
3298
4885
|
);
|
|
3299
4886
|
}
|
|
3300
4887
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
@@ -3308,7 +4895,12 @@ var OwneySDK = class {
|
|
|
3308
4895
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
3309
4896
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
3310
4897
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3311
|
-
void reportAgentFailure(
|
|
4898
|
+
void reportAgentFailure(
|
|
4899
|
+
this.apiKey,
|
|
4900
|
+
agent.id,
|
|
4901
|
+
code,
|
|
4902
|
+
this.routingApiBaseUrl
|
|
4903
|
+
);
|
|
3312
4904
|
}
|
|
3313
4905
|
}
|
|
3314
4906
|
if (Object.keys(results2).length === 0) {
|
|
@@ -3325,6 +4917,10 @@ var OwneySDK = class {
|
|
|
3325
4917
|
}
|
|
3326
4918
|
const requested = BigInt(amount);
|
|
3327
4919
|
const aggregated = await this.getBalances();
|
|
4920
|
+
const unavailableAgents = eligibleAgents.filter(
|
|
4921
|
+
(agent) => !(agent.id in aggregated.agentBalances)
|
|
4922
|
+
);
|
|
4923
|
+
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
3328
4924
|
const balances = projectAgentBalancesForAsset(
|
|
3329
4925
|
eligibleAgents,
|
|
3330
4926
|
aggregated.agentBalances,
|
|
@@ -3333,7 +4929,18 @@ var OwneySDK = class {
|
|
|
3333
4929
|
assetInfo.decimals
|
|
3334
4930
|
);
|
|
3335
4931
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
3336
|
-
if (totalAvailable
|
|
4932
|
+
if (totalAvailable === 0n && unavailableAgents.length > 0) {
|
|
4933
|
+
throw new OwneyError(
|
|
4934
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
4935
|
+
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
4936
|
+
{
|
|
4937
|
+
asset,
|
|
4938
|
+
unavailableAgents: unavailableAgentIds,
|
|
4939
|
+
agentErrors: aggregated.agentErrors
|
|
4940
|
+
}
|
|
4941
|
+
);
|
|
4942
|
+
}
|
|
4943
|
+
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3337
4944
|
throw new OwneyError(
|
|
3338
4945
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3339
4946
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -3344,6 +4951,7 @@ var OwneySDK = class {
|
|
|
3344
4951
|
}
|
|
3345
4952
|
);
|
|
3346
4953
|
}
|
|
4954
|
+
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
3347
4955
|
const disabledBalances = balances.filter(
|
|
3348
4956
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
3349
4957
|
);
|
|
@@ -3352,7 +4960,7 @@ var OwneySDK = class {
|
|
|
3352
4960
|
);
|
|
3353
4961
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
3354
4962
|
disabledBalances,
|
|
3355
|
-
|
|
4963
|
+
plannedTarget
|
|
3356
4964
|
);
|
|
3357
4965
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
3358
4966
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -3362,7 +4970,9 @@ var OwneySDK = class {
|
|
|
3362
4970
|
}));
|
|
3363
4971
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
3364
4972
|
const results = {};
|
|
3365
|
-
const agentErrors = {
|
|
4973
|
+
const agentErrors = {
|
|
4974
|
+
...aggregated.agentErrors ?? {}
|
|
4975
|
+
};
|
|
3366
4976
|
for (let i = 0; i < plans.length; i++) {
|
|
3367
4977
|
const p = plans[i];
|
|
3368
4978
|
if (p.planned === 0n) continue;
|
|
@@ -3377,7 +4987,12 @@ var OwneySDK = class {
|
|
|
3377
4987
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
3378
4988
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
3379
4989
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3380
|
-
void reportAgentFailure(
|
|
4990
|
+
void reportAgentFailure(
|
|
4991
|
+
this.apiKey,
|
|
4992
|
+
p.agent.id,
|
|
4993
|
+
code,
|
|
4994
|
+
this.routingApiBaseUrl
|
|
4995
|
+
);
|
|
3381
4996
|
const failedAmount = p.planned;
|
|
3382
4997
|
p.planned = 0n;
|
|
3383
4998
|
redistributeShare(plans, i, failedAmount);
|
|
@@ -3404,7 +5019,8 @@ var OwneySDK = class {
|
|
|
3404
5019
|
requested: amount,
|
|
3405
5020
|
withdrawn: withdrawn.toString(),
|
|
3406
5021
|
partialResults: results,
|
|
3407
|
-
agentErrors
|
|
5022
|
+
agentErrors,
|
|
5023
|
+
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3408
5024
|
}
|
|
3409
5025
|
);
|
|
3410
5026
|
}
|
|
@@ -3445,7 +5061,12 @@ var OwneySDK = class {
|
|
|
3445
5061
|
continue;
|
|
3446
5062
|
}
|
|
3447
5063
|
const reason = settledResult.reason;
|
|
3448
|
-
|
|
5064
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
5065
|
+
agentErrors[agentId2] = message;
|
|
5066
|
+
console.error(
|
|
5067
|
+
`[owney-sdk] Balance fetch failed for agent "${agentId2}":`,
|
|
5068
|
+
reason
|
|
5069
|
+
);
|
|
3449
5070
|
}
|
|
3450
5071
|
if (successCount === 0) {
|
|
3451
5072
|
throw new OwneyError(
|
|
@@ -3457,7 +5078,8 @@ var OwneySDK = class {
|
|
|
3457
5078
|
return {
|
|
3458
5079
|
totalBalance: String(totalBalance),
|
|
3459
5080
|
totalBalanceAsset: "usdc",
|
|
3460
|
-
agentBalances: results
|
|
5081
|
+
agentBalances: results,
|
|
5082
|
+
...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
|
|
3461
5083
|
};
|
|
3462
5084
|
}
|
|
3463
5085
|
/**
|
|
@@ -3551,7 +5173,10 @@ var OwneySDK = class {
|
|
|
3551
5173
|
Promise.all(
|
|
3552
5174
|
entries.map(async ([id, agent]) => {
|
|
3553
5175
|
const b = await agent.getBalances(state, chainId);
|
|
3554
|
-
return [
|
|
5176
|
+
return [
|
|
5177
|
+
id,
|
|
5178
|
+
balanceForApyScope(b, chainId, tokenSymbol)
|
|
5179
|
+
];
|
|
3555
5180
|
})
|
|
3556
5181
|
)
|
|
3557
5182
|
]);
|
|
@@ -3579,10 +5204,12 @@ var OwneySDK = class {
|
|
|
3579
5204
|
}
|
|
3580
5205
|
}
|
|
3581
5206
|
const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
|
|
5207
|
+
const history = aggregateApyHistory(results, balances);
|
|
3582
5208
|
return {
|
|
3583
5209
|
totalApy: String(totalApy),
|
|
3584
5210
|
agentApy: results,
|
|
3585
|
-
apyByChainAndAsset
|
|
5211
|
+
apyByChainAndAsset,
|
|
5212
|
+
history
|
|
3586
5213
|
};
|
|
3587
5214
|
}
|
|
3588
5215
|
/**
|
|
@@ -3712,10 +5339,10 @@ var OwneySDK = class {
|
|
|
3712
5339
|
);
|
|
3713
5340
|
}
|
|
3714
5341
|
const provider = this.requireConnectedProvider();
|
|
3715
|
-
const wallet =
|
|
5342
|
+
const wallet = createWalletClient3({
|
|
3716
5343
|
account: state.walletAddress,
|
|
3717
5344
|
chain: VIEM_CHAIN2[chainId],
|
|
3718
|
-
transport:
|
|
5345
|
+
transport: custom3(provider)
|
|
3719
5346
|
});
|
|
3720
5347
|
const hash = await wallet.writeContract({
|
|
3721
5348
|
address: token,
|
|
@@ -3725,9 +5352,9 @@ var OwneySDK = class {
|
|
|
3725
5352
|
account: state.walletAddress,
|
|
3726
5353
|
chain: VIEM_CHAIN2[chainId]
|
|
3727
5354
|
});
|
|
3728
|
-
const publicClient =
|
|
5355
|
+
const publicClient = createPublicClient4({
|
|
3729
5356
|
chain: VIEM_CHAIN2[chainId],
|
|
3730
|
-
transport:
|
|
5357
|
+
transport: custom3(provider)
|
|
3731
5358
|
});
|
|
3732
5359
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3733
5360
|
hash,
|
|
@@ -3763,7 +5390,9 @@ var OwneySDK = class {
|
|
|
3763
5390
|
return this.getAgent(agentId).getAgentApy(days, agentOptions);
|
|
3764
5391
|
}
|
|
3765
5392
|
const results = {};
|
|
3766
|
-
const agentEntries = [...this.agents.entries()]
|
|
5393
|
+
const agentEntries = [...this.agents.entries()].filter(
|
|
5394
|
+
([id]) => !this.isAgentDisabled(id)
|
|
5395
|
+
);
|
|
3767
5396
|
const apyResults = await Promise.all(
|
|
3768
5397
|
agentEntries.map(async ([id, agent]) => {
|
|
3769
5398
|
const apy = await agent.getAgentApy(days, agentOptions);
|
|
@@ -3836,13 +5465,13 @@ var OwneySDK = class {
|
|
|
3836
5465
|
};
|
|
3837
5466
|
|
|
3838
5467
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
3839
|
-
import { getAddress } from "viem";
|
|
3840
|
-
import { SiweMessage } from "siwe";
|
|
5468
|
+
import { getAddress as getAddress3 } from "viem";
|
|
5469
|
+
import { SiweMessage as SiweMessage2 } from "siwe";
|
|
3841
5470
|
import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
|
|
3842
5471
|
|
|
3843
5472
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
3844
|
-
var
|
|
3845
|
-
var
|
|
5473
|
+
var KEY_PREFIX4 = "owney.siwx.session";
|
|
5474
|
+
var storage4 = () => {
|
|
3846
5475
|
if (typeof window === "undefined") return null;
|
|
3847
5476
|
try {
|
|
3848
5477
|
return window.localStorage;
|
|
@@ -3850,8 +5479,8 @@ var storage2 = () => {
|
|
|
3850
5479
|
return null;
|
|
3851
5480
|
}
|
|
3852
5481
|
};
|
|
3853
|
-
var
|
|
3854
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
5482
|
+
var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
|
|
5483
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
|
|
3855
5484
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
3856
5485
|
var readLegacySiwxSession = (store, address) => {
|
|
3857
5486
|
if (!store) return null;
|
|
@@ -3882,17 +5511,17 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
3882
5511
|
};
|
|
3883
5512
|
var readSiwxSession = (address, chainId) => {
|
|
3884
5513
|
if (typeof window === "undefined") return null;
|
|
3885
|
-
const key2 =
|
|
3886
|
-
const store =
|
|
3887
|
-
let
|
|
5514
|
+
const key2 = buildKey3(address);
|
|
5515
|
+
const store = storage4();
|
|
5516
|
+
let raw2 = null;
|
|
3888
5517
|
try {
|
|
3889
|
-
|
|
5518
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
3890
5519
|
} catch {
|
|
3891
|
-
|
|
5520
|
+
raw2 = null;
|
|
3892
5521
|
}
|
|
3893
|
-
if (
|
|
5522
|
+
if (raw2) {
|
|
3894
5523
|
try {
|
|
3895
|
-
return JSON.parse(
|
|
5524
|
+
return JSON.parse(raw2);
|
|
3896
5525
|
} catch {
|
|
3897
5526
|
memorySiwxSessions.delete(key2);
|
|
3898
5527
|
try {
|
|
@@ -3911,18 +5540,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
3911
5540
|
};
|
|
3912
5541
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
3913
5542
|
if (typeof window === "undefined") return;
|
|
3914
|
-
const key2 =
|
|
5543
|
+
const key2 = buildKey3(address);
|
|
3915
5544
|
memorySiwxSessions.set(key2, session);
|
|
3916
|
-
const store =
|
|
5545
|
+
const store = storage4();
|
|
3917
5546
|
try {
|
|
3918
5547
|
store?.setItem(key2, JSON.stringify(session));
|
|
3919
5548
|
} catch {
|
|
3920
5549
|
}
|
|
3921
5550
|
};
|
|
3922
5551
|
var clearSiwxSession = (address, _chainId) => {
|
|
3923
|
-
const key2 =
|
|
5552
|
+
const key2 = buildKey3(address);
|
|
3924
5553
|
memorySiwxSessions.delete(key2);
|
|
3925
|
-
const store =
|
|
5554
|
+
const store = storage4();
|
|
3926
5555
|
try {
|
|
3927
5556
|
store?.removeItem(key2);
|
|
3928
5557
|
} catch {
|
|
@@ -3962,8 +5591,8 @@ function buildSIWXConfig(deps) {
|
|
|
3962
5591
|
statement: STATEMENT,
|
|
3963
5592
|
issuedAt,
|
|
3964
5593
|
toString() {
|
|
3965
|
-
return new
|
|
3966
|
-
address:
|
|
5594
|
+
return new SiweMessage2({
|
|
5595
|
+
address: getAddress3(accountAddress),
|
|
3967
5596
|
chainId: numericChainId(chainId),
|
|
3968
5597
|
domain,
|
|
3969
5598
|
uri,
|
|
@@ -4005,7 +5634,7 @@ function buildSIWXConfig(deps) {
|
|
|
4005
5634
|
const persistSession = async (session) => {
|
|
4006
5635
|
const address = session.data.accountAddress;
|
|
4007
5636
|
const id = numericChainId(session.data.chainId);
|
|
4008
|
-
const message = new
|
|
5637
|
+
const message = new SiweMessage2(session.message);
|
|
4009
5638
|
const login = await post("/auth/login", {
|
|
4010
5639
|
message,
|
|
4011
5640
|
signature: session.signature,
|
|
@@ -4053,6 +5682,7 @@ export {
|
|
|
4053
5682
|
NotConnectedError,
|
|
4054
5683
|
OwneyError,
|
|
4055
5684
|
OwneySDK,
|
|
5685
|
+
YieldseekerAgent,
|
|
4056
5686
|
createOwneySIWX,
|
|
4057
5687
|
setOwneyDebug
|
|
4058
5688
|
};
|