@owney/sdk 0.7.16-beta.1 → 0.7.16-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1090 -532
- package/dist/index.d.cts +23 -6
- package/dist/index.d.ts +23 -6
- package/dist/index.js +1100 -541
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -147,18 +147,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
147
147
|
return explicit;
|
|
148
148
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
149
149
|
}
|
|
150
|
-
function mapDeposit(
|
|
150
|
+
function mapDeposit(raw2) {
|
|
151
151
|
return {
|
|
152
|
-
txHash:
|
|
153
|
-
smartWallet:
|
|
154
|
-
amount:
|
|
152
|
+
txHash: raw2.txHash,
|
|
153
|
+
smartWallet: raw2.smartWallet,
|
|
154
|
+
amount: raw2.amount
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
|
-
function mapWithdraw(
|
|
157
|
+
function mapWithdraw(raw2) {
|
|
158
158
|
return {
|
|
159
|
-
txHash:
|
|
160
|
-
type:
|
|
161
|
-
amount:
|
|
159
|
+
txHash: raw2.txHash,
|
|
160
|
+
type: raw2.type,
|
|
161
|
+
amount: raw2.amount
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -177,10 +177,10 @@ function resolveChainId(chain) {
|
|
|
177
177
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
178
178
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
179
179
|
}
|
|
180
|
-
function mapPendingAllocations(
|
|
181
|
-
if (!Array.isArray(
|
|
180
|
+
function mapPendingAllocations(raw2) {
|
|
181
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
182
182
|
const pending = [];
|
|
183
|
-
for (const entry of
|
|
183
|
+
for (const entry of raw2) {
|
|
184
184
|
if (typeof entry !== "object" || entry === null) continue;
|
|
185
185
|
const e = entry;
|
|
186
186
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -203,8 +203,8 @@ function mapPendingAllocations(raw) {
|
|
|
203
203
|
}
|
|
204
204
|
return pending.length > 0 ? pending : void 0;
|
|
205
205
|
}
|
|
206
|
-
function mapBalances(
|
|
207
|
-
const portfolio =
|
|
206
|
+
function mapBalances(raw2, _chainId, smartWallet) {
|
|
207
|
+
const portfolio = raw2.portfolio;
|
|
208
208
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
209
209
|
let totalBalance = 0;
|
|
210
210
|
const tokens = [];
|
|
@@ -273,8 +273,8 @@ function sumTokenValues(tokens) {
|
|
|
273
273
|
function sumTokenEarnings(tokens) {
|
|
274
274
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
275
275
|
}
|
|
276
|
-
function mapEarnings(
|
|
277
|
-
const totalEarningsByChain =
|
|
276
|
+
function mapEarnings(raw2, smartWallet) {
|
|
277
|
+
const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
|
|
278
278
|
const tokens = [];
|
|
279
279
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
280
280
|
totalEarningsByChain
|
|
@@ -293,22 +293,22 @@ function mapEarnings(raw, smartWallet) {
|
|
|
293
293
|
return {
|
|
294
294
|
smartWallet,
|
|
295
295
|
lifetimeEarnings: sumTokenEarnings(
|
|
296
|
-
|
|
296
|
+
raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
|
|
297
297
|
),
|
|
298
298
|
tokens
|
|
299
299
|
};
|
|
300
300
|
}
|
|
301
|
-
function mapWeightedApyByChain(
|
|
302
|
-
if (!
|
|
301
|
+
function mapWeightedApyByChain(raw2) {
|
|
302
|
+
if (!raw2) return void 0;
|
|
303
303
|
const out = {};
|
|
304
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
304
|
+
for (const [chainKey, tokenApy] of Object.entries(raw2)) {
|
|
305
305
|
const chainId = Number(chainKey);
|
|
306
306
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
307
307
|
const perAsset = {};
|
|
308
308
|
for (const [symbol, apy] of Object.entries(tokenApy)) {
|
|
309
|
-
const
|
|
310
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
311
|
-
perAsset[
|
|
309
|
+
const token = symbol;
|
|
310
|
+
if (!SUPPORTED_TOKENS.includes(token)) continue;
|
|
311
|
+
perAsset[token] = apy;
|
|
312
312
|
}
|
|
313
313
|
if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
|
|
314
314
|
}
|
|
@@ -327,15 +327,15 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
327
327
|
}
|
|
328
328
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
329
329
|
}
|
|
330
|
-
function mapApyHistory(
|
|
331
|
-
const history = Object.entries(
|
|
330
|
+
function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
331
|
+
const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
|
|
332
332
|
date,
|
|
333
333
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
|
|
334
334
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
335
335
|
return {
|
|
336
|
-
walletAddress:
|
|
337
|
-
weightedApyAfterFee:
|
|
338
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
336
|
+
walletAddress: raw2.walletAddress,
|
|
337
|
+
weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
|
|
338
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
|
|
339
339
|
history
|
|
340
340
|
};
|
|
341
341
|
}
|
|
@@ -427,23 +427,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
427
427
|
};
|
|
428
428
|
});
|
|
429
429
|
}
|
|
430
|
-
function mapUserProfile(
|
|
430
|
+
function mapUserProfile(raw2, userAddress) {
|
|
431
431
|
return {
|
|
432
432
|
address: userAddress,
|
|
433
|
-
smartWallet:
|
|
434
|
-
chains:
|
|
435
|
-
strategy:
|
|
436
|
-
hasActiveSessionKey:
|
|
437
|
-
protocols:
|
|
438
|
-
splitting:
|
|
439
|
-
minSplits:
|
|
433
|
+
smartWallet: raw2.smartWallet || "",
|
|
434
|
+
chains: raw2.chains || [],
|
|
435
|
+
strategy: raw2.strategy,
|
|
436
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey || false,
|
|
437
|
+
protocols: raw2.protocols || [],
|
|
438
|
+
splitting: raw2.splitting,
|
|
439
|
+
minSplits: raw2.minSplits
|
|
440
440
|
};
|
|
441
441
|
}
|
|
442
|
-
function mapApyByStrategy(
|
|
442
|
+
function mapApyByStrategy(raw2) {
|
|
443
443
|
const apyPerAsset = {};
|
|
444
444
|
let apySum = 0;
|
|
445
445
|
let apyCount = 0;
|
|
446
|
-
for (const entry of
|
|
446
|
+
for (const entry of raw2.data) {
|
|
447
447
|
const supported = SupportedAssets.find(
|
|
448
448
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
449
449
|
);
|
|
@@ -477,23 +477,23 @@ function computeAllocationApy(positions) {
|
|
|
477
477
|
weightedSum += apy * value;
|
|
478
478
|
totalValue += value;
|
|
479
479
|
const chainId = resolveChainId(p.chain);
|
|
480
|
-
const
|
|
480
|
+
const token = p.asset;
|
|
481
481
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
482
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
482
|
+
if (!SUPPORTED_TOKENS.includes(token)) continue;
|
|
483
483
|
const perAsset = buckets[chainId] ?? {};
|
|
484
|
-
const cell = perAsset[
|
|
484
|
+
const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
|
|
485
485
|
cell.weightedSum += apy * value;
|
|
486
486
|
cell.value += value;
|
|
487
|
-
perAsset[
|
|
487
|
+
perAsset[token] = cell;
|
|
488
488
|
buckets[chainId] = perAsset;
|
|
489
489
|
}
|
|
490
490
|
const apyByChainAndAsset = {};
|
|
491
491
|
for (const [chainKey, perAsset] of Object.entries(buckets)) {
|
|
492
492
|
const chainId = Number(chainKey);
|
|
493
493
|
const out = {};
|
|
494
|
-
for (const [
|
|
494
|
+
for (const [token, cell] of Object.entries(perAsset ?? {})) {
|
|
495
495
|
if (cell && cell.value > 0) {
|
|
496
|
-
out[
|
|
496
|
+
out[token] = cell.weightedSum / cell.value;
|
|
497
497
|
}
|
|
498
498
|
}
|
|
499
499
|
if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
|
|
@@ -507,9 +507,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
|
|
|
507
507
|
function topicToAddress(topic) {
|
|
508
508
|
return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
|
|
509
509
|
}
|
|
510
|
-
function extractWithdrawnAmount(logs, recipient,
|
|
510
|
+
function extractWithdrawnAmount(logs, recipient, token, decimals) {
|
|
511
511
|
const wantRecipient = recipient.toLowerCase();
|
|
512
|
-
const wantToken =
|
|
512
|
+
const wantToken = token.toLowerCase();
|
|
513
513
|
let total = 0n;
|
|
514
514
|
let matched = false;
|
|
515
515
|
for (const log of logs) {
|
|
@@ -535,11 +535,11 @@ var InvalidHistoryCursorError = class extends Error {
|
|
|
535
535
|
function encodeHistoryCursor(payload) {
|
|
536
536
|
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
|
|
537
537
|
}
|
|
538
|
-
function decodeHistoryCursor(
|
|
539
|
-
if (!
|
|
538
|
+
function decodeHistoryCursor(token) {
|
|
539
|
+
if (!token) throw new InvalidHistoryCursorError("Cursor is empty.");
|
|
540
540
|
let parsed;
|
|
541
541
|
try {
|
|
542
|
-
const json = Buffer.from(
|
|
542
|
+
const json = Buffer.from(token, "base64").toString("utf8");
|
|
543
543
|
parsed = JSON.parse(json);
|
|
544
544
|
} catch {
|
|
545
545
|
throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
|
|
@@ -562,8 +562,8 @@ var storage = () => {
|
|
|
562
562
|
};
|
|
563
563
|
var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
|
|
564
564
|
var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
|
|
565
|
-
var isJwtExpired = (
|
|
566
|
-
const parts =
|
|
565
|
+
var isJwtExpired = (token) => {
|
|
566
|
+
const parts = token.split(".");
|
|
567
567
|
if (parts.length !== 3) return false;
|
|
568
568
|
try {
|
|
569
569
|
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -610,15 +610,15 @@ var readSession = (address, _chainId) => {
|
|
|
610
610
|
if (typeof window === "undefined") return null;
|
|
611
611
|
const key2 = buildKey(address);
|
|
612
612
|
const store = storage();
|
|
613
|
-
let
|
|
613
|
+
let raw2 = null;
|
|
614
614
|
try {
|
|
615
|
-
|
|
615
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
616
616
|
} catch {
|
|
617
|
-
|
|
617
|
+
raw2 = null;
|
|
618
618
|
}
|
|
619
|
-
if (
|
|
619
|
+
if (raw2) {
|
|
620
620
|
try {
|
|
621
|
-
const parsed = JSON.parse(
|
|
621
|
+
const parsed = JSON.parse(raw2);
|
|
622
622
|
if (isFreshSession(parsed)) return parsed;
|
|
623
623
|
} catch {
|
|
624
624
|
}
|
|
@@ -786,8 +786,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
786
786
|
}
|
|
787
787
|
return result;
|
|
788
788
|
}
|
|
789
|
-
function flattenAvailablePools(
|
|
790
|
-
const byChain =
|
|
789
|
+
function flattenAvailablePools(raw2) {
|
|
790
|
+
const byChain = raw2 ?? {};
|
|
791
791
|
const names = [];
|
|
792
792
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
793
793
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -1298,8 +1298,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1298
1298
|
const poolResults = await Promise.all(
|
|
1299
1299
|
universe.map(async (protocol) => {
|
|
1300
1300
|
try {
|
|
1301
|
-
const
|
|
1302
|
-
return [protocol.id, flattenAvailablePools(
|
|
1301
|
+
const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1302
|
+
return [protocol.id, flattenAvailablePools(raw2)];
|
|
1303
1303
|
} catch (error) {
|
|
1304
1304
|
console.warn(
|
|
1305
1305
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1365,14 +1365,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1365
1365
|
async readWalletState(ownerAddress) {
|
|
1366
1366
|
try {
|
|
1367
1367
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1368
|
-
const
|
|
1368
|
+
const raw2 = portfolio;
|
|
1369
1369
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1370
|
-
predeployed:
|
|
1371
|
-
hasActiveSessionKey:
|
|
1370
|
+
predeployed: raw2?.predeployed,
|
|
1371
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1372
1372
|
});
|
|
1373
1373
|
return {
|
|
1374
|
-
predeployed:
|
|
1375
|
-
hasActiveSessionKey:
|
|
1374
|
+
predeployed: raw2?.predeployed,
|
|
1375
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1376
1376
|
};
|
|
1377
1377
|
} catch (error) {
|
|
1378
1378
|
console.warn(
|
|
@@ -1652,48 +1652,48 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1652
1652
|
return { txHash, smartWallet, amount };
|
|
1653
1653
|
}
|
|
1654
1654
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1655
|
-
const
|
|
1655
|
+
const raw2 = await this.sdk.depositFunds(
|
|
1656
1656
|
this.getAddress(),
|
|
1657
1657
|
validChainId,
|
|
1658
1658
|
amount,
|
|
1659
1659
|
asset,
|
|
1660
1660
|
"aggressive"
|
|
1661
1661
|
);
|
|
1662
|
-
return mapDeposit(
|
|
1662
|
+
return mapDeposit(raw2);
|
|
1663
1663
|
} catch (error) {
|
|
1664
1664
|
throw error;
|
|
1665
1665
|
}
|
|
1666
1666
|
}
|
|
1667
1667
|
// --- IAgent: Fund operations ---
|
|
1668
|
-
async withdraw(state, chainId,
|
|
1668
|
+
async withdraw(state, chainId, token, amount) {
|
|
1669
1669
|
const validChainId = isValidChainId(chainId);
|
|
1670
1670
|
await this.ensureConnected(state, validChainId);
|
|
1671
|
-
const
|
|
1671
|
+
const raw2 = await this.sdk.withdrawFunds(
|
|
1672
1672
|
this.getAddress(),
|
|
1673
1673
|
validChainId,
|
|
1674
1674
|
amount,
|
|
1675
|
-
|
|
1675
|
+
token
|
|
1676
1676
|
);
|
|
1677
|
-
if (!
|
|
1677
|
+
if (!raw2.success) {
|
|
1678
1678
|
throw new OwneyError(
|
|
1679
1679
|
"WITHDRAW_FAILED",
|
|
1680
|
-
|
|
1681
|
-
{ chainId: validChainId, token
|
|
1680
|
+
raw2.message || "Zyfai withdraw failed.",
|
|
1681
|
+
{ chainId: validChainId, token, amount, response: raw2 },
|
|
1682
1682
|
this.id
|
|
1683
1683
|
);
|
|
1684
1684
|
}
|
|
1685
|
-
return mapWithdraw(
|
|
1685
|
+
return mapWithdraw(raw2);
|
|
1686
1686
|
}
|
|
1687
1687
|
// --- IAgent: Portfolio reads ---
|
|
1688
1688
|
async getBalances(state, chainId) {
|
|
1689
1689
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1690
|
-
const
|
|
1691
|
-
return mapBalances(
|
|
1690
|
+
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
1691
|
+
return mapBalances(raw2, validChainId, smartWallet);
|
|
1692
1692
|
}
|
|
1693
1693
|
async getEarnings(state, chainId) {
|
|
1694
1694
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1695
|
-
const
|
|
1696
|
-
return mapEarnings(
|
|
1695
|
+
const raw2 = await this.sdk.getOnchainEarnings(smartWallet);
|
|
1696
|
+
return mapEarnings(raw2, smartWallet);
|
|
1697
1697
|
}
|
|
1698
1698
|
async refreshEarnings(state, chainId) {
|
|
1699
1699
|
if (this.earningsRefreshInFlight) return this.earningsRefreshInFlight;
|
|
@@ -1713,8 +1713,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1713
1713
|
}
|
|
1714
1714
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1715
1715
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1716
|
-
const
|
|
1717
|
-
return mapApyHistory(
|
|
1716
|
+
const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
1717
|
+
return mapApyHistory(raw2, chainId, tokenSymbol);
|
|
1718
1718
|
}
|
|
1719
1719
|
/**
|
|
1720
1720
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -1749,7 +1749,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1749
1749
|
const matched = [];
|
|
1750
1750
|
let backendExhausted = false;
|
|
1751
1751
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
1752
|
-
const
|
|
1752
|
+
const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
1753
1753
|
limit: backendPageSize,
|
|
1754
1754
|
offset,
|
|
1755
1755
|
fromDate: options?.fromDate,
|
|
@@ -1760,13 +1760,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1760
1760
|
// asset's rows and handing back a page that filters to nothing.
|
|
1761
1761
|
assetType
|
|
1762
1762
|
});
|
|
1763
|
-
|
|
1763
|
+
raw2.data.forEach((entry, idx) => {
|
|
1764
1764
|
if (entry.chainId === validChainId) {
|
|
1765
1765
|
matched.push({ entry, rawIdx: offset + idx });
|
|
1766
1766
|
}
|
|
1767
1767
|
});
|
|
1768
|
-
offset +=
|
|
1769
|
-
if (
|
|
1768
|
+
offset += raw2.data.length;
|
|
1769
|
+
if (raw2.data.length < backendPageSize) {
|
|
1770
1770
|
backendExhausted = true;
|
|
1771
1771
|
break;
|
|
1772
1772
|
}
|
|
@@ -1788,18 +1788,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1788
1788
|
}
|
|
1789
1789
|
async getUserProfile(state, chainId) {
|
|
1790
1790
|
await this.connectAuth(state, chainId);
|
|
1791
|
-
const
|
|
1791
|
+
const raw2 = await this.sdk.getUserDetails();
|
|
1792
1792
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
1793
1793
|
asset: "USDC (default \u2014 no asset passed)",
|
|
1794
|
-
splitting:
|
|
1795
|
-
minSplits:
|
|
1796
|
-
strategy:
|
|
1797
|
-
chains:
|
|
1798
|
-
protocolCount:
|
|
1799
|
-
hasActiveSessionKey:
|
|
1800
|
-
smartWallet:
|
|
1794
|
+
splitting: raw2.splitting,
|
|
1795
|
+
minSplits: raw2.minSplits,
|
|
1796
|
+
strategy: raw2.strategy,
|
|
1797
|
+
chains: raw2.chains,
|
|
1798
|
+
protocolCount: raw2.protocols?.length,
|
|
1799
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey,
|
|
1800
|
+
smartWallet: raw2.smartWallet
|
|
1801
1801
|
});
|
|
1802
|
-
return mapUserProfile(
|
|
1802
|
+
return mapUserProfile(raw2, this.connectedAddress);
|
|
1803
1803
|
}
|
|
1804
1804
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
1805
1805
|
await this.connectAuth(state, chainId);
|
|
@@ -1818,14 +1818,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1818
1818
|
}
|
|
1819
1819
|
// --- IAgent: Discovery (no wallet required) ---
|
|
1820
1820
|
async getAgentApy(days, options) {
|
|
1821
|
-
const
|
|
1821
|
+
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
1822
1822
|
false,
|
|
1823
1823
|
DayFilterMapping[days],
|
|
1824
1824
|
"aggressive",
|
|
1825
1825
|
options?.chainId,
|
|
1826
1826
|
options?.tokenSymbol
|
|
1827
1827
|
);
|
|
1828
|
-
return mapApyByStrategy(
|
|
1828
|
+
return mapApyByStrategy(raw2);
|
|
1829
1829
|
}
|
|
1830
1830
|
};
|
|
1831
1831
|
|
|
@@ -1834,9 +1834,10 @@ import {
|
|
|
1834
1834
|
createPublicClient as createPublicClient3,
|
|
1835
1835
|
createWalletClient as createWalletClient2,
|
|
1836
1836
|
custom as custom2,
|
|
1837
|
+
encodeFunctionData,
|
|
1838
|
+
erc20Abi,
|
|
1837
1839
|
getAddress as getAddress2,
|
|
1838
|
-
isAddress
|
|
1839
|
-
parseUnits
|
|
1840
|
+
isAddress as isAddress2
|
|
1840
1841
|
} from "viem";
|
|
1841
1842
|
import { base as base3 } from "viem/chains";
|
|
1842
1843
|
|
|
@@ -1898,10 +1899,10 @@ function buildTransferWithAuthorizationTypedData(input) {
|
|
|
1898
1899
|
message: input.message
|
|
1899
1900
|
};
|
|
1900
1901
|
}
|
|
1901
|
-
async function readTokenMeta(publicClient,
|
|
1902
|
+
async function readTokenMeta(publicClient, token) {
|
|
1902
1903
|
const [tokenName, tokenVersion] = await Promise.all([
|
|
1903
|
-
publicClient.readContract({ address:
|
|
1904
|
-
publicClient.readContract({ address:
|
|
1904
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
1905
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
1905
1906
|
]);
|
|
1906
1907
|
return { tokenName, tokenVersion };
|
|
1907
1908
|
}
|
|
@@ -1913,6 +1914,45 @@ function randomAuthNonce() {
|
|
|
1913
1914
|
|
|
1914
1915
|
// src/lib/sponsor-client.ts
|
|
1915
1916
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1917
|
+
async function postPaymasterIntent(input) {
|
|
1918
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1919
|
+
let res;
|
|
1920
|
+
try {
|
|
1921
|
+
res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
|
|
1922
|
+
method: "POST",
|
|
1923
|
+
headers: {
|
|
1924
|
+
"content-type": "application/json",
|
|
1925
|
+
"x-owney-api-key": input.apiKey,
|
|
1926
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
1927
|
+
},
|
|
1928
|
+
body: JSON.stringify(input.body)
|
|
1929
|
+
});
|
|
1930
|
+
} catch (networkError) {
|
|
1931
|
+
throw new OwneyError(
|
|
1932
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1933
|
+
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1934
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
const text = await res.text();
|
|
1938
|
+
let parsed = null;
|
|
1939
|
+
try {
|
|
1940
|
+
parsed = JSON.parse(text);
|
|
1941
|
+
} catch {
|
|
1942
|
+
}
|
|
1943
|
+
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
1944
|
+
throw new OwneyError(
|
|
1945
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1946
|
+
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1947
|
+
{
|
|
1948
|
+
statusCode: res.status,
|
|
1949
|
+
responseBody: text.slice(0, 500),
|
|
1950
|
+
safeToFallback: true
|
|
1951
|
+
}
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
return parsed.data;
|
|
1955
|
+
}
|
|
1916
1956
|
async function postSponsorTransferAuth(input) {
|
|
1917
1957
|
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
1918
1958
|
let res;
|
|
@@ -1922,7 +1962,7 @@ async function postSponsorTransferAuth(input) {
|
|
|
1922
1962
|
headers: {
|
|
1923
1963
|
"content-type": "application/json",
|
|
1924
1964
|
"x-owney-api-key": input.apiKey,
|
|
1925
|
-
...input.yieldseekerSignature ? {
|
|
1965
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
1926
1966
|
},
|
|
1927
1967
|
body: JSON.stringify(input.body)
|
|
1928
1968
|
});
|
|
@@ -1963,7 +2003,7 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
1963
2003
|
headers: {
|
|
1964
2004
|
"content-type": "application/json",
|
|
1965
2005
|
"x-owney-api-key": input.apiKey,
|
|
1966
|
-
...input.yieldseekerSignature ? {
|
|
2006
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
1967
2007
|
},
|
|
1968
2008
|
body: JSON.stringify(input.body)
|
|
1969
2009
|
});
|
|
@@ -2091,17 +2131,17 @@ function randomPermit2Nonce() {
|
|
|
2091
2131
|
globalThis.crypto.getRandomValues(bytes);
|
|
2092
2132
|
return BigInt(bytesToHex2(bytes));
|
|
2093
2133
|
}
|
|
2094
|
-
async function readPermit2Allowance(publicClient,
|
|
2134
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2095
2135
|
return publicClient.readContract({
|
|
2096
|
-
address:
|
|
2136
|
+
address: token,
|
|
2097
2137
|
abi: ERC20_ALLOWANCE_ABI,
|
|
2098
2138
|
functionName: "allowance",
|
|
2099
2139
|
args: [owner, PERMIT2_ADDRESS]
|
|
2100
2140
|
});
|
|
2101
2141
|
}
|
|
2102
|
-
async function readErc20Balance(publicClient,
|
|
2142
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
2103
2143
|
return publicClient.readContract({
|
|
2104
|
-
address:
|
|
2144
|
+
address: token,
|
|
2105
2145
|
abi: ERC20_ALLOWANCE_ABI,
|
|
2106
2146
|
functionName: "balanceOf",
|
|
2107
2147
|
args: [owner]
|
|
@@ -2133,8 +2173,8 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2133
2173
|
return makeVerificationAwareDepositCallback(
|
|
2134
2174
|
async (smartWallet, chainId, amount, verification) => {
|
|
2135
2175
|
const cid = chainId;
|
|
2136
|
-
const
|
|
2137
|
-
if (!
|
|
2176
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2177
|
+
if (!token) {
|
|
2138
2178
|
throw new OwneyError(
|
|
2139
2179
|
"CHAIN_UNSUPPORTED",
|
|
2140
2180
|
`No sponsored token configured for chain ${chainId}`
|
|
@@ -2144,12 +2184,12 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2144
2184
|
const wallet = deps.getWalletClient(cid);
|
|
2145
2185
|
await ensureWalletOnChain(pub, wallet, cid);
|
|
2146
2186
|
try {
|
|
2147
|
-
const balance = await readErc20Balance(pub,
|
|
2187
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2148
2188
|
if (balance < BigInt(amount)) {
|
|
2149
2189
|
throw new OwneyError(
|
|
2150
2190
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2151
2191
|
"Insufficient balance for this deposit.",
|
|
2152
|
-
{ token
|
|
2192
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2153
2193
|
);
|
|
2154
2194
|
}
|
|
2155
2195
|
} catch (err) {
|
|
@@ -2159,14 +2199,14 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2159
2199
|
err instanceof Error ? err.message : String(err)
|
|
2160
2200
|
);
|
|
2161
2201
|
}
|
|
2162
|
-
const { tokenName, tokenVersion } = await readTokenMeta(pub,
|
|
2202
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2163
2203
|
const validAfter = 0n;
|
|
2164
2204
|
const validBefore = BigInt(
|
|
2165
2205
|
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2166
2206
|
);
|
|
2167
2207
|
const nonce = randomAuthNonce();
|
|
2168
2208
|
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2169
|
-
token
|
|
2209
|
+
token,
|
|
2170
2210
|
chainId: cid,
|
|
2171
2211
|
tokenName,
|
|
2172
2212
|
tokenVersion,
|
|
@@ -2190,7 +2230,7 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2190
2230
|
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2191
2231
|
body: {
|
|
2192
2232
|
chainId: cid,
|
|
2193
|
-
token
|
|
2233
|
+
token,
|
|
2194
2234
|
from: deps.ownerAddress,
|
|
2195
2235
|
to: smartWallet,
|
|
2196
2236
|
value: amount,
|
|
@@ -2199,7 +2239,11 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
2199
2239
|
nonce,
|
|
2200
2240
|
authSignature,
|
|
2201
2241
|
tokenName,
|
|
2202
|
-
tokenVersion
|
|
2242
|
+
tokenVersion,
|
|
2243
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
2244
|
+
yieldseekerUserId: verification.userId,
|
|
2245
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
2246
|
+
} : {}
|
|
2203
2247
|
}
|
|
2204
2248
|
});
|
|
2205
2249
|
return result.txHash;
|
|
@@ -2218,7 +2262,11 @@ import {
|
|
|
2218
2262
|
import { base as base2 } from "viem/chains";
|
|
2219
2263
|
|
|
2220
2264
|
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2221
|
-
var KEY_PREFIX2 = "owney.yieldseeker.session";
|
|
2265
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session.v4";
|
|
2266
|
+
var INVALIDATED_KEY_PREFIXES = [
|
|
2267
|
+
"owney.yieldseeker.session",
|
|
2268
|
+
"owney.yieldseeker.session.v3"
|
|
2269
|
+
];
|
|
2222
2270
|
var storage2 = () => {
|
|
2223
2271
|
if (typeof window === "undefined") return null;
|
|
2224
2272
|
try {
|
|
@@ -2228,6 +2276,18 @@ var storage2 = () => {
|
|
|
2228
2276
|
}
|
|
2229
2277
|
};
|
|
2230
2278
|
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2279
|
+
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2280
|
+
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2281
|
+
);
|
|
2282
|
+
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2283
|
+
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2284
|
+
memorySessions2.delete(key2);
|
|
2285
|
+
try {
|
|
2286
|
+
store?.removeItem(key2);
|
|
2287
|
+
} catch {
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
};
|
|
2231
2291
|
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2232
2292
|
var isValidSession = (session) => {
|
|
2233
2293
|
if (!session?.token) return false;
|
|
@@ -2242,15 +2302,16 @@ var readYieldseekerSession = (address, chainId) => {
|
|
|
2242
2302
|
if (typeof window === "undefined") return null;
|
|
2243
2303
|
const key2 = buildKey2(address, chainId);
|
|
2244
2304
|
const store = storage2();
|
|
2245
|
-
|
|
2305
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2306
|
+
let raw2 = null;
|
|
2246
2307
|
try {
|
|
2247
|
-
|
|
2308
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
2248
2309
|
} catch {
|
|
2249
|
-
|
|
2310
|
+
raw2 = null;
|
|
2250
2311
|
}
|
|
2251
|
-
if (
|
|
2312
|
+
if (raw2) {
|
|
2252
2313
|
try {
|
|
2253
|
-
const parsed = JSON.parse(
|
|
2314
|
+
const parsed = JSON.parse(raw2);
|
|
2254
2315
|
if (isValidSession(parsed)) return parsed.token;
|
|
2255
2316
|
} catch {
|
|
2256
2317
|
}
|
|
@@ -2266,9 +2327,9 @@ var readYieldseekerSession = (address, chainId) => {
|
|
|
2266
2327
|
if (cached) memorySessions2.delete(key2);
|
|
2267
2328
|
return null;
|
|
2268
2329
|
};
|
|
2269
|
-
var writeYieldseekerSession = (address, chainId,
|
|
2330
|
+
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2270
2331
|
if (typeof window === "undefined") return;
|
|
2271
|
-
const session = { token
|
|
2332
|
+
const session = { token };
|
|
2272
2333
|
if (!isValidSession(session)) return;
|
|
2273
2334
|
const key2 = buildKey2(address, chainId);
|
|
2274
2335
|
memorySessions2.set(key2, session);
|
|
@@ -2282,6 +2343,7 @@ var clearYieldseekerSession = (address, chainId) => {
|
|
|
2282
2343
|
const key2 = buildKey2(address, chainId);
|
|
2283
2344
|
memorySessions2.delete(key2);
|
|
2284
2345
|
const store = storage2();
|
|
2346
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2285
2347
|
try {
|
|
2286
2348
|
store?.removeItem(key2);
|
|
2287
2349
|
} catch {
|
|
@@ -2300,8 +2362,8 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
|
2300
2362
|
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2301
2363
|
}).prepareMessage();
|
|
2302
2364
|
}
|
|
2303
|
-
function encodeYieldseekerAuthToken(
|
|
2304
|
-
const bytes = new TextEncoder().encode(JSON.stringify(
|
|
2365
|
+
function encodeYieldseekerAuthToken(token) {
|
|
2366
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2305
2367
|
let binary = "";
|
|
2306
2368
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2307
2369
|
return btoa(binary);
|
|
@@ -2330,10 +2392,10 @@ var YieldseekerAuth = class {
|
|
|
2330
2392
|
}
|
|
2331
2393
|
const inFlight = this.pending.get(key2);
|
|
2332
2394
|
if (inFlight) return inFlight;
|
|
2333
|
-
const request = this.sign(state, chainId).then((
|
|
2334
|
-
this.tokens.set(key2,
|
|
2335
|
-
writeYieldseekerSession(scope.address, scope.chainId,
|
|
2336
|
-
return
|
|
2395
|
+
const request = this.sign(state, chainId).then((token) => {
|
|
2396
|
+
this.tokens.set(key2, token);
|
|
2397
|
+
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2398
|
+
return token;
|
|
2337
2399
|
});
|
|
2338
2400
|
this.pending.set(key2, request);
|
|
2339
2401
|
try {
|
|
@@ -2384,6 +2446,72 @@ var YieldseekerAuth = class {
|
|
|
2384
2446
|
}
|
|
2385
2447
|
};
|
|
2386
2448
|
|
|
2449
|
+
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2450
|
+
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2451
|
+
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2452
|
+
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2453
|
+
var storage3 = () => {
|
|
2454
|
+
if (typeof window === "undefined") return null;
|
|
2455
|
+
try {
|
|
2456
|
+
return window.localStorage;
|
|
2457
|
+
} catch {
|
|
2458
|
+
return null;
|
|
2459
|
+
}
|
|
2460
|
+
};
|
|
2461
|
+
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2462
|
+
function valid(value, walletAddress, chainId, now) {
|
|
2463
|
+
return Boolean(
|
|
2464
|
+
value && typeof value.userId === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(value.userId) && value.walletAddress.toLowerCase() === walletAddress.toLowerCase() && value.chainId === chainId && Number.isSafeInteger(value.expiresAt) && value.expiresAt > now
|
|
2465
|
+
);
|
|
2466
|
+
}
|
|
2467
|
+
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2468
|
+
if (typeof window === "undefined") return null;
|
|
2469
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2470
|
+
const store = storage3();
|
|
2471
|
+
let parsed = null;
|
|
2472
|
+
try {
|
|
2473
|
+
const raw2 = store?.getItem(key2);
|
|
2474
|
+
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2475
|
+
} catch {
|
|
2476
|
+
parsed = null;
|
|
2477
|
+
}
|
|
2478
|
+
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2479
|
+
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2480
|
+
memoryIdentities.set(key2, candidate);
|
|
2481
|
+
return { userId: candidate.userId };
|
|
2482
|
+
}
|
|
2483
|
+
memoryIdentities.delete(key2);
|
|
2484
|
+
try {
|
|
2485
|
+
store?.removeItem(key2);
|
|
2486
|
+
} catch {
|
|
2487
|
+
}
|
|
2488
|
+
return null;
|
|
2489
|
+
}
|
|
2490
|
+
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2491
|
+
if (typeof window === "undefined") return;
|
|
2492
|
+
const identity = {
|
|
2493
|
+
userId,
|
|
2494
|
+
walletAddress,
|
|
2495
|
+
chainId,
|
|
2496
|
+
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2497
|
+
};
|
|
2498
|
+
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2499
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2500
|
+
memoryIdentities.set(key2, identity);
|
|
2501
|
+
try {
|
|
2502
|
+
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2503
|
+
} catch {
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2507
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2508
|
+
memoryIdentities.delete(key2);
|
|
2509
|
+
try {
|
|
2510
|
+
storage3()?.removeItem(key2);
|
|
2511
|
+
} catch {
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2387
2515
|
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2388
2516
|
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2389
2517
|
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
@@ -2434,7 +2562,7 @@ var YieldseekerApiClient = class {
|
|
|
2434
2562
|
headers: {
|
|
2435
2563
|
"Content-Type": "application/json",
|
|
2436
2564
|
"x-owney-api-key": this.owneyApiKey,
|
|
2437
|
-
...options.signature ? {
|
|
2565
|
+
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2438
2566
|
},
|
|
2439
2567
|
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2440
2568
|
signal: controller.signal
|
|
@@ -2467,25 +2595,21 @@ var YieldseekerApiClient = class {
|
|
|
2467
2595
|
};
|
|
2468
2596
|
|
|
2469
2597
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2470
|
-
import { formatUnits } from "viem";
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
);
|
|
2482
|
-
}
|
|
2483
|
-
try {
|
|
2484
|
-
return formatUnits(BigInt(value), decimals);
|
|
2485
|
-
} catch {
|
|
2486
|
-
return invalid("history", `invalid rebalance amount ${value}`);
|
|
2598
|
+
import { formatUnits, isAddress } from "viem";
|
|
2599
|
+
|
|
2600
|
+
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2601
|
+
var YIELDSEEKER_ASSET_METADATA = {
|
|
2602
|
+
USDC: {
|
|
2603
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2604
|
+
decimals: 6
|
|
2605
|
+
},
|
|
2606
|
+
WETH: {
|
|
2607
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
2608
|
+
decimals: 18
|
|
2487
2609
|
}
|
|
2488
|
-
}
|
|
2610
|
+
};
|
|
2611
|
+
|
|
2612
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2489
2613
|
function invalid(endpoint, detail) {
|
|
2490
2614
|
throw new OwneyError(
|
|
2491
2615
|
"AGENT_INVALID_RESPONSE",
|
|
@@ -2494,193 +2618,313 @@ function invalid(endpoint, detail) {
|
|
|
2494
2618
|
"yieldseeker"
|
|
2495
2619
|
);
|
|
2496
2620
|
}
|
|
2497
|
-
function
|
|
2498
|
-
if (
|
|
2499
|
-
return invalid(endpoint, "expected
|
|
2621
|
+
function raw(value, endpoint) {
|
|
2622
|
+
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2623
|
+
return invalid(endpoint, "expected a base-10 integer string");
|
|
2500
2624
|
}
|
|
2501
|
-
return value;
|
|
2625
|
+
return BigInt(value);
|
|
2502
2626
|
}
|
|
2503
|
-
function
|
|
2504
|
-
|
|
2505
|
-
return requireObject(root[key2], endpoint);
|
|
2627
|
+
function decimal(value, decimals, endpoint) {
|
|
2628
|
+
return formatUnits(raw(value, endpoint), decimals);
|
|
2506
2629
|
}
|
|
2507
|
-
function
|
|
2508
|
-
return
|
|
2509
|
-
chain: value.chain,
|
|
2510
|
-
chainId: value.chainId,
|
|
2511
|
-
asset: value.asset,
|
|
2512
|
-
amount: String(value.amount)
|
|
2513
|
-
};
|
|
2630
|
+
function usd(rawAmount, decimals, price) {
|
|
2631
|
+
return Number(formatUnits(rawAmount, decimals)) * price;
|
|
2514
2632
|
}
|
|
2515
|
-
function
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
protocol: String(value.protocol),
|
|
2519
|
-
...value.protocolId ? { protocolId: value.protocolId } : {},
|
|
2520
|
-
...value.pool ? { pool: value.pool } : {},
|
|
2521
|
-
asset: String(value.asset),
|
|
2522
|
-
amount: String(value.amount),
|
|
2523
|
-
...value.amountRaw !== void 0 ? { amountRaw: value.amountRaw } : {},
|
|
2524
|
-
...typeof value.apy === "number" ? { apy: value.apy } : {},
|
|
2525
|
-
...typeof value.tvl === "number" ? { tvl: value.tvl } : {},
|
|
2526
|
-
...typeof value.liquidity === "number" ? { liquidity: value.liquidity } : {}
|
|
2527
|
-
};
|
|
2633
|
+
function percent(value) {
|
|
2634
|
+
const result = Number(value);
|
|
2635
|
+
return Number.isFinite(result) ? result * 100 : 0;
|
|
2528
2636
|
}
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
for (const [asset, apy] of Object.entries(assets)) {
|
|
2536
|
-
if ((asset === "USDC" || asset === "WETH") && typeof apy === "number") {
|
|
2537
|
-
mapped[8453][asset] = apy;
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
return mapped;
|
|
2637
|
+
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2638
|
+
function publicApyAfterYieldseekerFee(value) {
|
|
2639
|
+
const grossPercent = percent(value);
|
|
2640
|
+
if (grossPercent <= 0) return grossPercent;
|
|
2641
|
+
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2642
|
+
return Math.round(netPercent * 1e12) / 1e12;
|
|
2542
2643
|
}
|
|
2543
|
-
function
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2644
|
+
function riskAdjustedApyForDays(option, days) {
|
|
2645
|
+
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2646
|
+
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2647
|
+
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2648
|
+
}
|
|
2649
|
+
function assetAddressValue(record, address) {
|
|
2650
|
+
const entry = Object.entries(record).find(
|
|
2651
|
+
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2548
2652
|
);
|
|
2549
|
-
|
|
2550
|
-
|
|
2653
|
+
return entry?.[1] ?? "0";
|
|
2654
|
+
}
|
|
2655
|
+
function position(value, asset) {
|
|
2656
|
+
const option = value?.yieldOption;
|
|
2657
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress(option.address)) {
|
|
2658
|
+
return invalid("yield positions", "missing vault metadata");
|
|
2551
2659
|
}
|
|
2660
|
+
const decimals = YIELDSEEKER_ASSET_METADATA[asset].decimals;
|
|
2552
2661
|
return {
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2662
|
+
chain: "BASE",
|
|
2663
|
+
protocol: option.provider,
|
|
2664
|
+
protocolId: option.address,
|
|
2665
|
+
pool: option.name,
|
|
2666
|
+
asset,
|
|
2667
|
+
amount: decimal(value.assetsRaw, decimals, "yield positions"),
|
|
2668
|
+
amountRaw: String(value.assetsRaw),
|
|
2669
|
+
apy: percent(option.riskAdjustedApy),
|
|
2670
|
+
tvl: Number(option.totalDepositsUsd),
|
|
2671
|
+
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2558
2672
|
};
|
|
2559
2673
|
}
|
|
2560
|
-
function mapYieldseekerBalances(
|
|
2561
|
-
const
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2674
|
+
function mapYieldseekerBalances(contexts) {
|
|
2675
|
+
const tokens = [];
|
|
2676
|
+
const positions = [];
|
|
2677
|
+
let totalUsd = 0;
|
|
2678
|
+
for (const context of contexts) {
|
|
2679
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2680
|
+
const idle = assetAddressValue(
|
|
2681
|
+
context.snapshot.tokenBalances,
|
|
2682
|
+
metadata.address
|
|
2683
|
+
);
|
|
2684
|
+
tokens.push({
|
|
2685
|
+
chain: "BASE",
|
|
2686
|
+
chainId: 8453,
|
|
2687
|
+
asset: context.asset,
|
|
2688
|
+
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2689
|
+
});
|
|
2690
|
+
positions.push(
|
|
2691
|
+
...context.positions.map((entry) => position(entry, context.asset))
|
|
2692
|
+
);
|
|
2693
|
+
totalUsd += usd(
|
|
2694
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2695
|
+
context.snapshot.baseAssetDecimals,
|
|
2696
|
+
context.snapshot.baseAssetPriceUsd
|
|
2697
|
+
);
|
|
2568
2698
|
}
|
|
2569
2699
|
return {
|
|
2570
|
-
smartWallet:
|
|
2571
|
-
totalBalance:
|
|
2572
|
-
totalBalanceAsset:
|
|
2573
|
-
tokens
|
|
2574
|
-
positions
|
|
2700
|
+
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2701
|
+
totalBalance: String(totalUsd),
|
|
2702
|
+
totalBalanceAsset: "usdc",
|
|
2703
|
+
tokens,
|
|
2704
|
+
positions
|
|
2575
2705
|
};
|
|
2576
2706
|
}
|
|
2577
|
-
function mapYieldseekerEarnings(
|
|
2578
|
-
const
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
"
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2707
|
+
function mapYieldseekerEarnings(contexts) {
|
|
2708
|
+
const tokens = [];
|
|
2709
|
+
let lifetimeEarnings = 0;
|
|
2710
|
+
for (const context of contexts) {
|
|
2711
|
+
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
2712
|
+
tokens.push({
|
|
2713
|
+
chain: "BASE",
|
|
2714
|
+
chainId: 8453,
|
|
2715
|
+
asset: context.asset,
|
|
2716
|
+
amount: formatUnits(amount, context.snapshot.baseAssetDecimals)
|
|
2717
|
+
});
|
|
2718
|
+
lifetimeEarnings += usd(
|
|
2719
|
+
amount,
|
|
2720
|
+
context.snapshot.baseAssetDecimals,
|
|
2721
|
+
context.snapshot.baseAssetPriceUsd
|
|
2722
|
+
);
|
|
2585
2723
|
}
|
|
2586
2724
|
return {
|
|
2587
|
-
smartWallet:
|
|
2588
|
-
lifetimeEarnings
|
|
2589
|
-
tokens
|
|
2725
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2726
|
+
lifetimeEarnings,
|
|
2727
|
+
tokens
|
|
2590
2728
|
};
|
|
2591
2729
|
}
|
|
2592
|
-
function
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2730
|
+
function apyForDays(snapshot, days) {
|
|
2731
|
+
if (days === "7D") return percent(snapshot.apy7d);
|
|
2732
|
+
if (days === "30D") return percent(snapshot.apy30d);
|
|
2733
|
+
return percent((snapshot.apy7d + snapshot.apy30d) / 2);
|
|
2734
|
+
}
|
|
2735
|
+
function dailyApy(point) {
|
|
2736
|
+
const total = raw(point.totalValueBase, "historic position");
|
|
2737
|
+
const earned = raw(point.dailyYieldBase, "historic position");
|
|
2738
|
+
const principal = total - earned;
|
|
2739
|
+
if (principal <= 0n || earned === 0n) return 0;
|
|
2740
|
+
return Number(earned) / Number(principal) * 365 * 100;
|
|
2741
|
+
}
|
|
2742
|
+
function aggregateHistory(contexts, dayCount) {
|
|
2743
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
2744
|
+
for (const context of contexts) {
|
|
2745
|
+
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
2746
|
+
for (const point of points.slice(-dayCount)) {
|
|
2747
|
+
const valueUsd = usd(
|
|
2748
|
+
raw(point.totalValueBase, "historic position"),
|
|
2749
|
+
point.baseAssetDecimals,
|
|
2750
|
+
point.baseAssetPriceUsd
|
|
2751
|
+
);
|
|
2752
|
+
const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
|
|
2753
|
+
current.weighted += dailyApy(point) * valueUsd;
|
|
2754
|
+
current.valueUsd += valueUsd;
|
|
2755
|
+
byDate.set(point.date, current);
|
|
2756
|
+
}
|
|
2600
2757
|
}
|
|
2758
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
2759
|
+
date,
|
|
2760
|
+
apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
|
|
2761
|
+
}));
|
|
2762
|
+
}
|
|
2763
|
+
function mapYieldseekerApy(walletAddress, contexts, days) {
|
|
2764
|
+
let weighted = 0;
|
|
2765
|
+
let totalUsd = 0;
|
|
2766
|
+
const byAsset = {};
|
|
2767
|
+
for (const context of contexts) {
|
|
2768
|
+
const valueUsd = usd(
|
|
2769
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2770
|
+
context.snapshot.baseAssetDecimals,
|
|
2771
|
+
context.snapshot.baseAssetPriceUsd
|
|
2772
|
+
);
|
|
2773
|
+
const apy = apyForDays(context.snapshot, days);
|
|
2774
|
+
weighted += apy * valueUsd;
|
|
2775
|
+
totalUsd += valueUsd;
|
|
2776
|
+
byAsset[context.asset] = apy;
|
|
2777
|
+
}
|
|
2778
|
+
const dayCount = Number(days.slice(0, -1));
|
|
2601
2779
|
return {
|
|
2602
|
-
walletAddress
|
|
2603
|
-
weightedApyAfterFee:
|
|
2604
|
-
apyByChainAndAsset:
|
|
2605
|
-
history:
|
|
2606
|
-
date: String(point.date),
|
|
2607
|
-
apy: Number(point.apy)
|
|
2608
|
-
}))
|
|
2780
|
+
walletAddress,
|
|
2781
|
+
weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
|
|
2782
|
+
apyByChainAndAsset: { 8453: byAsset },
|
|
2783
|
+
history: aggregateHistory(contexts, dayCount)
|
|
2609
2784
|
};
|
|
2610
2785
|
}
|
|
2611
|
-
function
|
|
2612
|
-
const normalized = value.
|
|
2613
|
-
if (normalized
|
|
2614
|
-
if (normalized
|
|
2615
|
-
if (normalized
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2786
|
+
function actionType(value) {
|
|
2787
|
+
const normalized = value.toLowerCase();
|
|
2788
|
+
if (normalized.includes("deposit")) return "Deposit";
|
|
2789
|
+
if (normalized.includes("withdraw")) return "Withdraw";
|
|
2790
|
+
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
2791
|
+
return "Earned";
|
|
2792
|
+
return "Rebalance";
|
|
2793
|
+
}
|
|
2794
|
+
function transactionHashes(details) {
|
|
2795
|
+
if (!details) return [];
|
|
2796
|
+
const values = [
|
|
2797
|
+
details.transactionHash,
|
|
2798
|
+
details.txHash,
|
|
2799
|
+
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
2800
|
+
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
2801
|
+
];
|
|
2802
|
+
return values.filter(
|
|
2803
|
+
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
2804
|
+
).filter((value, index, all) => all.indexOf(value) === index);
|
|
2620
2805
|
}
|
|
2621
|
-
function
|
|
2806
|
+
function actionEntry(action) {
|
|
2622
2807
|
return {
|
|
2623
2808
|
agent: "yieldseeker",
|
|
2624
|
-
action:
|
|
2625
|
-
date:
|
|
2626
|
-
oldApy:
|
|
2627
|
-
newApy:
|
|
2628
|
-
transactions:
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
rebalanceLog:
|
|
2635
|
-
fromProtocol: String(entry.fromProtocol),
|
|
2636
|
-
toProtocol: String(entry.toProtocol),
|
|
2637
|
-
...entry.fromPool ? { fromPool: entry.fromPool } : {},
|
|
2638
|
-
...entry.toPool ? { toPool: entry.toPool } : {},
|
|
2639
|
-
tokenSymbol: String(entry.tokenSymbol),
|
|
2640
|
-
// Unlike Yieldseeker's other read-response amounts, live history
|
|
2641
|
-
// rebalance amounts are returned in the token's smallest unit. Normalize
|
|
2642
|
-
// them at the adapter boundary so every Owney consumer receives the
|
|
2643
|
-
// shared human-readable decimal shape.
|
|
2644
|
-
amount: rebalanceAmount(String(entry.amount), String(entry.tokenSymbol)),
|
|
2645
|
-
status: entry.status
|
|
2646
|
-
}))
|
|
2809
|
+
action: actionType(action.actionType),
|
|
2810
|
+
date: action.createdDate,
|
|
2811
|
+
oldApy: null,
|
|
2812
|
+
newApy: null,
|
|
2813
|
+
transactions: [
|
|
2814
|
+
{
|
|
2815
|
+
txHashes: transactionHashes(action.details),
|
|
2816
|
+
chainId: 8453
|
|
2817
|
+
}
|
|
2818
|
+
],
|
|
2819
|
+
rebalanceLog: []
|
|
2647
2820
|
};
|
|
2648
2821
|
}
|
|
2649
|
-
function
|
|
2650
|
-
const
|
|
2651
|
-
|
|
2652
|
-
"
|
|
2653
|
-
"
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2822
|
+
function movementEntry(movement, wallet, agent, asset) {
|
|
2823
|
+
const isDeposit = movement.toAddress.toLowerCase() === wallet.walletAddress.toLowerCase();
|
|
2824
|
+
return {
|
|
2825
|
+
agent: "yieldseeker",
|
|
2826
|
+
action: isDeposit ? "Deposit" : "Withdraw",
|
|
2827
|
+
date: movement.blockDate,
|
|
2828
|
+
oldApy: null,
|
|
2829
|
+
newApy: null,
|
|
2830
|
+
transactions: [
|
|
2831
|
+
{
|
|
2832
|
+
txHashes: [movement.transactionHash],
|
|
2833
|
+
chainId: agent.chainId,
|
|
2834
|
+
tokenSymbol: asset,
|
|
2835
|
+
amount: decimal(
|
|
2836
|
+
movement.assetAmount,
|
|
2837
|
+
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
2838
|
+
"historic position"
|
|
2839
|
+
)
|
|
2840
|
+
}
|
|
2841
|
+
],
|
|
2842
|
+
rebalanceLog: []
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
function mapYieldseekerHistory(contexts, options) {
|
|
2846
|
+
const entries = contexts.flatMap((context) => [
|
|
2847
|
+
...(context.actions ?? []).map(actionEntry),
|
|
2848
|
+
...(context.historic?.movements ?? []).map(
|
|
2849
|
+
(movement) => movementEntry(movement, context.wallet, context.agent, context.asset)
|
|
2850
|
+
)
|
|
2851
|
+
]);
|
|
2852
|
+
const filtered = entries.filter(
|
|
2853
|
+
(entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
|
|
2854
|
+
).filter(
|
|
2855
|
+
(entry, index, all) => all.findIndex(
|
|
2856
|
+
(candidate) => candidate.action === entry.action && candidate.date === entry.date && candidate.transactions[0]?.txHashes[0] === entry.transactions[0]?.txHashes[0]
|
|
2857
|
+
) === index
|
|
2858
|
+
).sort((left, right) => right.date.localeCompare(left.date));
|
|
2859
|
+
return {
|
|
2860
|
+
data: filtered.slice(0, options.limit),
|
|
2861
|
+
// v1 returns the whole action/movement collection and defines no cursor.
|
|
2862
|
+
// Report a terminal page so callers never loop over the same prefix.
|
|
2863
|
+
hasMore: false
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
function mapYieldseekerProfile(address, contexts) {
|
|
2867
|
+
const protocols = /* @__PURE__ */ new Set();
|
|
2868
|
+
for (const context of contexts) {
|
|
2869
|
+
for (const current of context.positions) {
|
|
2870
|
+
if (current.yieldOption?.provider) {
|
|
2871
|
+
protocols.add(String(current.yieldOption.provider));
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2657
2874
|
}
|
|
2658
2875
|
return {
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2876
|
+
address,
|
|
2877
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2878
|
+
chains: contexts.length > 0 ? [8453] : [],
|
|
2879
|
+
hasActiveSessionKey: contexts.some(
|
|
2880
|
+
(context) => context.wallet.initializedDate != null
|
|
2881
|
+
),
|
|
2882
|
+
protocols: [...protocols]
|
|
2662
2883
|
};
|
|
2663
2884
|
}
|
|
2664
|
-
function mapYieldseekerAgentApy(
|
|
2665
|
-
const
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2885
|
+
function mapYieldseekerAgentApy(options, days) {
|
|
2886
|
+
const perAsset = {};
|
|
2887
|
+
const all = [];
|
|
2888
|
+
for (const entry of options) {
|
|
2889
|
+
const apys = entry.yieldOptions.map(
|
|
2890
|
+
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
2891
|
+
).filter(Number.isFinite);
|
|
2892
|
+
if (apys.length === 0) continue;
|
|
2893
|
+
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
2894
|
+
perAsset[entry.asset] = average;
|
|
2895
|
+
all.push(average);
|
|
2672
2896
|
}
|
|
2673
2897
|
return {
|
|
2674
|
-
averageApy:
|
|
2675
|
-
|
|
2676
|
-
detailedApys: {
|
|
2677
|
-
apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
|
|
2678
|
-
}
|
|
2679
|
-
} : {}
|
|
2898
|
+
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
2899
|
+
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
2680
2900
|
};
|
|
2681
2901
|
}
|
|
2682
2902
|
|
|
2683
2903
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2904
|
+
var OWNEY_AGENT_NAME = "owney";
|
|
2905
|
+
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
2906
|
+
{
|
|
2907
|
+
type: "function",
|
|
2908
|
+
name: "withdrawAssetToUser",
|
|
2909
|
+
stateMutability: "nonpayable",
|
|
2910
|
+
inputs: [
|
|
2911
|
+
{ name: "recipient", type: "address" },
|
|
2912
|
+
{ name: "asset", type: "address" },
|
|
2913
|
+
{ name: "amount", type: "uint256" }
|
|
2914
|
+
],
|
|
2915
|
+
outputs: []
|
|
2916
|
+
},
|
|
2917
|
+
{
|
|
2918
|
+
type: "function",
|
|
2919
|
+
name: "withdrawAllAssetToUser",
|
|
2920
|
+
stateMutability: "nonpayable",
|
|
2921
|
+
inputs: [
|
|
2922
|
+
{ name: "recipient", type: "address" },
|
|
2923
|
+
{ name: "asset", type: "address" }
|
|
2924
|
+
],
|
|
2925
|
+
outputs: []
|
|
2926
|
+
}
|
|
2927
|
+
];
|
|
2684
2928
|
function query(params) {
|
|
2685
2929
|
const search = new URLSearchParams();
|
|
2686
2930
|
for (const [key2, value] of Object.entries(params)) {
|
|
@@ -2698,7 +2942,7 @@ var YieldseekerAgent = class {
|
|
|
2698
2942
|
chainId: 8453,
|
|
2699
2943
|
chain: "BASE",
|
|
2700
2944
|
assets: [
|
|
2701
|
-
{ symbol: "USDC", minDepositAmount: "
|
|
2945
|
+
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
2702
2946
|
{ symbol: "WETH", minDepositAmount: "1" }
|
|
2703
2947
|
]
|
|
2704
2948
|
}
|
|
@@ -2707,7 +2951,9 @@ var YieldseekerAgent = class {
|
|
|
2707
2951
|
auth;
|
|
2708
2952
|
transactionExecutor;
|
|
2709
2953
|
unwindReceiptWaiter;
|
|
2710
|
-
|
|
2954
|
+
agentContexts = /* @__PURE__ */ new Map();
|
|
2955
|
+
users = /* @__PURE__ */ new Map();
|
|
2956
|
+
pendingAgents = /* @__PURE__ */ new Map();
|
|
2711
2957
|
constructor(owneyApiKey, options = {}) {
|
|
2712
2958
|
this.api = new YieldseekerApiClient(
|
|
2713
2959
|
owneyApiKey,
|
|
@@ -2720,14 +2966,19 @@ var YieldseekerAgent = class {
|
|
|
2720
2966
|
}
|
|
2721
2967
|
async disconnect() {
|
|
2722
2968
|
this.auth.clear();
|
|
2723
|
-
this.
|
|
2969
|
+
for (const key2 of this.users.keys()) {
|
|
2970
|
+
const [walletAddress, chainId] = key2.split(":");
|
|
2971
|
+
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
2972
|
+
}
|
|
2973
|
+
this.users.clear();
|
|
2974
|
+
this.agentContexts.clear();
|
|
2975
|
+
this.pendingAgents.clear();
|
|
2724
2976
|
}
|
|
2725
2977
|
async activateAgent(state, chainId, asset) {
|
|
2726
2978
|
this.assertChain(chainId);
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
}
|
|
2979
|
+
const targetAsset = asset ?? "USDC";
|
|
2980
|
+
this.assertAsset(targetAsset);
|
|
2981
|
+
await this.ensureAgent(state, chainId, targetAsset);
|
|
2731
2982
|
}
|
|
2732
2983
|
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
2733
2984
|
this.assertChain(chainId);
|
|
@@ -2740,42 +2991,48 @@ var YieldseekerAgent = class {
|
|
|
2740
2991
|
this.id
|
|
2741
2992
|
);
|
|
2742
2993
|
}
|
|
2743
|
-
await this.
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2994
|
+
const context = await this.ensureAgent(state, chainId, asset);
|
|
2995
|
+
let txHash;
|
|
2996
|
+
try {
|
|
2997
|
+
if (depositCallback) {
|
|
2998
|
+
provideDepositVerificationContext(depositCallback, {
|
|
2999
|
+
agentId: "yieldseeker",
|
|
3000
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3001
|
+
userId: context.user.userId,
|
|
3002
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3003
|
+
});
|
|
3004
|
+
txHash = await depositCallback(
|
|
3005
|
+
context.wallet.walletAddress,
|
|
3006
|
+
chainId,
|
|
3007
|
+
amount
|
|
3008
|
+
);
|
|
3009
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3010
|
+
} else {
|
|
3011
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3012
|
+
from: getAddress2(state.walletAddress),
|
|
3013
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3014
|
+
data: encodeFunctionData({
|
|
3015
|
+
abi: erc20Abi,
|
|
3016
|
+
functionName: "transfer",
|
|
3017
|
+
args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
|
|
3018
|
+
}),
|
|
3019
|
+
value: "0",
|
|
3020
|
+
chainId
|
|
3021
|
+
});
|
|
3022
|
+
}
|
|
3023
|
+
await this.deployAfterFunding(state, chainId, context);
|
|
3024
|
+
} finally {
|
|
3025
|
+
await this.refreshSnapshotAfterMovement(
|
|
3026
|
+
state,
|
|
3027
|
+
chainId,
|
|
3028
|
+
context,
|
|
3029
|
+
"deposit"
|
|
3030
|
+
);
|
|
2774
3031
|
}
|
|
2775
3032
|
return {
|
|
2776
3033
|
txHash,
|
|
2777
|
-
smartWallet:
|
|
2778
|
-
amount
|
|
3034
|
+
smartWallet: context.wallet.walletAddress,
|
|
3035
|
+
amount
|
|
2779
3036
|
};
|
|
2780
3037
|
}
|
|
2781
3038
|
async withdraw(state, chainId, asset, amount) {
|
|
@@ -2789,198 +3046,454 @@ var YieldseekerAgent = class {
|
|
|
2789
3046
|
this.id
|
|
2790
3047
|
);
|
|
2791
3048
|
}
|
|
2792
|
-
const
|
|
2793
|
-
|
|
2794
|
-
const targetAsset = asset.toUpperCase();
|
|
2795
|
-
const idle = balances.tokens.filter(
|
|
2796
|
-
(token2) => token2.chainId === chainId && token2.asset.toUpperCase() === targetAsset
|
|
2797
|
-
).reduce((total, token2) => total + parseUnits(token2.amount, decimals), 0n);
|
|
2798
|
-
const positions = (balances.positions ?? []).filter(
|
|
2799
|
-
(position2) => this.positionMatchesChain(position2.chain, chainId) && position2.asset.toUpperCase() === targetAsset
|
|
2800
|
-
).map((position2) => ({
|
|
2801
|
-
position: position2,
|
|
2802
|
-
amount: position2.amountRaw !== void 0 ? BigInt(position2.amountRaw) : parseUnits(position2.amount, decimals)
|
|
2803
|
-
}));
|
|
2804
|
-
const deployed = positions.reduce(
|
|
2805
|
-
(total, position2) => total + position2.amount,
|
|
2806
|
-
0n
|
|
2807
|
-
);
|
|
2808
|
-
const totalAvailable = idle + deployed;
|
|
2809
|
-
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
2810
|
-
if (requested > totalAvailable) {
|
|
3049
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
3050
|
+
if (!context) {
|
|
2811
3051
|
throw new OwneyError(
|
|
2812
3052
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2813
|
-
`
|
|
2814
|
-
{
|
|
2815
|
-
asset,
|
|
2816
|
-
requested: requested.toString(),
|
|
2817
|
-
available: totalAvailable.toString()
|
|
2818
|
-
},
|
|
3053
|
+
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3054
|
+
{ asset, available: "0" },
|
|
2819
3055
|
this.id
|
|
2820
3056
|
);
|
|
2821
3057
|
}
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
3058
|
+
try {
|
|
3059
|
+
const portfolio = await this.loadPortfolioContext(
|
|
3060
|
+
state,
|
|
3061
|
+
chainId,
|
|
3062
|
+
context
|
|
3063
|
+
);
|
|
3064
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3065
|
+
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3066
|
+
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3067
|
+
);
|
|
3068
|
+
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3069
|
+
const deployed = portfolio.positions.reduce(
|
|
3070
|
+
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3071
|
+
0n
|
|
3072
|
+
);
|
|
3073
|
+
const totalAvailable = idle + deployed;
|
|
3074
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3075
|
+
if (requested > totalAvailable) {
|
|
3076
|
+
throw new OwneyError(
|
|
3077
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3078
|
+
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3079
|
+
{
|
|
3080
|
+
asset,
|
|
3081
|
+
requested: requested.toString(),
|
|
3082
|
+
available: totalAvailable.toString()
|
|
3083
|
+
},
|
|
3084
|
+
this.id
|
|
3085
|
+
);
|
|
3086
|
+
}
|
|
3087
|
+
let remaining = requested > idle ? requested - idle : 0n;
|
|
3088
|
+
for (const position2 of portfolio.positions) {
|
|
3089
|
+
if (remaining === 0n) break;
|
|
3090
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3091
|
+
if (available <= 0n) continue;
|
|
3092
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
3093
|
+
const response = await this.walletRequest(
|
|
3094
|
+
state,
|
|
3095
|
+
chainId,
|
|
3096
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
3097
|
+
{
|
|
3098
|
+
method: "POST",
|
|
3099
|
+
body: {
|
|
3100
|
+
chainId,
|
|
3101
|
+
vaultAddress: position2.yieldOption.address,
|
|
3102
|
+
assetsRaw: assetsRaw.toString()
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
);
|
|
3106
|
+
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3107
|
+
throw this.invalidResponse("position withdrawal");
|
|
3108
|
+
}
|
|
3109
|
+
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3110
|
+
remaining -= assetsRaw;
|
|
3111
|
+
}
|
|
3112
|
+
if (remaining > 0n) {
|
|
3113
|
+
throw this.invalidResponse("yield positions", {
|
|
3114
|
+
reason: "Withdrawable positions could not cover the request.",
|
|
3115
|
+
remaining: remaining.toString()
|
|
2833
3116
|
});
|
|
2834
3117
|
}
|
|
2835
|
-
const
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
3118
|
+
const account = getAddress2(state.walletAddress);
|
|
3119
|
+
const txHash = await this.submitTransaction(state, chainId, {
|
|
3120
|
+
from: account,
|
|
3121
|
+
to: getAddress2(context.wallet.walletAddress),
|
|
3122
|
+
data: amount === void 0 ? encodeFunctionData({
|
|
3123
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3124
|
+
functionName: "withdrawAllAssetToUser",
|
|
3125
|
+
args: [account, metadata.address]
|
|
3126
|
+
}) : encodeFunctionData({
|
|
3127
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3128
|
+
functionName: "withdrawAssetToUser",
|
|
3129
|
+
args: [account, metadata.address, requested]
|
|
3130
|
+
}),
|
|
3131
|
+
value: "0",
|
|
3132
|
+
chainId
|
|
2839
3133
|
});
|
|
2840
|
-
|
|
3134
|
+
return {
|
|
3135
|
+
txHash,
|
|
3136
|
+
type: amount === void 0 ? "full" : "partial",
|
|
3137
|
+
amount: requested.toString()
|
|
3138
|
+
};
|
|
3139
|
+
} finally {
|
|
3140
|
+
await this.refreshSnapshotAfterMovement(
|
|
3141
|
+
state,
|
|
3142
|
+
chainId,
|
|
3143
|
+
context,
|
|
3144
|
+
"withdrawal"
|
|
3145
|
+
);
|
|
2841
3146
|
}
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
3147
|
+
}
|
|
3148
|
+
async getBalances(state, chainId) {
|
|
3149
|
+
this.assertChain(chainId);
|
|
3150
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3151
|
+
}
|
|
3152
|
+
async getEarnings(state, chainId) {
|
|
3153
|
+
this.assertChain(chainId);
|
|
3154
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3155
|
+
}
|
|
3156
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3157
|
+
this.assertChain(chainId);
|
|
3158
|
+
const asset = tokenSymbol?.toUpperCase();
|
|
3159
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3160
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3161
|
+
...asset ? { asset } : {},
|
|
3162
|
+
historic: true
|
|
3163
|
+
});
|
|
3164
|
+
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3165
|
+
}
|
|
3166
|
+
async getHistory(state, chainId, options) {
|
|
3167
|
+
this.assertChain(chainId);
|
|
3168
|
+
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3169
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3170
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3171
|
+
...asset ? { asset } : {},
|
|
3172
|
+
historic: true,
|
|
3173
|
+
actions: true
|
|
3174
|
+
});
|
|
3175
|
+
return mapYieldseekerHistory(contexts, {
|
|
3176
|
+
limit: options?.limit ?? 10,
|
|
3177
|
+
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3178
|
+
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3181
|
+
async getUserProfile(state, chainId) {
|
|
3182
|
+
this.assertChain(chainId);
|
|
3183
|
+
return mapYieldseekerProfile(
|
|
3184
|
+
state.walletAddress,
|
|
3185
|
+
await this.loadPortfolio(state, chainId, {})
|
|
3186
|
+
);
|
|
3187
|
+
}
|
|
3188
|
+
async getAgentApy(days, options) {
|
|
3189
|
+
this.assertOptionalChain(options?.chainId);
|
|
3190
|
+
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3191
|
+
if (requested !== void 0) this.assertAsset(requested);
|
|
3192
|
+
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3193
|
+
const values = await Promise.all(
|
|
3194
|
+
assets.map(async (asset) => {
|
|
3195
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3196
|
+
const response = await this.api.request(
|
|
3197
|
+
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3198
|
+
);
|
|
3199
|
+
if (!Array.isArray(response?.yieldOptions)) {
|
|
3200
|
+
throw this.invalidResponse("yield options");
|
|
3201
|
+
}
|
|
3202
|
+
return { asset, yieldOptions: response.yieldOptions };
|
|
3203
|
+
})
|
|
3204
|
+
);
|
|
3205
|
+
return mapYieldseekerAgentApy(values, days);
|
|
3206
|
+
}
|
|
3207
|
+
userKey(state, chainId) {
|
|
3208
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3209
|
+
}
|
|
3210
|
+
contextKey(state, chainId, asset) {
|
|
3211
|
+
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3212
|
+
}
|
|
3213
|
+
async resolveUser(state, chainId) {
|
|
3214
|
+
const key2 = this.userKey(state, chainId);
|
|
3215
|
+
const inMemory = this.users.get(key2);
|
|
3216
|
+
if (inMemory) return inMemory;
|
|
3217
|
+
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3218
|
+
if (persisted) {
|
|
3219
|
+
this.users.set(key2, persisted);
|
|
3220
|
+
return persisted;
|
|
3221
|
+
}
|
|
3222
|
+
const walletAddress = getAddress2(state.walletAddress);
|
|
3223
|
+
let user = null;
|
|
3224
|
+
try {
|
|
3225
|
+
const login = await this.providerRequest(
|
|
3226
|
+
state,
|
|
3227
|
+
chainId,
|
|
3228
|
+
"/users/login-with-wallet",
|
|
3229
|
+
{ method: "POST", body: { walletAddress } }
|
|
3230
|
+
);
|
|
3231
|
+
user = login?.user ?? null;
|
|
3232
|
+
if (!user) {
|
|
3233
|
+
throw this.invalidResponse("wallet login", {
|
|
3234
|
+
reason: "A successful login returned no user."
|
|
3235
|
+
});
|
|
3236
|
+
}
|
|
3237
|
+
} catch (error) {
|
|
3238
|
+
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3239
|
+
if (error instanceof OwneyError) throw error;
|
|
3240
|
+
throw this.mapApiError(error);
|
|
3241
|
+
}
|
|
3242
|
+
let created;
|
|
3243
|
+
try {
|
|
3244
|
+
created = await this.providerRequest(
|
|
3245
|
+
state,
|
|
3246
|
+
chainId,
|
|
3247
|
+
"/users",
|
|
3248
|
+
{
|
|
3249
|
+
method: "POST",
|
|
3250
|
+
body: {
|
|
3251
|
+
walletAddress,
|
|
3252
|
+
username: `owney-${walletAddress.slice(2).toLowerCase()}`
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
);
|
|
3256
|
+
} catch (createError) {
|
|
3257
|
+
throw this.mapApiError(createError);
|
|
3258
|
+
}
|
|
3259
|
+
user = created?.user ?? null;
|
|
3260
|
+
}
|
|
3261
|
+
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3262
|
+
throw this.invalidResponse("wallet identity");
|
|
3263
|
+
}
|
|
3264
|
+
const resolved = { userId: user.userId };
|
|
3265
|
+
this.users.set(key2, resolved);
|
|
3266
|
+
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3267
|
+
return resolved;
|
|
3268
|
+
}
|
|
3269
|
+
forgetUser(state, chainId) {
|
|
3270
|
+
this.users.delete(this.userKey(state, chainId));
|
|
3271
|
+
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3272
|
+
}
|
|
3273
|
+
async ensureAgent(state, chainId, asset) {
|
|
3274
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3275
|
+
const cached = this.agentContexts.get(key2);
|
|
3276
|
+
if (cached) return cached;
|
|
3277
|
+
const pending = this.pendingAgents.get(key2);
|
|
3278
|
+
if (pending) return pending;
|
|
3279
|
+
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3280
|
+
(context) => {
|
|
3281
|
+
if (!context) throw this.invalidResponse("agent creation");
|
|
3282
|
+
this.agentContexts.set(key2, context);
|
|
3283
|
+
return context;
|
|
3284
|
+
}
|
|
3285
|
+
);
|
|
3286
|
+
this.pendingAgents.set(key2, request);
|
|
3287
|
+
try {
|
|
3288
|
+
return await request;
|
|
3289
|
+
} finally {
|
|
3290
|
+
this.pendingAgents.delete(key2);
|
|
2848
3291
|
}
|
|
2849
|
-
|
|
2850
|
-
|
|
3292
|
+
}
|
|
3293
|
+
async findAgent(state, chainId, asset) {
|
|
3294
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3295
|
+
const cached = this.agentContexts.get(key2);
|
|
3296
|
+
if (cached) return cached;
|
|
3297
|
+
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3298
|
+
if (context) this.agentContexts.set(key2, context);
|
|
3299
|
+
return context;
|
|
3300
|
+
}
|
|
3301
|
+
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3302
|
+
const user = await this.resolveUser(state, chainId);
|
|
3303
|
+
const response = await this.walletRequest(
|
|
3304
|
+
state,
|
|
3305
|
+
chainId,
|
|
3306
|
+
`/users/${user.userId}/agents`
|
|
3307
|
+
);
|
|
3308
|
+
if (!Array.isArray(response?.agents)) {
|
|
3309
|
+
throw this.invalidResponse("agent list");
|
|
3310
|
+
}
|
|
3311
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3312
|
+
let agent = response.agents.find(
|
|
3313
|
+
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3314
|
+
);
|
|
3315
|
+
if (!agent && createIfMissing) {
|
|
3316
|
+
const created = await this.walletRequest(
|
|
2851
3317
|
state,
|
|
2852
3318
|
chainId,
|
|
2853
|
-
|
|
3319
|
+
`/users/${user.userId}/agents`,
|
|
2854
3320
|
{
|
|
2855
3321
|
method: "POST",
|
|
2856
3322
|
body: {
|
|
3323
|
+
name: OWNEY_AGENT_NAME,
|
|
3324
|
+
emoji: "\u{1F989}",
|
|
2857
3325
|
chainId,
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
3326
|
+
assetAddress: metadata.address,
|
|
3327
|
+
type: "vault",
|
|
3328
|
+
rulePreset: null
|
|
2861
3329
|
}
|
|
2862
3330
|
}
|
|
2863
3331
|
);
|
|
2864
|
-
|
|
2865
|
-
if (!this.isTransactionHash(transactionHash)) {
|
|
2866
|
-
throw this.invalidResponse("position unwind");
|
|
2867
|
-
}
|
|
2868
|
-
await this.waitForUnwindReceipt(state, chainId, transactionHash);
|
|
3332
|
+
agent = created?.agent;
|
|
2869
3333
|
}
|
|
2870
|
-
|
|
3334
|
+
if (!agent) return null;
|
|
3335
|
+
this.assertAgent(agent);
|
|
3336
|
+
const walletResponse = await this.walletRequest(
|
|
2871
3337
|
state,
|
|
2872
3338
|
chainId,
|
|
2873
|
-
|
|
2874
|
-
{
|
|
2875
|
-
method: "POST",
|
|
2876
|
-
body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
|
|
2877
|
-
}
|
|
3339
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
2878
3340
|
);
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
throw this.invalidResponse("withdrawal");
|
|
3341
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3342
|
+
throw this.invalidResponse("agent wallet");
|
|
2882
3343
|
}
|
|
2883
|
-
|
|
3344
|
+
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3345
|
+
}
|
|
3346
|
+
async loadPortfolio(state, chainId, options) {
|
|
3347
|
+
const user = await this.resolveUser(state, chainId);
|
|
3348
|
+
const response = await this.walletRequest(
|
|
2884
3349
|
state,
|
|
2885
3350
|
chainId,
|
|
2886
|
-
|
|
3351
|
+
`/users/${user.userId}/agents`
|
|
2887
3352
|
);
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
3353
|
+
if (!Array.isArray(response?.agents)) {
|
|
3354
|
+
throw this.invalidResponse("agent list");
|
|
3355
|
+
}
|
|
3356
|
+
const contexts = [];
|
|
3357
|
+
for (const agent of response.agents) {
|
|
3358
|
+
const asset = this.assetForAgent(agent);
|
|
3359
|
+
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3360
|
+
continue;
|
|
3361
|
+
}
|
|
3362
|
+
this.assertAgent(agent);
|
|
3363
|
+
const walletResponse = await this.walletRequest(
|
|
2897
3364
|
state,
|
|
2898
3365
|
chainId,
|
|
2899
|
-
`/
|
|
3366
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3367
|
+
);
|
|
3368
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3369
|
+
throw this.invalidResponse("agent wallet");
|
|
3370
|
+
}
|
|
3371
|
+
const context = {
|
|
3372
|
+
user,
|
|
3373
|
+
agent,
|
|
3374
|
+
wallet: walletResponse.agentWallet,
|
|
3375
|
+
asset
|
|
3376
|
+
};
|
|
3377
|
+
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3378
|
+
contexts.push(context);
|
|
3379
|
+
}
|
|
3380
|
+
return Promise.all(
|
|
3381
|
+
contexts.map(
|
|
3382
|
+
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
2900
3383
|
)
|
|
2901
3384
|
);
|
|
2902
3385
|
}
|
|
2903
|
-
async
|
|
2904
|
-
|
|
2905
|
-
|
|
3386
|
+
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3387
|
+
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3388
|
+
this.walletRequest(
|
|
2906
3389
|
state,
|
|
2907
3390
|
chainId,
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
3391
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3392
|
+
shouldOnlyUseRecentValue: true,
|
|
3393
|
+
shouldAllowStaleOnError: true
|
|
3394
|
+
})}`
|
|
3395
|
+
),
|
|
3396
|
+
this.walletRequest(
|
|
3397
|
+
state,
|
|
3398
|
+
chainId,
|
|
3399
|
+
this.agentPath(context, "yield-positions")
|
|
3400
|
+
),
|
|
3401
|
+
options.historic ? this.walletRequest(
|
|
3402
|
+
state,
|
|
3403
|
+
chainId,
|
|
3404
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3405
|
+
) : Promise.resolve(void 0),
|
|
3406
|
+
options.actions ? this.walletRequest(
|
|
3407
|
+
state,
|
|
3408
|
+
chainId,
|
|
3409
|
+
this.agentPath(context, "actions")
|
|
3410
|
+
) : Promise.resolve(void 0)
|
|
3411
|
+
]);
|
|
3412
|
+
if (!snapshot?.agentSnapshot) {
|
|
3413
|
+
throw this.invalidResponse("agent snapshot");
|
|
3414
|
+
}
|
|
3415
|
+
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3416
|
+
throw this.invalidResponse("yield positions");
|
|
3417
|
+
}
|
|
3418
|
+
return {
|
|
3419
|
+
...context,
|
|
3420
|
+
snapshot: snapshot.agentSnapshot,
|
|
3421
|
+
positions: positions.yieldPositions,
|
|
3422
|
+
...historic?.position ? { historic: historic.position } : {},
|
|
3423
|
+
...actions?.actions ? { actions: actions.actions } : {}
|
|
3424
|
+
};
|
|
2911
3425
|
}
|
|
2912
|
-
async
|
|
2913
|
-
|
|
2914
|
-
|
|
3426
|
+
async deployAfterFunding(state, chainId, context) {
|
|
3427
|
+
if (context.wallet.initializedDate != null) return;
|
|
3428
|
+
try {
|
|
3429
|
+
const deployed = await this.walletRequest(
|
|
2915
3430
|
state,
|
|
2916
3431
|
chainId,
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
3432
|
+
this.agentPath(context, "deploy"),
|
|
3433
|
+
{ method: "POST", body: {} }
|
|
3434
|
+
);
|
|
3435
|
+
if (deployed?.agentWallet) {
|
|
3436
|
+
context.wallet = deployed.agentWallet;
|
|
3437
|
+
}
|
|
3438
|
+
} catch (error) {
|
|
3439
|
+
console.warn(
|
|
3440
|
+
"[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
|
|
3441
|
+
error
|
|
3442
|
+
);
|
|
3443
|
+
}
|
|
2920
3444
|
}
|
|
2921
|
-
async
|
|
2922
|
-
|
|
2923
|
-
await this.walletRequest(
|
|
3445
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3446
|
+
try {
|
|
3447
|
+
const response = await this.walletRequest(
|
|
2924
3448
|
state,
|
|
2925
3449
|
chainId,
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
limit: options?.limit ?? 10,
|
|
2929
|
-
cursor: options?.cursor,
|
|
2930
|
-
fromDate: options?.fromDate,
|
|
2931
|
-
toDate: options?.toDate
|
|
3450
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3451
|
+
shouldForceRefresh: true
|
|
2932
3452
|
})}`
|
|
2933
|
-
)
|
|
2934
|
-
|
|
3453
|
+
);
|
|
3454
|
+
if (!response?.agentSnapshot) {
|
|
3455
|
+
throw this.invalidResponse("agent snapshot refresh");
|
|
3456
|
+
}
|
|
3457
|
+
} catch (error) {
|
|
3458
|
+
console.warn(
|
|
3459
|
+
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3460
|
+
error
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
2935
3463
|
}
|
|
2936
|
-
|
|
2937
|
-
return
|
|
2938
|
-
await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
|
|
2939
|
-
);
|
|
3464
|
+
agentPath(context, suffix) {
|
|
3465
|
+
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
2940
3466
|
}
|
|
2941
|
-
async
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
days,
|
|
2947
|
-
tokenSymbol: options?.tokenSymbol,
|
|
2948
|
-
chainId: options?.chainId
|
|
2949
|
-
})}`
|
|
2950
|
-
)
|
|
2951
|
-
);
|
|
2952
|
-
}
|
|
2953
|
-
async ensureActivated(state, chainId, asset) {
|
|
2954
|
-
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${asset}`;
|
|
2955
|
-
if (this.activated.has(key2)) return;
|
|
2956
|
-
const response = await this.walletRequest(
|
|
2957
|
-
state,
|
|
2958
|
-
chainId,
|
|
2959
|
-
"/activation",
|
|
2960
|
-
{ method: "POST", body: { chainId, asset } }
|
|
2961
|
-
);
|
|
2962
|
-
if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
|
|
2963
|
-
throw this.invalidResponse("activation");
|
|
3467
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
3468
|
+
try {
|
|
3469
|
+
return await this.providerRequest(state, chainId, path, options);
|
|
3470
|
+
} catch (error) {
|
|
3471
|
+
throw this.mapApiError(error);
|
|
2964
3472
|
}
|
|
2965
|
-
this.activated.add(key2);
|
|
2966
3473
|
}
|
|
2967
|
-
async
|
|
3474
|
+
async providerRequest(state, chainId, path, options = {}) {
|
|
2968
3475
|
this.assertChain(chainId);
|
|
3476
|
+
const request = (signature2) => this.api.request(path, {
|
|
3477
|
+
...options,
|
|
3478
|
+
signature: signature2
|
|
3479
|
+
});
|
|
2969
3480
|
let signature = await this.auth.getToken(state, chainId);
|
|
2970
3481
|
try {
|
|
2971
|
-
return await
|
|
3482
|
+
return await request(signature);
|
|
2972
3483
|
} catch (error) {
|
|
2973
3484
|
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
2974
|
-
if (error.
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
3485
|
+
if (error.providerCode === "NO_USER") throw error;
|
|
3486
|
+
if (!error.isAuthenticationError) throw error;
|
|
3487
|
+
this.auth.clear(state, chainId);
|
|
3488
|
+
signature = await this.auth.getToken(state, chainId);
|
|
3489
|
+
try {
|
|
3490
|
+
return await request(signature);
|
|
3491
|
+
} catch (retryError) {
|
|
3492
|
+
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3493
|
+
this.forgetUser(state, chainId);
|
|
2981
3494
|
}
|
|
3495
|
+
throw retryError;
|
|
2982
3496
|
}
|
|
2983
|
-
throw this.mapApiError(error);
|
|
2984
3497
|
}
|
|
2985
3498
|
}
|
|
2986
3499
|
mapApiError(error) {
|
|
@@ -3008,15 +3521,8 @@ var YieldseekerAgent = class {
|
|
|
3008
3521
|
if (this.transactionExecutor) {
|
|
3009
3522
|
return this.transactionExecutor(state, chainId, transaction);
|
|
3010
3523
|
}
|
|
3011
|
-
|
|
3012
|
-
throw this.invalidResponse("transaction");
|
|
3013
|
-
}
|
|
3524
|
+
this.assertTransaction(transaction, state, chainId);
|
|
3014
3525
|
const account = getAddress2(state.walletAddress);
|
|
3015
|
-
if (getAddress2(transaction.from) !== account) {
|
|
3016
|
-
throw this.invalidResponse("transaction", {
|
|
3017
|
-
reason: "transaction.from does not match the connected wallet"
|
|
3018
|
-
});
|
|
3019
|
-
}
|
|
3020
3526
|
const walletClient = createWalletClient2({
|
|
3021
3527
|
account,
|
|
3022
3528
|
chain: base3,
|
|
@@ -3052,7 +3558,7 @@ var YieldseekerAgent = class {
|
|
|
3052
3558
|
}
|
|
3053
3559
|
return hash;
|
|
3054
3560
|
}
|
|
3055
|
-
async
|
|
3561
|
+
async waitForReceipt(state, chainId, transactionHash) {
|
|
3056
3562
|
if (this.unwindReceiptWaiter) {
|
|
3057
3563
|
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3058
3564
|
return;
|
|
@@ -3068,15 +3574,32 @@ var YieldseekerAgent = class {
|
|
|
3068
3574
|
if (receipt.status !== "success") {
|
|
3069
3575
|
throw new OwneyError(
|
|
3070
3576
|
"AGENT_TRANSACTION_REVERTED",
|
|
3071
|
-
`Yieldseeker
|
|
3577
|
+
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3072
3578
|
{ transactionHash },
|
|
3073
3579
|
this.id
|
|
3074
3580
|
);
|
|
3075
3581
|
}
|
|
3076
3582
|
}
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3583
|
+
assertTransaction(transaction, state, chainId) {
|
|
3584
|
+
if (!transaction || typeof transaction.from !== "string" || !isAddress2(transaction.from) || typeof transaction.to !== "string" || !isAddress2(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || getAddress2(transaction.from) !== getAddress2(state.walletAddress)) {
|
|
3585
|
+
throw this.invalidResponse("transaction");
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
assertAgent(agent) {
|
|
3589
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress2(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3590
|
+
throw this.invalidResponse("agent");
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
isOwneyAgent(agent) {
|
|
3594
|
+
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3595
|
+
}
|
|
3596
|
+
assetForAgent(agent) {
|
|
3597
|
+
for (const asset of ["USDC", "WETH"]) {
|
|
3598
|
+
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3599
|
+
return asset;
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
return null;
|
|
3080
3603
|
}
|
|
3081
3604
|
isTransactionHash(value) {
|
|
3082
3605
|
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
@@ -3098,8 +3621,12 @@ var YieldseekerAgent = class {
|
|
|
3098
3621
|
if (asset !== "USDC" && asset !== "WETH") {
|
|
3099
3622
|
throw new OwneyError(
|
|
3100
3623
|
"ASSET_UNSUPPORTED",
|
|
3101
|
-
`Yieldseeker does not support asset ${asset}.`,
|
|
3102
|
-
{
|
|
3624
|
+
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
3625
|
+
{
|
|
3626
|
+
asset,
|
|
3627
|
+
supportedAssets: ["USDC", "WETH"],
|
|
3628
|
+
providerAlsoAdvertises: ["cbBTC"]
|
|
3629
|
+
},
|
|
3103
3630
|
this.id
|
|
3104
3631
|
);
|
|
3105
3632
|
}
|
|
@@ -3212,7 +3739,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
|
3212
3739
|
}
|
|
3213
3740
|
|
|
3214
3741
|
// src/lib/helpers/withdraw-helper.ts
|
|
3215
|
-
import { parseUnits
|
|
3742
|
+
import { parseUnits } from "viem";
|
|
3216
3743
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
3217
3744
|
const target = asset.toUpperCase();
|
|
3218
3745
|
return agents.map((agent) => {
|
|
@@ -3220,7 +3747,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
3220
3747
|
const tokenBalance = agentBalance?.tokens.find(
|
|
3221
3748
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
3222
3749
|
);
|
|
3223
|
-
let balance = tokenBalance ?
|
|
3750
|
+
let balance = tokenBalance ? parseUnits(tokenBalance.amount, decimals) : 0n;
|
|
3224
3751
|
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
3225
3752
|
const chainNameById = {
|
|
3226
3753
|
1: "ETHEREUM",
|
|
@@ -3239,7 +3766,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
3239
3766
|
} catch {
|
|
3240
3767
|
}
|
|
3241
3768
|
}
|
|
3242
|
-
balance +=
|
|
3769
|
+
balance += parseUnits(position2.amount, decimals);
|
|
3243
3770
|
}
|
|
3244
3771
|
}
|
|
3245
3772
|
return { agent, balance };
|
|
@@ -3321,11 +3848,11 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
|
3321
3848
|
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
3322
3849
|
}
|
|
3323
3850
|
const normalizedToken = tokenSymbol.toUpperCase();
|
|
3324
|
-
return balance.tokens.reduce((total,
|
|
3325
|
-
if (Number(
|
|
3851
|
+
return balance.tokens.reduce((total, token) => {
|
|
3852
|
+
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
3326
3853
|
return total;
|
|
3327
3854
|
}
|
|
3328
|
-
const amount = Number(
|
|
3855
|
+
const amount = Number(token.amount);
|
|
3329
3856
|
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
3330
3857
|
}, 0);
|
|
3331
3858
|
}
|
|
@@ -3399,8 +3926,8 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3399
3926
|
return makeVerificationAwareDepositCallback(
|
|
3400
3927
|
async (smartWallet, chainId, amount, verification) => {
|
|
3401
3928
|
const cid = chainId;
|
|
3402
|
-
const
|
|
3403
|
-
if (!
|
|
3929
|
+
const token = deps.tokenAddressByChain[cid];
|
|
3930
|
+
if (!token) {
|
|
3404
3931
|
throw new OwneyError(
|
|
3405
3932
|
"CHAIN_UNSUPPORTED",
|
|
3406
3933
|
`No sponsored WETH configured for chain ${chainId}`
|
|
@@ -3411,12 +3938,12 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3411
3938
|
const wallet = deps.getWalletClient(cid);
|
|
3412
3939
|
await ensureWalletOnChain(pub, wallet, cid);
|
|
3413
3940
|
try {
|
|
3414
|
-
const balance = await readErc20Balance(pub,
|
|
3941
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
3415
3942
|
if (balance < amountWei) {
|
|
3416
3943
|
throw new OwneyError(
|
|
3417
3944
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
3418
3945
|
"Insufficient WETH balance for this deposit.",
|
|
3419
|
-
{ token
|
|
3946
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3420
3947
|
);
|
|
3421
3948
|
}
|
|
3422
3949
|
} catch (err) {
|
|
@@ -3428,14 +3955,14 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3428
3955
|
}
|
|
3429
3956
|
const allowance = await readPermit2Allowance(
|
|
3430
3957
|
pub,
|
|
3431
|
-
|
|
3958
|
+
token,
|
|
3432
3959
|
deps.ownerAddress
|
|
3433
3960
|
);
|
|
3434
3961
|
if (allowance < amountWei) {
|
|
3435
3962
|
throw new OwneyError(
|
|
3436
3963
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
3437
3964
|
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
3438
|
-
{ token
|
|
3965
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
3439
3966
|
);
|
|
3440
3967
|
}
|
|
3441
3968
|
const relayer = await get({
|
|
@@ -3450,7 +3977,7 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3450
3977
|
const typedData = buildPermitTransferFromTypedData({
|
|
3451
3978
|
chainId: cid,
|
|
3452
3979
|
message: {
|
|
3453
|
-
permitted: { token
|
|
3980
|
+
permitted: { token, amount: amountWei },
|
|
3454
3981
|
spender: relayer,
|
|
3455
3982
|
nonce,
|
|
3456
3983
|
deadline
|
|
@@ -3467,13 +3994,17 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3467
3994
|
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
3468
3995
|
body: {
|
|
3469
3996
|
chainId: cid,
|
|
3470
|
-
token
|
|
3997
|
+
token,
|
|
3471
3998
|
from: deps.ownerAddress,
|
|
3472
3999
|
to: smartWallet,
|
|
3473
4000
|
amount,
|
|
3474
4001
|
nonce: nonce.toString(),
|
|
3475
4002
|
deadline: deadline.toString(),
|
|
3476
|
-
signature
|
|
4003
|
+
signature,
|
|
4004
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
4005
|
+
yieldseekerUserId: verification.userId,
|
|
4006
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4007
|
+
} : {}
|
|
3477
4008
|
}
|
|
3478
4009
|
});
|
|
3479
4010
|
return result.txHash;
|
|
@@ -3482,7 +4013,7 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3482
4013
|
}
|
|
3483
4014
|
|
|
3484
4015
|
// src/lib/sponsored-calls-deposit.ts
|
|
3485
|
-
import { encodeFunctionData, erc20Abi, toHex } from "viem";
|
|
4016
|
+
import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi2, toHex } from "viem";
|
|
3486
4017
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
3487
4018
|
var DEFAULT_MAX_POLLS = 30;
|
|
3488
4019
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -3508,10 +4039,10 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3508
4039
|
}
|
|
3509
4040
|
return new URL(configured, origin).toString();
|
|
3510
4041
|
};
|
|
3511
|
-
return async (smartWallet, chainId, amount) => {
|
|
4042
|
+
return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
|
|
3512
4043
|
const cid = chainId;
|
|
3513
|
-
const
|
|
3514
|
-
if (!
|
|
4044
|
+
const token = deps.tokenAddressByChain[cid];
|
|
4045
|
+
if (!token) {
|
|
3515
4046
|
throw new OwneyError(
|
|
3516
4047
|
"CHAIN_UNSUPPORTED",
|
|
3517
4048
|
`No sponsored token configured for chain ${chainId}`
|
|
@@ -3524,11 +4055,37 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3524
4055
|
{ chainId }
|
|
3525
4056
|
);
|
|
3526
4057
|
}
|
|
3527
|
-
const data =
|
|
3528
|
-
abi:
|
|
4058
|
+
const data = encodeFunctionData2({
|
|
4059
|
+
abi: erc20Abi2,
|
|
3529
4060
|
functionName: "transfer",
|
|
3530
4061
|
args: [smartWallet, BigInt(amount)]
|
|
3531
4062
|
});
|
|
4063
|
+
let paymasterUrl = absolutePaymasterUrl();
|
|
4064
|
+
if (verification?.agentId === "yieldseeker") {
|
|
4065
|
+
if (chainId !== 8453) {
|
|
4066
|
+
throw new OwneyError(
|
|
4067
|
+
"CHAIN_UNSUPPORTED",
|
|
4068
|
+
`Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
|
|
4069
|
+
);
|
|
4070
|
+
}
|
|
4071
|
+
const { intent } = await postPaymasterIntent({
|
|
4072
|
+
baseUrl: deps.routingApiBaseUrl,
|
|
4073
|
+
apiKey: deps.apiKey,
|
|
4074
|
+
yieldseekerSignature: verification.signature,
|
|
4075
|
+
body: {
|
|
4076
|
+
chainId,
|
|
4077
|
+
token,
|
|
4078
|
+
from: deps.ownerAddress,
|
|
4079
|
+
to: smartWallet,
|
|
4080
|
+
amount,
|
|
4081
|
+
yieldseekerUserId: verification.userId,
|
|
4082
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4083
|
+
}
|
|
4084
|
+
});
|
|
4085
|
+
const url = new URL(paymasterUrl);
|
|
4086
|
+
url.searchParams.set("owneyIntent", intent);
|
|
4087
|
+
paymasterUrl = url.toString();
|
|
4088
|
+
}
|
|
3532
4089
|
const sendResult = await deps.provider.request({
|
|
3533
4090
|
method: "wallet_sendCalls",
|
|
3534
4091
|
params: [
|
|
@@ -3537,9 +4094,9 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3537
4094
|
from: deps.ownerAddress,
|
|
3538
4095
|
chainId: toHex(chainId),
|
|
3539
4096
|
atomicRequired: false,
|
|
3540
|
-
calls: [{ to:
|
|
4097
|
+
calls: [{ to: token, value: "0x0", data }],
|
|
3541
4098
|
capabilities: {
|
|
3542
|
-
paymasterService: { url:
|
|
4099
|
+
paymasterService: { url: paymasterUrl }
|
|
3543
4100
|
}
|
|
3544
4101
|
}
|
|
3545
4102
|
]
|
|
@@ -3569,17 +4126,17 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3569
4126
|
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
3570
4127
|
{ chainId, callsId }
|
|
3571
4128
|
);
|
|
3572
|
-
};
|
|
4129
|
+
});
|
|
3573
4130
|
}
|
|
3574
4131
|
|
|
3575
4132
|
// src/client.ts
|
|
3576
4133
|
function encodeMultiAgentCursor(map) {
|
|
3577
4134
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
3578
4135
|
}
|
|
3579
|
-
function decodeMultiAgentCursor(
|
|
4136
|
+
function decodeMultiAgentCursor(token) {
|
|
3580
4137
|
let parsed;
|
|
3581
4138
|
try {
|
|
3582
|
-
const json = Buffer.from(
|
|
4139
|
+
const json = Buffer.from(token, "base64").toString("utf8");
|
|
3583
4140
|
parsed = JSON.parse(json);
|
|
3584
4141
|
} catch {
|
|
3585
4142
|
throw new InvalidHistoryCursorError(
|
|
@@ -3768,6 +4325,8 @@ var OwneySDK = class {
|
|
|
3768
4325
|
if (!onApproved && cached) return cached;
|
|
3769
4326
|
const provider = this.requireConnectedProvider();
|
|
3770
4327
|
const callback = makeSponsoredCallsCallback({
|
|
4328
|
+
apiKey: this.apiKey,
|
|
4329
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
3771
4330
|
provider,
|
|
3772
4331
|
ownerAddress: this.state.walletAddress,
|
|
3773
4332
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
@@ -4210,15 +4769,15 @@ var OwneySDK = class {
|
|
|
4210
4769
|
agent,
|
|
4211
4770
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
4212
4771
|
}));
|
|
4213
|
-
const
|
|
4772
|
+
const valid2 = splits.filter(
|
|
4214
4773
|
(s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
|
|
4215
4774
|
);
|
|
4216
|
-
if (
|
|
4775
|
+
if (valid2.length === agents.length) {
|
|
4217
4776
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
4218
4777
|
}
|
|
4219
4778
|
return this.splitDepositAmount(
|
|
4220
4779
|
totalAmount,
|
|
4221
|
-
|
|
4780
|
+
valid2.map((s) => s.agent),
|
|
4222
4781
|
chainId,
|
|
4223
4782
|
asset,
|
|
4224
4783
|
exempt
|
|
@@ -4251,10 +4810,10 @@ var OwneySDK = class {
|
|
|
4251
4810
|
try {
|
|
4252
4811
|
const balance = await agent.getBalances(state, chainId);
|
|
4253
4812
|
const target = asset.toLowerCase();
|
|
4254
|
-
const
|
|
4813
|
+
const token = balance.tokens.find(
|
|
4255
4814
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
4256
4815
|
);
|
|
4257
|
-
return !!
|
|
4816
|
+
return !!token && Number(token.amount) > 0;
|
|
4258
4817
|
} catch {
|
|
4259
4818
|
return false;
|
|
4260
4819
|
}
|
|
@@ -4305,9 +4864,9 @@ var OwneySDK = class {
|
|
|
4305
4864
|
const { asset, amount, agentId } = options;
|
|
4306
4865
|
const state = this.requireState();
|
|
4307
4866
|
const chainId = this.requireChainId();
|
|
4308
|
-
const
|
|
4867
|
+
const token = asset;
|
|
4309
4868
|
const assetInfo = SupportedAssets.find(
|
|
4310
|
-
(a) => a.chainId === chainId && a.symbol ===
|
|
4869
|
+
(a) => a.chainId === chainId && a.symbol === token
|
|
4311
4870
|
);
|
|
4312
4871
|
if (!assetInfo) {
|
|
4313
4872
|
throw new OwneyError(
|
|
@@ -4321,7 +4880,7 @@ var OwneySDK = class {
|
|
|
4321
4880
|
return withFailureReporting(
|
|
4322
4881
|
this.apiKey,
|
|
4323
4882
|
agent.id,
|
|
4324
|
-
() => agent.withdraw(state, chainId,
|
|
4883
|
+
() => agent.withdraw(state, chainId, token, amount),
|
|
4325
4884
|
this.routingApiBaseUrl
|
|
4326
4885
|
);
|
|
4327
4886
|
}
|
|
@@ -4331,7 +4890,7 @@ var OwneySDK = class {
|
|
|
4331
4890
|
const agentErrors2 = {};
|
|
4332
4891
|
for (const agent of eligibleAgents) {
|
|
4333
4892
|
try {
|
|
4334
|
-
results2[agent.id] = await agent.withdraw(state, chainId,
|
|
4893
|
+
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
4335
4894
|
} catch (err) {
|
|
4336
4895
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
4337
4896
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
@@ -4421,7 +4980,7 @@ var OwneySDK = class {
|
|
|
4421
4980
|
results[p.agent.id] = await p.agent.withdraw(
|
|
4422
4981
|
state,
|
|
4423
4982
|
chainId,
|
|
4424
|
-
|
|
4983
|
+
token,
|
|
4425
4984
|
p.planned.toString()
|
|
4426
4985
|
);
|
|
4427
4986
|
} catch (err) {
|
|
@@ -4772,8 +5331,8 @@ var OwneySDK = class {
|
|
|
4772
5331
|
const state = this.requireState();
|
|
4773
5332
|
const chainId = this.requireChainId();
|
|
4774
5333
|
this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
|
|
4775
|
-
const
|
|
4776
|
-
if (!
|
|
5334
|
+
const token = SPONSORED_WETH_BY_CHAIN[chainId];
|
|
5335
|
+
if (!token) {
|
|
4777
5336
|
throw new OwneyError(
|
|
4778
5337
|
"CHAIN_UNSUPPORTED",
|
|
4779
5338
|
`No sponsored WETH on chain ${chainId}`
|
|
@@ -4786,7 +5345,7 @@ var OwneySDK = class {
|
|
|
4786
5345
|
transport: custom3(provider)
|
|
4787
5346
|
});
|
|
4788
5347
|
const hash = await wallet.writeContract({
|
|
4789
|
-
address:
|
|
5348
|
+
address: token,
|
|
4790
5349
|
abi: ERC20_ALLOWANCE_ABI,
|
|
4791
5350
|
functionName: "approve",
|
|
4792
5351
|
args: [PERMIT2_ADDRESS, MAX_UINT256],
|
|
@@ -4911,8 +5470,8 @@ import { SiweMessage as SiweMessage2 } from "siwe";
|
|
|
4911
5470
|
import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
|
|
4912
5471
|
|
|
4913
5472
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
4914
|
-
var
|
|
4915
|
-
var
|
|
5473
|
+
var KEY_PREFIX4 = "owney.siwx.session";
|
|
5474
|
+
var storage4 = () => {
|
|
4916
5475
|
if (typeof window === "undefined") return null;
|
|
4917
5476
|
try {
|
|
4918
5477
|
return window.localStorage;
|
|
@@ -4920,8 +5479,8 @@ var storage3 = () => {
|
|
|
4920
5479
|
return null;
|
|
4921
5480
|
}
|
|
4922
5481
|
};
|
|
4923
|
-
var buildKey3 = (address) => `${
|
|
4924
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
5482
|
+
var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
|
|
5483
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
|
|
4925
5484
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
4926
5485
|
var readLegacySiwxSession = (store, address) => {
|
|
4927
5486
|
if (!store) return null;
|
|
@@ -4953,16 +5512,16 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
4953
5512
|
var readSiwxSession = (address, chainId) => {
|
|
4954
5513
|
if (typeof window === "undefined") return null;
|
|
4955
5514
|
const key2 = buildKey3(address);
|
|
4956
|
-
const store =
|
|
4957
|
-
let
|
|
5515
|
+
const store = storage4();
|
|
5516
|
+
let raw2 = null;
|
|
4958
5517
|
try {
|
|
4959
|
-
|
|
5518
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
4960
5519
|
} catch {
|
|
4961
|
-
|
|
5520
|
+
raw2 = null;
|
|
4962
5521
|
}
|
|
4963
|
-
if (
|
|
5522
|
+
if (raw2) {
|
|
4964
5523
|
try {
|
|
4965
|
-
return JSON.parse(
|
|
5524
|
+
return JSON.parse(raw2);
|
|
4966
5525
|
} catch {
|
|
4967
5526
|
memorySiwxSessions.delete(key2);
|
|
4968
5527
|
try {
|
|
@@ -4983,7 +5542,7 @@ var writeSiwxSession = (address, _chainId, session) => {
|
|
|
4983
5542
|
if (typeof window === "undefined") return;
|
|
4984
5543
|
const key2 = buildKey3(address);
|
|
4985
5544
|
memorySiwxSessions.set(key2, session);
|
|
4986
|
-
const store =
|
|
5545
|
+
const store = storage4();
|
|
4987
5546
|
try {
|
|
4988
5547
|
store?.setItem(key2, JSON.stringify(session));
|
|
4989
5548
|
} catch {
|
|
@@ -4992,7 +5551,7 @@ var writeSiwxSession = (address, _chainId, session) => {
|
|
|
4992
5551
|
var clearSiwxSession = (address, _chainId) => {
|
|
4993
5552
|
const key2 = buildKey3(address);
|
|
4994
5553
|
memorySiwxSessions.delete(key2);
|
|
4995
|
-
const store =
|
|
5554
|
+
const store = storage4();
|
|
4996
5555
|
try {
|
|
4997
5556
|
store?.removeItem(key2);
|
|
4998
5557
|
} catch {
|