@owney/sdk 0.7.17-beta.0 → 0.7.17-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.cjs +2296 -563
- package/dist/index.d.cts +140 -9
- package/dist/index.d.ts +140 -9
- package/dist/index.js +2309 -564
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -277,18 +277,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
277
277
|
return explicit;
|
|
278
278
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
279
279
|
}
|
|
280
|
-
function mapDeposit(
|
|
280
|
+
function mapDeposit(raw2) {
|
|
281
281
|
return {
|
|
282
|
-
txHash:
|
|
283
|
-
smartWallet:
|
|
284
|
-
amount:
|
|
282
|
+
txHash: raw2.txHash,
|
|
283
|
+
smartWallet: raw2.smartWallet,
|
|
284
|
+
amount: raw2.amount
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
|
-
function mapWithdraw(
|
|
287
|
+
function mapWithdraw(raw2) {
|
|
288
288
|
return {
|
|
289
|
-
txHash:
|
|
290
|
-
type:
|
|
291
|
-
amount:
|
|
289
|
+
txHash: raw2.txHash,
|
|
290
|
+
type: raw2.type,
|
|
291
|
+
amount: raw2.amount
|
|
292
292
|
};
|
|
293
293
|
}
|
|
294
294
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -307,10 +307,10 @@ function resolveChainId(chain) {
|
|
|
307
307
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
308
308
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
309
309
|
}
|
|
310
|
-
function mapPendingAllocations(
|
|
311
|
-
if (!Array.isArray(
|
|
310
|
+
function mapPendingAllocations(raw2) {
|
|
311
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
312
312
|
const pending = [];
|
|
313
|
-
for (const entry of
|
|
313
|
+
for (const entry of raw2) {
|
|
314
314
|
if (typeof entry !== "object" || entry === null) continue;
|
|
315
315
|
const e = entry;
|
|
316
316
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -333,8 +333,8 @@ function mapPendingAllocations(raw) {
|
|
|
333
333
|
}
|
|
334
334
|
return pending.length > 0 ? pending : void 0;
|
|
335
335
|
}
|
|
336
|
-
function mapBalances(
|
|
337
|
-
const portfolio =
|
|
336
|
+
function mapBalances(raw2, _chainId, smartWallet) {
|
|
337
|
+
const portfolio = raw2.portfolio;
|
|
338
338
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
339
339
|
let totalBalance = 0;
|
|
340
340
|
const tokens = [];
|
|
@@ -403,8 +403,8 @@ function sumTokenValues(tokens) {
|
|
|
403
403
|
function sumTokenEarnings(tokens) {
|
|
404
404
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
405
405
|
}
|
|
406
|
-
function mapEarnings(
|
|
407
|
-
const totalEarningsByChain =
|
|
406
|
+
function mapEarnings(raw2, smartWallet) {
|
|
407
|
+
const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
|
|
408
408
|
const tokens = [];
|
|
409
409
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
410
410
|
totalEarningsByChain
|
|
@@ -423,15 +423,15 @@ function mapEarnings(raw, smartWallet) {
|
|
|
423
423
|
return {
|
|
424
424
|
smartWallet,
|
|
425
425
|
lifetimeEarnings: sumTokenEarnings(
|
|
426
|
-
|
|
426
|
+
raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
|
|
427
427
|
),
|
|
428
428
|
tokens
|
|
429
429
|
};
|
|
430
430
|
}
|
|
431
|
-
function mapWeightedApyByChain(
|
|
432
|
-
if (!
|
|
431
|
+
function mapWeightedApyByChain(raw2) {
|
|
432
|
+
if (!raw2) return void 0;
|
|
433
433
|
const out = {};
|
|
434
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
434
|
+
for (const [chainKey, tokenApy] of Object.entries(raw2)) {
|
|
435
435
|
const chainId = Number(chainKey);
|
|
436
436
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
437
437
|
const perAsset = {};
|
|
@@ -471,15 +471,15 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
471
471
|
}
|
|
472
472
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
473
473
|
}
|
|
474
|
-
function mapApyHistory(
|
|
475
|
-
const history = Object.entries(
|
|
474
|
+
function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
475
|
+
const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
|
|
476
476
|
date,
|
|
477
477
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
|
|
478
478
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
479
479
|
return {
|
|
480
|
-
walletAddress:
|
|
481
|
-
weightedApyAfterFee:
|
|
482
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
480
|
+
walletAddress: raw2.walletAddress,
|
|
481
|
+
weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
|
|
482
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
|
|
483
483
|
history
|
|
484
484
|
};
|
|
485
485
|
}
|
|
@@ -575,23 +575,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
575
575
|
};
|
|
576
576
|
});
|
|
577
577
|
}
|
|
578
|
-
function mapUserProfile(
|
|
578
|
+
function mapUserProfile(raw2, userAddress) {
|
|
579
579
|
return {
|
|
580
580
|
address: userAddress,
|
|
581
|
-
smartWallet:
|
|
582
|
-
chains:
|
|
583
|
-
strategy:
|
|
584
|
-
hasActiveSessionKey:
|
|
585
|
-
protocols:
|
|
586
|
-
splitting:
|
|
587
|
-
minSplits:
|
|
581
|
+
smartWallet: raw2.smartWallet || "",
|
|
582
|
+
chains: raw2.chains || [],
|
|
583
|
+
strategy: raw2.strategy,
|
|
584
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey || false,
|
|
585
|
+
protocols: raw2.protocols || [],
|
|
586
|
+
splitting: raw2.splitting,
|
|
587
|
+
minSplits: raw2.minSplits
|
|
588
588
|
};
|
|
589
589
|
}
|
|
590
|
-
function mapApyByStrategy(
|
|
590
|
+
function mapApyByStrategy(raw2) {
|
|
591
591
|
const apyPerAsset = {};
|
|
592
592
|
let apySum = 0;
|
|
593
593
|
let apyCount = 0;
|
|
594
|
-
for (const entry of
|
|
594
|
+
for (const entry of raw2.data) {
|
|
595
595
|
const supported = SupportedAssets.find(
|
|
596
596
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
597
597
|
);
|
|
@@ -762,15 +762,15 @@ var readSession = (address, _chainId) => {
|
|
|
762
762
|
if (typeof window === "undefined") return null;
|
|
763
763
|
const key2 = buildKey(address);
|
|
764
764
|
const store = storage();
|
|
765
|
-
let
|
|
765
|
+
let raw2 = null;
|
|
766
766
|
try {
|
|
767
|
-
|
|
767
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
768
768
|
} catch {
|
|
769
|
-
|
|
769
|
+
raw2 = null;
|
|
770
770
|
}
|
|
771
|
-
if (
|
|
771
|
+
if (raw2) {
|
|
772
772
|
try {
|
|
773
|
-
const parsed = JSON.parse(
|
|
773
|
+
const parsed = JSON.parse(raw2);
|
|
774
774
|
if (isFreshSession(parsed)) return parsed;
|
|
775
775
|
} catch {
|
|
776
776
|
}
|
|
@@ -938,8 +938,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
938
938
|
}
|
|
939
939
|
return result;
|
|
940
940
|
}
|
|
941
|
-
function flattenAvailablePools(
|
|
942
|
-
const byChain =
|
|
941
|
+
function flattenAvailablePools(raw2) {
|
|
942
|
+
const byChain = raw2 ?? {};
|
|
943
943
|
const names = [];
|
|
944
944
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
945
945
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -1440,8 +1440,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1440
1440
|
const poolResults = await Promise.all(
|
|
1441
1441
|
universe.map(async (protocol) => {
|
|
1442
1442
|
try {
|
|
1443
|
-
const
|
|
1444
|
-
return [protocol.id, flattenAvailablePools(
|
|
1443
|
+
const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1444
|
+
return [protocol.id, flattenAvailablePools(raw2)];
|
|
1445
1445
|
} catch (error) {
|
|
1446
1446
|
console.warn(
|
|
1447
1447
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1507,14 +1507,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1507
1507
|
async readWalletState(ownerAddress) {
|
|
1508
1508
|
try {
|
|
1509
1509
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1510
|
-
const
|
|
1510
|
+
const raw2 = portfolio;
|
|
1511
1511
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1512
|
-
predeployed:
|
|
1513
|
-
hasActiveSessionKey:
|
|
1512
|
+
predeployed: raw2?.predeployed,
|
|
1513
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1514
1514
|
});
|
|
1515
1515
|
return {
|
|
1516
|
-
predeployed:
|
|
1517
|
-
hasActiveSessionKey:
|
|
1516
|
+
predeployed: raw2?.predeployed,
|
|
1517
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1518
1518
|
};
|
|
1519
1519
|
} catch (error) {
|
|
1520
1520
|
console.warn(
|
|
@@ -1794,14 +1794,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1794
1794
|
return { txHash, smartWallet, amount };
|
|
1795
1795
|
}
|
|
1796
1796
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1797
|
-
const
|
|
1797
|
+
const raw2 = await this.sdk.depositFunds(
|
|
1798
1798
|
this.getAddress(),
|
|
1799
1799
|
validChainId,
|
|
1800
1800
|
amount,
|
|
1801
1801
|
asset,
|
|
1802
1802
|
"aggressive"
|
|
1803
1803
|
);
|
|
1804
|
-
return mapDeposit(
|
|
1804
|
+
return mapDeposit(raw2);
|
|
1805
1805
|
} catch (error) {
|
|
1806
1806
|
throw error;
|
|
1807
1807
|
}
|
|
@@ -1810,27 +1810,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1810
1810
|
async withdraw(state, chainId, token, amount) {
|
|
1811
1811
|
const validChainId = isValidChainId(chainId);
|
|
1812
1812
|
await this.ensureConnected(state, validChainId);
|
|
1813
|
-
const
|
|
1813
|
+
const raw2 = await this.sdk.withdrawFunds(
|
|
1814
1814
|
this.getAddress(),
|
|
1815
1815
|
validChainId,
|
|
1816
1816
|
amount,
|
|
1817
1817
|
token
|
|
1818
1818
|
);
|
|
1819
|
-
if (!
|
|
1819
|
+
if (!raw2.success) {
|
|
1820
1820
|
throw new OwneyError(
|
|
1821
1821
|
"WITHDRAW_FAILED",
|
|
1822
|
-
|
|
1823
|
-
{ chainId: validChainId, token, amount, response:
|
|
1822
|
+
raw2.message || "Zyfai withdraw failed.",
|
|
1823
|
+
{ chainId: validChainId, token, amount, response: raw2 },
|
|
1824
1824
|
this.id
|
|
1825
1825
|
);
|
|
1826
1826
|
}
|
|
1827
|
-
return mapWithdraw(
|
|
1827
|
+
return mapWithdraw(raw2);
|
|
1828
1828
|
}
|
|
1829
1829
|
// --- IAgent: Portfolio reads ---
|
|
1830
1830
|
async getBalances(state, chainId) {
|
|
1831
1831
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1832
|
-
const
|
|
1833
|
-
return mapBalances(
|
|
1832
|
+
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
1833
|
+
return mapBalances(raw2, validChainId, smartWallet);
|
|
1834
1834
|
}
|
|
1835
1835
|
earningsKey(state, chainId, smartWallet) {
|
|
1836
1836
|
return JSON.stringify([
|
|
@@ -1843,11 +1843,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1843
1843
|
const existing = this.earningsReads.get(key2);
|
|
1844
1844
|
if (existing) return existing;
|
|
1845
1845
|
const generation = this.earningsGeneration;
|
|
1846
|
-
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((
|
|
1846
|
+
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw2) => {
|
|
1847
1847
|
if (generation === this.earningsGeneration) {
|
|
1848
|
-
this.earningsSnapshot = { key: key2, raw, at: Date.now() };
|
|
1848
|
+
this.earningsSnapshot = { key: key2, raw: raw2, at: Date.now() };
|
|
1849
1849
|
}
|
|
1850
|
-
return
|
|
1850
|
+
return raw2;
|
|
1851
1851
|
}).finally(() => {
|
|
1852
1852
|
if (this.earningsReads.get(key2) === pending)
|
|
1853
1853
|
this.earningsReads.delete(key2);
|
|
@@ -1857,11 +1857,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1857
1857
|
}
|
|
1858
1858
|
async getEarnings(state, chainId) {
|
|
1859
1859
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1860
|
-
const
|
|
1860
|
+
const raw2 = await this.readEarnings(
|
|
1861
1861
|
this.earningsKey(state, chainId, smartWallet),
|
|
1862
1862
|
smartWallet
|
|
1863
1863
|
);
|
|
1864
|
-
return mapEarnings(
|
|
1864
|
+
return mapEarnings(raw2, smartWallet);
|
|
1865
1865
|
}
|
|
1866
1866
|
async refreshEarnings(state, chainId) {
|
|
1867
1867
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -1888,8 +1888,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1888
1888
|
}
|
|
1889
1889
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1890
1890
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1891
|
-
const
|
|
1892
|
-
return mapApyHistory(
|
|
1891
|
+
const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
1892
|
+
return mapApyHistory(raw2, chainId, tokenSymbol);
|
|
1893
1893
|
}
|
|
1894
1894
|
/**
|
|
1895
1895
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -1924,7 +1924,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1924
1924
|
const matched = [];
|
|
1925
1925
|
let backendExhausted = false;
|
|
1926
1926
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
1927
|
-
const
|
|
1927
|
+
const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
1928
1928
|
limit: backendPageSize,
|
|
1929
1929
|
offset,
|
|
1930
1930
|
fromDate: options?.fromDate,
|
|
@@ -1935,13 +1935,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1935
1935
|
// asset's rows and handing back a page that filters to nothing.
|
|
1936
1936
|
assetType
|
|
1937
1937
|
});
|
|
1938
|
-
|
|
1938
|
+
raw2.data.forEach((entry, idx) => {
|
|
1939
1939
|
if (entry.chainId === validChainId) {
|
|
1940
1940
|
matched.push({ entry, rawIdx: offset + idx });
|
|
1941
1941
|
}
|
|
1942
1942
|
});
|
|
1943
|
-
offset +=
|
|
1944
|
-
if (
|
|
1943
|
+
offset += raw2.data.length;
|
|
1944
|
+
if (raw2.data.length < backendPageSize) {
|
|
1945
1945
|
backendExhausted = true;
|
|
1946
1946
|
break;
|
|
1947
1947
|
}
|
|
@@ -1963,18 +1963,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1963
1963
|
}
|
|
1964
1964
|
async getUserProfile(state, chainId) {
|
|
1965
1965
|
await this.connectAuth(state, chainId);
|
|
1966
|
-
const
|
|
1966
|
+
const raw2 = await this.sdk.getUserDetails();
|
|
1967
1967
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
1968
1968
|
asset: "USDC (default \u2014 no asset passed)",
|
|
1969
|
-
splitting:
|
|
1970
|
-
minSplits:
|
|
1971
|
-
strategy:
|
|
1972
|
-
chains:
|
|
1973
|
-
protocolCount:
|
|
1974
|
-
hasActiveSessionKey:
|
|
1975
|
-
smartWallet:
|
|
1969
|
+
splitting: raw2.splitting,
|
|
1970
|
+
minSplits: raw2.minSplits,
|
|
1971
|
+
strategy: raw2.strategy,
|
|
1972
|
+
chains: raw2.chains,
|
|
1973
|
+
protocolCount: raw2.protocols?.length,
|
|
1974
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey,
|
|
1975
|
+
smartWallet: raw2.smartWallet
|
|
1976
1976
|
});
|
|
1977
|
-
return mapUserProfile(
|
|
1977
|
+
return mapUserProfile(raw2, this.connectedAddress);
|
|
1978
1978
|
}
|
|
1979
1979
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
1980
1980
|
await this.connectAuth(state, chainId);
|
|
@@ -1993,238 +1993,64 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1993
1993
|
}
|
|
1994
1994
|
// --- IAgent: Discovery (no wallet required) ---
|
|
1995
1995
|
async getAgentApy(days, options) {
|
|
1996
|
-
const
|
|
1996
|
+
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
1997
1997
|
false,
|
|
1998
1998
|
DayFilterMapping[days],
|
|
1999
1999
|
"aggressive",
|
|
2000
2000
|
options?.chainId,
|
|
2001
2001
|
options?.tokenSymbol
|
|
2002
2002
|
);
|
|
2003
|
-
return mapApyByStrategy(
|
|
2003
|
+
return mapApyByStrategy(raw2);
|
|
2004
2004
|
}
|
|
2005
2005
|
};
|
|
2006
2006
|
|
|
2007
|
-
// src/
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2007
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2008
|
+
import {
|
|
2009
|
+
createPublicClient as createPublicClient3,
|
|
2010
|
+
createWalletClient as createWalletClient2,
|
|
2011
|
+
custom as custom2,
|
|
2012
|
+
encodeFunctionData,
|
|
2013
|
+
erc20Abi,
|
|
2014
|
+
getAddress as getAddress2,
|
|
2015
|
+
isAddress as isAddress2
|
|
2016
|
+
} from "viem";
|
|
2017
|
+
import { base as base3 } from "viem/chains";
|
|
2018
|
+
|
|
2019
|
+
// src/lib/chain-guard.ts
|
|
2020
|
+
var CHAIN_NAMES = {
|
|
2021
|
+
1: "Ethereum",
|
|
2022
|
+
8453: "Base",
|
|
2023
|
+
42161: "Arbitrum"
|
|
2024
|
+
};
|
|
2025
|
+
function chainName(chainId) {
|
|
2026
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2027
|
+
}
|
|
2028
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2029
|
+
const actual = await pub.getChainId();
|
|
2030
|
+
if (actual === expected) return;
|
|
2011
2031
|
try {
|
|
2012
|
-
|
|
2013
|
-
method: "GET",
|
|
2014
|
-
headers: {
|
|
2015
|
-
"Content-Type": "application/json",
|
|
2016
|
-
"x-owney-api-key": `${apiKey}`
|
|
2017
|
-
}
|
|
2018
|
-
});
|
|
2019
|
-
if (!res.ok) {
|
|
2020
|
-
if (res.status !== 404) {
|
|
2021
|
-
console.warn(
|
|
2022
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
2023
|
-
);
|
|
2024
|
-
}
|
|
2025
|
-
return null;
|
|
2026
|
-
}
|
|
2027
|
-
const json = await res.json();
|
|
2028
|
-
const policy = json.success ? json.data ?? null : null;
|
|
2029
|
-
debugLog(
|
|
2030
|
-
"owney-sdk",
|
|
2031
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
2032
|
-
policy ?? void 0
|
|
2033
|
-
);
|
|
2034
|
-
return policy;
|
|
2032
|
+
await wallet.switchChain({ id: expected });
|
|
2035
2033
|
} catch (error) {
|
|
2036
|
-
console.warn(
|
|
2037
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
2038
|
-
error instanceof Error ? error.message : String(error)
|
|
2039
|
-
);
|
|
2040
|
-
return null;
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
2044
|
-
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
2045
|
-
const res = await fetch(url, {
|
|
2046
|
-
method: "GET",
|
|
2047
|
-
headers: {
|
|
2048
|
-
"Content-Type": "application/json",
|
|
2049
|
-
"x-owney-api-key": `${apiKey}`
|
|
2050
|
-
}
|
|
2051
|
-
});
|
|
2052
|
-
if (!res.ok) {
|
|
2053
|
-
const text = await res.text().catch(() => "");
|
|
2054
2034
|
throw new OwneyError(
|
|
2055
|
-
"
|
|
2056
|
-
`
|
|
2057
|
-
{
|
|
2035
|
+
"CHAIN_MISMATCH",
|
|
2036
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2037
|
+
{
|
|
2038
|
+
expectedChainId: expected,
|
|
2039
|
+
actualChainId: actual,
|
|
2040
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2041
|
+
}
|
|
2058
2042
|
);
|
|
2059
2043
|
}
|
|
2060
|
-
const
|
|
2061
|
-
if (
|
|
2044
|
+
const after = await pub.getChainId();
|
|
2045
|
+
if (after !== expected) {
|
|
2062
2046
|
throw new OwneyError(
|
|
2063
|
-
"
|
|
2064
|
-
`
|
|
2065
|
-
{
|
|
2066
|
-
);
|
|
2067
|
-
}
|
|
2068
|
-
return json.data;
|
|
2069
|
-
}
|
|
2070
|
-
|
|
2071
|
-
// src/lib/health-report.ts
|
|
2072
|
-
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2073
|
-
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
|
|
2074
|
-
try {
|
|
2075
|
-
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
2076
|
-
method: "POST",
|
|
2077
|
-
headers: {
|
|
2078
|
-
"Content-Type": "application/json",
|
|
2079
|
-
"x-owney-api-key": apiKey
|
|
2080
|
-
},
|
|
2081
|
-
body: JSON.stringify({
|
|
2082
|
-
agent_type: agentType,
|
|
2083
|
-
error_code: errorCode,
|
|
2084
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2085
|
-
})
|
|
2086
|
-
});
|
|
2087
|
-
} catch (err) {
|
|
2088
|
-
console.warn(
|
|
2089
|
-
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
2090
|
-
err instanceof Error ? err.message : err
|
|
2091
|
-
);
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
2095
|
-
try {
|
|
2096
|
-
return await fn();
|
|
2097
|
-
} catch (err) {
|
|
2098
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
2099
|
-
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
2100
|
-
throw err;
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
// src/lib/helpers/withdraw-helper.ts
|
|
2105
|
-
import { parseUnits } from "viem";
|
|
2106
|
-
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
2107
|
-
const target = asset.toUpperCase();
|
|
2108
|
-
return agents.map((agent) => {
|
|
2109
|
-
const agentBalance = aggregated[agent.id];
|
|
2110
|
-
const tokenBalance = agentBalance?.tokens.find(
|
|
2111
|
-
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
2047
|
+
"CHAIN_MISMATCH",
|
|
2048
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2049
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
2112
2050
|
);
|
|
2113
|
-
if (!tokenBalance) return { agent, balance: 0n };
|
|
2114
|
-
return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
|
|
2115
|
-
});
|
|
2116
|
-
}
|
|
2117
|
-
function planProportionalShares(balances, requested, totalAvailable) {
|
|
2118
|
-
const plans = balances.map(({ agent, balance }) => ({
|
|
2119
|
-
agent,
|
|
2120
|
-
balance,
|
|
2121
|
-
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
2122
|
-
}));
|
|
2123
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
2124
|
-
let remainder = requested - assigned;
|
|
2125
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
2126
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
2127
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2128
|
-
});
|
|
2129
|
-
for (const p of byHeadroom) {
|
|
2130
|
-
if (remainder === 0n) break;
|
|
2131
|
-
const headroom = p.balance - p.planned;
|
|
2132
|
-
if (headroom <= 0n) continue;
|
|
2133
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
2134
|
-
p.planned += take;
|
|
2135
|
-
remainder -= take;
|
|
2136
|
-
}
|
|
2137
|
-
return plans;
|
|
2138
|
-
}
|
|
2139
|
-
function planDisabledDrain(disabled, requested) {
|
|
2140
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
2141
|
-
const plans = [];
|
|
2142
|
-
let remaining = requested;
|
|
2143
|
-
for (const { agent, balance } of sorted) {
|
|
2144
|
-
if (remaining === 0n) {
|
|
2145
|
-
plans.push({ agent, balance, planned: 0n });
|
|
2146
|
-
continue;
|
|
2147
|
-
}
|
|
2148
|
-
const take = balance < remaining ? balance : remaining;
|
|
2149
|
-
plans.push({ agent, balance, planned: take });
|
|
2150
|
-
remaining -= take;
|
|
2151
|
-
}
|
|
2152
|
-
return { plans, remaining };
|
|
2153
|
-
}
|
|
2154
|
-
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
2155
|
-
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
2156
|
-
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
2157
|
-
const totalHeadroom = candidates.reduce(
|
|
2158
|
-
(s, c) => s + (c.balance - c.planned),
|
|
2159
|
-
0n
|
|
2160
|
-
);
|
|
2161
|
-
if (totalHeadroom === 0n) return;
|
|
2162
|
-
let distributed = 0n;
|
|
2163
|
-
for (const c of candidates) {
|
|
2164
|
-
const headroom = c.balance - c.planned;
|
|
2165
|
-
const proportional = headroom * amount / totalHeadroom;
|
|
2166
|
-
const give = proportional > headroom ? headroom : proportional;
|
|
2167
|
-
c.planned += give;
|
|
2168
|
-
distributed += give;
|
|
2169
|
-
}
|
|
2170
|
-
let leftover = amount - distributed;
|
|
2171
|
-
for (const c of candidates) {
|
|
2172
|
-
if (leftover === 0n) break;
|
|
2173
|
-
const headroom = c.balance - c.planned;
|
|
2174
|
-
if (headroom <= 0n) continue;
|
|
2175
|
-
const take = headroom < leftover ? headroom : leftover;
|
|
2176
|
-
c.planned += take;
|
|
2177
|
-
leftover -= take;
|
|
2178
|
-
}
|
|
2179
|
-
}
|
|
2180
|
-
function sumWithdrawnAmount(results) {
|
|
2181
|
-
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
2182
|
-
}
|
|
2183
|
-
|
|
2184
|
-
// src/lib/helpers/account-apy-helper.ts
|
|
2185
|
-
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
2186
|
-
const sums = {};
|
|
2187
|
-
const weights = {};
|
|
2188
|
-
for (const id of Object.keys(agentApys)) {
|
|
2189
|
-
const cells = agentApys[id].apyByChainAndAsset;
|
|
2190
|
-
const balance = agentBalances[id] ?? 0;
|
|
2191
|
-
if (!cells || balance <= 0) continue;
|
|
2192
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
2193
|
-
if (!perAsset) continue;
|
|
2194
|
-
const chainId = Number(chainKey);
|
|
2195
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
2196
|
-
const apy = Number(apyValue ?? 0);
|
|
2197
|
-
if (apy === 0) continue;
|
|
2198
|
-
sums[chainId] ??= {};
|
|
2199
|
-
weights[chainId] ??= {};
|
|
2200
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
2201
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
const out = {};
|
|
2206
|
-
for (const chainKey of Object.keys(sums)) {
|
|
2207
|
-
const chainId = Number(chainKey);
|
|
2208
|
-
const perAssetOut = {};
|
|
2209
|
-
for (const asset of Object.keys(sums[chainId])) {
|
|
2210
|
-
const w = weights[chainId][asset];
|
|
2211
|
-
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
2212
|
-
}
|
|
2213
|
-
if (Object.keys(perAssetOut).length > 0) {
|
|
2214
|
-
out[chainId] = perAssetOut;
|
|
2215
|
-
}
|
|
2216
2051
|
}
|
|
2217
|
-
return out;
|
|
2218
2052
|
}
|
|
2219
2053
|
|
|
2220
|
-
// src/client.ts
|
|
2221
|
-
import {
|
|
2222
|
-
createPublicClient as createPublicClient2,
|
|
2223
|
-
createWalletClient,
|
|
2224
|
-
custom
|
|
2225
|
-
} from "viem";
|
|
2226
|
-
import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
2227
|
-
|
|
2228
2054
|
// src/lib/transfer-auth.ts
|
|
2229
2055
|
import { bytesToHex } from "viem";
|
|
2230
2056
|
var ERC20_META_ABI = [
|
|
@@ -2262,24 +2088,25 @@ function randomAuthNonce() {
|
|
|
2262
2088
|
}
|
|
2263
2089
|
|
|
2264
2090
|
// src/lib/sponsor-client.ts
|
|
2265
|
-
var
|
|
2266
|
-
async function
|
|
2267
|
-
const
|
|
2091
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2092
|
+
async function postPaymasterIntent(input) {
|
|
2093
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2268
2094
|
let res;
|
|
2269
2095
|
try {
|
|
2270
|
-
res = await fetch(`${
|
|
2096
|
+
res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
|
|
2271
2097
|
method: "POST",
|
|
2272
2098
|
headers: {
|
|
2273
2099
|
"content-type": "application/json",
|
|
2274
|
-
"x-owney-api-key": input.apiKey
|
|
2100
|
+
"x-owney-api-key": input.apiKey,
|
|
2101
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
2275
2102
|
},
|
|
2276
2103
|
body: JSON.stringify(input.body)
|
|
2277
2104
|
});
|
|
2278
2105
|
} catch (networkError) {
|
|
2279
2106
|
throw new OwneyError(
|
|
2280
2107
|
"SPONSOR_REQUEST_FAILED",
|
|
2281
|
-
`
|
|
2282
|
-
{ cause: String(networkError) }
|
|
2108
|
+
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2109
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
2283
2110
|
);
|
|
2284
2111
|
}
|
|
2285
2112
|
const text = await res.text();
|
|
@@ -2288,30 +2115,29 @@ async function postSponsorTransferAuth(input) {
|
|
|
2288
2115
|
parsed = JSON.parse(text);
|
|
2289
2116
|
} catch {
|
|
2290
2117
|
}
|
|
2291
|
-
if (!res.ok || !parsed?.success ||
|
|
2118
|
+
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
2292
2119
|
throw new OwneyError(
|
|
2293
2120
|
"SPONSOR_REQUEST_FAILED",
|
|
2294
|
-
`
|
|
2121
|
+
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2295
2122
|
{
|
|
2296
2123
|
statusCode: res.status,
|
|
2297
2124
|
responseBody: text.slice(0, 500),
|
|
2298
|
-
|
|
2299
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2300
|
-
safeToFallback: res.status === 503
|
|
2125
|
+
safeToFallback: true
|
|
2301
2126
|
}
|
|
2302
2127
|
);
|
|
2303
2128
|
}
|
|
2304
2129
|
return parsed.data;
|
|
2305
2130
|
}
|
|
2306
|
-
async function
|
|
2307
|
-
const
|
|
2131
|
+
async function postSponsorTransferAuth(input) {
|
|
2132
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2308
2133
|
let res;
|
|
2309
2134
|
try {
|
|
2310
|
-
res = await fetch(`${
|
|
2135
|
+
res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2311
2136
|
method: "POST",
|
|
2312
2137
|
headers: {
|
|
2313
2138
|
"content-type": "application/json",
|
|
2314
|
-
"x-owney-api-key": input.apiKey
|
|
2139
|
+
"x-owney-api-key": input.apiKey,
|
|
2140
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2315
2141
|
},
|
|
2316
2142
|
body: JSON.stringify(input.body)
|
|
2317
2143
|
});
|
|
@@ -2319,7 +2145,7 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2319
2145
|
throw new OwneyError(
|
|
2320
2146
|
"SPONSOR_REQUEST_FAILED",
|
|
2321
2147
|
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2322
|
-
{ cause: String(networkError)
|
|
2148
|
+
{ cause: String(networkError) }
|
|
2323
2149
|
);
|
|
2324
2150
|
}
|
|
2325
2151
|
const text = await res.text();
|
|
@@ -2335,20 +2161,61 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2335
2161
|
{
|
|
2336
2162
|
statusCode: res.status,
|
|
2337
2163
|
responseBody: text.slice(0, 500),
|
|
2338
|
-
|
|
2164
|
+
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2165
|
+
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2166
|
+
safeToFallback: res.status === 503
|
|
2339
2167
|
}
|
|
2340
2168
|
);
|
|
2341
2169
|
}
|
|
2342
2170
|
return parsed.data;
|
|
2343
2171
|
}
|
|
2344
|
-
async function
|
|
2345
|
-
const
|
|
2172
|
+
async function postSponsorPermit2Transfer(input) {
|
|
2173
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2346
2174
|
let res;
|
|
2347
2175
|
try {
|
|
2348
|
-
res = await fetch(
|
|
2349
|
-
|
|
2350
|
-
{
|
|
2351
|
-
|
|
2176
|
+
res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
|
|
2177
|
+
method: "POST",
|
|
2178
|
+
headers: {
|
|
2179
|
+
"content-type": "application/json",
|
|
2180
|
+
"x-owney-api-key": input.apiKey,
|
|
2181
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2182
|
+
},
|
|
2183
|
+
body: JSON.stringify(input.body)
|
|
2184
|
+
});
|
|
2185
|
+
} catch (networkError) {
|
|
2186
|
+
throw new OwneyError(
|
|
2187
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2188
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2189
|
+
{ cause: String(networkError), safeToFallback: false }
|
|
2190
|
+
);
|
|
2191
|
+
}
|
|
2192
|
+
const text = await res.text();
|
|
2193
|
+
let parsed = null;
|
|
2194
|
+
try {
|
|
2195
|
+
parsed = JSON.parse(text);
|
|
2196
|
+
} catch {
|
|
2197
|
+
}
|
|
2198
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2199
|
+
throw new OwneyError(
|
|
2200
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2201
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2202
|
+
{
|
|
2203
|
+
statusCode: res.status,
|
|
2204
|
+
responseBody: text.slice(0, 500),
|
|
2205
|
+
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
2206
|
+
}
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
return parsed.data;
|
|
2210
|
+
}
|
|
2211
|
+
async function getSponsorRelayerAddress(input) {
|
|
2212
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2213
|
+
let res;
|
|
2214
|
+
try {
|
|
2215
|
+
res = await fetch(
|
|
2216
|
+
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2217
|
+
{
|
|
2218
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
2352
2219
|
}
|
|
2353
2220
|
);
|
|
2354
2221
|
} catch (networkError) {
|
|
@@ -2447,213 +2314,1981 @@ async function readPermit2Allowance(publicClient, token, owner) {
|
|
|
2447
2314
|
args: [owner, PERMIT2_ADDRESS]
|
|
2448
2315
|
});
|
|
2449
2316
|
}
|
|
2450
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
2451
|
-
return publicClient.readContract({
|
|
2452
|
-
address: token,
|
|
2453
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2454
|
-
functionName: "balanceOf",
|
|
2455
|
-
args: [owner]
|
|
2317
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
2318
|
+
return publicClient.readContract({
|
|
2319
|
+
address: token,
|
|
2320
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2321
|
+
functionName: "balanceOf",
|
|
2322
|
+
args: [owner]
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
// src/lib/sponsored-deposit.ts
|
|
2327
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2328
|
+
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2329
|
+
function provideDepositVerificationContext(callback, context) {
|
|
2330
|
+
callback[verificationSetter]?.(context);
|
|
2331
|
+
}
|
|
2332
|
+
function makeVerificationAwareDepositCallback(implementation) {
|
|
2333
|
+
let nextVerification;
|
|
2334
|
+
const callback = async (smartWallet, chainId, amount) => {
|
|
2335
|
+
const verification = nextVerification;
|
|
2336
|
+
nextVerification = void 0;
|
|
2337
|
+
return implementation(smartWallet, chainId, amount, verification);
|
|
2338
|
+
};
|
|
2339
|
+
Object.defineProperty(callback, verificationSetter, {
|
|
2340
|
+
value: (context) => {
|
|
2341
|
+
nextVerification = context;
|
|
2342
|
+
}
|
|
2343
|
+
});
|
|
2344
|
+
return callback;
|
|
2345
|
+
}
|
|
2346
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2347
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2348
|
+
return makeVerificationAwareDepositCallback(
|
|
2349
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
2350
|
+
const cid = chainId;
|
|
2351
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2352
|
+
if (!token) {
|
|
2353
|
+
throw new OwneyError(
|
|
2354
|
+
"CHAIN_UNSUPPORTED",
|
|
2355
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
const pub = deps.getPublicClient(cid);
|
|
2359
|
+
const wallet = deps.getWalletClient(cid);
|
|
2360
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2361
|
+
try {
|
|
2362
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2363
|
+
if (balance < BigInt(amount)) {
|
|
2364
|
+
throw new OwneyError(
|
|
2365
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2366
|
+
"Insufficient balance for this deposit.",
|
|
2367
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
} catch (err) {
|
|
2371
|
+
if (err instanceof OwneyError) throw err;
|
|
2372
|
+
console.warn(
|
|
2373
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2374
|
+
err instanceof Error ? err.message : String(err)
|
|
2375
|
+
);
|
|
2376
|
+
}
|
|
2377
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2378
|
+
const validAfter = 0n;
|
|
2379
|
+
const validBefore = BigInt(
|
|
2380
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2381
|
+
);
|
|
2382
|
+
const nonce = randomAuthNonce();
|
|
2383
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2384
|
+
token,
|
|
2385
|
+
chainId: cid,
|
|
2386
|
+
tokenName,
|
|
2387
|
+
tokenVersion,
|
|
2388
|
+
message: {
|
|
2389
|
+
from: deps.ownerAddress,
|
|
2390
|
+
to: smartWallet,
|
|
2391
|
+
value: BigInt(amount),
|
|
2392
|
+
validAfter,
|
|
2393
|
+
validBefore,
|
|
2394
|
+
nonce
|
|
2395
|
+
}
|
|
2396
|
+
});
|
|
2397
|
+
const authSignature = await wallet.signTypedData({
|
|
2398
|
+
account: deps.ownerAddress,
|
|
2399
|
+
...typedData
|
|
2400
|
+
});
|
|
2401
|
+
deps.onApproved?.();
|
|
2402
|
+
const result = await post({
|
|
2403
|
+
baseUrl: deps.baseUrl,
|
|
2404
|
+
apiKey: deps.apiKey,
|
|
2405
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2406
|
+
body: {
|
|
2407
|
+
chainId: cid,
|
|
2408
|
+
token,
|
|
2409
|
+
from: deps.ownerAddress,
|
|
2410
|
+
to: smartWallet,
|
|
2411
|
+
value: amount,
|
|
2412
|
+
validAfter: validAfter.toString(),
|
|
2413
|
+
validBefore: validBefore.toString(),
|
|
2414
|
+
nonce,
|
|
2415
|
+
authSignature,
|
|
2416
|
+
tokenName,
|
|
2417
|
+
tokenVersion,
|
|
2418
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
2419
|
+
yieldseekerUserId: verification.userId,
|
|
2420
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
2421
|
+
} : {}
|
|
2422
|
+
}
|
|
2423
|
+
});
|
|
2424
|
+
return result.txHash;
|
|
2425
|
+
}
|
|
2426
|
+
);
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2430
|
+
import { SiweMessage, generateNonce } from "siwe";
|
|
2431
|
+
import {
|
|
2432
|
+
createPublicClient as createPublicClient2,
|
|
2433
|
+
createWalletClient,
|
|
2434
|
+
custom,
|
|
2435
|
+
getAddress
|
|
2436
|
+
} from "viem";
|
|
2437
|
+
import { base as base2 } from "viem/chains";
|
|
2438
|
+
|
|
2439
|
+
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2440
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session.v5";
|
|
2441
|
+
var INVALIDATED_KEY_PREFIXES = [
|
|
2442
|
+
"owney.yieldseeker.session",
|
|
2443
|
+
"owney.yieldseeker.session.v3",
|
|
2444
|
+
"owney.yieldseeker.session.v4"
|
|
2445
|
+
];
|
|
2446
|
+
var storage2 = () => {
|
|
2447
|
+
if (typeof window === "undefined") return null;
|
|
2448
|
+
try {
|
|
2449
|
+
return window.localStorage;
|
|
2450
|
+
} catch {
|
|
2451
|
+
return null;
|
|
2452
|
+
}
|
|
2453
|
+
};
|
|
2454
|
+
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2455
|
+
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2456
|
+
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2457
|
+
);
|
|
2458
|
+
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2459
|
+
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2460
|
+
memorySessions2.delete(key2);
|
|
2461
|
+
try {
|
|
2462
|
+
store?.removeItem(key2);
|
|
2463
|
+
} catch {
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
};
|
|
2467
|
+
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2468
|
+
var isValidSession = (session) => {
|
|
2469
|
+
if (!session?.token) return false;
|
|
2470
|
+
try {
|
|
2471
|
+
const parsed = JSON.parse(atob(session.token));
|
|
2472
|
+
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2473
|
+
} catch {
|
|
2474
|
+
return false;
|
|
2475
|
+
}
|
|
2476
|
+
};
|
|
2477
|
+
var readYieldseekerSession = (address, chainId) => {
|
|
2478
|
+
if (typeof window === "undefined") return null;
|
|
2479
|
+
const key2 = buildKey2(address, chainId);
|
|
2480
|
+
const store = storage2();
|
|
2481
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2482
|
+
let raw2 = null;
|
|
2483
|
+
try {
|
|
2484
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
2485
|
+
} catch {
|
|
2486
|
+
raw2 = null;
|
|
2487
|
+
}
|
|
2488
|
+
if (raw2) {
|
|
2489
|
+
try {
|
|
2490
|
+
const parsed = JSON.parse(raw2);
|
|
2491
|
+
if (isValidSession(parsed)) return parsed.token;
|
|
2492
|
+
} catch {
|
|
2493
|
+
}
|
|
2494
|
+
memorySessions2.delete(key2);
|
|
2495
|
+
try {
|
|
2496
|
+
store?.removeItem(key2);
|
|
2497
|
+
} catch {
|
|
2498
|
+
}
|
|
2499
|
+
return null;
|
|
2500
|
+
}
|
|
2501
|
+
const cached = memorySessions2.get(key2);
|
|
2502
|
+
if (isValidSession(cached)) return cached.token;
|
|
2503
|
+
if (cached) memorySessions2.delete(key2);
|
|
2504
|
+
return null;
|
|
2505
|
+
};
|
|
2506
|
+
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2507
|
+
if (typeof window === "undefined") return;
|
|
2508
|
+
const session = { token };
|
|
2509
|
+
if (!isValidSession(session)) return;
|
|
2510
|
+
const key2 = buildKey2(address, chainId);
|
|
2511
|
+
memorySessions2.set(key2, session);
|
|
2512
|
+
const store = storage2();
|
|
2513
|
+
try {
|
|
2514
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2515
|
+
} catch {
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
var clearYieldseekerSession = (address, chainId) => {
|
|
2519
|
+
const key2 = buildKey2(address, chainId);
|
|
2520
|
+
memorySessions2.delete(key2);
|
|
2521
|
+
const store = storage2();
|
|
2522
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2523
|
+
try {
|
|
2524
|
+
store?.removeItem(key2);
|
|
2525
|
+
} catch {
|
|
2526
|
+
}
|
|
2527
|
+
};
|
|
2528
|
+
|
|
2529
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2530
|
+
function resolveSiweOrigin(override) {
|
|
2531
|
+
const origin = override?.trim() || (typeof window !== "undefined" ? window.location?.origin : void 0);
|
|
2532
|
+
if (!origin || origin === "null") {
|
|
2533
|
+
throw new Error("Yieldseeker sign-in requires the requesting app's browser origin.");
|
|
2534
|
+
}
|
|
2535
|
+
const url = new URL(origin);
|
|
2536
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
2537
|
+
throw new Error("Yieldseeker sign-in requires an HTTP or HTTPS app origin.");
|
|
2538
|
+
}
|
|
2539
|
+
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
2540
|
+
throw new Error("Yieldseeker SIWE override must be an origin without credentials, path, query, or fragment.");
|
|
2541
|
+
}
|
|
2542
|
+
return url;
|
|
2543
|
+
}
|
|
2544
|
+
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2545
|
+
const url = resolveSiweOrigin(dependencies.origin);
|
|
2546
|
+
return new SiweMessage({
|
|
2547
|
+
scheme: url.protocol.slice(0, -1),
|
|
2548
|
+
domain: url.host,
|
|
2549
|
+
address: getAddress(address),
|
|
2550
|
+
uri: url.origin,
|
|
2551
|
+
version: "1",
|
|
2552
|
+
chainId,
|
|
2553
|
+
nonce: (dependencies.nonce ?? generateNonce)(),
|
|
2554
|
+
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2555
|
+
}).prepareMessage();
|
|
2556
|
+
}
|
|
2557
|
+
function encodeYieldseekerAuthToken(token) {
|
|
2558
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2559
|
+
let binary = "";
|
|
2560
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2561
|
+
return btoa(binary);
|
|
2562
|
+
}
|
|
2563
|
+
var YieldseekerAuth = class {
|
|
2564
|
+
constructor(dependencies = {}) {
|
|
2565
|
+
this.dependencies = dependencies;
|
|
2566
|
+
}
|
|
2567
|
+
dependencies;
|
|
2568
|
+
tokens = /* @__PURE__ */ new Map();
|
|
2569
|
+
pending = /* @__PURE__ */ new Map();
|
|
2570
|
+
scopes = /* @__PURE__ */ new Map();
|
|
2571
|
+
key(state, chainId) {
|
|
2572
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
|
|
2573
|
+
}
|
|
2574
|
+
async getToken(state, chainId) {
|
|
2575
|
+
const key2 = this.key(state, chainId);
|
|
2576
|
+
const scope = { address: state.walletAddress, chainId };
|
|
2577
|
+
this.scopes.set(key2, scope);
|
|
2578
|
+
const cached = this.tokens.get(key2);
|
|
2579
|
+
if (cached) return cached;
|
|
2580
|
+
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2581
|
+
if (persisted && this.matchesOrigin(persisted)) {
|
|
2582
|
+
this.tokens.set(key2, persisted);
|
|
2583
|
+
return persisted;
|
|
2584
|
+
}
|
|
2585
|
+
if (persisted) clearYieldseekerSession(scope.address, scope.chainId);
|
|
2586
|
+
const inFlight = this.pending.get(key2);
|
|
2587
|
+
if (inFlight) return inFlight;
|
|
2588
|
+
const request = this.sign(state, chainId).then((token) => {
|
|
2589
|
+
this.tokens.set(key2, token);
|
|
2590
|
+
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2591
|
+
return token;
|
|
2592
|
+
});
|
|
2593
|
+
this.pending.set(key2, request);
|
|
2594
|
+
try {
|
|
2595
|
+
return await request;
|
|
2596
|
+
} finally {
|
|
2597
|
+
this.pending.delete(key2);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
matchesOrigin(token) {
|
|
2601
|
+
try {
|
|
2602
|
+
const message = new SiweMessage(JSON.parse(atob(token)).message);
|
|
2603
|
+
const url = resolveSiweOrigin(this.dependencies.origin);
|
|
2604
|
+
return message.domain === url.host && message.uri === url.origin && message.scheme === url.protocol.slice(0, -1);
|
|
2605
|
+
} catch {
|
|
2606
|
+
return false;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
clear(state, chainId) {
|
|
2610
|
+
if (!state || chainId === void 0) {
|
|
2611
|
+
for (const scope of this.scopes.values()) {
|
|
2612
|
+
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2613
|
+
}
|
|
2614
|
+
this.tokens.clear();
|
|
2615
|
+
this.pending.clear();
|
|
2616
|
+
this.scopes.clear();
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
const key2 = this.key(state, chainId);
|
|
2620
|
+
this.tokens.delete(key2);
|
|
2621
|
+
this.pending.delete(key2);
|
|
2622
|
+
this.scopes.delete(key2);
|
|
2623
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2624
|
+
}
|
|
2625
|
+
async sign(state, chainId) {
|
|
2626
|
+
const account = getAddress(state.walletAddress);
|
|
2627
|
+
const publicClient = createPublicClient2({
|
|
2628
|
+
chain: base2,
|
|
2629
|
+
transport: custom(state.provider)
|
|
2630
|
+
});
|
|
2631
|
+
const walletClient = createWalletClient({
|
|
2632
|
+
account,
|
|
2633
|
+
chain: base2,
|
|
2634
|
+
transport: custom(state.provider)
|
|
2635
|
+
});
|
|
2636
|
+
await ensureWalletOnChain(
|
|
2637
|
+
publicClient,
|
|
2638
|
+
walletClient,
|
|
2639
|
+
8453
|
|
2640
|
+
);
|
|
2641
|
+
const message = createYieldseekerSiweMessage(
|
|
2642
|
+
account,
|
|
2643
|
+
chainId,
|
|
2644
|
+
this.dependencies
|
|
2645
|
+
);
|
|
2646
|
+
const signature = await walletClient.signMessage({ account, message });
|
|
2647
|
+
return encodeYieldseekerAuthToken({ message, signature });
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2650
|
+
|
|
2651
|
+
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2652
|
+
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2653
|
+
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2654
|
+
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2655
|
+
var storage3 = () => {
|
|
2656
|
+
if (typeof window === "undefined") return null;
|
|
2657
|
+
try {
|
|
2658
|
+
return window.localStorage;
|
|
2659
|
+
} catch {
|
|
2660
|
+
return null;
|
|
2661
|
+
}
|
|
2662
|
+
};
|
|
2663
|
+
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2664
|
+
function valid(value, walletAddress, chainId, now) {
|
|
2665
|
+
return Boolean(
|
|
2666
|
+
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
|
|
2667
|
+
);
|
|
2668
|
+
}
|
|
2669
|
+
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2670
|
+
if (typeof window === "undefined") return null;
|
|
2671
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2672
|
+
const store = storage3();
|
|
2673
|
+
let parsed = null;
|
|
2674
|
+
try {
|
|
2675
|
+
const raw2 = store?.getItem(key2);
|
|
2676
|
+
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2677
|
+
} catch {
|
|
2678
|
+
parsed = null;
|
|
2679
|
+
}
|
|
2680
|
+
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2681
|
+
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2682
|
+
memoryIdentities.set(key2, candidate);
|
|
2683
|
+
return { userId: candidate.userId };
|
|
2684
|
+
}
|
|
2685
|
+
memoryIdentities.delete(key2);
|
|
2686
|
+
try {
|
|
2687
|
+
store?.removeItem(key2);
|
|
2688
|
+
} catch {
|
|
2689
|
+
}
|
|
2690
|
+
return null;
|
|
2691
|
+
}
|
|
2692
|
+
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2693
|
+
if (typeof window === "undefined") return;
|
|
2694
|
+
const identity = {
|
|
2695
|
+
userId,
|
|
2696
|
+
walletAddress,
|
|
2697
|
+
chainId,
|
|
2698
|
+
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2699
|
+
};
|
|
2700
|
+
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2701
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2702
|
+
memoryIdentities.set(key2, identity);
|
|
2703
|
+
try {
|
|
2704
|
+
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2705
|
+
} catch {
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2709
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2710
|
+
memoryIdentities.delete(key2);
|
|
2711
|
+
try {
|
|
2712
|
+
storage3()?.removeItem(key2);
|
|
2713
|
+
} catch {
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2718
|
+
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2719
|
+
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2720
|
+
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2721
|
+
}
|
|
2722
|
+
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2723
|
+
var YieldseekerApiError = class extends Error {
|
|
2724
|
+
constructor(status, providerCode, responseFields) {
|
|
2725
|
+
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2726
|
+
this.status = status;
|
|
2727
|
+
this.providerCode = providerCode;
|
|
2728
|
+
this.responseFields = responseFields;
|
|
2729
|
+
this.name = "YieldseekerApiError";
|
|
2730
|
+
}
|
|
2731
|
+
status;
|
|
2732
|
+
providerCode;
|
|
2733
|
+
responseFields;
|
|
2734
|
+
get isAuthenticationError() {
|
|
2735
|
+
return this.status === 401 || this.status === 403;
|
|
2736
|
+
}
|
|
2737
|
+
};
|
|
2738
|
+
function providerError(body, fallback) {
|
|
2739
|
+
if (!body || typeof body !== "object") return { code: fallback };
|
|
2740
|
+
const record = body;
|
|
2741
|
+
return {
|
|
2742
|
+
code: typeof record.message === "string" ? record.message : fallback,
|
|
2743
|
+
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2744
|
+
};
|
|
2745
|
+
}
|
|
2746
|
+
var YieldseekerApiClient = class {
|
|
2747
|
+
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
|
|
2748
|
+
this.owneyApiKey = owneyApiKey;
|
|
2749
|
+
this.baseUrl = baseUrl;
|
|
2750
|
+
this.fetchFn = fetchFn;
|
|
2751
|
+
}
|
|
2752
|
+
owneyApiKey;
|
|
2753
|
+
baseUrl;
|
|
2754
|
+
fetchFn;
|
|
2755
|
+
async request(path, options = {}) {
|
|
2756
|
+
const controller = new AbortController();
|
|
2757
|
+
const timer = setTimeout(
|
|
2758
|
+
() => controller.abort(),
|
|
2759
|
+
options.timeoutMs ?? 15e3
|
|
2760
|
+
);
|
|
2761
|
+
try {
|
|
2762
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2763
|
+
method: options.method ?? "GET",
|
|
2764
|
+
headers: {
|
|
2765
|
+
"Content-Type": "application/json",
|
|
2766
|
+
"x-owney-api-key": this.owneyApiKey,
|
|
2767
|
+
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2768
|
+
},
|
|
2769
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2770
|
+
signal: controller.signal
|
|
2771
|
+
});
|
|
2772
|
+
const payload = await response.json().catch(() => null);
|
|
2773
|
+
if (!response.ok) {
|
|
2774
|
+
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2775
|
+
throw new YieldseekerApiError(
|
|
2776
|
+
response.status,
|
|
2777
|
+
error.code,
|
|
2778
|
+
error.fields
|
|
2779
|
+
);
|
|
2780
|
+
}
|
|
2781
|
+
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2782
|
+
return payload.data;
|
|
2783
|
+
}
|
|
2784
|
+
return payload;
|
|
2785
|
+
} catch (error) {
|
|
2786
|
+
if (error instanceof YieldseekerApiError) throw error;
|
|
2787
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2788
|
+
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2789
|
+
}
|
|
2790
|
+
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2791
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2792
|
+
});
|
|
2793
|
+
} finally {
|
|
2794
|
+
clearTimeout(timer);
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
};
|
|
2798
|
+
|
|
2799
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2800
|
+
import { formatUnits, isAddress } from "viem";
|
|
2801
|
+
|
|
2802
|
+
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2803
|
+
var YIELDSEEKER_ASSET_METADATA = {
|
|
2804
|
+
USDC: {
|
|
2805
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2806
|
+
decimals: 6
|
|
2807
|
+
},
|
|
2808
|
+
WETH: {
|
|
2809
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
2810
|
+
decimals: 18
|
|
2811
|
+
}
|
|
2812
|
+
};
|
|
2813
|
+
|
|
2814
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2815
|
+
function invalid(endpoint, detail) {
|
|
2816
|
+
throw new OwneyError(
|
|
2817
|
+
"AGENT_INVALID_RESPONSE",
|
|
2818
|
+
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2819
|
+
{ endpoint, detail },
|
|
2820
|
+
"yieldseeker"
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
function raw(value, endpoint) {
|
|
2824
|
+
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2825
|
+
return invalid(endpoint, "expected a base-10 integer string");
|
|
2826
|
+
}
|
|
2827
|
+
return BigInt(value);
|
|
2828
|
+
}
|
|
2829
|
+
function decimal(value, decimals, endpoint) {
|
|
2830
|
+
return formatUnits(raw(value, endpoint), decimals);
|
|
2831
|
+
}
|
|
2832
|
+
function usd(rawAmount, decimals, price) {
|
|
2833
|
+
return Number(formatUnits(rawAmount, decimals)) * price;
|
|
2834
|
+
}
|
|
2835
|
+
function percent(value) {
|
|
2836
|
+
const result = Number(value);
|
|
2837
|
+
return Number.isFinite(result) ? result * 100 : 0;
|
|
2838
|
+
}
|
|
2839
|
+
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2840
|
+
function publicApyAfterYieldseekerFee(value) {
|
|
2841
|
+
const grossPercent = percent(value);
|
|
2842
|
+
if (grossPercent <= 0) return grossPercent;
|
|
2843
|
+
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2844
|
+
return Math.round(netPercent * 1e12) / 1e12;
|
|
2845
|
+
}
|
|
2846
|
+
function riskAdjustedApyForDays(option, days) {
|
|
2847
|
+
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2848
|
+
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2849
|
+
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2850
|
+
}
|
|
2851
|
+
function assetAddressValue(record, address) {
|
|
2852
|
+
const entry = Object.entries(record).find(
|
|
2853
|
+
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2854
|
+
);
|
|
2855
|
+
return entry?.[1] ?? "0";
|
|
2856
|
+
}
|
|
2857
|
+
function position(value, asset, baseAssetDecimals) {
|
|
2858
|
+
const option = value?.yieldOption;
|
|
2859
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress(option.address)) {
|
|
2860
|
+
return invalid("yield positions", "missing vault metadata");
|
|
2861
|
+
}
|
|
2862
|
+
return {
|
|
2863
|
+
chain: "BASE",
|
|
2864
|
+
protocol: option.provider,
|
|
2865
|
+
protocolId: option.address,
|
|
2866
|
+
pool: option.name,
|
|
2867
|
+
asset,
|
|
2868
|
+
// `assetsRaw` is the ERC-4626 vault-share quantity, whose decimals can
|
|
2869
|
+
// differ from the underlying asset. Yieldseeker already converts it to
|
|
2870
|
+
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
2871
|
+
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
2872
|
+
// share quantity separately because withdraw-from-position expects it.
|
|
2873
|
+
amount: decimal(
|
|
2874
|
+
value.assetsBase,
|
|
2875
|
+
baseAssetDecimals,
|
|
2876
|
+
"yield positions"
|
|
2877
|
+
),
|
|
2878
|
+
amountRaw: String(value.assetsRaw),
|
|
2879
|
+
apy: percent(option.riskAdjustedApy),
|
|
2880
|
+
tvl: Number(option.totalDepositsUsd),
|
|
2881
|
+
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
function mapYieldseekerBalances(contexts) {
|
|
2885
|
+
const tokens = [];
|
|
2886
|
+
const assetBalances = [];
|
|
2887
|
+
const positions = [];
|
|
2888
|
+
let totalUsd = 0;
|
|
2889
|
+
for (const context of contexts) {
|
|
2890
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2891
|
+
assetBalances.push({
|
|
2892
|
+
chain: "BASE",
|
|
2893
|
+
chainId: 8453,
|
|
2894
|
+
asset: context.asset,
|
|
2895
|
+
amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
|
|
2896
|
+
});
|
|
2897
|
+
const idle = assetAddressValue(
|
|
2898
|
+
context.snapshot.tokenBalances,
|
|
2899
|
+
metadata.address
|
|
2900
|
+
);
|
|
2901
|
+
tokens.push({
|
|
2902
|
+
chain: "BASE",
|
|
2903
|
+
chainId: 8453,
|
|
2904
|
+
asset: context.asset,
|
|
2905
|
+
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2906
|
+
});
|
|
2907
|
+
positions.push(
|
|
2908
|
+
...context.positions.map(
|
|
2909
|
+
(entry) => position(
|
|
2910
|
+
entry,
|
|
2911
|
+
context.asset,
|
|
2912
|
+
context.snapshot.baseAssetDecimals
|
|
2913
|
+
)
|
|
2914
|
+
)
|
|
2915
|
+
);
|
|
2916
|
+
totalUsd += usd(
|
|
2917
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2918
|
+
context.snapshot.baseAssetDecimals,
|
|
2919
|
+
context.snapshot.baseAssetPriceUsd
|
|
2920
|
+
);
|
|
2921
|
+
}
|
|
2922
|
+
return {
|
|
2923
|
+
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2924
|
+
totalBalance: String(totalUsd),
|
|
2925
|
+
totalBalanceAsset: "usdc",
|
|
2926
|
+
assetBalances,
|
|
2927
|
+
tokens,
|
|
2928
|
+
positions
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
function mapYieldseekerEarnings(contexts) {
|
|
2932
|
+
const tokens = [];
|
|
2933
|
+
let lifetimeEarnings = 0;
|
|
2934
|
+
for (const context of contexts) {
|
|
2935
|
+
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
2936
|
+
tokens.push({
|
|
2937
|
+
chain: "BASE",
|
|
2938
|
+
chainId: 8453,
|
|
2939
|
+
asset: context.asset,
|
|
2940
|
+
amount: formatUnits(amount, context.snapshot.baseAssetDecimals)
|
|
2941
|
+
});
|
|
2942
|
+
lifetimeEarnings += usd(
|
|
2943
|
+
amount,
|
|
2944
|
+
context.snapshot.baseAssetDecimals,
|
|
2945
|
+
context.snapshot.baseAssetPriceUsd
|
|
2946
|
+
);
|
|
2947
|
+
}
|
|
2948
|
+
return {
|
|
2949
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2950
|
+
lifetimeEarnings,
|
|
2951
|
+
tokens
|
|
2952
|
+
};
|
|
2953
|
+
}
|
|
2954
|
+
function apyForDays(snapshot, days) {
|
|
2955
|
+
if (days === "7D") return percent(snapshot.apy7d);
|
|
2956
|
+
if (days === "30D") return percent(snapshot.apy30d);
|
|
2957
|
+
return percent((snapshot.apy7d + snapshot.apy30d) / 2);
|
|
2958
|
+
}
|
|
2959
|
+
function dailyApy(point) {
|
|
2960
|
+
const total = raw(point.totalValueBase, "historic position");
|
|
2961
|
+
const earned = raw(point.dailyYieldBase, "historic position");
|
|
2962
|
+
const principal = total - earned;
|
|
2963
|
+
if (principal <= 0n || earned === 0n) return 0;
|
|
2964
|
+
return Number(earned) / Number(principal) * 365 * 100;
|
|
2965
|
+
}
|
|
2966
|
+
function aggregateHistory(contexts, dayCount) {
|
|
2967
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
2968
|
+
for (const context of contexts) {
|
|
2969
|
+
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
2970
|
+
for (const point of points.slice(-dayCount)) {
|
|
2971
|
+
const valueUsd = usd(
|
|
2972
|
+
raw(point.totalValueBase, "historic position"),
|
|
2973
|
+
point.baseAssetDecimals,
|
|
2974
|
+
point.baseAssetPriceUsd
|
|
2975
|
+
);
|
|
2976
|
+
const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
|
|
2977
|
+
current.weighted += dailyApy(point) * valueUsd;
|
|
2978
|
+
current.valueUsd += valueUsd;
|
|
2979
|
+
byDate.set(point.date, current);
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
2983
|
+
date,
|
|
2984
|
+
apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
|
|
2985
|
+
}));
|
|
2986
|
+
}
|
|
2987
|
+
function mapYieldseekerApy(walletAddress, contexts, days) {
|
|
2988
|
+
let weighted = 0;
|
|
2989
|
+
let totalUsd = 0;
|
|
2990
|
+
const byAsset = {};
|
|
2991
|
+
for (const context of contexts) {
|
|
2992
|
+
const valueUsd = usd(
|
|
2993
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2994
|
+
context.snapshot.baseAssetDecimals,
|
|
2995
|
+
context.snapshot.baseAssetPriceUsd
|
|
2996
|
+
);
|
|
2997
|
+
const apy = apyForDays(context.snapshot, days);
|
|
2998
|
+
weighted += apy * valueUsd;
|
|
2999
|
+
totalUsd += valueUsd;
|
|
3000
|
+
byAsset[context.asset] = apy;
|
|
3001
|
+
}
|
|
3002
|
+
const dayCount = Number(days.slice(0, -1));
|
|
3003
|
+
return {
|
|
3004
|
+
walletAddress,
|
|
3005
|
+
weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
|
|
3006
|
+
apyByChainAndAsset: { 8453: byAsset },
|
|
3007
|
+
history: aggregateHistory(contexts, dayCount)
|
|
3008
|
+
};
|
|
3009
|
+
}
|
|
3010
|
+
function actionType(value) {
|
|
3011
|
+
const normalized = value.toLowerCase();
|
|
3012
|
+
if (normalized.includes("deposit")) return "Deposit";
|
|
3013
|
+
if (normalized.includes("withdraw")) return "Withdraw";
|
|
3014
|
+
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
3015
|
+
return "Earned";
|
|
3016
|
+
return "Rebalance";
|
|
3017
|
+
}
|
|
3018
|
+
function transactionHashes(details) {
|
|
3019
|
+
if (!details) return [];
|
|
3020
|
+
const values = [
|
|
3021
|
+
details.transactionHash,
|
|
3022
|
+
details.txHash,
|
|
3023
|
+
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
3024
|
+
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
3025
|
+
];
|
|
3026
|
+
return values.filter(
|
|
3027
|
+
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
3028
|
+
).filter((value, index, all) => all.indexOf(value) === index);
|
|
3029
|
+
}
|
|
3030
|
+
function actionEntry(action) {
|
|
3031
|
+
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
3032
|
+
return {
|
|
3033
|
+
agent: "yieldseeker",
|
|
3034
|
+
action: actionType(action.actionType),
|
|
3035
|
+
date: action.createdDate,
|
|
3036
|
+
oldApy: null,
|
|
3037
|
+
newApy: null,
|
|
3038
|
+
transactions: [
|
|
3039
|
+
{
|
|
3040
|
+
txHashes: transactionHashes(action.details),
|
|
3041
|
+
chainId: 8453
|
|
3042
|
+
}
|
|
3043
|
+
],
|
|
3044
|
+
rebalanceLog: []
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
3047
|
+
function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses) {
|
|
3048
|
+
const from = movement.fromAddress.toLowerCase();
|
|
3049
|
+
const to = movement.toAddress.toLowerCase();
|
|
3050
|
+
const owner = ownerAddress.toLowerCase();
|
|
3051
|
+
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
3052
|
+
const baseAsset = agent.assetAddress.toLowerCase();
|
|
3053
|
+
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
3054
|
+
return void 0;
|
|
3055
|
+
}
|
|
3056
|
+
let action;
|
|
3057
|
+
if (from === owner && to === agentWallet) {
|
|
3058
|
+
action = "Top up";
|
|
3059
|
+
} else if (from === agentWallet && to === owner) {
|
|
3060
|
+
action = "Withdraw";
|
|
3061
|
+
} else if (from === agentWallet && vaultAddresses.has(to)) {
|
|
3062
|
+
action = "Deposit";
|
|
3063
|
+
}
|
|
3064
|
+
if (!action) return void 0;
|
|
3065
|
+
return {
|
|
3066
|
+
agent: "yieldseeker",
|
|
3067
|
+
action,
|
|
3068
|
+
date: movement.blockDate,
|
|
3069
|
+
oldApy: null,
|
|
3070
|
+
newApy: null,
|
|
3071
|
+
transactions: [
|
|
3072
|
+
{
|
|
3073
|
+
txHashes: [movement.transactionHash],
|
|
3074
|
+
chainId: agent.chainId,
|
|
3075
|
+
tokenSymbol: asset,
|
|
3076
|
+
amount: decimal(
|
|
3077
|
+
movement.assetAmount,
|
|
3078
|
+
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
3079
|
+
"historic position"
|
|
3080
|
+
)
|
|
3081
|
+
}
|
|
3082
|
+
],
|
|
3083
|
+
rebalanceLog: []
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
function mapYieldseekerHistory(contexts, options) {
|
|
3087
|
+
const entries = contexts.flatMap((context) => {
|
|
3088
|
+
const vaultAddresses = new Set(
|
|
3089
|
+
context.positions.map(
|
|
3090
|
+
(position2) => position2.yieldOption.address.toLowerCase()
|
|
3091
|
+
)
|
|
3092
|
+
);
|
|
3093
|
+
return [
|
|
3094
|
+
...(context.historic?.movements ?? []).map(
|
|
3095
|
+
(movement) => movementEntry(
|
|
3096
|
+
movement,
|
|
3097
|
+
context.wallet,
|
|
3098
|
+
context.agent,
|
|
3099
|
+
context.asset,
|
|
3100
|
+
options.ownerAddress,
|
|
3101
|
+
vaultAddresses
|
|
3102
|
+
)
|
|
3103
|
+
),
|
|
3104
|
+
...(context.actions ?? []).map(actionEntry)
|
|
3105
|
+
].filter((entry) => entry !== void 0);
|
|
3106
|
+
});
|
|
3107
|
+
const filtered = entries.filter(
|
|
3108
|
+
(entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
|
|
3109
|
+
).filter((entry, index, all) => {
|
|
3110
|
+
const transactionHash = entry.transactions[0]?.txHashes[0];
|
|
3111
|
+
if (!transactionHash) return true;
|
|
3112
|
+
return all.findIndex(
|
|
3113
|
+
(candidate) => candidate.action === entry.action && candidate.transactions[0]?.txHashes[0] === transactionHash
|
|
3114
|
+
) === index;
|
|
3115
|
+
}).sort((left, right) => right.date.localeCompare(left.date));
|
|
3116
|
+
return {
|
|
3117
|
+
data: filtered.slice(0, options.limit),
|
|
3118
|
+
// v1 returns the whole action/movement collection and defines no cursor.
|
|
3119
|
+
// Report a terminal page so callers never loop over the same prefix.
|
|
3120
|
+
hasMore: false
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
function mapYieldseekerProfile(address, contexts) {
|
|
3124
|
+
const protocols = /* @__PURE__ */ new Set();
|
|
3125
|
+
for (const context of contexts) {
|
|
3126
|
+
for (const current of context.positions) {
|
|
3127
|
+
if (current.yieldOption?.provider) {
|
|
3128
|
+
protocols.add(String(current.yieldOption.provider));
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
return {
|
|
3133
|
+
address,
|
|
3134
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
3135
|
+
chains: contexts.length > 0 ? [8453] : [],
|
|
3136
|
+
hasActiveSessionKey: contexts.some(
|
|
3137
|
+
(context) => context.wallet.initializedDate != null
|
|
3138
|
+
),
|
|
3139
|
+
protocols: [...protocols]
|
|
3140
|
+
};
|
|
3141
|
+
}
|
|
3142
|
+
function mapYieldseekerAgentApy(options, days) {
|
|
3143
|
+
const perAsset = {};
|
|
3144
|
+
const all = [];
|
|
3145
|
+
for (const entry of options) {
|
|
3146
|
+
const apys = entry.yieldOptions.map(
|
|
3147
|
+
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
3148
|
+
).filter(Number.isFinite);
|
|
3149
|
+
if (apys.length === 0) continue;
|
|
3150
|
+
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
3151
|
+
perAsset[entry.asset] = average;
|
|
3152
|
+
all.push(average);
|
|
3153
|
+
}
|
|
3154
|
+
return {
|
|
3155
|
+
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
3156
|
+
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
3157
|
+
};
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3161
|
+
var OWNEY_AGENT_NAME = "owney";
|
|
3162
|
+
var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
3163
|
+
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3164
|
+
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3165
|
+
function generateYieldseekerUsername() {
|
|
3166
|
+
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3167
|
+
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
3168
|
+
}
|
|
3169
|
+
function isUsernameConflict(error) {
|
|
3170
|
+
if (!(error instanceof YieldseekerApiError)) return false;
|
|
3171
|
+
const code = error.providerCode.toUpperCase();
|
|
3172
|
+
return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
|
|
3173
|
+
}
|
|
3174
|
+
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
3175
|
+
{
|
|
3176
|
+
type: "function",
|
|
3177
|
+
name: "withdrawAssetToUser",
|
|
3178
|
+
stateMutability: "nonpayable",
|
|
3179
|
+
inputs: [
|
|
3180
|
+
{ name: "recipient", type: "address" },
|
|
3181
|
+
{ name: "asset", type: "address" },
|
|
3182
|
+
{ name: "amount", type: "uint256" }
|
|
3183
|
+
],
|
|
3184
|
+
outputs: []
|
|
3185
|
+
},
|
|
3186
|
+
{
|
|
3187
|
+
type: "function",
|
|
3188
|
+
name: "withdrawAllAssetToUser",
|
|
3189
|
+
stateMutability: "nonpayable",
|
|
3190
|
+
inputs: [
|
|
3191
|
+
{ name: "recipient", type: "address" },
|
|
3192
|
+
{ name: "asset", type: "address" }
|
|
3193
|
+
],
|
|
3194
|
+
outputs: []
|
|
3195
|
+
}
|
|
3196
|
+
];
|
|
3197
|
+
function query(params) {
|
|
3198
|
+
const search = new URLSearchParams();
|
|
3199
|
+
for (const [key2, value] of Object.entries(params)) {
|
|
3200
|
+
if (value !== void 0) search.set(key2, String(value));
|
|
3201
|
+
}
|
|
3202
|
+
const encoded = search.toString();
|
|
3203
|
+
return encoded ? `?${encoded}` : "";
|
|
3204
|
+
}
|
|
3205
|
+
var YieldseekerAgent = class {
|
|
3206
|
+
id = "yieldseeker";
|
|
3207
|
+
balanceComposition = "tokens-plus-positions";
|
|
3208
|
+
supportedChainIds = [8453];
|
|
3209
|
+
supportedAssets = [
|
|
3210
|
+
{
|
|
3211
|
+
chainId: 8453,
|
|
3212
|
+
chain: "BASE",
|
|
3213
|
+
assets: [
|
|
3214
|
+
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
3215
|
+
{ symbol: "WETH", minDepositAmount: "1" }
|
|
3216
|
+
]
|
|
3217
|
+
}
|
|
3218
|
+
];
|
|
3219
|
+
api;
|
|
3220
|
+
auth;
|
|
3221
|
+
transactionExecutor;
|
|
3222
|
+
unwindReceiptWaiter;
|
|
3223
|
+
agentContexts = /* @__PURE__ */ new Map();
|
|
3224
|
+
users = /* @__PURE__ */ new Map();
|
|
3225
|
+
pendingAgents = /* @__PURE__ */ new Map();
|
|
3226
|
+
constructor(owneyApiKey, options = {}) {
|
|
3227
|
+
this.api = new YieldseekerApiClient(
|
|
3228
|
+
owneyApiKey,
|
|
3229
|
+
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
3230
|
+
options.fetchFn
|
|
3231
|
+
);
|
|
3232
|
+
this.auth = new YieldseekerAuth(options.auth);
|
|
3233
|
+
this.transactionExecutor = options.transactionExecutor;
|
|
3234
|
+
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3235
|
+
}
|
|
3236
|
+
async disconnect() {
|
|
3237
|
+
this.auth.clear();
|
|
3238
|
+
for (const key2 of this.users.keys()) {
|
|
3239
|
+
const [walletAddress, chainId] = key2.split(":");
|
|
3240
|
+
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
3241
|
+
}
|
|
3242
|
+
this.users.clear();
|
|
3243
|
+
this.agentContexts.clear();
|
|
3244
|
+
this.pendingAgents.clear();
|
|
3245
|
+
}
|
|
3246
|
+
async activateAgent(state, chainId, asset) {
|
|
3247
|
+
this.assertChain(chainId);
|
|
3248
|
+
const targetAsset = asset ?? "USDC";
|
|
3249
|
+
this.assertAsset(targetAsset);
|
|
3250
|
+
await this.ensureAgent(state, chainId, targetAsset);
|
|
3251
|
+
}
|
|
3252
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3253
|
+
this.assertChain(chainId);
|
|
3254
|
+
this.assertAsset(asset);
|
|
3255
|
+
if (BigInt(amount) <= 0n) {
|
|
3256
|
+
throw new OwneyError(
|
|
3257
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
3258
|
+
"Yieldseeker deposits must be greater than zero.",
|
|
3259
|
+
{ amount, minDepositAmount: "1" },
|
|
3260
|
+
this.id
|
|
3261
|
+
);
|
|
3262
|
+
}
|
|
3263
|
+
const context = await this.ensureAgent(state, chainId, asset);
|
|
3264
|
+
let txHash;
|
|
3265
|
+
try {
|
|
3266
|
+
if (depositCallback) {
|
|
3267
|
+
provideDepositVerificationContext(depositCallback, {
|
|
3268
|
+
agentId: "yieldseeker",
|
|
3269
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3270
|
+
userId: context.user.userId,
|
|
3271
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3272
|
+
});
|
|
3273
|
+
txHash = await depositCallback(
|
|
3274
|
+
context.wallet.walletAddress,
|
|
3275
|
+
chainId,
|
|
3276
|
+
amount
|
|
3277
|
+
);
|
|
3278
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3279
|
+
} else {
|
|
3280
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3281
|
+
from: getAddress2(state.walletAddress),
|
|
3282
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3283
|
+
data: encodeFunctionData({
|
|
3284
|
+
abi: erc20Abi,
|
|
3285
|
+
functionName: "transfer",
|
|
3286
|
+
args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
|
|
3287
|
+
}),
|
|
3288
|
+
value: "0",
|
|
3289
|
+
chainId
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
3292
|
+
await this.deployAfterFunding(state, chainId, context);
|
|
3293
|
+
} finally {
|
|
3294
|
+
await this.refreshSnapshotAfterMovement(
|
|
3295
|
+
state,
|
|
3296
|
+
chainId,
|
|
3297
|
+
context,
|
|
3298
|
+
"deposit"
|
|
3299
|
+
);
|
|
3300
|
+
}
|
|
3301
|
+
return {
|
|
3302
|
+
txHash,
|
|
3303
|
+
smartWallet: context.wallet.walletAddress,
|
|
3304
|
+
amount
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
async withdraw(state, chainId, asset, amount) {
|
|
3308
|
+
this.assertChain(chainId);
|
|
3309
|
+
this.assertAsset(asset);
|
|
3310
|
+
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
3311
|
+
throw new OwneyError(
|
|
3312
|
+
"WITHDRAW_FAILED",
|
|
3313
|
+
"Yieldseeker withdrawals must be greater than zero.",
|
|
3314
|
+
{ amount },
|
|
3315
|
+
this.id
|
|
3316
|
+
);
|
|
3317
|
+
}
|
|
3318
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
3319
|
+
if (!context) {
|
|
3320
|
+
throw new OwneyError(
|
|
3321
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3322
|
+
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3323
|
+
{ asset, available: "0" },
|
|
3324
|
+
this.id
|
|
3325
|
+
);
|
|
3326
|
+
}
|
|
3327
|
+
try {
|
|
3328
|
+
const portfolio = await this.loadPortfolioContext(
|
|
3329
|
+
state,
|
|
3330
|
+
chainId,
|
|
3331
|
+
context
|
|
3332
|
+
);
|
|
3333
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3334
|
+
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3335
|
+
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3336
|
+
);
|
|
3337
|
+
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3338
|
+
const deployed = portfolio.positions.reduce(
|
|
3339
|
+
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3340
|
+
0n
|
|
3341
|
+
);
|
|
3342
|
+
const totalAvailable = idle + deployed;
|
|
3343
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3344
|
+
if (requested > totalAvailable) {
|
|
3345
|
+
throw new OwneyError(
|
|
3346
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3347
|
+
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3348
|
+
{
|
|
3349
|
+
asset,
|
|
3350
|
+
requested: requested.toString(),
|
|
3351
|
+
available: totalAvailable.toString()
|
|
3352
|
+
},
|
|
3353
|
+
this.id
|
|
3354
|
+
);
|
|
3355
|
+
}
|
|
3356
|
+
let remaining = requested > idle ? requested - idle : 0n;
|
|
3357
|
+
for (const position2 of portfolio.positions) {
|
|
3358
|
+
if (remaining === 0n) break;
|
|
3359
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3360
|
+
if (available <= 0n) continue;
|
|
3361
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
3362
|
+
const response = await this.walletRequest(
|
|
3363
|
+
state,
|
|
3364
|
+
chainId,
|
|
3365
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
3366
|
+
{
|
|
3367
|
+
method: "POST",
|
|
3368
|
+
body: {
|
|
3369
|
+
chainId,
|
|
3370
|
+
vaultAddress: position2.yieldOption.address,
|
|
3371
|
+
assetsRaw: assetsRaw.toString()
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
);
|
|
3375
|
+
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3376
|
+
throw this.invalidResponse("position withdrawal");
|
|
3377
|
+
}
|
|
3378
|
+
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3379
|
+
remaining -= assetsRaw;
|
|
3380
|
+
}
|
|
3381
|
+
if (remaining > 0n) {
|
|
3382
|
+
throw this.invalidResponse("yield positions", {
|
|
3383
|
+
reason: "Withdrawable positions could not cover the request.",
|
|
3384
|
+
remaining: remaining.toString()
|
|
3385
|
+
});
|
|
3386
|
+
}
|
|
3387
|
+
const account = getAddress2(state.walletAddress);
|
|
3388
|
+
const txHash = await this.submitTransaction(state, chainId, {
|
|
3389
|
+
from: account,
|
|
3390
|
+
to: getAddress2(context.wallet.walletAddress),
|
|
3391
|
+
data: amount === void 0 ? encodeFunctionData({
|
|
3392
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3393
|
+
functionName: "withdrawAllAssetToUser",
|
|
3394
|
+
args: [account, metadata.address]
|
|
3395
|
+
}) : encodeFunctionData({
|
|
3396
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3397
|
+
functionName: "withdrawAssetToUser",
|
|
3398
|
+
args: [account, metadata.address, requested]
|
|
3399
|
+
}),
|
|
3400
|
+
value: "0",
|
|
3401
|
+
chainId
|
|
3402
|
+
});
|
|
3403
|
+
return {
|
|
3404
|
+
txHash,
|
|
3405
|
+
type: amount === void 0 ? "full" : "partial",
|
|
3406
|
+
amount: requested.toString()
|
|
3407
|
+
};
|
|
3408
|
+
} finally {
|
|
3409
|
+
await this.refreshSnapshotAfterMovement(
|
|
3410
|
+
state,
|
|
3411
|
+
chainId,
|
|
3412
|
+
context,
|
|
3413
|
+
"withdrawal"
|
|
3414
|
+
);
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
async getBalances(state, chainId) {
|
|
3418
|
+
this.assertChain(chainId);
|
|
3419
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3420
|
+
}
|
|
3421
|
+
async getEarnings(state, chainId) {
|
|
3422
|
+
this.assertChain(chainId);
|
|
3423
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3424
|
+
}
|
|
3425
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3426
|
+
this.assertChain(chainId);
|
|
3427
|
+
const asset = tokenSymbol?.toUpperCase();
|
|
3428
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3429
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3430
|
+
...asset ? { asset } : {},
|
|
3431
|
+
historic: true
|
|
3432
|
+
});
|
|
3433
|
+
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3434
|
+
}
|
|
3435
|
+
async getHistory(state, chainId, options) {
|
|
3436
|
+
this.assertChain(chainId);
|
|
3437
|
+
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3438
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3439
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3440
|
+
...asset ? { asset } : {},
|
|
3441
|
+
historic: true,
|
|
3442
|
+
actions: true
|
|
3443
|
+
});
|
|
3444
|
+
return mapYieldseekerHistory(contexts, {
|
|
3445
|
+
limit: options?.limit ?? 10,
|
|
3446
|
+
ownerAddress: state.walletAddress,
|
|
3447
|
+
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3448
|
+
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
async getUserProfile(state, chainId) {
|
|
3452
|
+
this.assertChain(chainId);
|
|
3453
|
+
return mapYieldseekerProfile(
|
|
3454
|
+
state.walletAddress,
|
|
3455
|
+
await this.loadPortfolio(state, chainId, {})
|
|
3456
|
+
);
|
|
3457
|
+
}
|
|
3458
|
+
async getAgentApy(days, options) {
|
|
3459
|
+
this.assertOptionalChain(options?.chainId);
|
|
3460
|
+
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3461
|
+
if (requested !== void 0) this.assertAsset(requested);
|
|
3462
|
+
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3463
|
+
const values = await Promise.all(
|
|
3464
|
+
assets.map(async (asset) => {
|
|
3465
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3466
|
+
const response = await this.api.request(
|
|
3467
|
+
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3468
|
+
);
|
|
3469
|
+
if (!Array.isArray(response?.yieldOptions)) {
|
|
3470
|
+
throw this.invalidResponse("yield options");
|
|
3471
|
+
}
|
|
3472
|
+
return { asset, yieldOptions: response.yieldOptions };
|
|
3473
|
+
})
|
|
3474
|
+
);
|
|
3475
|
+
return mapYieldseekerAgentApy(values, days);
|
|
3476
|
+
}
|
|
3477
|
+
userKey(state, chainId) {
|
|
3478
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3479
|
+
}
|
|
3480
|
+
contextKey(state, chainId, asset) {
|
|
3481
|
+
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3482
|
+
}
|
|
3483
|
+
async resolveUser(state, chainId) {
|
|
3484
|
+
const key2 = this.userKey(state, chainId);
|
|
3485
|
+
const inMemory = this.users.get(key2);
|
|
3486
|
+
if (inMemory) return inMemory;
|
|
3487
|
+
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3488
|
+
if (persisted) {
|
|
3489
|
+
this.users.set(key2, persisted);
|
|
3490
|
+
return persisted;
|
|
3491
|
+
}
|
|
3492
|
+
const walletAddress = getAddress2(state.walletAddress);
|
|
3493
|
+
let user = null;
|
|
3494
|
+
try {
|
|
3495
|
+
const login = await this.providerRequest(
|
|
3496
|
+
state,
|
|
3497
|
+
chainId,
|
|
3498
|
+
"/users/login-with-wallet",
|
|
3499
|
+
{ method: "POST", body: { walletAddress } }
|
|
3500
|
+
);
|
|
3501
|
+
user = login?.user ?? null;
|
|
3502
|
+
if (!user) {
|
|
3503
|
+
throw this.invalidResponse("wallet login", {
|
|
3504
|
+
reason: "A successful login returned no user."
|
|
3505
|
+
});
|
|
3506
|
+
}
|
|
3507
|
+
} catch (error) {
|
|
3508
|
+
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3509
|
+
if (error instanceof OwneyError) throw error;
|
|
3510
|
+
throw this.mapApiError(error);
|
|
3511
|
+
}
|
|
3512
|
+
let created;
|
|
3513
|
+
for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
|
|
3514
|
+
try {
|
|
3515
|
+
created = await this.providerRequest(
|
|
3516
|
+
state,
|
|
3517
|
+
chainId,
|
|
3518
|
+
"/users",
|
|
3519
|
+
{
|
|
3520
|
+
method: "POST",
|
|
3521
|
+
body: {
|
|
3522
|
+
walletAddress,
|
|
3523
|
+
username: generateYieldseekerUsername()
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
);
|
|
3527
|
+
break;
|
|
3528
|
+
} catch (createError) {
|
|
3529
|
+
const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
|
|
3530
|
+
if (canRetry) continue;
|
|
3531
|
+
throw this.mapApiError(createError);
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
user = created?.user ?? null;
|
|
3535
|
+
}
|
|
3536
|
+
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3537
|
+
throw this.invalidResponse("wallet identity");
|
|
3538
|
+
}
|
|
3539
|
+
const resolved = { userId: user.userId };
|
|
3540
|
+
this.users.set(key2, resolved);
|
|
3541
|
+
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3542
|
+
return resolved;
|
|
3543
|
+
}
|
|
3544
|
+
forgetUser(state, chainId) {
|
|
3545
|
+
this.users.delete(this.userKey(state, chainId));
|
|
3546
|
+
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3547
|
+
}
|
|
3548
|
+
async ensureAgent(state, chainId, asset) {
|
|
3549
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3550
|
+
const cached = this.agentContexts.get(key2);
|
|
3551
|
+
if (cached) return cached;
|
|
3552
|
+
const pending = this.pendingAgents.get(key2);
|
|
3553
|
+
if (pending) return pending;
|
|
3554
|
+
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3555
|
+
(context) => {
|
|
3556
|
+
if (!context) throw this.invalidResponse("agent creation");
|
|
3557
|
+
this.agentContexts.set(key2, context);
|
|
3558
|
+
return context;
|
|
3559
|
+
}
|
|
3560
|
+
);
|
|
3561
|
+
this.pendingAgents.set(key2, request);
|
|
3562
|
+
try {
|
|
3563
|
+
return await request;
|
|
3564
|
+
} finally {
|
|
3565
|
+
this.pendingAgents.delete(key2);
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
async findAgent(state, chainId, asset) {
|
|
3569
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3570
|
+
const cached = this.agentContexts.get(key2);
|
|
3571
|
+
if (cached) return cached;
|
|
3572
|
+
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3573
|
+
if (context) this.agentContexts.set(key2, context);
|
|
3574
|
+
return context;
|
|
3575
|
+
}
|
|
3576
|
+
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3577
|
+
const user = await this.resolveUser(state, chainId);
|
|
3578
|
+
const response = await this.walletRequest(
|
|
3579
|
+
state,
|
|
3580
|
+
chainId,
|
|
3581
|
+
`/users/${user.userId}/agents`
|
|
3582
|
+
);
|
|
3583
|
+
if (!Array.isArray(response?.agents)) {
|
|
3584
|
+
throw this.invalidResponse("agent list");
|
|
3585
|
+
}
|
|
3586
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3587
|
+
let agent = response.agents.find(
|
|
3588
|
+
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3589
|
+
);
|
|
3590
|
+
if (!agent && createIfMissing) {
|
|
3591
|
+
const created = await this.walletRequest(
|
|
3592
|
+
state,
|
|
3593
|
+
chainId,
|
|
3594
|
+
`/users/${user.userId}/agents`,
|
|
3595
|
+
{
|
|
3596
|
+
method: "POST",
|
|
3597
|
+
body: {
|
|
3598
|
+
name: OWNEY_AGENT_NAME,
|
|
3599
|
+
emoji: "\u{1F989}",
|
|
3600
|
+
chainId,
|
|
3601
|
+
assetAddress: metadata.address,
|
|
3602
|
+
type: "vault",
|
|
3603
|
+
rulePreset: null
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
);
|
|
3607
|
+
agent = created?.agent;
|
|
3608
|
+
}
|
|
3609
|
+
if (!agent) return null;
|
|
3610
|
+
this.assertAgent(agent);
|
|
3611
|
+
const walletResponse = await this.walletRequest(
|
|
3612
|
+
state,
|
|
3613
|
+
chainId,
|
|
3614
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3615
|
+
);
|
|
3616
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3617
|
+
throw this.invalidResponse("agent wallet");
|
|
3618
|
+
}
|
|
3619
|
+
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3620
|
+
}
|
|
3621
|
+
async loadPortfolio(state, chainId, options) {
|
|
3622
|
+
const user = await this.resolveUser(state, chainId);
|
|
3623
|
+
const response = await this.walletRequest(
|
|
3624
|
+
state,
|
|
3625
|
+
chainId,
|
|
3626
|
+
`/users/${user.userId}/agents`
|
|
3627
|
+
);
|
|
3628
|
+
if (!Array.isArray(response?.agents)) {
|
|
3629
|
+
throw this.invalidResponse("agent list");
|
|
3630
|
+
}
|
|
3631
|
+
const contexts = [];
|
|
3632
|
+
for (const agent of response.agents) {
|
|
3633
|
+
const asset = this.assetForAgent(agent);
|
|
3634
|
+
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3635
|
+
continue;
|
|
3636
|
+
}
|
|
3637
|
+
this.assertAgent(agent);
|
|
3638
|
+
const walletResponse = await this.walletRequest(
|
|
3639
|
+
state,
|
|
3640
|
+
chainId,
|
|
3641
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3642
|
+
);
|
|
3643
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3644
|
+
throw this.invalidResponse("agent wallet");
|
|
3645
|
+
}
|
|
3646
|
+
const context = {
|
|
3647
|
+
user,
|
|
3648
|
+
agent,
|
|
3649
|
+
wallet: walletResponse.agentWallet,
|
|
3650
|
+
asset
|
|
3651
|
+
};
|
|
3652
|
+
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3653
|
+
contexts.push(context);
|
|
3654
|
+
}
|
|
3655
|
+
return Promise.all(
|
|
3656
|
+
contexts.map(
|
|
3657
|
+
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3658
|
+
)
|
|
3659
|
+
);
|
|
3660
|
+
}
|
|
3661
|
+
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3662
|
+
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3663
|
+
this.walletRequest(
|
|
3664
|
+
state,
|
|
3665
|
+
chainId,
|
|
3666
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3667
|
+
shouldOnlyUseRecentValue: true,
|
|
3668
|
+
shouldAllowStaleOnError: true
|
|
3669
|
+
})}`
|
|
3670
|
+
),
|
|
3671
|
+
this.walletRequest(
|
|
3672
|
+
state,
|
|
3673
|
+
chainId,
|
|
3674
|
+
this.agentPath(context, "yield-positions")
|
|
3675
|
+
),
|
|
3676
|
+
options.historic ? this.walletRequest(
|
|
3677
|
+
state,
|
|
3678
|
+
chainId,
|
|
3679
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3680
|
+
) : Promise.resolve(void 0),
|
|
3681
|
+
options.actions ? this.walletRequest(
|
|
3682
|
+
state,
|
|
3683
|
+
chainId,
|
|
3684
|
+
this.agentPath(context, "actions")
|
|
3685
|
+
) : Promise.resolve(void 0)
|
|
3686
|
+
]);
|
|
3687
|
+
if (!snapshot?.agentSnapshot) {
|
|
3688
|
+
throw this.invalidResponse("agent snapshot");
|
|
3689
|
+
}
|
|
3690
|
+
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3691
|
+
throw this.invalidResponse("yield positions");
|
|
3692
|
+
}
|
|
3693
|
+
return {
|
|
3694
|
+
...context,
|
|
3695
|
+
snapshot: snapshot.agentSnapshot,
|
|
3696
|
+
positions: positions.yieldPositions,
|
|
3697
|
+
...historic?.position ? { historic: historic.position } : {},
|
|
3698
|
+
...actions?.actions ? { actions: actions.actions } : {}
|
|
3699
|
+
};
|
|
3700
|
+
}
|
|
3701
|
+
async deployAfterFunding(state, chainId, context) {
|
|
3702
|
+
if (context.wallet.initializedDate != null) return;
|
|
3703
|
+
try {
|
|
3704
|
+
const deployed = await this.walletRequest(
|
|
3705
|
+
state,
|
|
3706
|
+
chainId,
|
|
3707
|
+
this.agentPath(context, "deploy"),
|
|
3708
|
+
{ method: "POST", body: {} }
|
|
3709
|
+
);
|
|
3710
|
+
if (deployed?.agentWallet) {
|
|
3711
|
+
context.wallet = deployed.agentWallet;
|
|
3712
|
+
}
|
|
3713
|
+
} catch (error) {
|
|
3714
|
+
console.warn(
|
|
3715
|
+
"[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
|
|
3716
|
+
error
|
|
3717
|
+
);
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3721
|
+
try {
|
|
3722
|
+
const response = await this.walletRequest(
|
|
3723
|
+
state,
|
|
3724
|
+
chainId,
|
|
3725
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3726
|
+
shouldForceRefresh: true
|
|
3727
|
+
})}`
|
|
3728
|
+
);
|
|
3729
|
+
if (!response?.agentSnapshot) {
|
|
3730
|
+
throw this.invalidResponse("agent snapshot refresh");
|
|
3731
|
+
}
|
|
3732
|
+
} catch (error) {
|
|
3733
|
+
console.warn(
|
|
3734
|
+
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3735
|
+
error
|
|
3736
|
+
);
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
agentPath(context, suffix) {
|
|
3740
|
+
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
3741
|
+
}
|
|
3742
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
3743
|
+
try {
|
|
3744
|
+
return await this.providerRequest(state, chainId, path, options);
|
|
3745
|
+
} catch (error) {
|
|
3746
|
+
throw this.mapApiError(error);
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
async providerRequest(state, chainId, path, options = {}) {
|
|
3750
|
+
this.assertChain(chainId);
|
|
3751
|
+
const request = (signature2) => this.api.request(path, {
|
|
3752
|
+
...options,
|
|
3753
|
+
signature: signature2
|
|
3754
|
+
});
|
|
3755
|
+
let signature = await this.auth.getToken(state, chainId);
|
|
3756
|
+
try {
|
|
3757
|
+
return await request(signature);
|
|
3758
|
+
} catch (error) {
|
|
3759
|
+
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
3760
|
+
if (error.providerCode === "NO_USER") throw error;
|
|
3761
|
+
if (!error.isAuthenticationError) throw error;
|
|
3762
|
+
this.auth.clear(state, chainId);
|
|
3763
|
+
signature = await this.auth.getToken(state, chainId);
|
|
3764
|
+
try {
|
|
3765
|
+
return await request(signature);
|
|
3766
|
+
} catch (retryError) {
|
|
3767
|
+
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3768
|
+
this.forgetUser(state, chainId);
|
|
3769
|
+
}
|
|
3770
|
+
throw retryError;
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
mapApiError(error) {
|
|
3775
|
+
if (!(error instanceof YieldseekerApiError)) {
|
|
3776
|
+
return new OwneyError(
|
|
3777
|
+
"AGENT_API_ERROR",
|
|
3778
|
+
"Yieldseeker request failed.",
|
|
3779
|
+
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3780
|
+
this.id
|
|
3781
|
+
);
|
|
3782
|
+
}
|
|
3783
|
+
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3784
|
+
return new OwneyError(
|
|
3785
|
+
code,
|
|
3786
|
+
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3787
|
+
{
|
|
3788
|
+
statusCode: error.status,
|
|
3789
|
+
providerCode: error.providerCode,
|
|
3790
|
+
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3791
|
+
},
|
|
3792
|
+
this.id
|
|
3793
|
+
);
|
|
3794
|
+
}
|
|
3795
|
+
async submitTransaction(state, chainId, transaction) {
|
|
3796
|
+
if (this.transactionExecutor) {
|
|
3797
|
+
return this.transactionExecutor(state, chainId, transaction);
|
|
3798
|
+
}
|
|
3799
|
+
this.assertTransaction(transaction, state, chainId);
|
|
3800
|
+
const account = getAddress2(state.walletAddress);
|
|
3801
|
+
const walletClient = createWalletClient2({
|
|
3802
|
+
account,
|
|
3803
|
+
chain: base3,
|
|
3804
|
+
transport: custom2(state.provider)
|
|
3805
|
+
});
|
|
3806
|
+
const publicClient = createPublicClient3({
|
|
3807
|
+
chain: base3,
|
|
3808
|
+
transport: custom2(state.provider)
|
|
3809
|
+
});
|
|
3810
|
+
await ensureWalletOnChain(
|
|
3811
|
+
publicClient,
|
|
3812
|
+
walletClient,
|
|
3813
|
+
8453
|
|
3814
|
+
);
|
|
3815
|
+
const hash = await walletClient.sendTransaction({
|
|
3816
|
+
account,
|
|
3817
|
+
chain: base3,
|
|
3818
|
+
to: getAddress2(transaction.to),
|
|
3819
|
+
data: transaction.data,
|
|
3820
|
+
value: BigInt(transaction.value)
|
|
3821
|
+
});
|
|
3822
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3823
|
+
hash,
|
|
3824
|
+
confirmations: 1
|
|
3825
|
+
});
|
|
3826
|
+
if (receipt.status !== "success") {
|
|
3827
|
+
throw new OwneyError(
|
|
3828
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3829
|
+
`Yieldseeker transaction reverted (${hash}).`,
|
|
3830
|
+
{ transactionHash: hash },
|
|
3831
|
+
this.id
|
|
3832
|
+
);
|
|
3833
|
+
}
|
|
3834
|
+
return hash;
|
|
3835
|
+
}
|
|
3836
|
+
async waitForReceipt(state, chainId, transactionHash) {
|
|
3837
|
+
if (this.unwindReceiptWaiter) {
|
|
3838
|
+
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3839
|
+
return;
|
|
3840
|
+
}
|
|
3841
|
+
const publicClient = createPublicClient3({
|
|
3842
|
+
chain: base3,
|
|
3843
|
+
transport: custom2(state.provider)
|
|
3844
|
+
});
|
|
3845
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3846
|
+
hash: transactionHash,
|
|
3847
|
+
confirmations: 1
|
|
3848
|
+
});
|
|
3849
|
+
if (receipt.status !== "success") {
|
|
3850
|
+
throw new OwneyError(
|
|
3851
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3852
|
+
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3853
|
+
{ transactionHash },
|
|
3854
|
+
this.id
|
|
3855
|
+
);
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
assertTransaction(transaction, state, chainId) {
|
|
3859
|
+
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)) {
|
|
3860
|
+
throw this.invalidResponse("transaction");
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
assertAgent(agent) {
|
|
3864
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress2(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3865
|
+
throw this.invalidResponse("agent");
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
isOwneyAgent(agent) {
|
|
3869
|
+
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3870
|
+
}
|
|
3871
|
+
assetForAgent(agent) {
|
|
3872
|
+
for (const asset of ["USDC", "WETH"]) {
|
|
3873
|
+
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3874
|
+
return asset;
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
return null;
|
|
3878
|
+
}
|
|
3879
|
+
isTransactionHash(value) {
|
|
3880
|
+
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3881
|
+
}
|
|
3882
|
+
assertChain(chainId) {
|
|
3883
|
+
if (chainId !== 8453) {
|
|
3884
|
+
throw new OwneyError(
|
|
3885
|
+
"CHAIN_UNSUPPORTED",
|
|
3886
|
+
`Yieldseeker does not support chain ${chainId}.`,
|
|
3887
|
+
{ chainId, supportedChainIds: [8453] },
|
|
3888
|
+
this.id
|
|
3889
|
+
);
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
assertOptionalChain(chainId) {
|
|
3893
|
+
if (chainId !== void 0) this.assertChain(chainId);
|
|
3894
|
+
}
|
|
3895
|
+
assertAsset(asset) {
|
|
3896
|
+
if (asset !== "USDC" && asset !== "WETH") {
|
|
3897
|
+
throw new OwneyError(
|
|
3898
|
+
"ASSET_UNSUPPORTED",
|
|
3899
|
+
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
3900
|
+
{
|
|
3901
|
+
asset,
|
|
3902
|
+
supportedAssets: ["USDC", "WETH"],
|
|
3903
|
+
providerAlsoAdvertises: ["cbBTC"]
|
|
3904
|
+
},
|
|
3905
|
+
this.id
|
|
3906
|
+
);
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
invalidResponse(operation, details = {}) {
|
|
3910
|
+
return new OwneyError(
|
|
3911
|
+
"AGENT_INVALID_RESPONSE",
|
|
3912
|
+
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3913
|
+
details,
|
|
3914
|
+
this.id
|
|
3915
|
+
);
|
|
3916
|
+
}
|
|
3917
|
+
};
|
|
3918
|
+
|
|
3919
|
+
// src/lib/routing-api.ts
|
|
3920
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3921
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3922
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3923
|
+
try {
|
|
3924
|
+
const res = await fetch(url, {
|
|
3925
|
+
method: "GET",
|
|
3926
|
+
headers: {
|
|
3927
|
+
"Content-Type": "application/json",
|
|
3928
|
+
"x-owney-api-key": `${apiKey}`
|
|
3929
|
+
}
|
|
3930
|
+
});
|
|
3931
|
+
if (!res.ok) {
|
|
3932
|
+
if (res.status !== 404) {
|
|
3933
|
+
console.warn(
|
|
3934
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
3935
|
+
);
|
|
3936
|
+
}
|
|
3937
|
+
return null;
|
|
3938
|
+
}
|
|
3939
|
+
const json = await res.json();
|
|
3940
|
+
const policy = json.success ? json.data ?? null : null;
|
|
3941
|
+
debugLog(
|
|
3942
|
+
"owney-sdk",
|
|
3943
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
3944
|
+
policy ?? void 0
|
|
3945
|
+
);
|
|
3946
|
+
return policy;
|
|
3947
|
+
} catch (error) {
|
|
3948
|
+
console.warn(
|
|
3949
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
3950
|
+
error instanceof Error ? error.message : String(error)
|
|
3951
|
+
);
|
|
3952
|
+
return null;
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3956
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
3957
|
+
const res = await fetch(url, {
|
|
3958
|
+
method: "GET",
|
|
3959
|
+
headers: {
|
|
3960
|
+
"Content-Type": "application/json",
|
|
3961
|
+
"x-owney-api-key": `${apiKey}`
|
|
3962
|
+
}
|
|
3963
|
+
});
|
|
3964
|
+
if (!res.ok) {
|
|
3965
|
+
const text = await res.text().catch(() => "");
|
|
3966
|
+
throw new OwneyError(
|
|
3967
|
+
"API_ROUTING_ERROR",
|
|
3968
|
+
`Routing API error ${res.status}: ${text}`,
|
|
3969
|
+
{ statusCode: res.status, responseBody: text }
|
|
3970
|
+
);
|
|
3971
|
+
}
|
|
3972
|
+
const json = await res.json();
|
|
3973
|
+
if (!json.success) {
|
|
3974
|
+
throw new OwneyError(
|
|
3975
|
+
"API_ROUTING_FAILED",
|
|
3976
|
+
`Routing API request failed: ${json.message}`,
|
|
3977
|
+
{ message: json.message }
|
|
3978
|
+
);
|
|
3979
|
+
}
|
|
3980
|
+
return json.data;
|
|
3981
|
+
}
|
|
3982
|
+
|
|
3983
|
+
// src/lib/health-report.ts
|
|
3984
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3985
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
3986
|
+
try {
|
|
3987
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
3988
|
+
method: "POST",
|
|
3989
|
+
headers: {
|
|
3990
|
+
"Content-Type": "application/json",
|
|
3991
|
+
"x-owney-api-key": apiKey
|
|
3992
|
+
},
|
|
3993
|
+
body: JSON.stringify({
|
|
3994
|
+
agent_type: agentType,
|
|
3995
|
+
error_code: errorCode,
|
|
3996
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3997
|
+
})
|
|
3998
|
+
});
|
|
3999
|
+
} catch (err) {
|
|
4000
|
+
console.warn(
|
|
4001
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
4002
|
+
err instanceof Error ? err.message : err
|
|
4003
|
+
);
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
4007
|
+
try {
|
|
4008
|
+
return await fn();
|
|
4009
|
+
} catch (err) {
|
|
4010
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
4011
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
4012
|
+
throw err;
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
4017
|
+
import { parseUnits } from "viem";
|
|
4018
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
4019
|
+
const target = asset.toUpperCase();
|
|
4020
|
+
return agents.map((agent) => {
|
|
4021
|
+
const agentBalance = aggregated[agent.id];
|
|
4022
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
4023
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4024
|
+
);
|
|
4025
|
+
let balance = tokenBalance ? parseUnits(tokenBalance.amount, decimals) : 0n;
|
|
4026
|
+
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
4027
|
+
const chainNameById = {
|
|
4028
|
+
1: "ETHEREUM",
|
|
4029
|
+
8453: "BASE",
|
|
4030
|
+
42161: "ARBITRUM"
|
|
4031
|
+
};
|
|
4032
|
+
const targetChain = chainNameById[chainId];
|
|
4033
|
+
for (const position2 of agentBalance?.positions ?? []) {
|
|
4034
|
+
const positionChain = position2.chain.trim().toUpperCase();
|
|
4035
|
+
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
4036
|
+
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
4037
|
+
if (position2.amountRaw !== void 0) {
|
|
4038
|
+
try {
|
|
4039
|
+
balance += BigInt(position2.amountRaw);
|
|
4040
|
+
continue;
|
|
4041
|
+
} catch {
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
balance += parseUnits(position2.amount, decimals);
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
return { agent, balance };
|
|
4048
|
+
});
|
|
4049
|
+
}
|
|
4050
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
4051
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
4052
|
+
agent,
|
|
4053
|
+
balance,
|
|
4054
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
4055
|
+
}));
|
|
4056
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
4057
|
+
let remainder = requested - assigned;
|
|
4058
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
4059
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
4060
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2456
4061
|
});
|
|
4062
|
+
for (const p of byHeadroom) {
|
|
4063
|
+
if (remainder === 0n) break;
|
|
4064
|
+
const headroom = p.balance - p.planned;
|
|
4065
|
+
if (headroom <= 0n) continue;
|
|
4066
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
4067
|
+
p.planned += take;
|
|
4068
|
+
remainder -= take;
|
|
4069
|
+
}
|
|
4070
|
+
return plans;
|
|
2457
4071
|
}
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
}
|
|
2465
|
-
|
|
2466
|
-
|
|
4072
|
+
function planDisabledDrain(disabled, requested) {
|
|
4073
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
4074
|
+
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
4075
|
+
);
|
|
4076
|
+
const plans = [];
|
|
4077
|
+
let remaining = requested;
|
|
4078
|
+
for (const { agent, balance } of sorted) {
|
|
4079
|
+
if (remaining === 0n) {
|
|
4080
|
+
plans.push({ agent, balance, planned: 0n });
|
|
4081
|
+
continue;
|
|
4082
|
+
}
|
|
4083
|
+
const take = balance < remaining ? balance : remaining;
|
|
4084
|
+
plans.push({ agent, balance, planned: take });
|
|
4085
|
+
remaining -= take;
|
|
4086
|
+
}
|
|
4087
|
+
return { plans, remaining };
|
|
2467
4088
|
}
|
|
2468
|
-
|
|
2469
|
-
const
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
4089
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
4090
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
4091
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
4092
|
+
const totalHeadroom = candidates.reduce(
|
|
4093
|
+
(s, c) => s + (c.balance - c.planned),
|
|
4094
|
+
0n
|
|
4095
|
+
);
|
|
4096
|
+
if (totalHeadroom === 0n) return;
|
|
4097
|
+
let distributed = 0n;
|
|
4098
|
+
for (const c of candidates) {
|
|
4099
|
+
const headroom = c.balance - c.planned;
|
|
4100
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
4101
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
4102
|
+
c.planned += give;
|
|
4103
|
+
distributed += give;
|
|
2483
4104
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
4105
|
+
let leftover = amount - distributed;
|
|
4106
|
+
for (const c of candidates) {
|
|
4107
|
+
if (leftover === 0n) break;
|
|
4108
|
+
const headroom = c.balance - c.planned;
|
|
4109
|
+
if (headroom <= 0n) continue;
|
|
4110
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
4111
|
+
c.planned += take;
|
|
4112
|
+
leftover -= take;
|
|
2491
4113
|
}
|
|
2492
4114
|
}
|
|
4115
|
+
function sumWithdrawnAmount(results) {
|
|
4116
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
4117
|
+
}
|
|
2493
4118
|
|
|
2494
|
-
// src/lib/
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
4119
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
4120
|
+
function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
4121
|
+
if (!tokenSymbol) {
|
|
4122
|
+
const total = Number(balance.totalBalance);
|
|
4123
|
+
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
4124
|
+
}
|
|
4125
|
+
const normalizedToken = tokenSymbol.toUpperCase();
|
|
4126
|
+
return balance.tokens.reduce((total, token) => {
|
|
4127
|
+
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
4128
|
+
return total;
|
|
4129
|
+
}
|
|
4130
|
+
const amount = Number(token.amount);
|
|
4131
|
+
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
4132
|
+
}, 0);
|
|
4133
|
+
}
|
|
4134
|
+
function aggregateApyHistory(agentApys, agentBalances) {
|
|
4135
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
4136
|
+
for (const [id, accountApy] of Object.entries(agentApys)) {
|
|
4137
|
+
const balance = agentBalances[id] ?? 0;
|
|
4138
|
+
if (!Number.isFinite(balance) || balance <= 0) continue;
|
|
4139
|
+
for (const point of accountApy.history ?? []) {
|
|
4140
|
+
const apy = Number(point.apy);
|
|
4141
|
+
if (!point.date || !Number.isFinite(apy)) continue;
|
|
4142
|
+
const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
|
|
4143
|
+
current.weightedSum += apy * balance;
|
|
4144
|
+
current.weight += balance;
|
|
4145
|
+
byDate.set(point.date, current);
|
|
2506
4146
|
}
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
4147
|
+
}
|
|
4148
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
4149
|
+
date,
|
|
4150
|
+
apy: value.weightedSum / value.weight
|
|
4151
|
+
}));
|
|
4152
|
+
}
|
|
4153
|
+
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
4154
|
+
const sums = {};
|
|
4155
|
+
const weights = {};
|
|
4156
|
+
for (const id of Object.keys(agentApys)) {
|
|
4157
|
+
const cells = agentApys[id].apyByChainAndAsset;
|
|
4158
|
+
const balance = agentBalances[id] ?? 0;
|
|
4159
|
+
if (!cells || balance <= 0) continue;
|
|
4160
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
4161
|
+
if (!perAsset) continue;
|
|
4162
|
+
const chainId = Number(chainKey);
|
|
4163
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
4164
|
+
const apy = Number(apyValue ?? 0);
|
|
4165
|
+
if (apy === 0) continue;
|
|
4166
|
+
sums[chainId] ??= {};
|
|
4167
|
+
weights[chainId] ??= {};
|
|
4168
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
4169
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2518
4170
|
}
|
|
2519
|
-
} catch (err) {
|
|
2520
|
-
if (err instanceof OwneyError) throw err;
|
|
2521
|
-
console.warn(
|
|
2522
|
-
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2523
|
-
err instanceof Error ? err.message : String(err)
|
|
2524
|
-
);
|
|
2525
4171
|
}
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
const
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
value: BigInt(amount),
|
|
2541
|
-
validAfter,
|
|
2542
|
-
validBefore,
|
|
2543
|
-
nonce
|
|
2544
|
-
}
|
|
2545
|
-
});
|
|
2546
|
-
const authSignature = await wallet.signTypedData({
|
|
2547
|
-
account: deps.ownerAddress,
|
|
2548
|
-
...typedData
|
|
2549
|
-
});
|
|
2550
|
-
deps.onApproved?.();
|
|
2551
|
-
const result = await post({
|
|
2552
|
-
baseUrl: deps.baseUrl,
|
|
2553
|
-
apiKey: deps.apiKey,
|
|
2554
|
-
body: {
|
|
2555
|
-
chainId: cid,
|
|
2556
|
-
token,
|
|
2557
|
-
from: deps.ownerAddress,
|
|
2558
|
-
to: smartWallet,
|
|
2559
|
-
value: amount,
|
|
2560
|
-
validAfter: validAfter.toString(),
|
|
2561
|
-
validBefore: validBefore.toString(),
|
|
2562
|
-
nonce,
|
|
2563
|
-
authSignature,
|
|
2564
|
-
tokenName,
|
|
2565
|
-
tokenVersion
|
|
2566
|
-
}
|
|
2567
|
-
});
|
|
2568
|
-
return result.txHash;
|
|
2569
|
-
};
|
|
4172
|
+
}
|
|
4173
|
+
const out = {};
|
|
4174
|
+
for (const chainKey of Object.keys(sums)) {
|
|
4175
|
+
const chainId = Number(chainKey);
|
|
4176
|
+
const perAssetOut = {};
|
|
4177
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
4178
|
+
const w = weights[chainId][asset];
|
|
4179
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
4180
|
+
}
|
|
4181
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
4182
|
+
out[chainId] = perAssetOut;
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
return out;
|
|
2570
4186
|
}
|
|
2571
4187
|
|
|
4188
|
+
// src/client.ts
|
|
4189
|
+
import {
|
|
4190
|
+
createPublicClient as createPublicClient4,
|
|
4191
|
+
createWalletClient as createWalletClient3,
|
|
4192
|
+
custom as custom3
|
|
4193
|
+
} from "viem";
|
|
4194
|
+
import { base as base4, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
4195
|
+
|
|
2572
4196
|
// src/lib/sponsored-weth-deposit.ts
|
|
2573
4197
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2574
4198
|
function makeSponsoredWethCallback(deps) {
|
|
2575
4199
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
2576
4200
|
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
2577
|
-
return
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
"CHAIN_UNSUPPORTED",
|
|
2583
|
-
`No sponsored WETH configured for chain ${chainId}`
|
|
2584
|
-
);
|
|
2585
|
-
}
|
|
2586
|
-
const amountWei = BigInt(amount);
|
|
2587
|
-
const pub = deps.getPublicClient(cid);
|
|
2588
|
-
const wallet = deps.getWalletClient(cid);
|
|
2589
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2590
|
-
try {
|
|
2591
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2592
|
-
if (balance < amountWei) {
|
|
4201
|
+
return makeVerificationAwareDepositCallback(
|
|
4202
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
4203
|
+
const cid = chainId;
|
|
4204
|
+
const token = deps.tokenAddressByChain[cid];
|
|
4205
|
+
if (!token) {
|
|
2593
4206
|
throw new OwneyError(
|
|
2594
|
-
"
|
|
2595
|
-
|
|
2596
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
4207
|
+
"CHAIN_UNSUPPORTED",
|
|
4208
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
2597
4209
|
);
|
|
2598
4210
|
}
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
)
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
});
|
|
2619
|
-
const nonce = randomPermit2Nonce();
|
|
2620
|
-
const deadline = BigInt(
|
|
2621
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2622
|
-
);
|
|
2623
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
2624
|
-
chainId: cid,
|
|
2625
|
-
message: {
|
|
2626
|
-
permitted: { token, amount: amountWei },
|
|
2627
|
-
spender: relayer,
|
|
2628
|
-
nonce,
|
|
2629
|
-
deadline
|
|
4211
|
+
const amountWei = BigInt(amount);
|
|
4212
|
+
const pub = deps.getPublicClient(cid);
|
|
4213
|
+
const wallet = deps.getWalletClient(cid);
|
|
4214
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
4215
|
+
try {
|
|
4216
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
4217
|
+
if (balance < amountWei) {
|
|
4218
|
+
throw new OwneyError(
|
|
4219
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4220
|
+
"Insufficient WETH balance for this deposit.",
|
|
4221
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
4222
|
+
);
|
|
4223
|
+
}
|
|
4224
|
+
} catch (err) {
|
|
4225
|
+
if (err instanceof OwneyError) throw err;
|
|
4226
|
+
console.warn(
|
|
4227
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
4228
|
+
err instanceof Error ? err.message : String(err)
|
|
4229
|
+
);
|
|
2630
4230
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
account: deps.ownerAddress,
|
|
2634
|
-
...typedData
|
|
2635
|
-
});
|
|
2636
|
-
deps.onApproved?.();
|
|
2637
|
-
const result = await post({
|
|
2638
|
-
baseUrl: deps.baseUrl,
|
|
2639
|
-
apiKey: deps.apiKey,
|
|
2640
|
-
body: {
|
|
2641
|
-
chainId: cid,
|
|
4231
|
+
const allowance = await readPermit2Allowance(
|
|
4232
|
+
pub,
|
|
2642
4233
|
token,
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
4234
|
+
deps.ownerAddress
|
|
4235
|
+
);
|
|
4236
|
+
if (allowance < amountWei) {
|
|
4237
|
+
throw new OwneyError(
|
|
4238
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
4239
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
4240
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
4241
|
+
);
|
|
2649
4242
|
}
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
4243
|
+
const relayer = await get({
|
|
4244
|
+
baseUrl: deps.baseUrl,
|
|
4245
|
+
apiKey: deps.apiKey,
|
|
4246
|
+
chainId: cid
|
|
4247
|
+
});
|
|
4248
|
+
const nonce = randomPermit2Nonce();
|
|
4249
|
+
const deadline = BigInt(
|
|
4250
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
4251
|
+
);
|
|
4252
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
4253
|
+
chainId: cid,
|
|
4254
|
+
message: {
|
|
4255
|
+
permitted: { token, amount: amountWei },
|
|
4256
|
+
spender: relayer,
|
|
4257
|
+
nonce,
|
|
4258
|
+
deadline
|
|
4259
|
+
}
|
|
4260
|
+
});
|
|
4261
|
+
const signature = await wallet.signTypedData({
|
|
4262
|
+
account: deps.ownerAddress,
|
|
4263
|
+
...typedData
|
|
4264
|
+
});
|
|
4265
|
+
deps.onApproved?.();
|
|
4266
|
+
const result = await post({
|
|
4267
|
+
baseUrl: deps.baseUrl,
|
|
4268
|
+
apiKey: deps.apiKey,
|
|
4269
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
4270
|
+
body: {
|
|
4271
|
+
chainId: cid,
|
|
4272
|
+
token,
|
|
4273
|
+
from: deps.ownerAddress,
|
|
4274
|
+
to: smartWallet,
|
|
4275
|
+
amount,
|
|
4276
|
+
nonce: nonce.toString(),
|
|
4277
|
+
deadline: deadline.toString(),
|
|
4278
|
+
signature,
|
|
4279
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
4280
|
+
yieldseekerUserId: verification.userId,
|
|
4281
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4282
|
+
} : {}
|
|
4283
|
+
}
|
|
4284
|
+
});
|
|
4285
|
+
return result.txHash;
|
|
4286
|
+
}
|
|
4287
|
+
);
|
|
2653
4288
|
}
|
|
2654
4289
|
|
|
2655
4290
|
// src/lib/sponsored-calls-deposit.ts
|
|
2656
|
-
import { encodeFunctionData, erc20Abi, toHex } from "viem";
|
|
4291
|
+
import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi2, toHex } from "viem";
|
|
2657
4292
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2658
4293
|
var DEFAULT_MAX_POLLS = 30;
|
|
2659
4294
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -2679,7 +4314,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2679
4314
|
}
|
|
2680
4315
|
return new URL(configured, origin).toString();
|
|
2681
4316
|
};
|
|
2682
|
-
return async (smartWallet, chainId, amount) => {
|
|
4317
|
+
return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
|
|
2683
4318
|
const cid = chainId;
|
|
2684
4319
|
const token = deps.tokenAddressByChain[cid];
|
|
2685
4320
|
if (!token) {
|
|
@@ -2695,11 +4330,37 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2695
4330
|
{ chainId }
|
|
2696
4331
|
);
|
|
2697
4332
|
}
|
|
2698
|
-
const data =
|
|
2699
|
-
abi:
|
|
4333
|
+
const data = encodeFunctionData2({
|
|
4334
|
+
abi: erc20Abi2,
|
|
2700
4335
|
functionName: "transfer",
|
|
2701
4336
|
args: [smartWallet, BigInt(amount)]
|
|
2702
4337
|
});
|
|
4338
|
+
let paymasterUrl = absolutePaymasterUrl();
|
|
4339
|
+
if (verification?.agentId === "yieldseeker") {
|
|
4340
|
+
if (chainId !== 8453) {
|
|
4341
|
+
throw new OwneyError(
|
|
4342
|
+
"CHAIN_UNSUPPORTED",
|
|
4343
|
+
`Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
|
|
4344
|
+
);
|
|
4345
|
+
}
|
|
4346
|
+
const { intent } = await postPaymasterIntent({
|
|
4347
|
+
baseUrl: deps.routingApiBaseUrl,
|
|
4348
|
+
apiKey: deps.apiKey,
|
|
4349
|
+
yieldseekerSignature: verification.signature,
|
|
4350
|
+
body: {
|
|
4351
|
+
chainId,
|
|
4352
|
+
token,
|
|
4353
|
+
from: deps.ownerAddress,
|
|
4354
|
+
to: smartWallet,
|
|
4355
|
+
amount,
|
|
4356
|
+
yieldseekerUserId: verification.userId,
|
|
4357
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4358
|
+
}
|
|
4359
|
+
});
|
|
4360
|
+
const url = new URL(paymasterUrl);
|
|
4361
|
+
url.searchParams.set("owneyIntent", intent);
|
|
4362
|
+
paymasterUrl = url.toString();
|
|
4363
|
+
}
|
|
2703
4364
|
const sendResult = await deps.provider.request({
|
|
2704
4365
|
method: "wallet_sendCalls",
|
|
2705
4366
|
params: [
|
|
@@ -2710,7 +4371,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2710
4371
|
atomicRequired: false,
|
|
2711
4372
|
calls: [{ to: token, value: "0x0", data }],
|
|
2712
4373
|
capabilities: {
|
|
2713
|
-
paymasterService: { url:
|
|
4374
|
+
paymasterService: { url: paymasterUrl }
|
|
2714
4375
|
}
|
|
2715
4376
|
}
|
|
2716
4377
|
]
|
|
@@ -2740,7 +4401,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2740
4401
|
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
2741
4402
|
{ chainId, callsId }
|
|
2742
4403
|
);
|
|
2743
|
-
};
|
|
4404
|
+
});
|
|
2744
4405
|
}
|
|
2745
4406
|
|
|
2746
4407
|
// src/client.ts
|
|
@@ -2770,7 +4431,7 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2770
4431
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2771
4432
|
};
|
|
2772
4433
|
var VIEM_CHAIN2 = {
|
|
2773
|
-
8453:
|
|
4434
|
+
8453: base4,
|
|
2774
4435
|
42161: arbitrum2,
|
|
2775
4436
|
1: mainnet2
|
|
2776
4437
|
};
|
|
@@ -2800,6 +4461,8 @@ var OwneySDK = class {
|
|
|
2800
4461
|
orgAgentConfig;
|
|
2801
4462
|
orgAgentConfigPromise = null;
|
|
2802
4463
|
zyfaiRpcUrls;
|
|
4464
|
+
yieldseekerApiBaseUrl;
|
|
4465
|
+
yieldseekerSiweOrigin;
|
|
2803
4466
|
routingApiBaseUrl;
|
|
2804
4467
|
referralSource;
|
|
2805
4468
|
cachedSponsoredCallback = null;
|
|
@@ -2822,6 +4485,8 @@ var OwneySDK = class {
|
|
|
2822
4485
|
this.apiKey = config.apiKey;
|
|
2823
4486
|
if (config.debug) setOwneyDebug(true);
|
|
2824
4487
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
4488
|
+
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
4489
|
+
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
2825
4490
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2826
4491
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2827
4492
|
this.referralSource = config.referralSource;
|
|
@@ -2928,14 +4593,14 @@ var OwneySDK = class {
|
|
|
2928
4593
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2929
4594
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2930
4595
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2931
|
-
getPublicClient: (cid) =>
|
|
4596
|
+
getPublicClient: (cid) => createPublicClient4({
|
|
2932
4597
|
chain: VIEM_CHAIN2[cid],
|
|
2933
|
-
transport:
|
|
4598
|
+
transport: custom3(provider)
|
|
2934
4599
|
}),
|
|
2935
|
-
getWalletClient: (cid) =>
|
|
4600
|
+
getWalletClient: (cid) => createWalletClient3({
|
|
2936
4601
|
account: owner,
|
|
2937
4602
|
chain: VIEM_CHAIN2[cid],
|
|
2938
|
-
transport:
|
|
4603
|
+
transport: custom3(provider)
|
|
2939
4604
|
})
|
|
2940
4605
|
});
|
|
2941
4606
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -2952,6 +4617,8 @@ var OwneySDK = class {
|
|
|
2952
4617
|
if (!onApproved && cached) return cached;
|
|
2953
4618
|
const provider = this.requireConnectedProvider();
|
|
2954
4619
|
const callback = makeSponsoredCallsCallback({
|
|
4620
|
+
apiKey: this.apiKey,
|
|
4621
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
2955
4622
|
provider,
|
|
2956
4623
|
ownerAddress: this.state.walletAddress,
|
|
2957
4624
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
@@ -2981,14 +4648,14 @@ var OwneySDK = class {
|
|
|
2981
4648
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2982
4649
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2983
4650
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2984
|
-
getPublicClient: (cid) =>
|
|
4651
|
+
getPublicClient: (cid) => createPublicClient4({
|
|
2985
4652
|
chain: VIEM_CHAIN2[cid],
|
|
2986
|
-
transport:
|
|
4653
|
+
transport: custom3(provider)
|
|
2987
4654
|
}),
|
|
2988
|
-
getWalletClient: (cid) =>
|
|
4655
|
+
getWalletClient: (cid) => createWalletClient3({
|
|
2989
4656
|
account: owner,
|
|
2990
4657
|
chain: VIEM_CHAIN2[cid],
|
|
2991
|
-
transport:
|
|
4658
|
+
transport: custom3(provider)
|
|
2992
4659
|
})
|
|
2993
4660
|
});
|
|
2994
4661
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -3019,12 +4686,10 @@ var OwneySDK = class {
|
|
|
3019
4686
|
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
3020
4687
|
this.apiKey,
|
|
3021
4688
|
this.routingApiBaseUrl
|
|
3022
|
-
).then(
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
}
|
|
3027
|
-
);
|
|
4689
|
+
).then((config) => {
|
|
4690
|
+
this.orgAgentConfig = config;
|
|
4691
|
+
return config;
|
|
4692
|
+
});
|
|
3028
4693
|
}
|
|
3029
4694
|
return this.orgAgentConfigPromise;
|
|
3030
4695
|
}
|
|
@@ -3064,7 +4729,14 @@ var OwneySDK = class {
|
|
|
3064
4729
|
this.routingApiBaseUrl
|
|
3065
4730
|
);
|
|
3066
4731
|
this.disabledAgents.clear();
|
|
3067
|
-
for (const {
|
|
4732
|
+
for (const {
|
|
4733
|
+
key: key2,
|
|
4734
|
+
agent_type,
|
|
4735
|
+
is_enabled,
|
|
4736
|
+
is_configured
|
|
4737
|
+
} of agentKeys) {
|
|
4738
|
+
const configured = is_configured ?? Boolean(key2);
|
|
4739
|
+
if (!configured) continue;
|
|
3068
4740
|
const agent = this.createAgent(agent_type, key2);
|
|
3069
4741
|
if (!agent) continue;
|
|
3070
4742
|
this.agents.set(agent_type, agent);
|
|
@@ -3088,8 +4760,15 @@ var OwneySDK = class {
|
|
|
3088
4760
|
}
|
|
3089
4761
|
createAgent(agentId, key2) {
|
|
3090
4762
|
if (agentId === "zyfai") {
|
|
4763
|
+
if (!key2) return null;
|
|
3091
4764
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
3092
4765
|
}
|
|
4766
|
+
if (agentId === "yieldseeker") {
|
|
4767
|
+
return new YieldseekerAgent(this.apiKey, {
|
|
4768
|
+
auth: { origin: this.yieldseekerSiweOrigin },
|
|
4769
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
4770
|
+
});
|
|
4771
|
+
}
|
|
3093
4772
|
return null;
|
|
3094
4773
|
}
|
|
3095
4774
|
/**
|
|
@@ -3134,7 +4813,7 @@ var OwneySDK = class {
|
|
|
3134
4813
|
* If provided, ALL specified agents must support the chainId or the call
|
|
3135
4814
|
* throws before activating any agent.
|
|
3136
4815
|
*/
|
|
3137
|
-
async activateAgent(chainId, agentId) {
|
|
4816
|
+
async activateAgent(chainId, agentId, asset) {
|
|
3138
4817
|
const state = this.requireState();
|
|
3139
4818
|
await this.ensureAgentsInitialized();
|
|
3140
4819
|
if (agentId !== void 0) {
|
|
@@ -3170,7 +4849,7 @@ var OwneySDK = class {
|
|
|
3170
4849
|
this.activeAgents.add(id);
|
|
3171
4850
|
}
|
|
3172
4851
|
state.chainId = chainId;
|
|
3173
|
-
await this.activateAgentsInTurn(agents, state, chainId);
|
|
4852
|
+
await this.activateAgentsInTurn(agents, state, chainId, asset);
|
|
3174
4853
|
return;
|
|
3175
4854
|
}
|
|
3176
4855
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -3191,7 +4870,7 @@ var OwneySDK = class {
|
|
|
3191
4870
|
const enabledCompatible = compatible.filter(
|
|
3192
4871
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
3193
4872
|
);
|
|
3194
|
-
await this.activateAgentsInTurn(enabledCompatible, state, chainId);
|
|
4873
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
|
|
3195
4874
|
}
|
|
3196
4875
|
/**
|
|
3197
4876
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -3207,17 +4886,25 @@ var OwneySDK = class {
|
|
|
3207
4886
|
* at a time anyway.
|
|
3208
4887
|
*
|
|
3209
4888
|
* Every agent is attempted even if an earlier one fails, so one declined
|
|
3210
|
-
* signature can't deny the remaining agents their turn.
|
|
3211
|
-
*
|
|
3212
|
-
*
|
|
4889
|
+
* signature can't deny the remaining agents their turn. Once all agents have
|
|
4890
|
+
* had a chance, a partial failure identifies the agents that still need a
|
|
4891
|
+
* retry; if none activated, the original provider error is preserved.
|
|
3213
4892
|
*/
|
|
3214
|
-
async activateAgentsInTurn(agents, state, chainId) {
|
|
4893
|
+
async activateAgentsInTurn(agents, state, chainId, asset) {
|
|
3215
4894
|
let firstError = null;
|
|
4895
|
+
const activatedAgentIds = [];
|
|
4896
|
+
const failedAgents = [];
|
|
3216
4897
|
for (const agent of agents) {
|
|
3217
4898
|
try {
|
|
3218
|
-
await agent.activateAgent(state, chainId);
|
|
4899
|
+
await agent.activateAgent(state, chainId, asset);
|
|
3219
4900
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
4901
|
+
activatedAgentIds.push(agent.id);
|
|
3220
4902
|
} catch (error) {
|
|
4903
|
+
failedAgents.push({
|
|
4904
|
+
agentId: agent.id,
|
|
4905
|
+
code: error instanceof OwneyError ? error.code : void 0,
|
|
4906
|
+
message: error instanceof Error ? error.message : String(error)
|
|
4907
|
+
});
|
|
3221
4908
|
if (firstError === null) {
|
|
3222
4909
|
firstError = error;
|
|
3223
4910
|
} else {
|
|
@@ -3225,7 +4912,16 @@ var OwneySDK = class {
|
|
|
3225
4912
|
}
|
|
3226
4913
|
}
|
|
3227
4914
|
}
|
|
3228
|
-
if (firstError
|
|
4915
|
+
if (firstError === null) return;
|
|
4916
|
+
if (activatedAgentIds.length === 0) throw firstError;
|
|
4917
|
+
const failedAgentIds = failedAgents.map(({ agentId }) => agentId);
|
|
4918
|
+
const activeNames = activatedAgentIds.map(this.formatAgentName).join(", ");
|
|
4919
|
+
const remainingNames = failedAgentIds.map(this.formatAgentName).join(", ");
|
|
4920
|
+
throw new OwneyError(
|
|
4921
|
+
"AGENT_ACTIVATION_PARTIAL_FAILURE",
|
|
4922
|
+
`${activeNames} activated, but ${remainingNames} still needs activation. Try again and approve the remaining wallet request.`,
|
|
4923
|
+
{ activatedAgentIds, failedAgentIds, failures: failedAgents }
|
|
4924
|
+
);
|
|
3229
4925
|
}
|
|
3230
4926
|
/**
|
|
3231
4927
|
* Deposit into a specific agent, or distribute across all eligible agents.
|
|
@@ -3439,10 +5135,10 @@ var OwneySDK = class {
|
|
|
3439
5135
|
agent,
|
|
3440
5136
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
3441
5137
|
}));
|
|
3442
|
-
const
|
|
5138
|
+
const valid2 = splits.filter(
|
|
3443
5139
|
(s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
|
|
3444
5140
|
);
|
|
3445
|
-
if (
|
|
5141
|
+
if (valid2.length === agents.length) {
|
|
3446
5142
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
3447
5143
|
}
|
|
3448
5144
|
const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
|
|
@@ -3461,6 +5157,11 @@ var OwneySDK = class {
|
|
|
3461
5157
|
)
|
|
3462
5158
|
}));
|
|
3463
5159
|
}
|
|
5160
|
+
formatAgentName(agentId) {
|
|
5161
|
+
if (agentId === "zyfai") return "Zyfai";
|
|
5162
|
+
if (agentId === "yieldseeker") return "Yieldseeker";
|
|
5163
|
+
return agentId;
|
|
5164
|
+
}
|
|
3464
5165
|
async validateMinDepositAmount(agent, state, chainId, asset, amount) {
|
|
3465
5166
|
const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
|
|
3466
5167
|
const parsedAmount = BigInt(amount);
|
|
@@ -3492,12 +5193,12 @@ var OwneySDK = class {
|
|
|
3492
5193
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
3493
5194
|
);
|
|
3494
5195
|
const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
|
|
3495
|
-
const
|
|
5196
|
+
const position2 = (balance.positions ?? []).find((p) => {
|
|
3496
5197
|
const positionChain = p.chain.trim().toUpperCase();
|
|
3497
5198
|
const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
|
|
3498
5199
|
return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
|
|
3499
5200
|
});
|
|
3500
|
-
return !!token && Number(token.amount) > 0 || !!
|
|
5201
|
+
return !!token && Number(token.amount) > 0 || !!position2;
|
|
3501
5202
|
} catch (error) {
|
|
3502
5203
|
if (requireReliableRead) {
|
|
3503
5204
|
throw new OwneyError(
|
|
@@ -3602,13 +5303,20 @@ var OwneySDK = class {
|
|
|
3602
5303
|
{ asset, agentErrors: agentErrors2 }
|
|
3603
5304
|
);
|
|
3604
5305
|
}
|
|
5306
|
+
const amountsKnown = Object.values(results2).every(
|
|
5307
|
+
(result) => typeof result.amount === "string" && /^\d+$/.test(result.amount)
|
|
5308
|
+
);
|
|
3605
5309
|
return {
|
|
3606
5310
|
agentResult: results2,
|
|
3607
|
-
totalWithdrawn: sumWithdrawnAmount(results2).toString()
|
|
5311
|
+
totalWithdrawn: amountsKnown ? sumWithdrawnAmount(results2).toString() : null
|
|
3608
5312
|
};
|
|
3609
5313
|
}
|
|
3610
5314
|
const requested = BigInt(amount);
|
|
3611
5315
|
const aggregated = await this.getBalances();
|
|
5316
|
+
const unavailableAgents = eligibleAgents.filter(
|
|
5317
|
+
(agent) => !(agent.id in aggregated.agentBalances)
|
|
5318
|
+
);
|
|
5319
|
+
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
3612
5320
|
const balances = projectAgentBalancesForAsset(
|
|
3613
5321
|
eligibleAgents,
|
|
3614
5322
|
aggregated.agentBalances,
|
|
@@ -3617,7 +5325,18 @@ var OwneySDK = class {
|
|
|
3617
5325
|
assetInfo.decimals
|
|
3618
5326
|
);
|
|
3619
5327
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
3620
|
-
if (totalAvailable
|
|
5328
|
+
if (totalAvailable === 0n && unavailableAgents.length > 0) {
|
|
5329
|
+
throw new OwneyError(
|
|
5330
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5331
|
+
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
5332
|
+
{
|
|
5333
|
+
asset,
|
|
5334
|
+
unavailableAgents: unavailableAgentIds,
|
|
5335
|
+
agentErrors: aggregated.agentErrors
|
|
5336
|
+
}
|
|
5337
|
+
);
|
|
5338
|
+
}
|
|
5339
|
+
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3621
5340
|
throw new OwneyError(
|
|
3622
5341
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3623
5342
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -3628,6 +5347,7 @@ var OwneySDK = class {
|
|
|
3628
5347
|
}
|
|
3629
5348
|
);
|
|
3630
5349
|
}
|
|
5350
|
+
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
3631
5351
|
const disabledBalances = balances.filter(
|
|
3632
5352
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
3633
5353
|
);
|
|
@@ -3636,7 +5356,7 @@ var OwneySDK = class {
|
|
|
3636
5356
|
);
|
|
3637
5357
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
3638
5358
|
disabledBalances,
|
|
3639
|
-
|
|
5359
|
+
plannedTarget
|
|
3640
5360
|
);
|
|
3641
5361
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
3642
5362
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -3646,7 +5366,9 @@ var OwneySDK = class {
|
|
|
3646
5366
|
}));
|
|
3647
5367
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
3648
5368
|
const results = {};
|
|
3649
|
-
const agentErrors = {
|
|
5369
|
+
const agentErrors = {
|
|
5370
|
+
...aggregated.agentErrors ?? {}
|
|
5371
|
+
};
|
|
3650
5372
|
for (let i = 0; i < plans.length; i++) {
|
|
3651
5373
|
const p = plans[i];
|
|
3652
5374
|
if (p.planned === 0n) continue;
|
|
@@ -3693,7 +5415,8 @@ var OwneySDK = class {
|
|
|
3693
5415
|
requested: amount,
|
|
3694
5416
|
withdrawn: withdrawn.toString(),
|
|
3695
5417
|
partialResults: results,
|
|
3696
|
-
agentErrors
|
|
5418
|
+
agentErrors,
|
|
5419
|
+
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3697
5420
|
}
|
|
3698
5421
|
);
|
|
3699
5422
|
}
|
|
@@ -3711,7 +5434,10 @@ var OwneySDK = class {
|
|
|
3711
5434
|
if (agentId) {
|
|
3712
5435
|
const agent = this.getAgent(agentId);
|
|
3713
5436
|
const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
3714
|
-
return
|
|
5437
|
+
return {
|
|
5438
|
+
...result,
|
|
5439
|
+
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5440
|
+
};
|
|
3715
5441
|
}
|
|
3716
5442
|
let totalBalance = 0;
|
|
3717
5443
|
const results = {};
|
|
@@ -3719,11 +5445,18 @@ var OwneySDK = class {
|
|
|
3719
5445
|
const balanceResults = await Promise.allSettled(
|
|
3720
5446
|
entries.map(async ([id, agent]) => {
|
|
3721
5447
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
3722
|
-
return [
|
|
5448
|
+
return [
|
|
5449
|
+
id,
|
|
5450
|
+
{
|
|
5451
|
+
...b,
|
|
5452
|
+
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5453
|
+
}
|
|
5454
|
+
];
|
|
3723
5455
|
})
|
|
3724
5456
|
);
|
|
3725
5457
|
let successCount = 0;
|
|
3726
5458
|
const agentErrors = {};
|
|
5459
|
+
const agentRetryAt = {};
|
|
3727
5460
|
const agentFailures = [];
|
|
3728
5461
|
for (let i = 0; i < balanceResults.length; i++) {
|
|
3729
5462
|
const settledResult = balanceResults[i];
|
|
@@ -3737,7 +5470,10 @@ var OwneySDK = class {
|
|
|
3737
5470
|
}
|
|
3738
5471
|
const reason = settledResult.reason;
|
|
3739
5472
|
agentFailures.push(reason);
|
|
5473
|
+
const retryDelay = rateLimitDelay(reason);
|
|
5474
|
+
if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
|
|
3740
5475
|
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
5476
|
+
console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
|
|
3741
5477
|
}
|
|
3742
5478
|
if (successCount === 0) {
|
|
3743
5479
|
throw new OwneyError(
|
|
@@ -3749,7 +5485,8 @@ var OwneySDK = class {
|
|
|
3749
5485
|
return {
|
|
3750
5486
|
totalBalance: String(totalBalance),
|
|
3751
5487
|
totalBalanceAsset: "usdc",
|
|
3752
|
-
agentBalances: results
|
|
5488
|
+
agentBalances: results,
|
|
5489
|
+
...Object.keys(agentErrors).length > 0 ? { agentErrors, agentRetryAt } : {}
|
|
3753
5490
|
};
|
|
3754
5491
|
}
|
|
3755
5492
|
/**
|
|
@@ -3849,7 +5586,10 @@ var OwneySDK = class {
|
|
|
3849
5586
|
Promise.all(
|
|
3850
5587
|
entries.map(async ([id, agent]) => {
|
|
3851
5588
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
3852
|
-
return [
|
|
5589
|
+
return [
|
|
5590
|
+
id,
|
|
5591
|
+
balanceForApyScope(b, chainId, tokenSymbol)
|
|
5592
|
+
];
|
|
3853
5593
|
})
|
|
3854
5594
|
)
|
|
3855
5595
|
]);
|
|
@@ -3877,10 +5617,12 @@ var OwneySDK = class {
|
|
|
3877
5617
|
}
|
|
3878
5618
|
}
|
|
3879
5619
|
const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
|
|
5620
|
+
const history = aggregateApyHistory(results, balances);
|
|
3880
5621
|
return {
|
|
3881
5622
|
totalApy: String(totalApy),
|
|
3882
5623
|
agentApy: results,
|
|
3883
|
-
apyByChainAndAsset
|
|
5624
|
+
apyByChainAndAsset,
|
|
5625
|
+
history
|
|
3884
5626
|
};
|
|
3885
5627
|
}
|
|
3886
5628
|
/**
|
|
@@ -4028,10 +5770,10 @@ var OwneySDK = class {
|
|
|
4028
5770
|
);
|
|
4029
5771
|
}
|
|
4030
5772
|
const provider = this.requireConnectedProvider();
|
|
4031
|
-
const wallet =
|
|
5773
|
+
const wallet = createWalletClient3({
|
|
4032
5774
|
account: state.walletAddress,
|
|
4033
5775
|
chain: VIEM_CHAIN2[chainId],
|
|
4034
|
-
transport:
|
|
5776
|
+
transport: custom3(provider)
|
|
4035
5777
|
});
|
|
4036
5778
|
const hash = await wallet.writeContract({
|
|
4037
5779
|
address: token,
|
|
@@ -4041,9 +5783,9 @@ var OwneySDK = class {
|
|
|
4041
5783
|
account: state.walletAddress,
|
|
4042
5784
|
chain: VIEM_CHAIN2[chainId]
|
|
4043
5785
|
});
|
|
4044
|
-
const publicClient =
|
|
5786
|
+
const publicClient = createPublicClient4({
|
|
4045
5787
|
chain: VIEM_CHAIN2[chainId],
|
|
4046
|
-
transport:
|
|
5788
|
+
transport: custom3(provider)
|
|
4047
5789
|
});
|
|
4048
5790
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
4049
5791
|
hash,
|
|
@@ -4080,7 +5822,9 @@ var OwneySDK = class {
|
|
|
4080
5822
|
return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
4081
5823
|
}
|
|
4082
5824
|
const results = {};
|
|
4083
|
-
const agentEntries = [...this.agents.entries()]
|
|
5825
|
+
const agentEntries = [...this.agents.entries()].filter(
|
|
5826
|
+
([id]) => !this.isAgentDisabled(id)
|
|
5827
|
+
);
|
|
4084
5828
|
const apyResults = await Promise.all(
|
|
4085
5829
|
agentEntries.map(async ([id, agent]) => {
|
|
4086
5830
|
const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
@@ -4155,13 +5899,13 @@ var OwneySDK = class {
|
|
|
4155
5899
|
};
|
|
4156
5900
|
|
|
4157
5901
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
4158
|
-
import { getAddress } from "viem";
|
|
4159
|
-
import { SiweMessage } from "siwe";
|
|
5902
|
+
import { getAddress as getAddress3 } from "viem";
|
|
5903
|
+
import { SiweMessage as SiweMessage2 } from "siwe";
|
|
4160
5904
|
import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
|
|
4161
5905
|
|
|
4162
5906
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
4163
|
-
var
|
|
4164
|
-
var
|
|
5907
|
+
var KEY_PREFIX4 = "owney.siwx.session";
|
|
5908
|
+
var storage4 = () => {
|
|
4165
5909
|
if (typeof window === "undefined") return null;
|
|
4166
5910
|
try {
|
|
4167
5911
|
return window.localStorage;
|
|
@@ -4169,8 +5913,8 @@ var storage2 = () => {
|
|
|
4169
5913
|
return null;
|
|
4170
5914
|
}
|
|
4171
5915
|
};
|
|
4172
|
-
var
|
|
4173
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
5916
|
+
var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
|
|
5917
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
|
|
4174
5918
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
4175
5919
|
var readLegacySiwxSession = (store, address) => {
|
|
4176
5920
|
if (!store) return null;
|
|
@@ -4201,17 +5945,17 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
4201
5945
|
};
|
|
4202
5946
|
var readSiwxSession = (address, chainId) => {
|
|
4203
5947
|
if (typeof window === "undefined") return null;
|
|
4204
|
-
const key2 =
|
|
4205
|
-
const store =
|
|
4206
|
-
let
|
|
5948
|
+
const key2 = buildKey3(address);
|
|
5949
|
+
const store = storage4();
|
|
5950
|
+
let raw2 = null;
|
|
4207
5951
|
try {
|
|
4208
|
-
|
|
5952
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
4209
5953
|
} catch {
|
|
4210
|
-
|
|
5954
|
+
raw2 = null;
|
|
4211
5955
|
}
|
|
4212
|
-
if (
|
|
5956
|
+
if (raw2) {
|
|
4213
5957
|
try {
|
|
4214
|
-
return JSON.parse(
|
|
5958
|
+
return JSON.parse(raw2);
|
|
4215
5959
|
} catch {
|
|
4216
5960
|
memorySiwxSessions.delete(key2);
|
|
4217
5961
|
try {
|
|
@@ -4230,18 +5974,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
4230
5974
|
};
|
|
4231
5975
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
4232
5976
|
if (typeof window === "undefined") return;
|
|
4233
|
-
const key2 =
|
|
5977
|
+
const key2 = buildKey3(address);
|
|
4234
5978
|
memorySiwxSessions.set(key2, session);
|
|
4235
|
-
const store =
|
|
5979
|
+
const store = storage4();
|
|
4236
5980
|
try {
|
|
4237
5981
|
store?.setItem(key2, JSON.stringify(session));
|
|
4238
5982
|
} catch {
|
|
4239
5983
|
}
|
|
4240
5984
|
};
|
|
4241
5985
|
var clearSiwxSession = (address, _chainId) => {
|
|
4242
|
-
const key2 =
|
|
5986
|
+
const key2 = buildKey3(address);
|
|
4243
5987
|
memorySiwxSessions.delete(key2);
|
|
4244
|
-
const store =
|
|
5988
|
+
const store = storage4();
|
|
4245
5989
|
try {
|
|
4246
5990
|
store?.removeItem(key2);
|
|
4247
5991
|
} catch {
|
|
@@ -4281,8 +6025,8 @@ function buildSIWXConfig(deps) {
|
|
|
4281
6025
|
statement: STATEMENT,
|
|
4282
6026
|
issuedAt,
|
|
4283
6027
|
toString() {
|
|
4284
|
-
return new
|
|
4285
|
-
address:
|
|
6028
|
+
return new SiweMessage2({
|
|
6029
|
+
address: getAddress3(accountAddress),
|
|
4286
6030
|
chainId: numericChainId(chainId),
|
|
4287
6031
|
domain,
|
|
4288
6032
|
uri,
|
|
@@ -4324,7 +6068,7 @@ function buildSIWXConfig(deps) {
|
|
|
4324
6068
|
const persistSession = async (session) => {
|
|
4325
6069
|
const address = session.data.accountAddress;
|
|
4326
6070
|
const id = numericChainId(session.data.chainId);
|
|
4327
|
-
const message = new
|
|
6071
|
+
const message = new SiweMessage2(session.message);
|
|
4328
6072
|
const login = await post("/auth/login", {
|
|
4329
6073
|
message,
|
|
4330
6074
|
signature: session.signature,
|
|
@@ -4372,6 +6116,7 @@ export {
|
|
|
4372
6116
|
NotConnectedError,
|
|
4373
6117
|
OwneyError,
|
|
4374
6118
|
OwneySDK,
|
|
6119
|
+
YieldseekerAgent,
|
|
4375
6120
|
createOwneySIWX,
|
|
4376
6121
|
setOwneyDebug
|
|
4377
6122
|
};
|