@owney/sdk 0.7.16-beta.1 → 0.7.16-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1124 -532
- package/dist/index.d.cts +23 -6
- package/dist/index.d.ts +23 -6
- package/dist/index.js +1134 -541
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -181,18 +181,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
181
181
|
return explicit;
|
|
182
182
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
183
183
|
}
|
|
184
|
-
function mapDeposit(
|
|
184
|
+
function mapDeposit(raw2) {
|
|
185
185
|
return {
|
|
186
|
-
txHash:
|
|
187
|
-
smartWallet:
|
|
188
|
-
amount:
|
|
186
|
+
txHash: raw2.txHash,
|
|
187
|
+
smartWallet: raw2.smartWallet,
|
|
188
|
+
amount: raw2.amount
|
|
189
189
|
};
|
|
190
190
|
}
|
|
191
|
-
function mapWithdraw(
|
|
191
|
+
function mapWithdraw(raw2) {
|
|
192
192
|
return {
|
|
193
|
-
txHash:
|
|
194
|
-
type:
|
|
195
|
-
amount:
|
|
193
|
+
txHash: raw2.txHash,
|
|
194
|
+
type: raw2.type,
|
|
195
|
+
amount: raw2.amount
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
198
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -211,10 +211,10 @@ function resolveChainId(chain) {
|
|
|
211
211
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
212
212
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
213
213
|
}
|
|
214
|
-
function mapPendingAllocations(
|
|
215
|
-
if (!Array.isArray(
|
|
214
|
+
function mapPendingAllocations(raw2) {
|
|
215
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
216
216
|
const pending = [];
|
|
217
|
-
for (const entry of
|
|
217
|
+
for (const entry of raw2) {
|
|
218
218
|
if (typeof entry !== "object" || entry === null) continue;
|
|
219
219
|
const e = entry;
|
|
220
220
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -237,8 +237,8 @@ function mapPendingAllocations(raw) {
|
|
|
237
237
|
}
|
|
238
238
|
return pending.length > 0 ? pending : void 0;
|
|
239
239
|
}
|
|
240
|
-
function mapBalances(
|
|
241
|
-
const portfolio =
|
|
240
|
+
function mapBalances(raw2, _chainId, smartWallet) {
|
|
241
|
+
const portfolio = raw2.portfolio;
|
|
242
242
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
243
243
|
let totalBalance = 0;
|
|
244
244
|
const tokens = [];
|
|
@@ -307,8 +307,8 @@ function sumTokenValues(tokens) {
|
|
|
307
307
|
function sumTokenEarnings(tokens) {
|
|
308
308
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
309
309
|
}
|
|
310
|
-
function mapEarnings(
|
|
311
|
-
const totalEarningsByChain =
|
|
310
|
+
function mapEarnings(raw2, smartWallet) {
|
|
311
|
+
const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
|
|
312
312
|
const tokens = [];
|
|
313
313
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
314
314
|
totalEarningsByChain
|
|
@@ -327,22 +327,22 @@ function mapEarnings(raw, smartWallet) {
|
|
|
327
327
|
return {
|
|
328
328
|
smartWallet,
|
|
329
329
|
lifetimeEarnings: sumTokenEarnings(
|
|
330
|
-
|
|
330
|
+
raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
|
|
331
331
|
),
|
|
332
332
|
tokens
|
|
333
333
|
};
|
|
334
334
|
}
|
|
335
|
-
function mapWeightedApyByChain(
|
|
336
|
-
if (!
|
|
335
|
+
function mapWeightedApyByChain(raw2) {
|
|
336
|
+
if (!raw2) return void 0;
|
|
337
337
|
const out = {};
|
|
338
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
338
|
+
for (const [chainKey, tokenApy] of Object.entries(raw2)) {
|
|
339
339
|
const chainId = Number(chainKey);
|
|
340
340
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
341
341
|
const perAsset = {};
|
|
342
342
|
for (const [symbol, apy] of Object.entries(tokenApy)) {
|
|
343
|
-
const
|
|
344
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
345
|
-
perAsset[
|
|
343
|
+
const token = symbol;
|
|
344
|
+
if (!SUPPORTED_TOKENS.includes(token)) continue;
|
|
345
|
+
perAsset[token] = apy;
|
|
346
346
|
}
|
|
347
347
|
if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
|
|
348
348
|
}
|
|
@@ -361,15 +361,15 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
361
361
|
}
|
|
362
362
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
363
363
|
}
|
|
364
|
-
function mapApyHistory(
|
|
365
|
-
const history = Object.entries(
|
|
364
|
+
function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
365
|
+
const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
|
|
366
366
|
date,
|
|
367
367
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
|
|
368
368
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
369
369
|
return {
|
|
370
|
-
walletAddress:
|
|
371
|
-
weightedApyAfterFee:
|
|
372
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
370
|
+
walletAddress: raw2.walletAddress,
|
|
371
|
+
weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
|
|
372
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
|
|
373
373
|
history
|
|
374
374
|
};
|
|
375
375
|
}
|
|
@@ -461,23 +461,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
461
461
|
};
|
|
462
462
|
});
|
|
463
463
|
}
|
|
464
|
-
function mapUserProfile(
|
|
464
|
+
function mapUserProfile(raw2, userAddress) {
|
|
465
465
|
return {
|
|
466
466
|
address: userAddress,
|
|
467
|
-
smartWallet:
|
|
468
|
-
chains:
|
|
469
|
-
strategy:
|
|
470
|
-
hasActiveSessionKey:
|
|
471
|
-
protocols:
|
|
472
|
-
splitting:
|
|
473
|
-
minSplits:
|
|
467
|
+
smartWallet: raw2.smartWallet || "",
|
|
468
|
+
chains: raw2.chains || [],
|
|
469
|
+
strategy: raw2.strategy,
|
|
470
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey || false,
|
|
471
|
+
protocols: raw2.protocols || [],
|
|
472
|
+
splitting: raw2.splitting,
|
|
473
|
+
minSplits: raw2.minSplits
|
|
474
474
|
};
|
|
475
475
|
}
|
|
476
|
-
function mapApyByStrategy(
|
|
476
|
+
function mapApyByStrategy(raw2) {
|
|
477
477
|
const apyPerAsset = {};
|
|
478
478
|
let apySum = 0;
|
|
479
479
|
let apyCount = 0;
|
|
480
|
-
for (const entry of
|
|
480
|
+
for (const entry of raw2.data) {
|
|
481
481
|
const supported = SupportedAssets.find(
|
|
482
482
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
483
483
|
);
|
|
@@ -511,23 +511,23 @@ function computeAllocationApy(positions) {
|
|
|
511
511
|
weightedSum += apy * value;
|
|
512
512
|
totalValue += value;
|
|
513
513
|
const chainId = resolveChainId(p.chain);
|
|
514
|
-
const
|
|
514
|
+
const token = p.asset;
|
|
515
515
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
516
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
516
|
+
if (!SUPPORTED_TOKENS.includes(token)) continue;
|
|
517
517
|
const perAsset = buckets[chainId] ?? {};
|
|
518
|
-
const cell = perAsset[
|
|
518
|
+
const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
|
|
519
519
|
cell.weightedSum += apy * value;
|
|
520
520
|
cell.value += value;
|
|
521
|
-
perAsset[
|
|
521
|
+
perAsset[token] = cell;
|
|
522
522
|
buckets[chainId] = perAsset;
|
|
523
523
|
}
|
|
524
524
|
const apyByChainAndAsset = {};
|
|
525
525
|
for (const [chainKey, perAsset] of Object.entries(buckets)) {
|
|
526
526
|
const chainId = Number(chainKey);
|
|
527
527
|
const out = {};
|
|
528
|
-
for (const [
|
|
528
|
+
for (const [token, cell] of Object.entries(perAsset ?? {})) {
|
|
529
529
|
if (cell && cell.value > 0) {
|
|
530
|
-
out[
|
|
530
|
+
out[token] = cell.weightedSum / cell.value;
|
|
531
531
|
}
|
|
532
532
|
}
|
|
533
533
|
if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
|
|
@@ -541,9 +541,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
|
|
|
541
541
|
function topicToAddress(topic) {
|
|
542
542
|
return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
|
|
543
543
|
}
|
|
544
|
-
function extractWithdrawnAmount(logs, recipient,
|
|
544
|
+
function extractWithdrawnAmount(logs, recipient, token, decimals) {
|
|
545
545
|
const wantRecipient = recipient.toLowerCase();
|
|
546
|
-
const wantToken =
|
|
546
|
+
const wantToken = token.toLowerCase();
|
|
547
547
|
let total = 0n;
|
|
548
548
|
let matched = false;
|
|
549
549
|
for (const log of logs) {
|
|
@@ -569,11 +569,11 @@ var InvalidHistoryCursorError = class extends Error {
|
|
|
569
569
|
function encodeHistoryCursor(payload) {
|
|
570
570
|
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
|
|
571
571
|
}
|
|
572
|
-
function decodeHistoryCursor(
|
|
573
|
-
if (!
|
|
572
|
+
function decodeHistoryCursor(token) {
|
|
573
|
+
if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
|
|
574
574
|
let parsed;
|
|
575
575
|
try {
|
|
576
|
-
const json = Buffer.from(
|
|
576
|
+
const json = Buffer.from(token, "base64").toString("utf8");
|
|
577
577
|
parsed = JSON.parse(json);
|
|
578
578
|
} catch {
|
|
579
579
|
throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
|
|
@@ -596,8 +596,8 @@ var storage = () => {
|
|
|
596
596
|
};
|
|
597
597
|
var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
|
|
598
598
|
var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
|
|
599
|
-
var isJwtExpired = (
|
|
600
|
-
const parts =
|
|
599
|
+
var isJwtExpired = (token) => {
|
|
600
|
+
const parts = token.split(".");
|
|
601
601
|
if (parts.length !== 3) return false;
|
|
602
602
|
try {
|
|
603
603
|
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -644,15 +644,15 @@ var readSession = (address, _chainId) => {
|
|
|
644
644
|
if (typeof window === "undefined") return null;
|
|
645
645
|
const key2 = buildKey(address);
|
|
646
646
|
const store = storage();
|
|
647
|
-
let
|
|
647
|
+
let raw2 = null;
|
|
648
648
|
try {
|
|
649
|
-
|
|
649
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
650
650
|
} catch {
|
|
651
|
-
|
|
651
|
+
raw2 = null;
|
|
652
652
|
}
|
|
653
|
-
if (
|
|
653
|
+
if (raw2) {
|
|
654
654
|
try {
|
|
655
|
-
const parsed = JSON.parse(
|
|
655
|
+
const parsed = JSON.parse(raw2);
|
|
656
656
|
if (isFreshSession(parsed)) return parsed;
|
|
657
657
|
} catch {
|
|
658
658
|
}
|
|
@@ -820,8 +820,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
820
820
|
}
|
|
821
821
|
return result;
|
|
822
822
|
}
|
|
823
|
-
function flattenAvailablePools(
|
|
824
|
-
const byChain =
|
|
823
|
+
function flattenAvailablePools(raw2) {
|
|
824
|
+
const byChain = raw2 ?? {};
|
|
825
825
|
const names = [];
|
|
826
826
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
827
827
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -1332,8 +1332,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1332
1332
|
const poolResults = await Promise.all(
|
|
1333
1333
|
universe.map(async (protocol) => {
|
|
1334
1334
|
try {
|
|
1335
|
-
const
|
|
1336
|
-
return [protocol.id, flattenAvailablePools(
|
|
1335
|
+
const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1336
|
+
return [protocol.id, flattenAvailablePools(raw2)];
|
|
1337
1337
|
} catch (error) {
|
|
1338
1338
|
console.warn(
|
|
1339
1339
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1399,14 +1399,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1399
1399
|
async readWalletState(ownerAddress) {
|
|
1400
1400
|
try {
|
|
1401
1401
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1402
|
-
const
|
|
1402
|
+
const raw2 = portfolio;
|
|
1403
1403
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1404
|
-
predeployed:
|
|
1405
|
-
hasActiveSessionKey:
|
|
1404
|
+
predeployed: raw2?.predeployed,
|
|
1405
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1406
1406
|
});
|
|
1407
1407
|
return {
|
|
1408
|
-
predeployed:
|
|
1409
|
-
hasActiveSessionKey:
|
|
1408
|
+
predeployed: raw2?.predeployed,
|
|
1409
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1410
1410
|
};
|
|
1411
1411
|
} catch (error) {
|
|
1412
1412
|
console.warn(
|
|
@@ -1686,48 +1686,48 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1686
1686
|
return { txHash, smartWallet, amount };
|
|
1687
1687
|
}
|
|
1688
1688
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1689
|
-
const
|
|
1689
|
+
const raw2 = await this.sdk.depositFunds(
|
|
1690
1690
|
this.getAddress(),
|
|
1691
1691
|
validChainId,
|
|
1692
1692
|
amount,
|
|
1693
1693
|
asset,
|
|
1694
1694
|
"aggressive"
|
|
1695
1695
|
);
|
|
1696
|
-
return mapDeposit(
|
|
1696
|
+
return mapDeposit(raw2);
|
|
1697
1697
|
} catch (error) {
|
|
1698
1698
|
throw error;
|
|
1699
1699
|
}
|
|
1700
1700
|
}
|
|
1701
1701
|
// --- IAgent: Fund operations ---
|
|
1702
|
-
async withdraw(state, chainId,
|
|
1702
|
+
async withdraw(state, chainId, token, amount) {
|
|
1703
1703
|
const validChainId = isValidChainId(chainId);
|
|
1704
1704
|
await this.ensureConnected(state, validChainId);
|
|
1705
|
-
const
|
|
1705
|
+
const raw2 = await this.sdk.withdrawFunds(
|
|
1706
1706
|
this.getAddress(),
|
|
1707
1707
|
validChainId,
|
|
1708
1708
|
amount,
|
|
1709
|
-
|
|
1709
|
+
token
|
|
1710
1710
|
);
|
|
1711
|
-
if (!
|
|
1711
|
+
if (!raw2.success) {
|
|
1712
1712
|
throw new OwneyError(
|
|
1713
1713
|
"WITHDRAW_FAILED",
|
|
1714
|
-
|
|
1715
|
-
{ chainId: validChainId, token
|
|
1714
|
+
raw2.message || "Zyfai withdraw failed.",
|
|
1715
|
+
{ chainId: validChainId, token, amount, response: raw2 },
|
|
1716
1716
|
this.id
|
|
1717
1717
|
);
|
|
1718
1718
|
}
|
|
1719
|
-
return mapWithdraw(
|
|
1719
|
+
return mapWithdraw(raw2);
|
|
1720
1720
|
}
|
|
1721
1721
|
// --- IAgent: Portfolio reads ---
|
|
1722
1722
|
async getBalances(state, chainId) {
|
|
1723
1723
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1724
|
-
const
|
|
1725
|
-
return mapBalances(
|
|
1724
|
+
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
1725
|
+
return mapBalances(raw2, validChainId, smartWallet);
|
|
1726
1726
|
}
|
|
1727
1727
|
async getEarnings(state, chainId) {
|
|
1728
1728
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1729
|
-
const
|
|
1730
|
-
return mapEarnings(
|
|
1729
|
+
const raw2 = await this.sdk.getOnchainEarnings(smartWallet);
|
|
1730
|
+
return mapEarnings(raw2, smartWallet);
|
|
1731
1731
|
}
|
|
1732
1732
|
async refreshEarnings(state, chainId) {
|
|
1733
1733
|
if (this.earningsRefreshInFlight) return this.earningsRefreshInFlight;
|
|
@@ -1747,8 +1747,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1747
1747
|
}
|
|
1748
1748
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1749
1749
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1750
|
-
const
|
|
1751
|
-
return mapApyHistory(
|
|
1750
|
+
const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
1751
|
+
return mapApyHistory(raw2, chainId, tokenSymbol);
|
|
1752
1752
|
}
|
|
1753
1753
|
/**
|
|
1754
1754
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -1783,7 +1783,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1783
1783
|
const matched = [];
|
|
1784
1784
|
let backendExhausted = false;
|
|
1785
1785
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
1786
|
-
const
|
|
1786
|
+
const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
1787
1787
|
limit: backendPageSize,
|
|
1788
1788
|
offset,
|
|
1789
1789
|
fromDate: options?.fromDate,
|
|
@@ -1794,13 +1794,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1794
1794
|
// asset's rows and handing back a page that filters to nothing.
|
|
1795
1795
|
assetType
|
|
1796
1796
|
});
|
|
1797
|
-
|
|
1797
|
+
raw2.data.forEach((entry, idx) => {
|
|
1798
1798
|
if (entry.chainId === validChainId) {
|
|
1799
1799
|
matched.push({ entry, rawIdx: offset + idx });
|
|
1800
1800
|
}
|
|
1801
1801
|
});
|
|
1802
|
-
offset +=
|
|
1803
|
-
if (
|
|
1802
|
+
offset += raw2.data.length;
|
|
1803
|
+
if (raw2.data.length < backendPageSize) {
|
|
1804
1804
|
backendExhausted = true;
|
|
1805
1805
|
break;
|
|
1806
1806
|
}
|
|
@@ -1822,18 +1822,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1822
1822
|
}
|
|
1823
1823
|
async getUserProfile(state, chainId) {
|
|
1824
1824
|
await this.connectAuth(state, chainId);
|
|
1825
|
-
const
|
|
1825
|
+
const raw2 = await this.sdk.getUserDetails();
|
|
1826
1826
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
1827
1827
|
asset: "USDC (default \u2014 no asset passed)",
|
|
1828
|
-
splitting:
|
|
1829
|
-
minSplits:
|
|
1830
|
-
strategy:
|
|
1831
|
-
chains:
|
|
1832
|
-
protocolCount:
|
|
1833
|
-
hasActiveSessionKey:
|
|
1834
|
-
smartWallet:
|
|
1828
|
+
splitting: raw2.splitting,
|
|
1829
|
+
minSplits: raw2.minSplits,
|
|
1830
|
+
strategy: raw2.strategy,
|
|
1831
|
+
chains: raw2.chains,
|
|
1832
|
+
protocolCount: raw2.protocols?.length,
|
|
1833
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey,
|
|
1834
|
+
smartWallet: raw2.smartWallet
|
|
1835
1835
|
});
|
|
1836
|
-
return mapUserProfile(
|
|
1836
|
+
return mapUserProfile(raw2, this.connectedAddress);
|
|
1837
1837
|
}
|
|
1838
1838
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
1839
1839
|
await this.connectAuth(state, chainId);
|
|
@@ -1852,14 +1852,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1852
1852
|
}
|
|
1853
1853
|
// --- IAgent: Discovery (no wallet required) ---
|
|
1854
1854
|
async getAgentApy(days, options) {
|
|
1855
|
-
const
|
|
1855
|
+
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
1856
1856
|
false,
|
|
1857
1857
|
DayFilterMapping[days],
|
|
1858
1858
|
"aggressive",
|
|
1859
1859
|
options?.chainId,
|
|
1860
1860
|
options?.tokenSymbol
|
|
1861
1861
|
);
|
|
1862
|
-
return mapApyByStrategy(
|
|
1862
|
+
return mapApyByStrategy(raw2);
|
|
1863
1863
|
}
|
|
1864
1864
|
};
|
|
1865
1865
|
|
|
@@ -1925,10 +1925,10 @@ function buildTransferWithAuthorizationTypedData(input) {
|
|
|
1925
1925
|
message: input.message
|
|
1926
1926
|
};
|
|
1927
1927
|
}
|
|
1928
|
-
async function readTokenMeta(publicClient,
|
|
1928
|
+
async function readTokenMeta(publicClient, token) {
|
|
1929
1929
|
const [tokenName, tokenVersion] = await Promise.all([
|
|
1930
|
-
publicClient.readContract({ address:
|
|
1931
|
-
publicClient.readContract({ address:
|
|
1930
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
1931
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
1932
1932
|
]);
|
|
1933
1933
|
return { tokenName, tokenVersion };
|
|
1934
1934
|
}
|
|
@@ -1940,6 +1940,45 @@ function randomAuthNonce() {
|
|
|
1940
1940
|
|
|
1941
1941
|
// src/lib/sponsor-client.ts
|
|
1942
1942
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1943
|
+
async function postPaymasterIntent(input) {
|
|
1944
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1945
|
+
let res;
|
|
1946
|
+
try {
|
|
1947
|
+
res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
|
|
1948
|
+
method: "POST",
|
|
1949
|
+
headers: {
|
|
1950
|
+
"content-type": "application/json",
|
|
1951
|
+
"x-owney-api-key": input.apiKey,
|
|
1952
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
1953
|
+
},
|
|
1954
|
+
body: JSON.stringify(input.body)
|
|
1955
|
+
});
|
|
1956
|
+
} catch (networkError) {
|
|
1957
|
+
throw new OwneyError(
|
|
1958
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1959
|
+
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1960
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
1961
|
+
);
|
|
1962
|
+
}
|
|
1963
|
+
const text = await res.text();
|
|
1964
|
+
let parsed = null;
|
|
1965
|
+
try {
|
|
1966
|
+
parsed = JSON.parse(text);
|
|
1967
|
+
} catch {
|
|
1968
|
+
}
|
|
1969
|
+
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
1970
|
+
throw new OwneyError(
|
|
1971
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1972
|
+
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1973
|
+
{
|
|
1974
|
+
statusCode: res.status,
|
|
1975
|
+
responseBody: text.slice(0, 500),
|
|
1976
|
+
safeToFallback: true
|
|
1977
|
+
}
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
return parsed.data;
|
|
1981
|
+
}
|
|
1943
1982
|
async function postSponsorTransferAuth(input) {
|
|
1944
1983
|
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1945
1984
|
let res;
|
|
@@ -1949,7 +1988,7 @@ async function postSponsorTransferAuth(input) {
|
|
|
1949
1988
|
headers: {
|
|
1950
1989
|
"content-type": "application/json",
|
|
1951
1990
|
"x-owney-api-key": input.apiKey,
|
|
1952
|
-
...input.yieldseekerSignature ? {
|
|
1991
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
1953
1992
|
},
|
|
1954
1993
|
body: JSON.stringify(input.body)
|
|
1955
1994
|
});
|
|
@@ -1990,7 +2029,7 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
1990
2029
|
headers: {
|
|
1991
2030
|
"content-type": "application/json",
|
|
1992
2031
|
"x-owney-api-key": input.apiKey,
|
|
1993
|
-
...input.yieldseekerSignature ? {
|
|
2032
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
1994
2033
|
},
|
|
1995
2034
|
body: JSON.stringify(input.body)
|
|
1996
2035
|
});
|
|
@@ -2118,17 +2157,17 @@ function randomPermit2Nonce() {
|
|
|
2118
2157
|
globalThis.crypto.getRandomValues(bytes);
|
|
2119
2158
|
return BigInt((0, import_viem3.bytesToHex)(bytes));
|
|
2120
2159
|
}
|
|
2121
|
-
async function readPermit2Allowance(publicClient,
|
|
2160
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2122
2161
|
return publicClient.readContract({
|
|
2123
|
-
address:
|
|
2162
|
+
address: token,
|
|
2124
2163
|
abi: ERC20_ALLOWANCE_ABI,
|
|
2125
2164
|
functionName: "allowance",
|
|
2126
2165
|
args: [owner, PERMIT2_ADDRESS]
|
|
2127
2166
|
});
|
|
2128
2167
|
}
|
|
2129
|
-
async function readErc20Balance(publicClient,
|
|
2168
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
2130
2169
|
return publicClient.readContract({
|
|
2131
|
-
address:
|
|
2170
|
+
address: token,
|
|
2132
2171
|
abi: ERC20_ALLOWANCE_ABI,
|
|
2133
2172
|
functionName: "balanceOf",
|
|
2134
2173
|
args: [owner]
|
|
@@ -2160,8 +2199,8 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2160
2199
|
return makeVerificationAwareDepositCallback(
|
|
2161
2200
|
async (smartWallet, chainId, amount, verification) => {
|
|
2162
2201
|
const cid = chainId;
|
|
2163
|
-
const
|
|
2164
|
-
if (!
|
|
2202
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2203
|
+
if (!token) {
|
|
2165
2204
|
throw new OwneyError(
|
|
2166
2205
|
"CHAIN_UNSUPPORTED",
|
|
2167
2206
|
`No sponsored token configured for chain ${chainId}`
|
|
@@ -2171,12 +2210,12 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2171
2210
|
const wallet = deps.getWalletClient(cid);
|
|
2172
2211
|
await ensureWalletOnChain(pub, wallet, cid);
|
|
2173
2212
|
try {
|
|
2174
|
-
const balance = await readErc20Balance(pub,
|
|
2213
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2175
2214
|
if (balance < BigInt(amount)) {
|
|
2176
2215
|
throw new OwneyError(
|
|
2177
2216
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2178
2217
|
"Insufficient balance for this deposit.",
|
|
2179
|
-
{ token
|
|
2218
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2180
2219
|
);
|
|
2181
2220
|
}
|
|
2182
2221
|
} catch (err) {
|
|
@@ -2186,14 +2225,14 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2186
2225
|
err instanceof Error ? err.message : String(err)
|
|
2187
2226
|
);
|
|
2188
2227
|
}
|
|
2189
|
-
const { tokenName, tokenVersion } = await readTokenMeta(pub,
|
|
2228
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2190
2229
|
const validAfter = 0n;
|
|
2191
2230
|
const validBefore = BigInt(
|
|
2192
2231
|
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2193
2232
|
);
|
|
2194
2233
|
const nonce = randomAuthNonce();
|
|
2195
2234
|
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2196
|
-
token
|
|
2235
|
+
token,
|
|
2197
2236
|
chainId: cid,
|
|
2198
2237
|
tokenName,
|
|
2199
2238
|
tokenVersion,
|
|
@@ -2217,7 +2256,7 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2217
2256
|
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2218
2257
|
body: {
|
|
2219
2258
|
chainId: cid,
|
|
2220
|
-
token
|
|
2259
|
+
token,
|
|
2221
2260
|
from: deps.ownerAddress,
|
|
2222
2261
|
to: smartWallet,
|
|
2223
2262
|
value: amount,
|
|
@@ -2226,7 +2265,11 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2226
2265
|
nonce,
|
|
2227
2266
|
authSignature,
|
|
2228
2267
|
tokenName,
|
|
2229
|
-
tokenVersion
|
|
2268
|
+
tokenVersion,
|
|
2269
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
2270
|
+
yieldseekerUserId: verification.userId,
|
|
2271
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
2272
|
+
} : {}
|
|
2230
2273
|
}
|
|
2231
2274
|
});
|
|
2232
2275
|
return result.txHash;
|
|
@@ -2240,7 +2283,11 @@ var import_viem4 = require("viem");
|
|
|
2240
2283
|
var import_chains2 = require("viem/chains");
|
|
2241
2284
|
|
|
2242
2285
|
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2243
|
-
var KEY_PREFIX2 = "owney.yieldseeker.session";
|
|
2286
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session.v4";
|
|
2287
|
+
var INVALIDATED_KEY_PREFIXES = [
|
|
2288
|
+
"owney.yieldseeker.session",
|
|
2289
|
+
"owney.yieldseeker.session.v3"
|
|
2290
|
+
];
|
|
2244
2291
|
var storage2 = () => {
|
|
2245
2292
|
if (typeof window === "undefined") return null;
|
|
2246
2293
|
try {
|
|
@@ -2250,6 +2297,18 @@ var storage2 = () => {
|
|
|
2250
2297
|
}
|
|
2251
2298
|
};
|
|
2252
2299
|
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2300
|
+
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2301
|
+
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2302
|
+
);
|
|
2303
|
+
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2304
|
+
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2305
|
+
memorySessions2.delete(key2);
|
|
2306
|
+
try {
|
|
2307
|
+
store?.removeItem(key2);
|
|
2308
|
+
} catch {
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
};
|
|
2253
2312
|
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2254
2313
|
var isValidSession = (session) => {
|
|
2255
2314
|
if (!session?.token) return false;
|
|
@@ -2264,15 +2323,16 @@ var readYieldseekerSession = (address, chainId) => {
|
|
|
2264
2323
|
if (typeof window === "undefined") return null;
|
|
2265
2324
|
const key2 = buildKey2(address, chainId);
|
|
2266
2325
|
const store = storage2();
|
|
2267
|
-
|
|
2326
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2327
|
+
let raw2 = null;
|
|
2268
2328
|
try {
|
|
2269
|
-
|
|
2329
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
2270
2330
|
} catch {
|
|
2271
|
-
|
|
2331
|
+
raw2 = null;
|
|
2272
2332
|
}
|
|
2273
|
-
if (
|
|
2333
|
+
if (raw2) {
|
|
2274
2334
|
try {
|
|
2275
|
-
const parsed = JSON.parse(
|
|
2335
|
+
const parsed = JSON.parse(raw2);
|
|
2276
2336
|
if (isValidSession(parsed)) return parsed.token;
|
|
2277
2337
|
} catch {
|
|
2278
2338
|
}
|
|
@@ -2288,9 +2348,9 @@ var readYieldseekerSession = (address, chainId) => {
|
|
|
2288
2348
|
if (cached) memorySessions2.delete(key2);
|
|
2289
2349
|
return null;
|
|
2290
2350
|
};
|
|
2291
|
-
var writeYieldseekerSession = (address, chainId,
|
|
2351
|
+
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2292
2352
|
if (typeof window === "undefined") return;
|
|
2293
|
-
const session = { token
|
|
2353
|
+
const session = { token };
|
|
2294
2354
|
if (!isValidSession(session)) return;
|
|
2295
2355
|
const key2 = buildKey2(address, chainId);
|
|
2296
2356
|
memorySessions2.set(key2, session);
|
|
@@ -2304,6 +2364,7 @@ var clearYieldseekerSession = (address, chainId) => {
|
|
|
2304
2364
|
const key2 = buildKey2(address, chainId);
|
|
2305
2365
|
memorySessions2.delete(key2);
|
|
2306
2366
|
const store = storage2();
|
|
2367
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2307
2368
|
try {
|
|
2308
2369
|
store?.removeItem(key2);
|
|
2309
2370
|
} catch {
|
|
@@ -2322,8 +2383,8 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
|
2322
2383
|
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2323
2384
|
}).prepareMessage();
|
|
2324
2385
|
}
|
|
2325
|
-
function encodeYieldseekerAuthToken(
|
|
2326
|
-
const bytes = new TextEncoder().encode(JSON.stringify(
|
|
2386
|
+
function encodeYieldseekerAuthToken(token) {
|
|
2387
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2327
2388
|
let binary = "";
|
|
2328
2389
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2329
2390
|
return btoa(binary);
|
|
@@ -2352,10 +2413,10 @@ var YieldseekerAuth = class {
|
|
|
2352
2413
|
}
|
|
2353
2414
|
const inFlight = this.pending.get(key2);
|
|
2354
2415
|
if (inFlight) return inFlight;
|
|
2355
|
-
const request = this.sign(state, chainId).then((
|
|
2356
|
-
this.tokens.set(key2,
|
|
2357
|
-
writeYieldseekerSession(scope.address, scope.chainId,
|
|
2358
|
-
return
|
|
2416
|
+
const request = this.sign(state, chainId).then((token) => {
|
|
2417
|
+
this.tokens.set(key2, token);
|
|
2418
|
+
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2419
|
+
return token;
|
|
2359
2420
|
});
|
|
2360
2421
|
this.pending.set(key2, request);
|
|
2361
2422
|
try {
|
|
@@ -2406,6 +2467,72 @@ var YieldseekerAuth = class {
|
|
|
2406
2467
|
}
|
|
2407
2468
|
};
|
|
2408
2469
|
|
|
2470
|
+
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2471
|
+
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2472
|
+
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2473
|
+
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2474
|
+
var storage3 = () => {
|
|
2475
|
+
if (typeof window === "undefined") return null;
|
|
2476
|
+
try {
|
|
2477
|
+
return window.localStorage;
|
|
2478
|
+
} catch {
|
|
2479
|
+
return null;
|
|
2480
|
+
}
|
|
2481
|
+
};
|
|
2482
|
+
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2483
|
+
function valid(value, walletAddress, chainId, now) {
|
|
2484
|
+
return Boolean(
|
|
2485
|
+
value && typeof value.userId === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(value.userId) && value.walletAddress.toLowerCase() === walletAddress.toLowerCase() && value.chainId === chainId && Number.isSafeInteger(value.expiresAt) && value.expiresAt > now
|
|
2486
|
+
);
|
|
2487
|
+
}
|
|
2488
|
+
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2489
|
+
if (typeof window === "undefined") return null;
|
|
2490
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2491
|
+
const store = storage3();
|
|
2492
|
+
let parsed = null;
|
|
2493
|
+
try {
|
|
2494
|
+
const raw2 = store?.getItem(key2);
|
|
2495
|
+
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2496
|
+
} catch {
|
|
2497
|
+
parsed = null;
|
|
2498
|
+
}
|
|
2499
|
+
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2500
|
+
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2501
|
+
memoryIdentities.set(key2, candidate);
|
|
2502
|
+
return { userId: candidate.userId };
|
|
2503
|
+
}
|
|
2504
|
+
memoryIdentities.delete(key2);
|
|
2505
|
+
try {
|
|
2506
|
+
store?.removeItem(key2);
|
|
2507
|
+
} catch {
|
|
2508
|
+
}
|
|
2509
|
+
return null;
|
|
2510
|
+
}
|
|
2511
|
+
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2512
|
+
if (typeof window === "undefined") return;
|
|
2513
|
+
const identity = {
|
|
2514
|
+
userId,
|
|
2515
|
+
walletAddress,
|
|
2516
|
+
chainId,
|
|
2517
|
+
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2518
|
+
};
|
|
2519
|
+
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2520
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2521
|
+
memoryIdentities.set(key2, identity);
|
|
2522
|
+
try {
|
|
2523
|
+
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2524
|
+
} catch {
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2528
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2529
|
+
memoryIdentities.delete(key2);
|
|
2530
|
+
try {
|
|
2531
|
+
storage3()?.removeItem(key2);
|
|
2532
|
+
} catch {
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2409
2536
|
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2410
2537
|
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2411
2538
|
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
@@ -2456,7 +2583,7 @@ var YieldseekerApiClient = class {
|
|
|
2456
2583
|
headers: {
|
|
2457
2584
|
"Content-Type": "application/json",
|
|
2458
2585
|
"x-owney-api-key": this.owneyApiKey,
|
|
2459
|
-
...options.signature ? {
|
|
2586
|
+
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2460
2587
|
},
|
|
2461
2588
|
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2462
2589
|
signal: controller.signal
|
|
@@ -2490,24 +2617,20 @@ var YieldseekerApiClient = class {
|
|
|
2490
2617
|
|
|
2491
2618
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2492
2619
|
var import_viem5 = require("viem");
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
);
|
|
2504
|
-
}
|
|
2505
|
-
try {
|
|
2506
|
-
return (0, import_viem5.formatUnits)(BigInt(value), decimals);
|
|
2507
|
-
} catch {
|
|
2508
|
-
return invalid("history", `invalid rebalance amount ${value}`);
|
|
2620
|
+
|
|
2621
|
+
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2622
|
+
var YIELDSEEKER_ASSET_METADATA = {
|
|
2623
|
+
USDC: {
|
|
2624
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2625
|
+
decimals: 6
|
|
2626
|
+
},
|
|
2627
|
+
WETH: {
|
|
2628
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
2629
|
+
decimals: 18
|
|
2509
2630
|
}
|
|
2510
|
-
}
|
|
2631
|
+
};
|
|
2632
|
+
|
|
2633
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2511
2634
|
function invalid(endpoint, detail) {
|
|
2512
2635
|
throw new OwneyError(
|
|
2513
2636
|
"AGENT_INVALID_RESPONSE",
|
|
@@ -2516,193 +2639,346 @@ function invalid(endpoint, detail) {
|
|
|
2516
2639
|
"yieldseeker"
|
|
2517
2640
|
);
|
|
2518
2641
|
}
|
|
2519
|
-
function
|
|
2520
|
-
if (
|
|
2521
|
-
return invalid(endpoint, "expected
|
|
2642
|
+
function raw(value, endpoint) {
|
|
2643
|
+
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2644
|
+
return invalid(endpoint, "expected a base-10 integer string");
|
|
2522
2645
|
}
|
|
2523
|
-
return value;
|
|
2646
|
+
return BigInt(value);
|
|
2524
2647
|
}
|
|
2525
|
-
function
|
|
2526
|
-
|
|
2527
|
-
return requireObject(root[key2], endpoint);
|
|
2648
|
+
function decimal(value, decimals, endpoint) {
|
|
2649
|
+
return (0, import_viem5.formatUnits)(raw(value, endpoint), decimals);
|
|
2528
2650
|
}
|
|
2529
|
-
function
|
|
2530
|
-
return
|
|
2531
|
-
chain: value.chain,
|
|
2532
|
-
chainId: value.chainId,
|
|
2533
|
-
asset: value.asset,
|
|
2534
|
-
amount: String(value.amount)
|
|
2535
|
-
};
|
|
2651
|
+
function usd(rawAmount, decimals, price) {
|
|
2652
|
+
return Number((0, import_viem5.formatUnits)(rawAmount, decimals)) * price;
|
|
2536
2653
|
}
|
|
2537
|
-
function
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
protocol: String(value.protocol),
|
|
2541
|
-
...value.protocolId ? { protocolId: value.protocolId } : {},
|
|
2542
|
-
...value.pool ? { pool: value.pool } : {},
|
|
2543
|
-
asset: String(value.asset),
|
|
2544
|
-
amount: String(value.amount),
|
|
2545
|
-
...value.amountRaw !== void 0 ? { amountRaw: value.amountRaw } : {},
|
|
2546
|
-
...typeof value.apy === "number" ? { apy: value.apy } : {},
|
|
2547
|
-
...typeof value.tvl === "number" ? { tvl: value.tvl } : {},
|
|
2548
|
-
...typeof value.liquidity === "number" ? { liquidity: value.liquidity } : {}
|
|
2549
|
-
};
|
|
2654
|
+
function percent(value) {
|
|
2655
|
+
const result = Number(value);
|
|
2656
|
+
return Number.isFinite(result) ? result * 100 : 0;
|
|
2550
2657
|
}
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
}
|
|
2563
|
-
return mapped;
|
|
2658
|
+
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2659
|
+
function publicApyAfterYieldseekerFee(value) {
|
|
2660
|
+
const grossPercent = percent(value);
|
|
2661
|
+
if (grossPercent <= 0) return grossPercent;
|
|
2662
|
+
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2663
|
+
return Math.round(netPercent * 1e12) / 1e12;
|
|
2664
|
+
}
|
|
2665
|
+
function riskAdjustedApyForDays(option, days) {
|
|
2666
|
+
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2667
|
+
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2668
|
+
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2564
2669
|
}
|
|
2565
|
-
function
|
|
2566
|
-
const
|
|
2567
|
-
|
|
2568
|
-
"profile",
|
|
2569
|
-
"profile"
|
|
2670
|
+
function assetAddressValue(record, address) {
|
|
2671
|
+
const entry = Object.entries(record).find(
|
|
2672
|
+
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2570
2673
|
);
|
|
2571
|
-
|
|
2572
|
-
|
|
2674
|
+
return entry?.[1] ?? "0";
|
|
2675
|
+
}
|
|
2676
|
+
function position(value, asset) {
|
|
2677
|
+
const option = value?.yieldOption;
|
|
2678
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem5.isAddress)(option.address)) {
|
|
2679
|
+
return invalid("yield positions", "missing vault metadata");
|
|
2573
2680
|
}
|
|
2681
|
+
const decimals = YIELDSEEKER_ASSET_METADATA[asset].decimals;
|
|
2574
2682
|
return {
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2683
|
+
chain: "BASE",
|
|
2684
|
+
protocol: option.provider,
|
|
2685
|
+
protocolId: option.address,
|
|
2686
|
+
pool: option.name,
|
|
2687
|
+
asset,
|
|
2688
|
+
amount: decimal(value.assetsRaw, decimals, "yield positions"),
|
|
2689
|
+
amountRaw: String(value.assetsRaw),
|
|
2690
|
+
apy: percent(option.riskAdjustedApy),
|
|
2691
|
+
tvl: Number(option.totalDepositsUsd),
|
|
2692
|
+
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2580
2693
|
};
|
|
2581
2694
|
}
|
|
2582
|
-
function mapYieldseekerBalances(
|
|
2583
|
-
const
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2695
|
+
function mapYieldseekerBalances(contexts) {
|
|
2696
|
+
const tokens = [];
|
|
2697
|
+
const positions = [];
|
|
2698
|
+
let totalUsd = 0;
|
|
2699
|
+
for (const context of contexts) {
|
|
2700
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2701
|
+
const idle = assetAddressValue(
|
|
2702
|
+
context.snapshot.tokenBalances,
|
|
2703
|
+
metadata.address
|
|
2704
|
+
);
|
|
2705
|
+
tokens.push({
|
|
2706
|
+
chain: "BASE",
|
|
2707
|
+
chainId: 8453,
|
|
2708
|
+
asset: context.asset,
|
|
2709
|
+
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2710
|
+
});
|
|
2711
|
+
positions.push(
|
|
2712
|
+
...context.positions.map((entry) => position(entry, context.asset))
|
|
2713
|
+
);
|
|
2714
|
+
totalUsd += usd(
|
|
2715
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2716
|
+
context.snapshot.baseAssetDecimals,
|
|
2717
|
+
context.snapshot.baseAssetPriceUsd
|
|
2718
|
+
);
|
|
2590
2719
|
}
|
|
2591
2720
|
return {
|
|
2592
|
-
smartWallet:
|
|
2593
|
-
totalBalance:
|
|
2594
|
-
totalBalanceAsset:
|
|
2595
|
-
tokens
|
|
2596
|
-
positions
|
|
2721
|
+
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2722
|
+
totalBalance: String(totalUsd),
|
|
2723
|
+
totalBalanceAsset: "usdc",
|
|
2724
|
+
tokens,
|
|
2725
|
+
positions
|
|
2597
2726
|
};
|
|
2598
2727
|
}
|
|
2599
|
-
function mapYieldseekerEarnings(
|
|
2600
|
-
const
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
"
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2728
|
+
function mapYieldseekerEarnings(contexts) {
|
|
2729
|
+
const tokens = [];
|
|
2730
|
+
let lifetimeEarnings = 0;
|
|
2731
|
+
for (const context of contexts) {
|
|
2732
|
+
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
2733
|
+
tokens.push({
|
|
2734
|
+
chain: "BASE",
|
|
2735
|
+
chainId: 8453,
|
|
2736
|
+
asset: context.asset,
|
|
2737
|
+
amount: (0, import_viem5.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
2738
|
+
});
|
|
2739
|
+
lifetimeEarnings += usd(
|
|
2740
|
+
amount,
|
|
2741
|
+
context.snapshot.baseAssetDecimals,
|
|
2742
|
+
context.snapshot.baseAssetPriceUsd
|
|
2743
|
+
);
|
|
2607
2744
|
}
|
|
2608
2745
|
return {
|
|
2609
|
-
smartWallet:
|
|
2610
|
-
lifetimeEarnings
|
|
2611
|
-
tokens
|
|
2746
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2747
|
+
lifetimeEarnings,
|
|
2748
|
+
tokens
|
|
2612
2749
|
};
|
|
2613
2750
|
}
|
|
2614
|
-
function
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2751
|
+
function apyForDays(snapshot, days) {
|
|
2752
|
+
if (days === "7D") return percent(snapshot.apy7d);
|
|
2753
|
+
if (days === "30D") return percent(snapshot.apy30d);
|
|
2754
|
+
return percent((snapshot.apy7d + snapshot.apy30d) / 2);
|
|
2755
|
+
}
|
|
2756
|
+
function dailyApy(point) {
|
|
2757
|
+
const total = raw(point.totalValueBase, "historic position");
|
|
2758
|
+
const earned = raw(point.dailyYieldBase, "historic position");
|
|
2759
|
+
const principal = total - earned;
|
|
2760
|
+
if (principal <= 0n || earned === 0n) return 0;
|
|
2761
|
+
return Number(earned) / Number(principal) * 365 * 100;
|
|
2762
|
+
}
|
|
2763
|
+
function aggregateHistory(contexts, dayCount) {
|
|
2764
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
2765
|
+
for (const context of contexts) {
|
|
2766
|
+
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
2767
|
+
for (const point of points.slice(-dayCount)) {
|
|
2768
|
+
const valueUsd = usd(
|
|
2769
|
+
raw(point.totalValueBase, "historic position"),
|
|
2770
|
+
point.baseAssetDecimals,
|
|
2771
|
+
point.baseAssetPriceUsd
|
|
2772
|
+
);
|
|
2773
|
+
const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
|
|
2774
|
+
current.weighted += dailyApy(point) * valueUsd;
|
|
2775
|
+
current.valueUsd += valueUsd;
|
|
2776
|
+
byDate.set(point.date, current);
|
|
2777
|
+
}
|
|
2622
2778
|
}
|
|
2779
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
2780
|
+
date,
|
|
2781
|
+
apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
|
|
2782
|
+
}));
|
|
2783
|
+
}
|
|
2784
|
+
function mapYieldseekerApy(walletAddress, contexts, days) {
|
|
2785
|
+
let weighted = 0;
|
|
2786
|
+
let totalUsd = 0;
|
|
2787
|
+
const byAsset = {};
|
|
2788
|
+
for (const context of contexts) {
|
|
2789
|
+
const valueUsd = usd(
|
|
2790
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2791
|
+
context.snapshot.baseAssetDecimals,
|
|
2792
|
+
context.snapshot.baseAssetPriceUsd
|
|
2793
|
+
);
|
|
2794
|
+
const apy = apyForDays(context.snapshot, days);
|
|
2795
|
+
weighted += apy * valueUsd;
|
|
2796
|
+
totalUsd += valueUsd;
|
|
2797
|
+
byAsset[context.asset] = apy;
|
|
2798
|
+
}
|
|
2799
|
+
const dayCount = Number(days.slice(0, -1));
|
|
2623
2800
|
return {
|
|
2624
|
-
walletAddress
|
|
2625
|
-
weightedApyAfterFee:
|
|
2626
|
-
apyByChainAndAsset:
|
|
2627
|
-
history:
|
|
2628
|
-
date: String(point.date),
|
|
2629
|
-
apy: Number(point.apy)
|
|
2630
|
-
}))
|
|
2801
|
+
walletAddress,
|
|
2802
|
+
weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
|
|
2803
|
+
apyByChainAndAsset: { 8453: byAsset },
|
|
2804
|
+
history: aggregateHistory(contexts, dayCount)
|
|
2631
2805
|
};
|
|
2632
2806
|
}
|
|
2633
|
-
function
|
|
2634
|
-
const normalized = value.
|
|
2635
|
-
if (normalized
|
|
2636
|
-
if (normalized
|
|
2637
|
-
if (normalized
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2807
|
+
function actionType(value) {
|
|
2808
|
+
const normalized = value.toLowerCase();
|
|
2809
|
+
if (normalized.includes("deposit")) return "Deposit";
|
|
2810
|
+
if (normalized.includes("withdraw")) return "Withdraw";
|
|
2811
|
+
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
2812
|
+
return "Earned";
|
|
2813
|
+
return "Rebalance";
|
|
2814
|
+
}
|
|
2815
|
+
function transactionHashes(details) {
|
|
2816
|
+
if (!details) return [];
|
|
2817
|
+
const values = [
|
|
2818
|
+
details.transactionHash,
|
|
2819
|
+
details.txHash,
|
|
2820
|
+
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
2821
|
+
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
2822
|
+
];
|
|
2823
|
+
return values.filter(
|
|
2824
|
+
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
2825
|
+
).filter((value, index, all) => all.indexOf(value) === index);
|
|
2642
2826
|
}
|
|
2643
|
-
function
|
|
2827
|
+
function actionEntry(action) {
|
|
2828
|
+
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
2644
2829
|
return {
|
|
2645
2830
|
agent: "yieldseeker",
|
|
2646
|
-
action:
|
|
2647
|
-
date:
|
|
2648
|
-
oldApy:
|
|
2649
|
-
newApy:
|
|
2650
|
-
transactions:
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
rebalanceLog:
|
|
2657
|
-
fromProtocol: String(entry.fromProtocol),
|
|
2658
|
-
toProtocol: String(entry.toProtocol),
|
|
2659
|
-
...entry.fromPool ? { fromPool: entry.fromPool } : {},
|
|
2660
|
-
...entry.toPool ? { toPool: entry.toPool } : {},
|
|
2661
|
-
tokenSymbol: String(entry.tokenSymbol),
|
|
2662
|
-
// Unlike Yieldseeker's other read-response amounts, live history
|
|
2663
|
-
// rebalance amounts are returned in the token's smallest unit. Normalize
|
|
2664
|
-
// them at the adapter boundary so every Owney consumer receives the
|
|
2665
|
-
// shared human-readable decimal shape.
|
|
2666
|
-
amount: rebalanceAmount(String(entry.amount), String(entry.tokenSymbol)),
|
|
2667
|
-
status: entry.status
|
|
2668
|
-
}))
|
|
2831
|
+
action: actionType(action.actionType),
|
|
2832
|
+
date: action.createdDate,
|
|
2833
|
+
oldApy: null,
|
|
2834
|
+
newApy: null,
|
|
2835
|
+
transactions: [
|
|
2836
|
+
{
|
|
2837
|
+
txHashes: transactionHashes(action.details),
|
|
2838
|
+
chainId: 8453
|
|
2839
|
+
}
|
|
2840
|
+
],
|
|
2841
|
+
rebalanceLog: []
|
|
2669
2842
|
};
|
|
2670
2843
|
}
|
|
2671
|
-
function
|
|
2672
|
-
const
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
);
|
|
2677
|
-
if (
|
|
2678
|
-
return
|
|
2844
|
+
function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses) {
|
|
2845
|
+
const from = movement.fromAddress.toLowerCase();
|
|
2846
|
+
const to = movement.toAddress.toLowerCase();
|
|
2847
|
+
const owner = ownerAddress.toLowerCase();
|
|
2848
|
+
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
2849
|
+
const baseAsset = agent.assetAddress.toLowerCase();
|
|
2850
|
+
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
2851
|
+
return void 0;
|
|
2852
|
+
}
|
|
2853
|
+
let action;
|
|
2854
|
+
if (from === owner && to === agentWallet) {
|
|
2855
|
+
action = "Top up";
|
|
2856
|
+
} else if (from === agentWallet && to === owner) {
|
|
2857
|
+
action = "Withdraw";
|
|
2858
|
+
} else if (from === agentWallet && vaultAddresses.has(to)) {
|
|
2859
|
+
action = "Deposit";
|
|
2860
|
+
}
|
|
2861
|
+
if (!action) return void 0;
|
|
2862
|
+
return {
|
|
2863
|
+
agent: "yieldseeker",
|
|
2864
|
+
action,
|
|
2865
|
+
date: movement.blockDate,
|
|
2866
|
+
oldApy: null,
|
|
2867
|
+
newApy: null,
|
|
2868
|
+
transactions: [
|
|
2869
|
+
{
|
|
2870
|
+
txHashes: [movement.transactionHash],
|
|
2871
|
+
chainId: agent.chainId,
|
|
2872
|
+
tokenSymbol: asset,
|
|
2873
|
+
amount: decimal(
|
|
2874
|
+
movement.assetAmount,
|
|
2875
|
+
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
2876
|
+
"historic position"
|
|
2877
|
+
)
|
|
2878
|
+
}
|
|
2879
|
+
],
|
|
2880
|
+
rebalanceLog: []
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
function mapYieldseekerHistory(contexts, options) {
|
|
2884
|
+
const entries = contexts.flatMap((context) => {
|
|
2885
|
+
const vaultAddresses = new Set(
|
|
2886
|
+
context.positions.map(
|
|
2887
|
+
(position2) => position2.yieldOption.address.toLowerCase()
|
|
2888
|
+
)
|
|
2889
|
+
);
|
|
2890
|
+
return [
|
|
2891
|
+
...(context.historic?.movements ?? []).map(
|
|
2892
|
+
(movement) => movementEntry(
|
|
2893
|
+
movement,
|
|
2894
|
+
context.wallet,
|
|
2895
|
+
context.agent,
|
|
2896
|
+
context.asset,
|
|
2897
|
+
options.ownerAddress,
|
|
2898
|
+
vaultAddresses
|
|
2899
|
+
)
|
|
2900
|
+
),
|
|
2901
|
+
...(context.actions ?? []).map(actionEntry)
|
|
2902
|
+
].filter((entry) => entry !== void 0);
|
|
2903
|
+
});
|
|
2904
|
+
const filtered = entries.filter(
|
|
2905
|
+
(entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
|
|
2906
|
+
).filter((entry, index, all) => {
|
|
2907
|
+
const transactionHash = entry.transactions[0]?.txHashes[0];
|
|
2908
|
+
if (!transactionHash) return true;
|
|
2909
|
+
return all.findIndex(
|
|
2910
|
+
(candidate) => candidate.action === entry.action && candidate.transactions[0]?.txHashes[0] === transactionHash
|
|
2911
|
+
) === index;
|
|
2912
|
+
}).sort((left, right) => right.date.localeCompare(left.date));
|
|
2913
|
+
return {
|
|
2914
|
+
data: filtered.slice(0, options.limit),
|
|
2915
|
+
// v1 returns the whole action/movement collection and defines no cursor.
|
|
2916
|
+
// Report a terminal page so callers never loop over the same prefix.
|
|
2917
|
+
hasMore: false
|
|
2918
|
+
};
|
|
2919
|
+
}
|
|
2920
|
+
function mapYieldseekerProfile(address, contexts) {
|
|
2921
|
+
const protocols = /* @__PURE__ */ new Set();
|
|
2922
|
+
for (const context of contexts) {
|
|
2923
|
+
for (const current of context.positions) {
|
|
2924
|
+
if (current.yieldOption?.provider) {
|
|
2925
|
+
protocols.add(String(current.yieldOption.provider));
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2679
2928
|
}
|
|
2680
2929
|
return {
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2930
|
+
address,
|
|
2931
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2932
|
+
chains: contexts.length > 0 ? [8453] : [],
|
|
2933
|
+
hasActiveSessionKey: contexts.some(
|
|
2934
|
+
(context) => context.wallet.initializedDate != null
|
|
2935
|
+
),
|
|
2936
|
+
protocols: [...protocols]
|
|
2684
2937
|
};
|
|
2685
2938
|
}
|
|
2686
|
-
function mapYieldseekerAgentApy(
|
|
2687
|
-
const
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2939
|
+
function mapYieldseekerAgentApy(options, days) {
|
|
2940
|
+
const perAsset = {};
|
|
2941
|
+
const all = [];
|
|
2942
|
+
for (const entry of options) {
|
|
2943
|
+
const apys = entry.yieldOptions.map(
|
|
2944
|
+
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
2945
|
+
).filter(Number.isFinite);
|
|
2946
|
+
if (apys.length === 0) continue;
|
|
2947
|
+
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
2948
|
+
perAsset[entry.asset] = average;
|
|
2949
|
+
all.push(average);
|
|
2694
2950
|
}
|
|
2695
2951
|
return {
|
|
2696
|
-
averageApy:
|
|
2697
|
-
|
|
2698
|
-
detailedApys: {
|
|
2699
|
-
apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
|
|
2700
|
-
}
|
|
2701
|
-
} : {}
|
|
2952
|
+
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
2953
|
+
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
2702
2954
|
};
|
|
2703
2955
|
}
|
|
2704
2956
|
|
|
2705
2957
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2958
|
+
var OWNEY_AGENT_NAME = "owney";
|
|
2959
|
+
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
2960
|
+
{
|
|
2961
|
+
type: "function",
|
|
2962
|
+
name: "withdrawAssetToUser",
|
|
2963
|
+
stateMutability: "nonpayable",
|
|
2964
|
+
inputs: [
|
|
2965
|
+
{ name: "recipient", type: "address" },
|
|
2966
|
+
{ name: "asset", type: "address" },
|
|
2967
|
+
{ name: "amount", type: "uint256" }
|
|
2968
|
+
],
|
|
2969
|
+
outputs: []
|
|
2970
|
+
},
|
|
2971
|
+
{
|
|
2972
|
+
type: "function",
|
|
2973
|
+
name: "withdrawAllAssetToUser",
|
|
2974
|
+
stateMutability: "nonpayable",
|
|
2975
|
+
inputs: [
|
|
2976
|
+
{ name: "recipient", type: "address" },
|
|
2977
|
+
{ name: "asset", type: "address" }
|
|
2978
|
+
],
|
|
2979
|
+
outputs: []
|
|
2980
|
+
}
|
|
2981
|
+
];
|
|
2706
2982
|
function query(params) {
|
|
2707
2983
|
const search = new URLSearchParams();
|
|
2708
2984
|
for (const [key2, value] of Object.entries(params)) {
|
|
@@ -2720,7 +2996,7 @@ var YieldseekerAgent = class {
|
|
|
2720
2996
|
chainId: 8453,
|
|
2721
2997
|
chain: "BASE",
|
|
2722
2998
|
assets: [
|
|
2723
|
-
{ symbol: "USDC", minDepositAmount: "
|
|
2999
|
+
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
2724
3000
|
{ symbol: "WETH", minDepositAmount: "1" }
|
|
2725
3001
|
]
|
|
2726
3002
|
}
|
|
@@ -2729,7 +3005,9 @@ var YieldseekerAgent = class {
|
|
|
2729
3005
|
auth;
|
|
2730
3006
|
transactionExecutor;
|
|
2731
3007
|
unwindReceiptWaiter;
|
|
2732
|
-
|
|
3008
|
+
agentContexts = /* @__PURE__ */ new Map();
|
|
3009
|
+
users = /* @__PURE__ */ new Map();
|
|
3010
|
+
pendingAgents = /* @__PURE__ */ new Map();
|
|
2733
3011
|
constructor(owneyApiKey, options = {}) {
|
|
2734
3012
|
this.api = new YieldseekerApiClient(
|
|
2735
3013
|
owneyApiKey,
|
|
@@ -2742,14 +3020,19 @@ var YieldseekerAgent = class {
|
|
|
2742
3020
|
}
|
|
2743
3021
|
async disconnect() {
|
|
2744
3022
|
this.auth.clear();
|
|
2745
|
-
this.
|
|
3023
|
+
for (const key2 of this.users.keys()) {
|
|
3024
|
+
const [walletAddress, chainId] = key2.split(":");
|
|
3025
|
+
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
3026
|
+
}
|
|
3027
|
+
this.users.clear();
|
|
3028
|
+
this.agentContexts.clear();
|
|
3029
|
+
this.pendingAgents.clear();
|
|
2746
3030
|
}
|
|
2747
3031
|
async activateAgent(state, chainId, asset) {
|
|
2748
3032
|
this.assertChain(chainId);
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
}
|
|
3033
|
+
const targetAsset = asset ?? "USDC";
|
|
3034
|
+
this.assertAsset(targetAsset);
|
|
3035
|
+
await this.ensureAgent(state, chainId, targetAsset);
|
|
2753
3036
|
}
|
|
2754
3037
|
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
2755
3038
|
this.assertChain(chainId);
|
|
@@ -2762,42 +3045,48 @@ var YieldseekerAgent = class {
|
|
|
2762
3045
|
this.id
|
|
2763
3046
|
);
|
|
2764
3047
|
}
|
|
2765
|
-
await this.
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
3048
|
+
const context = await this.ensureAgent(state, chainId, asset);
|
|
3049
|
+
let txHash;
|
|
3050
|
+
try {
|
|
3051
|
+
if (depositCallback) {
|
|
3052
|
+
provideDepositVerificationContext(depositCallback, {
|
|
3053
|
+
agentId: "yieldseeker",
|
|
3054
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3055
|
+
userId: context.user.userId,
|
|
3056
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3057
|
+
});
|
|
3058
|
+
txHash = await depositCallback(
|
|
3059
|
+
context.wallet.walletAddress,
|
|
3060
|
+
chainId,
|
|
3061
|
+
amount
|
|
3062
|
+
);
|
|
3063
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3064
|
+
} else {
|
|
3065
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3066
|
+
from: (0, import_viem6.getAddress)(state.walletAddress),
|
|
3067
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3068
|
+
data: (0, import_viem6.encodeFunctionData)({
|
|
3069
|
+
abi: import_viem6.erc20Abi,
|
|
3070
|
+
functionName: "transfer",
|
|
3071
|
+
args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
3072
|
+
}),
|
|
3073
|
+
value: "0",
|
|
3074
|
+
chainId
|
|
3075
|
+
});
|
|
3076
|
+
}
|
|
3077
|
+
await this.deployAfterFunding(state, chainId, context);
|
|
3078
|
+
} finally {
|
|
3079
|
+
await this.refreshSnapshotAfterMovement(
|
|
3080
|
+
state,
|
|
3081
|
+
chainId,
|
|
3082
|
+
context,
|
|
3083
|
+
"deposit"
|
|
3084
|
+
);
|
|
2796
3085
|
}
|
|
2797
3086
|
return {
|
|
2798
3087
|
txHash,
|
|
2799
|
-
smartWallet:
|
|
2800
|
-
amount
|
|
3088
|
+
smartWallet: context.wallet.walletAddress,
|
|
3089
|
+
amount
|
|
2801
3090
|
};
|
|
2802
3091
|
}
|
|
2803
3092
|
async withdraw(state, chainId, asset, amount) {
|
|
@@ -2811,198 +3100,455 @@ var YieldseekerAgent = class {
|
|
|
2811
3100
|
this.id
|
|
2812
3101
|
);
|
|
2813
3102
|
}
|
|
2814
|
-
const
|
|
2815
|
-
|
|
2816
|
-
const targetAsset = asset.toUpperCase();
|
|
2817
|
-
const idle = balances.tokens.filter(
|
|
2818
|
-
(token2) => token2.chainId === chainId && token2.asset.toUpperCase() === targetAsset
|
|
2819
|
-
).reduce((total, token2) => total + (0, import_viem6.parseUnits)(token2.amount, decimals), 0n);
|
|
2820
|
-
const positions = (balances.positions ?? []).filter(
|
|
2821
|
-
(position2) => this.positionMatchesChain(position2.chain, chainId) && position2.asset.toUpperCase() === targetAsset
|
|
2822
|
-
).map((position2) => ({
|
|
2823
|
-
position: position2,
|
|
2824
|
-
amount: position2.amountRaw !== void 0 ? BigInt(position2.amountRaw) : (0, import_viem6.parseUnits)(position2.amount, decimals)
|
|
2825
|
-
}));
|
|
2826
|
-
const deployed = positions.reduce(
|
|
2827
|
-
(total, position2) => total + position2.amount,
|
|
2828
|
-
0n
|
|
2829
|
-
);
|
|
2830
|
-
const totalAvailable = idle + deployed;
|
|
2831
|
-
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
2832
|
-
if (requested > totalAvailable) {
|
|
3103
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
3104
|
+
if (!context) {
|
|
2833
3105
|
throw new OwneyError(
|
|
2834
3106
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2835
|
-
`
|
|
2836
|
-
{
|
|
2837
|
-
asset,
|
|
2838
|
-
requested: requested.toString(),
|
|
2839
|
-
available: totalAvailable.toString()
|
|
2840
|
-
},
|
|
3107
|
+
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3108
|
+
{ asset, available: "0" },
|
|
2841
3109
|
this.id
|
|
2842
3110
|
);
|
|
2843
3111
|
}
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
3112
|
+
try {
|
|
3113
|
+
const portfolio = await this.loadPortfolioContext(
|
|
3114
|
+
state,
|
|
3115
|
+
chainId,
|
|
3116
|
+
context
|
|
3117
|
+
);
|
|
3118
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3119
|
+
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3120
|
+
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3121
|
+
);
|
|
3122
|
+
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3123
|
+
const deployed = portfolio.positions.reduce(
|
|
3124
|
+
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3125
|
+
0n
|
|
3126
|
+
);
|
|
3127
|
+
const totalAvailable = idle + deployed;
|
|
3128
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3129
|
+
if (requested > totalAvailable) {
|
|
3130
|
+
throw new OwneyError(
|
|
3131
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3132
|
+
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3133
|
+
{
|
|
3134
|
+
asset,
|
|
3135
|
+
requested: requested.toString(),
|
|
3136
|
+
available: totalAvailable.toString()
|
|
3137
|
+
},
|
|
3138
|
+
this.id
|
|
3139
|
+
);
|
|
3140
|
+
}
|
|
3141
|
+
let remaining = requested > idle ? requested - idle : 0n;
|
|
3142
|
+
for (const position2 of portfolio.positions) {
|
|
3143
|
+
if (remaining === 0n) break;
|
|
3144
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3145
|
+
if (available <= 0n) continue;
|
|
3146
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
3147
|
+
const response = await this.walletRequest(
|
|
3148
|
+
state,
|
|
3149
|
+
chainId,
|
|
3150
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
3151
|
+
{
|
|
3152
|
+
method: "POST",
|
|
3153
|
+
body: {
|
|
3154
|
+
chainId,
|
|
3155
|
+
vaultAddress: position2.yieldOption.address,
|
|
3156
|
+
assetsRaw: assetsRaw.toString()
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
);
|
|
3160
|
+
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3161
|
+
throw this.invalidResponse("position withdrawal");
|
|
3162
|
+
}
|
|
3163
|
+
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3164
|
+
remaining -= assetsRaw;
|
|
3165
|
+
}
|
|
3166
|
+
if (remaining > 0n) {
|
|
3167
|
+
throw this.invalidResponse("yield positions", {
|
|
3168
|
+
reason: "Withdrawable positions could not cover the request.",
|
|
3169
|
+
remaining: remaining.toString()
|
|
2855
3170
|
});
|
|
2856
3171
|
}
|
|
2857
|
-
const
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
3172
|
+
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3173
|
+
const txHash = await this.submitTransaction(state, chainId, {
|
|
3174
|
+
from: account,
|
|
3175
|
+
to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
|
|
3176
|
+
data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
|
|
3177
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3178
|
+
functionName: "withdrawAllAssetToUser",
|
|
3179
|
+
args: [account, metadata.address]
|
|
3180
|
+
}) : (0, import_viem6.encodeFunctionData)({
|
|
3181
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3182
|
+
functionName: "withdrawAssetToUser",
|
|
3183
|
+
args: [account, metadata.address, requested]
|
|
3184
|
+
}),
|
|
3185
|
+
value: "0",
|
|
3186
|
+
chainId
|
|
2861
3187
|
});
|
|
2862
|
-
|
|
3188
|
+
return {
|
|
3189
|
+
txHash,
|
|
3190
|
+
type: amount === void 0 ? "full" : "partial",
|
|
3191
|
+
amount: requested.toString()
|
|
3192
|
+
};
|
|
3193
|
+
} finally {
|
|
3194
|
+
await this.refreshSnapshotAfterMovement(
|
|
3195
|
+
state,
|
|
3196
|
+
chainId,
|
|
3197
|
+
context,
|
|
3198
|
+
"withdrawal"
|
|
3199
|
+
);
|
|
2863
3200
|
}
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
3201
|
+
}
|
|
3202
|
+
async getBalances(state, chainId) {
|
|
3203
|
+
this.assertChain(chainId);
|
|
3204
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3205
|
+
}
|
|
3206
|
+
async getEarnings(state, chainId) {
|
|
3207
|
+
this.assertChain(chainId);
|
|
3208
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3209
|
+
}
|
|
3210
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3211
|
+
this.assertChain(chainId);
|
|
3212
|
+
const asset = tokenSymbol?.toUpperCase();
|
|
3213
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3214
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3215
|
+
...asset ? { asset } : {},
|
|
3216
|
+
historic: true
|
|
3217
|
+
});
|
|
3218
|
+
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3219
|
+
}
|
|
3220
|
+
async getHistory(state, chainId, options) {
|
|
3221
|
+
this.assertChain(chainId);
|
|
3222
|
+
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3223
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3224
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3225
|
+
...asset ? { asset } : {},
|
|
3226
|
+
historic: true,
|
|
3227
|
+
actions: true
|
|
3228
|
+
});
|
|
3229
|
+
return mapYieldseekerHistory(contexts, {
|
|
3230
|
+
limit: options?.limit ?? 10,
|
|
3231
|
+
ownerAddress: state.walletAddress,
|
|
3232
|
+
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3233
|
+
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3234
|
+
});
|
|
3235
|
+
}
|
|
3236
|
+
async getUserProfile(state, chainId) {
|
|
3237
|
+
this.assertChain(chainId);
|
|
3238
|
+
return mapYieldseekerProfile(
|
|
3239
|
+
state.walletAddress,
|
|
3240
|
+
await this.loadPortfolio(state, chainId, {})
|
|
3241
|
+
);
|
|
3242
|
+
}
|
|
3243
|
+
async getAgentApy(days, options) {
|
|
3244
|
+
this.assertOptionalChain(options?.chainId);
|
|
3245
|
+
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3246
|
+
if (requested !== void 0) this.assertAsset(requested);
|
|
3247
|
+
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3248
|
+
const values = await Promise.all(
|
|
3249
|
+
assets.map(async (asset) => {
|
|
3250
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3251
|
+
const response = await this.api.request(
|
|
3252
|
+
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3253
|
+
);
|
|
3254
|
+
if (!Array.isArray(response?.yieldOptions)) {
|
|
3255
|
+
throw this.invalidResponse("yield options");
|
|
3256
|
+
}
|
|
3257
|
+
return { asset, yieldOptions: response.yieldOptions };
|
|
3258
|
+
})
|
|
3259
|
+
);
|
|
3260
|
+
return mapYieldseekerAgentApy(values, days);
|
|
3261
|
+
}
|
|
3262
|
+
userKey(state, chainId) {
|
|
3263
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3264
|
+
}
|
|
3265
|
+
contextKey(state, chainId, asset) {
|
|
3266
|
+
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3267
|
+
}
|
|
3268
|
+
async resolveUser(state, chainId) {
|
|
3269
|
+
const key2 = this.userKey(state, chainId);
|
|
3270
|
+
const inMemory = this.users.get(key2);
|
|
3271
|
+
if (inMemory) return inMemory;
|
|
3272
|
+
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3273
|
+
if (persisted) {
|
|
3274
|
+
this.users.set(key2, persisted);
|
|
3275
|
+
return persisted;
|
|
2870
3276
|
}
|
|
2871
|
-
|
|
2872
|
-
|
|
3277
|
+
const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3278
|
+
let user = null;
|
|
3279
|
+
try {
|
|
3280
|
+
const login = await this.providerRequest(
|
|
2873
3281
|
state,
|
|
2874
3282
|
chainId,
|
|
2875
|
-
"/
|
|
3283
|
+
"/users/login-with-wallet",
|
|
3284
|
+
{ method: "POST", body: { walletAddress } }
|
|
3285
|
+
);
|
|
3286
|
+
user = login?.user ?? null;
|
|
3287
|
+
if (!user) {
|
|
3288
|
+
throw this.invalidResponse("wallet login", {
|
|
3289
|
+
reason: "A successful login returned no user."
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
3292
|
+
} catch (error) {
|
|
3293
|
+
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3294
|
+
if (error instanceof OwneyError) throw error;
|
|
3295
|
+
throw this.mapApiError(error);
|
|
3296
|
+
}
|
|
3297
|
+
let created;
|
|
3298
|
+
try {
|
|
3299
|
+
created = await this.providerRequest(
|
|
3300
|
+
state,
|
|
3301
|
+
chainId,
|
|
3302
|
+
"/users",
|
|
3303
|
+
{
|
|
3304
|
+
method: "POST",
|
|
3305
|
+
body: {
|
|
3306
|
+
walletAddress,
|
|
3307
|
+
username: `owney-${walletAddress.slice(2).toLowerCase()}`
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
);
|
|
3311
|
+
} catch (createError) {
|
|
3312
|
+
throw this.mapApiError(createError);
|
|
3313
|
+
}
|
|
3314
|
+
user = created?.user ?? null;
|
|
3315
|
+
}
|
|
3316
|
+
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3317
|
+
throw this.invalidResponse("wallet identity");
|
|
3318
|
+
}
|
|
3319
|
+
const resolved = { userId: user.userId };
|
|
3320
|
+
this.users.set(key2, resolved);
|
|
3321
|
+
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3322
|
+
return resolved;
|
|
3323
|
+
}
|
|
3324
|
+
forgetUser(state, chainId) {
|
|
3325
|
+
this.users.delete(this.userKey(state, chainId));
|
|
3326
|
+
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3327
|
+
}
|
|
3328
|
+
async ensureAgent(state, chainId, asset) {
|
|
3329
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3330
|
+
const cached = this.agentContexts.get(key2);
|
|
3331
|
+
if (cached) return cached;
|
|
3332
|
+
const pending = this.pendingAgents.get(key2);
|
|
3333
|
+
if (pending) return pending;
|
|
3334
|
+
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3335
|
+
(context) => {
|
|
3336
|
+
if (!context) throw this.invalidResponse("agent creation");
|
|
3337
|
+
this.agentContexts.set(key2, context);
|
|
3338
|
+
return context;
|
|
3339
|
+
}
|
|
3340
|
+
);
|
|
3341
|
+
this.pendingAgents.set(key2, request);
|
|
3342
|
+
try {
|
|
3343
|
+
return await request;
|
|
3344
|
+
} finally {
|
|
3345
|
+
this.pendingAgents.delete(key2);
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
async findAgent(state, chainId, asset) {
|
|
3349
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3350
|
+
const cached = this.agentContexts.get(key2);
|
|
3351
|
+
if (cached) return cached;
|
|
3352
|
+
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3353
|
+
if (context) this.agentContexts.set(key2, context);
|
|
3354
|
+
return context;
|
|
3355
|
+
}
|
|
3356
|
+
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3357
|
+
const user = await this.resolveUser(state, chainId);
|
|
3358
|
+
const response = await this.walletRequest(
|
|
3359
|
+
state,
|
|
3360
|
+
chainId,
|
|
3361
|
+
`/users/${user.userId}/agents`
|
|
3362
|
+
);
|
|
3363
|
+
if (!Array.isArray(response?.agents)) {
|
|
3364
|
+
throw this.invalidResponse("agent list");
|
|
3365
|
+
}
|
|
3366
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3367
|
+
let agent = response.agents.find(
|
|
3368
|
+
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3369
|
+
);
|
|
3370
|
+
if (!agent && createIfMissing) {
|
|
3371
|
+
const created = await this.walletRequest(
|
|
3372
|
+
state,
|
|
3373
|
+
chainId,
|
|
3374
|
+
`/users/${user.userId}/agents`,
|
|
2876
3375
|
{
|
|
2877
3376
|
method: "POST",
|
|
2878
3377
|
body: {
|
|
3378
|
+
name: OWNEY_AGENT_NAME,
|
|
3379
|
+
emoji: "\u{1F989}",
|
|
2879
3380
|
chainId,
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
3381
|
+
assetAddress: metadata.address,
|
|
3382
|
+
type: "vault",
|
|
3383
|
+
rulePreset: null
|
|
2883
3384
|
}
|
|
2884
3385
|
}
|
|
2885
3386
|
);
|
|
2886
|
-
|
|
2887
|
-
if (!this.isTransactionHash(transactionHash)) {
|
|
2888
|
-
throw this.invalidResponse("position unwind");
|
|
2889
|
-
}
|
|
2890
|
-
await this.waitForUnwindReceipt(state, chainId, transactionHash);
|
|
3387
|
+
agent = created?.agent;
|
|
2891
3388
|
}
|
|
2892
|
-
|
|
3389
|
+
if (!agent) return null;
|
|
3390
|
+
this.assertAgent(agent);
|
|
3391
|
+
const walletResponse = await this.walletRequest(
|
|
2893
3392
|
state,
|
|
2894
3393
|
chainId,
|
|
2895
|
-
|
|
2896
|
-
{
|
|
2897
|
-
method: "POST",
|
|
2898
|
-
body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
|
|
2899
|
-
}
|
|
3394
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
2900
3395
|
);
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
throw this.invalidResponse("withdrawal");
|
|
3396
|
+
if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3397
|
+
throw this.invalidResponse("agent wallet");
|
|
2904
3398
|
}
|
|
2905
|
-
|
|
3399
|
+
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3400
|
+
}
|
|
3401
|
+
async loadPortfolio(state, chainId, options) {
|
|
3402
|
+
const user = await this.resolveUser(state, chainId);
|
|
3403
|
+
const response = await this.walletRequest(
|
|
2906
3404
|
state,
|
|
2907
3405
|
chainId,
|
|
2908
|
-
|
|
3406
|
+
`/users/${user.userId}/agents`
|
|
2909
3407
|
);
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
3408
|
+
if (!Array.isArray(response?.agents)) {
|
|
3409
|
+
throw this.invalidResponse("agent list");
|
|
3410
|
+
}
|
|
3411
|
+
const contexts = [];
|
|
3412
|
+
for (const agent of response.agents) {
|
|
3413
|
+
const asset = this.assetForAgent(agent);
|
|
3414
|
+
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3415
|
+
continue;
|
|
3416
|
+
}
|
|
3417
|
+
this.assertAgent(agent);
|
|
3418
|
+
const walletResponse = await this.walletRequest(
|
|
2919
3419
|
state,
|
|
2920
3420
|
chainId,
|
|
2921
|
-
`/
|
|
3421
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3422
|
+
);
|
|
3423
|
+
if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3424
|
+
throw this.invalidResponse("agent wallet");
|
|
3425
|
+
}
|
|
3426
|
+
const context = {
|
|
3427
|
+
user,
|
|
3428
|
+
agent,
|
|
3429
|
+
wallet: walletResponse.agentWallet,
|
|
3430
|
+
asset
|
|
3431
|
+
};
|
|
3432
|
+
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3433
|
+
contexts.push(context);
|
|
3434
|
+
}
|
|
3435
|
+
return Promise.all(
|
|
3436
|
+
contexts.map(
|
|
3437
|
+
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
2922
3438
|
)
|
|
2923
3439
|
);
|
|
2924
3440
|
}
|
|
2925
|
-
async
|
|
2926
|
-
|
|
2927
|
-
|
|
3441
|
+
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3442
|
+
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3443
|
+
this.walletRequest(
|
|
2928
3444
|
state,
|
|
2929
3445
|
chainId,
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
3446
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3447
|
+
shouldOnlyUseRecentValue: true,
|
|
3448
|
+
shouldAllowStaleOnError: true
|
|
3449
|
+
})}`
|
|
3450
|
+
),
|
|
3451
|
+
this.walletRequest(
|
|
3452
|
+
state,
|
|
3453
|
+
chainId,
|
|
3454
|
+
this.agentPath(context, "yield-positions")
|
|
3455
|
+
),
|
|
3456
|
+
options.historic ? this.walletRequest(
|
|
3457
|
+
state,
|
|
3458
|
+
chainId,
|
|
3459
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3460
|
+
) : Promise.resolve(void 0),
|
|
3461
|
+
options.actions ? this.walletRequest(
|
|
3462
|
+
state,
|
|
3463
|
+
chainId,
|
|
3464
|
+
this.agentPath(context, "actions")
|
|
3465
|
+
) : Promise.resolve(void 0)
|
|
3466
|
+
]);
|
|
3467
|
+
if (!snapshot?.agentSnapshot) {
|
|
3468
|
+
throw this.invalidResponse("agent snapshot");
|
|
3469
|
+
}
|
|
3470
|
+
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3471
|
+
throw this.invalidResponse("yield positions");
|
|
3472
|
+
}
|
|
3473
|
+
return {
|
|
3474
|
+
...context,
|
|
3475
|
+
snapshot: snapshot.agentSnapshot,
|
|
3476
|
+
positions: positions.yieldPositions,
|
|
3477
|
+
...historic?.position ? { historic: historic.position } : {},
|
|
3478
|
+
...actions?.actions ? { actions: actions.actions } : {}
|
|
3479
|
+
};
|
|
2933
3480
|
}
|
|
2934
|
-
async
|
|
2935
|
-
|
|
2936
|
-
|
|
3481
|
+
async deployAfterFunding(state, chainId, context) {
|
|
3482
|
+
if (context.wallet.initializedDate != null) return;
|
|
3483
|
+
try {
|
|
3484
|
+
const deployed = await this.walletRequest(
|
|
2937
3485
|
state,
|
|
2938
3486
|
chainId,
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
3487
|
+
this.agentPath(context, "deploy"),
|
|
3488
|
+
{ method: "POST", body: {} }
|
|
3489
|
+
);
|
|
3490
|
+
if (deployed?.agentWallet) {
|
|
3491
|
+
context.wallet = deployed.agentWallet;
|
|
3492
|
+
}
|
|
3493
|
+
} catch (error) {
|
|
3494
|
+
console.warn(
|
|
3495
|
+
"[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
|
|
3496
|
+
error
|
|
3497
|
+
);
|
|
3498
|
+
}
|
|
2942
3499
|
}
|
|
2943
|
-
async
|
|
2944
|
-
|
|
2945
|
-
await this.walletRequest(
|
|
3500
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3501
|
+
try {
|
|
3502
|
+
const response = await this.walletRequest(
|
|
2946
3503
|
state,
|
|
2947
3504
|
chainId,
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
limit: options?.limit ?? 10,
|
|
2951
|
-
cursor: options?.cursor,
|
|
2952
|
-
fromDate: options?.fromDate,
|
|
2953
|
-
toDate: options?.toDate
|
|
3505
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3506
|
+
shouldForceRefresh: true
|
|
2954
3507
|
})}`
|
|
2955
|
-
)
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
3508
|
+
);
|
|
3509
|
+
if (!response?.agentSnapshot) {
|
|
3510
|
+
throw this.invalidResponse("agent snapshot refresh");
|
|
3511
|
+
}
|
|
3512
|
+
} catch (error) {
|
|
3513
|
+
console.warn(
|
|
3514
|
+
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3515
|
+
error
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
2962
3518
|
}
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
return mapYieldseekerAgentApy(
|
|
2966
|
-
await this.api.request(
|
|
2967
|
-
`/agent/apy${query({
|
|
2968
|
-
days,
|
|
2969
|
-
tokenSymbol: options?.tokenSymbol,
|
|
2970
|
-
chainId: options?.chainId
|
|
2971
|
-
})}`
|
|
2972
|
-
)
|
|
2973
|
-
);
|
|
3519
|
+
agentPath(context, suffix) {
|
|
3520
|
+
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
2974
3521
|
}
|
|
2975
|
-
async
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
chainId,
|
|
2981
|
-
"/activation",
|
|
2982
|
-
{ method: "POST", body: { chainId, asset } }
|
|
2983
|
-
);
|
|
2984
|
-
if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
|
|
2985
|
-
throw this.invalidResponse("activation");
|
|
3522
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
3523
|
+
try {
|
|
3524
|
+
return await this.providerRequest(state, chainId, path, options);
|
|
3525
|
+
} catch (error) {
|
|
3526
|
+
throw this.mapApiError(error);
|
|
2986
3527
|
}
|
|
2987
|
-
this.activated.add(key2);
|
|
2988
3528
|
}
|
|
2989
|
-
async
|
|
3529
|
+
async providerRequest(state, chainId, path, options = {}) {
|
|
2990
3530
|
this.assertChain(chainId);
|
|
3531
|
+
const request = (signature2) => this.api.request(path, {
|
|
3532
|
+
...options,
|
|
3533
|
+
signature: signature2
|
|
3534
|
+
});
|
|
2991
3535
|
let signature = await this.auth.getToken(state, chainId);
|
|
2992
3536
|
try {
|
|
2993
|
-
return await
|
|
3537
|
+
return await request(signature);
|
|
2994
3538
|
} catch (error) {
|
|
2995
3539
|
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
2996
|
-
if (error.
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3540
|
+
if (error.providerCode === "NO_USER") throw error;
|
|
3541
|
+
if (!error.isAuthenticationError) throw error;
|
|
3542
|
+
this.auth.clear(state, chainId);
|
|
3543
|
+
signature = await this.auth.getToken(state, chainId);
|
|
3544
|
+
try {
|
|
3545
|
+
return await request(signature);
|
|
3546
|
+
} catch (retryError) {
|
|
3547
|
+
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3548
|
+
this.forgetUser(state, chainId);
|
|
3003
3549
|
}
|
|
3550
|
+
throw retryError;
|
|
3004
3551
|
}
|
|
3005
|
-
throw this.mapApiError(error);
|
|
3006
3552
|
}
|
|
3007
3553
|
}
|
|
3008
3554
|
mapApiError(error) {
|
|
@@ -3030,15 +3576,8 @@ var YieldseekerAgent = class {
|
|
|
3030
3576
|
if (this.transactionExecutor) {
|
|
3031
3577
|
return this.transactionExecutor(state, chainId, transaction);
|
|
3032
3578
|
}
|
|
3033
|
-
|
|
3034
|
-
throw this.invalidResponse("transaction");
|
|
3035
|
-
}
|
|
3579
|
+
this.assertTransaction(transaction, state, chainId);
|
|
3036
3580
|
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3037
|
-
if ((0, import_viem6.getAddress)(transaction.from) !== account) {
|
|
3038
|
-
throw this.invalidResponse("transaction", {
|
|
3039
|
-
reason: "transaction.from does not match the connected wallet"
|
|
3040
|
-
});
|
|
3041
|
-
}
|
|
3042
3581
|
const walletClient = (0, import_viem6.createWalletClient)({
|
|
3043
3582
|
account,
|
|
3044
3583
|
chain: import_chains3.base,
|
|
@@ -3074,7 +3613,7 @@ var YieldseekerAgent = class {
|
|
|
3074
3613
|
}
|
|
3075
3614
|
return hash;
|
|
3076
3615
|
}
|
|
3077
|
-
async
|
|
3616
|
+
async waitForReceipt(state, chainId, transactionHash) {
|
|
3078
3617
|
if (this.unwindReceiptWaiter) {
|
|
3079
3618
|
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3080
3619
|
return;
|
|
@@ -3090,15 +3629,32 @@ var YieldseekerAgent = class {
|
|
|
3090
3629
|
if (receipt.status !== "success") {
|
|
3091
3630
|
throw new OwneyError(
|
|
3092
3631
|
"AGENT_TRANSACTION_REVERTED",
|
|
3093
|
-
`Yieldseeker
|
|
3632
|
+
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3094
3633
|
{ transactionHash },
|
|
3095
3634
|
this.id
|
|
3096
3635
|
);
|
|
3097
3636
|
}
|
|
3098
3637
|
}
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3638
|
+
assertTransaction(transaction, state, chainId) {
|
|
3639
|
+
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem6.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem6.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem6.getAddress)(transaction.from) !== (0, import_viem6.getAddress)(state.walletAddress)) {
|
|
3640
|
+
throw this.invalidResponse("transaction");
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
assertAgent(agent) {
|
|
3644
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3645
|
+
throw this.invalidResponse("agent");
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
isOwneyAgent(agent) {
|
|
3649
|
+
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3650
|
+
}
|
|
3651
|
+
assetForAgent(agent) {
|
|
3652
|
+
for (const asset of ["USDC", "WETH"]) {
|
|
3653
|
+
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3654
|
+
return asset;
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
return null;
|
|
3102
3658
|
}
|
|
3103
3659
|
isTransactionHash(value) {
|
|
3104
3660
|
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
@@ -3120,8 +3676,12 @@ var YieldseekerAgent = class {
|
|
|
3120
3676
|
if (asset !== "USDC" && asset !== "WETH") {
|
|
3121
3677
|
throw new OwneyError(
|
|
3122
3678
|
"ASSET_UNSUPPORTED",
|
|
3123
|
-
`Yieldseeker does not support asset ${asset}.`,
|
|
3124
|
-
{
|
|
3679
|
+
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
3680
|
+
{
|
|
3681
|
+
asset,
|
|
3682
|
+
supportedAssets: ["USDC", "WETH"],
|
|
3683
|
+
providerAlsoAdvertises: ["cbBTC"]
|
|
3684
|
+
},
|
|
3125
3685
|
this.id
|
|
3126
3686
|
);
|
|
3127
3687
|
}
|
|
@@ -3343,11 +3903,11 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
|
3343
3903
|
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
3344
3904
|
}
|
|
3345
3905
|
const normalizedToken = tokenSymbol.toUpperCase();
|
|
3346
|
-
return balance.tokens.reduce((total,
|
|
3347
|
-
if (Number(
|
|
3906
|
+
return balance.tokens.reduce((total, token) => {
|
|
3907
|
+
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
3348
3908
|
return total;
|
|
3349
3909
|
}
|
|
3350
|
-
const amount = Number(
|
|
3910
|
+
const amount = Number(token.amount);
|
|
3351
3911
|
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
3352
3912
|
}, 0);
|
|
3353
3913
|
}
|
|
@@ -3417,8 +3977,8 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3417
3977
|
return makeVerificationAwareDepositCallback(
|
|
3418
3978
|
async (smartWallet, chainId, amount, verification) => {
|
|
3419
3979
|
const cid = chainId;
|
|
3420
|
-
const
|
|
3421
|
-
if (!
|
|
3980
|
+
const token = deps.tokenAddressByChain[cid];
|
|
3981
|
+
if (!token) {
|
|
3422
3982
|
throw new OwneyError(
|
|
3423
3983
|
"CHAIN_UNSUPPORTED",
|
|
3424
3984
|
`No sponsored WETH configured for chain ${chainId}`
|
|
@@ -3429,12 +3989,12 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3429
3989
|
const wallet = deps.getWalletClient(cid);
|
|
3430
3990
|
await ensureWalletOnChain(pub, wallet, cid);
|
|
3431
3991
|
try {
|
|
3432
|
-
const balance = await readErc20Balance(pub,
|
|
3992
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
3433
3993
|
if (balance < amountWei) {
|
|
3434
3994
|
throw new OwneyError(
|
|
3435
3995
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
3436
3996
|
"Insufficient WETH balance for this deposit.",
|
|
3437
|
-
{ token
|
|
3997
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3438
3998
|
);
|
|
3439
3999
|
}
|
|
3440
4000
|
} catch (err) {
|
|
@@ -3446,14 +4006,14 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3446
4006
|
}
|
|
3447
4007
|
const allowance = await readPermit2Allowance(
|
|
3448
4008
|
pub,
|
|
3449
|
-
|
|
4009
|
+
token,
|
|
3450
4010
|
deps.ownerAddress
|
|
3451
4011
|
);
|
|
3452
4012
|
if (allowance < amountWei) {
|
|
3453
4013
|
throw new OwneyError(
|
|
3454
4014
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
3455
4015
|
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
3456
|
-
{ token
|
|
4016
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
3457
4017
|
);
|
|
3458
4018
|
}
|
|
3459
4019
|
const relayer = await get({
|
|
@@ -3468,7 +4028,7 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3468
4028
|
const typedData = buildPermitTransferFromTypedData({
|
|
3469
4029
|
chainId: cid,
|
|
3470
4030
|
message: {
|
|
3471
|
-
permitted: { token
|
|
4031
|
+
permitted: { token, amount: amountWei },
|
|
3472
4032
|
spender: relayer,
|
|
3473
4033
|
nonce,
|
|
3474
4034
|
deadline
|
|
@@ -3485,13 +4045,17 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3485
4045
|
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
3486
4046
|
body: {
|
|
3487
4047
|
chainId: cid,
|
|
3488
|
-
token
|
|
4048
|
+
token,
|
|
3489
4049
|
from: deps.ownerAddress,
|
|
3490
4050
|
to: smartWallet,
|
|
3491
4051
|
amount,
|
|
3492
4052
|
nonce: nonce.toString(),
|
|
3493
4053
|
deadline: deadline.toString(),
|
|
3494
|
-
signature
|
|
4054
|
+
signature,
|
|
4055
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
4056
|
+
yieldseekerUserId: verification.userId,
|
|
4057
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4058
|
+
} : {}
|
|
3495
4059
|
}
|
|
3496
4060
|
});
|
|
3497
4061
|
return result.txHash;
|
|
@@ -3526,10 +4090,10 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3526
4090
|
}
|
|
3527
4091
|
return new URL(configured, origin).toString();
|
|
3528
4092
|
};
|
|
3529
|
-
return async (smartWallet, chainId, amount) => {
|
|
4093
|
+
return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
|
|
3530
4094
|
const cid = chainId;
|
|
3531
|
-
const
|
|
3532
|
-
if (!
|
|
4095
|
+
const token = deps.tokenAddressByChain[cid];
|
|
4096
|
+
if (!token) {
|
|
3533
4097
|
throw new OwneyError(
|
|
3534
4098
|
"CHAIN_UNSUPPORTED",
|
|
3535
4099
|
`No sponsored token configured for chain ${chainId}`
|
|
@@ -3547,6 +4111,32 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3547
4111
|
functionName: "transfer",
|
|
3548
4112
|
args: [smartWallet, BigInt(amount)]
|
|
3549
4113
|
});
|
|
4114
|
+
let paymasterUrl = absolutePaymasterUrl();
|
|
4115
|
+
if (verification?.agentId === "yieldseeker") {
|
|
4116
|
+
if (chainId !== 8453) {
|
|
4117
|
+
throw new OwneyError(
|
|
4118
|
+
"CHAIN_UNSUPPORTED",
|
|
4119
|
+
`Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
|
|
4120
|
+
);
|
|
4121
|
+
}
|
|
4122
|
+
const { intent } = await postPaymasterIntent({
|
|
4123
|
+
baseUrl: deps.routingApiBaseUrl,
|
|
4124
|
+
apiKey: deps.apiKey,
|
|
4125
|
+
yieldseekerSignature: verification.signature,
|
|
4126
|
+
body: {
|
|
4127
|
+
chainId,
|
|
4128
|
+
token,
|
|
4129
|
+
from: deps.ownerAddress,
|
|
4130
|
+
to: smartWallet,
|
|
4131
|
+
amount,
|
|
4132
|
+
yieldseekerUserId: verification.userId,
|
|
4133
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4134
|
+
}
|
|
4135
|
+
});
|
|
4136
|
+
const url = new URL(paymasterUrl);
|
|
4137
|
+
url.searchParams.set("owneyIntent", intent);
|
|
4138
|
+
paymasterUrl = url.toString();
|
|
4139
|
+
}
|
|
3550
4140
|
const sendResult = await deps.provider.request({
|
|
3551
4141
|
method: "wallet_sendCalls",
|
|
3552
4142
|
params: [
|
|
@@ -3555,9 +4145,9 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3555
4145
|
from: deps.ownerAddress,
|
|
3556
4146
|
chainId: (0, import_viem8.toHex)(chainId),
|
|
3557
4147
|
atomicRequired: false,
|
|
3558
|
-
calls: [{ to:
|
|
4148
|
+
calls: [{ to: token, value: "0x0", data }],
|
|
3559
4149
|
capabilities: {
|
|
3560
|
-
paymasterService: { url:
|
|
4150
|
+
paymasterService: { url: paymasterUrl }
|
|
3561
4151
|
}
|
|
3562
4152
|
}
|
|
3563
4153
|
]
|
|
@@ -3587,17 +4177,17 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3587
4177
|
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
3588
4178
|
{ chainId, callsId }
|
|
3589
4179
|
);
|
|
3590
|
-
};
|
|
4180
|
+
});
|
|
3591
4181
|
}
|
|
3592
4182
|
|
|
3593
4183
|
// src/client.ts
|
|
3594
4184
|
function encodeMultiAgentCursor(map) {
|
|
3595
4185
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
3596
4186
|
}
|
|
3597
|
-
function decodeMultiAgentCursor(
|
|
4187
|
+
function decodeMultiAgentCursor(token) {
|
|
3598
4188
|
let parsed;
|
|
3599
4189
|
try {
|
|
3600
|
-
const json = Buffer.from(
|
|
4190
|
+
const json = Buffer.from(token, "base64").toString("utf8");
|
|
3601
4191
|
parsed = JSON.parse(json);
|
|
3602
4192
|
} catch {
|
|
3603
4193
|
throw new InvalidHistoryCursorError(
|
|
@@ -3786,6 +4376,8 @@ var OwneySDK = class {
|
|
|
3786
4376
|
if (!onApproved && cached) return cached;
|
|
3787
4377
|
const provider = this.requireConnectedProvider();
|
|
3788
4378
|
const callback = makeSponsoredCallsCallback({
|
|
4379
|
+
apiKey: this.apiKey,
|
|
4380
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
3789
4381
|
provider,
|
|
3790
4382
|
ownerAddress: this.state.walletAddress,
|
|
3791
4383
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
@@ -4228,15 +4820,15 @@ var OwneySDK = class {
|
|
|
4228
4820
|
agent,
|
|
4229
4821
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
4230
4822
|
}));
|
|
4231
|
-
const
|
|
4823
|
+
const valid2 = splits.filter(
|
|
4232
4824
|
(s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
|
|
4233
4825
|
);
|
|
4234
|
-
if (
|
|
4826
|
+
if (valid2.length === agents.length) {
|
|
4235
4827
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
4236
4828
|
}
|
|
4237
4829
|
return this.splitDepositAmount(
|
|
4238
4830
|
totalAmount,
|
|
4239
|
-
|
|
4831
|
+
valid2.map((s) => s.agent),
|
|
4240
4832
|
chainId,
|
|
4241
4833
|
asset,
|
|
4242
4834
|
exempt
|
|
@@ -4269,10 +4861,10 @@ var OwneySDK = class {
|
|
|
4269
4861
|
try {
|
|
4270
4862
|
const balance = await agent.getBalances(state, chainId);
|
|
4271
4863
|
const target = asset.toLowerCase();
|
|
4272
|
-
const
|
|
4864
|
+
const token = balance.tokens.find(
|
|
4273
4865
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
4274
4866
|
);
|
|
4275
|
-
return !!
|
|
4867
|
+
return !!token && Number(token.amount) > 0;
|
|
4276
4868
|
} catch {
|
|
4277
4869
|
return false;
|
|
4278
4870
|
}
|
|
@@ -4323,9 +4915,9 @@ var OwneySDK = class {
|
|
|
4323
4915
|
const { asset, amount, agentId } = options;
|
|
4324
4916
|
const state = this.requireState();
|
|
4325
4917
|
const chainId = this.requireChainId();
|
|
4326
|
-
const
|
|
4918
|
+
const token = asset;
|
|
4327
4919
|
const assetInfo = SupportedAssets.find(
|
|
4328
|
-
(a) => a.chainId === chainId && a.symbol ===
|
|
4920
|
+
(a) => a.chainId === chainId && a.symbol === token
|
|
4329
4921
|
);
|
|
4330
4922
|
if (!assetInfo) {
|
|
4331
4923
|
throw new OwneyError(
|
|
@@ -4339,7 +4931,7 @@ var OwneySDK = class {
|
|
|
4339
4931
|
return withFailureReporting(
|
|
4340
4932
|
this.apiKey,
|
|
4341
4933
|
agent.id,
|
|
4342
|
-
() => agent.withdraw(state, chainId,
|
|
4934
|
+
() => agent.withdraw(state, chainId, token, amount),
|
|
4343
4935
|
this.routingApiBaseUrl
|
|
4344
4936
|
);
|
|
4345
4937
|
}
|
|
@@ -4349,7 +4941,7 @@ var OwneySDK = class {
|
|
|
4349
4941
|
const agentErrors2 = {};
|
|
4350
4942
|
for (const agent of eligibleAgents) {
|
|
4351
4943
|
try {
|
|
4352
|
-
results2[agent.id] = await agent.withdraw(state, chainId,
|
|
4944
|
+
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
4353
4945
|
} catch (err) {
|
|
4354
4946
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
4355
4947
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
@@ -4439,7 +5031,7 @@ var OwneySDK = class {
|
|
|
4439
5031
|
results[p.agent.id] = await p.agent.withdraw(
|
|
4440
5032
|
state,
|
|
4441
5033
|
chainId,
|
|
4442
|
-
|
|
5034
|
+
token,
|
|
4443
5035
|
p.planned.toString()
|
|
4444
5036
|
);
|
|
4445
5037
|
} catch (err) {
|
|
@@ -4790,8 +5382,8 @@ var OwneySDK = class {
|
|
|
4790
5382
|
const state = this.requireState();
|
|
4791
5383
|
const chainId = this.requireChainId();
|
|
4792
5384
|
this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
|
|
4793
|
-
const
|
|
4794
|
-
if (!
|
|
5385
|
+
const token = SPONSORED_WETH_BY_CHAIN[chainId];
|
|
5386
|
+
if (!token) {
|
|
4795
5387
|
throw new OwneyError(
|
|
4796
5388
|
"CHAIN_UNSUPPORTED",
|
|
4797
5389
|
`No sponsored WETH on chain ${chainId}`
|
|
@@ -4804,7 +5396,7 @@ var OwneySDK = class {
|
|
|
4804
5396
|
transport: (0, import_viem9.custom)(provider)
|
|
4805
5397
|
});
|
|
4806
5398
|
const hash = await wallet.writeContract({
|
|
4807
|
-
address:
|
|
5399
|
+
address: token,
|
|
4808
5400
|
abi: ERC20_ALLOWANCE_ABI,
|
|
4809
5401
|
functionName: "approve",
|
|
4810
5402
|
args: [PERMIT2_ADDRESS, MAX_UINT256],
|
|
@@ -4929,8 +5521,8 @@ var import_siwe2 = require("siwe");
|
|
|
4929
5521
|
var import_sdk2 = require("@zyfai/sdk");
|
|
4930
5522
|
|
|
4931
5523
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
4932
|
-
var
|
|
4933
|
-
var
|
|
5524
|
+
var KEY_PREFIX4 = "owney.siwx.session";
|
|
5525
|
+
var storage4 = () => {
|
|
4934
5526
|
if (typeof window === "undefined") return null;
|
|
4935
5527
|
try {
|
|
4936
5528
|
return window.localStorage;
|
|
@@ -4938,8 +5530,8 @@ var storage3 = () => {
|
|
|
4938
5530
|
return null;
|
|
4939
5531
|
}
|
|
4940
5532
|
};
|
|
4941
|
-
var buildKey3 = (address) => `${
|
|
4942
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
5533
|
+
var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
|
|
5534
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
|
|
4943
5535
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
4944
5536
|
var readLegacySiwxSession = (store, address) => {
|
|
4945
5537
|
if (!store) return null;
|
|
@@ -4971,16 +5563,16 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
4971
5563
|
var readSiwxSession = (address, chainId) => {
|
|
4972
5564
|
if (typeof window === "undefined") return null;
|
|
4973
5565
|
const key2 = buildKey3(address);
|
|
4974
|
-
const store =
|
|
4975
|
-
let
|
|
5566
|
+
const store = storage4();
|
|
5567
|
+
let raw2 = null;
|
|
4976
5568
|
try {
|
|
4977
|
-
|
|
5569
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
4978
5570
|
} catch {
|
|
4979
|
-
|
|
5571
|
+
raw2 = null;
|
|
4980
5572
|
}
|
|
4981
|
-
if (
|
|
5573
|
+
if (raw2) {
|
|
4982
5574
|
try {
|
|
4983
|
-
return JSON.parse(
|
|
5575
|
+
return JSON.parse(raw2);
|
|
4984
5576
|
} catch {
|
|
4985
5577
|
memorySiwxSessions.delete(key2);
|
|
4986
5578
|
try {
|
|
@@ -5001,7 +5593,7 @@ var writeSiwxSession = (address, _chainId, session) => {
|
|
|
5001
5593
|
if (typeof window === "undefined") return;
|
|
5002
5594
|
const key2 = buildKey3(address);
|
|
5003
5595
|
memorySiwxSessions.set(key2, session);
|
|
5004
|
-
const store =
|
|
5596
|
+
const store = storage4();
|
|
5005
5597
|
try {
|
|
5006
5598
|
store?.setItem(key2, JSON.stringify(session));
|
|
5007
5599
|
} catch {
|
|
@@ -5010,7 +5602,7 @@ var writeSiwxSession = (address, _chainId, session) => {
|
|
|
5010
5602
|
var clearSiwxSession = (address, _chainId) => {
|
|
5011
5603
|
const key2 = buildKey3(address);
|
|
5012
5604
|
memorySiwxSessions.delete(key2);
|
|
5013
|
-
const store =
|
|
5605
|
+
const store = storage4();
|
|
5014
5606
|
try {
|
|
5015
5607
|
store?.removeItem(key2);
|
|
5016
5608
|
} catch {
|