@owney/sdk 0.7.17-beta.1 → 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 +2293 -567
- package/dist/index.d.cts +133 -11
- package/dist/index.d.ts +133 -11
- package/dist/index.js +2300 -562
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
NotConnectedError: () => NotConnectedError,
|
|
27
27
|
OwneyError: () => OwneyError,
|
|
28
28
|
OwneySDK: () => OwneySDK,
|
|
29
|
+
YieldseekerAgent: () => YieldseekerAgent,
|
|
29
30
|
createOwneySIWX: () => createOwneySIWX,
|
|
30
31
|
setOwneyDebug: () => setOwneyDebug
|
|
31
32
|
});
|
|
@@ -310,18 +311,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
310
311
|
return explicit;
|
|
311
312
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
312
313
|
}
|
|
313
|
-
function mapDeposit(
|
|
314
|
+
function mapDeposit(raw2) {
|
|
314
315
|
return {
|
|
315
|
-
txHash:
|
|
316
|
-
smartWallet:
|
|
317
|
-
amount:
|
|
316
|
+
txHash: raw2.txHash,
|
|
317
|
+
smartWallet: raw2.smartWallet,
|
|
318
|
+
amount: raw2.amount
|
|
318
319
|
};
|
|
319
320
|
}
|
|
320
|
-
function mapWithdraw(
|
|
321
|
+
function mapWithdraw(raw2) {
|
|
321
322
|
return {
|
|
322
|
-
txHash:
|
|
323
|
-
type:
|
|
324
|
-
amount:
|
|
323
|
+
txHash: raw2.txHash,
|
|
324
|
+
type: raw2.type,
|
|
325
|
+
amount: raw2.amount
|
|
325
326
|
};
|
|
326
327
|
}
|
|
327
328
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -340,10 +341,10 @@ function resolveChainId(chain) {
|
|
|
340
341
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
341
342
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
342
343
|
}
|
|
343
|
-
function mapPendingAllocations(
|
|
344
|
-
if (!Array.isArray(
|
|
344
|
+
function mapPendingAllocations(raw2) {
|
|
345
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
345
346
|
const pending = [];
|
|
346
|
-
for (const entry of
|
|
347
|
+
for (const entry of raw2) {
|
|
347
348
|
if (typeof entry !== "object" || entry === null) continue;
|
|
348
349
|
const e = entry;
|
|
349
350
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -366,8 +367,8 @@ function mapPendingAllocations(raw) {
|
|
|
366
367
|
}
|
|
367
368
|
return pending.length > 0 ? pending : void 0;
|
|
368
369
|
}
|
|
369
|
-
function mapBalances(
|
|
370
|
-
const portfolio =
|
|
370
|
+
function mapBalances(raw2, _chainId, smartWallet) {
|
|
371
|
+
const portfolio = raw2.portfolio;
|
|
371
372
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
372
373
|
let totalBalance = 0;
|
|
373
374
|
const tokens = [];
|
|
@@ -436,8 +437,8 @@ function sumTokenValues(tokens) {
|
|
|
436
437
|
function sumTokenEarnings(tokens) {
|
|
437
438
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
438
439
|
}
|
|
439
|
-
function mapEarnings(
|
|
440
|
-
const totalEarningsByChain =
|
|
440
|
+
function mapEarnings(raw2, smartWallet) {
|
|
441
|
+
const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
|
|
441
442
|
const tokens = [];
|
|
442
443
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
443
444
|
totalEarningsByChain
|
|
@@ -456,15 +457,15 @@ function mapEarnings(raw, smartWallet) {
|
|
|
456
457
|
return {
|
|
457
458
|
smartWallet,
|
|
458
459
|
lifetimeEarnings: sumTokenEarnings(
|
|
459
|
-
|
|
460
|
+
raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
|
|
460
461
|
),
|
|
461
462
|
tokens
|
|
462
463
|
};
|
|
463
464
|
}
|
|
464
|
-
function mapWeightedApyByChain(
|
|
465
|
-
if (!
|
|
465
|
+
function mapWeightedApyByChain(raw2) {
|
|
466
|
+
if (!raw2) return void 0;
|
|
466
467
|
const out = {};
|
|
467
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
468
|
+
for (const [chainKey, tokenApy] of Object.entries(raw2)) {
|
|
468
469
|
const chainId = Number(chainKey);
|
|
469
470
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
470
471
|
const perAsset = {};
|
|
@@ -504,15 +505,15 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
504
505
|
}
|
|
505
506
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
506
507
|
}
|
|
507
|
-
function mapApyHistory(
|
|
508
|
-
const history = Object.entries(
|
|
508
|
+
function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
509
|
+
const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
|
|
509
510
|
date,
|
|
510
511
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
|
|
511
512
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
512
513
|
return {
|
|
513
|
-
walletAddress:
|
|
514
|
-
weightedApyAfterFee:
|
|
515
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
514
|
+
walletAddress: raw2.walletAddress,
|
|
515
|
+
weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
|
|
516
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
|
|
516
517
|
history
|
|
517
518
|
};
|
|
518
519
|
}
|
|
@@ -608,23 +609,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
608
609
|
};
|
|
609
610
|
});
|
|
610
611
|
}
|
|
611
|
-
function mapUserProfile(
|
|
612
|
+
function mapUserProfile(raw2, userAddress) {
|
|
612
613
|
return {
|
|
613
614
|
address: userAddress,
|
|
614
|
-
smartWallet:
|
|
615
|
-
chains:
|
|
616
|
-
strategy:
|
|
617
|
-
hasActiveSessionKey:
|
|
618
|
-
protocols:
|
|
619
|
-
splitting:
|
|
620
|
-
minSplits:
|
|
615
|
+
smartWallet: raw2.smartWallet || "",
|
|
616
|
+
chains: raw2.chains || [],
|
|
617
|
+
strategy: raw2.strategy,
|
|
618
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey || false,
|
|
619
|
+
protocols: raw2.protocols || [],
|
|
620
|
+
splitting: raw2.splitting,
|
|
621
|
+
minSplits: raw2.minSplits
|
|
621
622
|
};
|
|
622
623
|
}
|
|
623
|
-
function mapApyByStrategy(
|
|
624
|
+
function mapApyByStrategy(raw2) {
|
|
624
625
|
const apyPerAsset = {};
|
|
625
626
|
let apySum = 0;
|
|
626
627
|
let apyCount = 0;
|
|
627
|
-
for (const entry of
|
|
628
|
+
for (const entry of raw2.data) {
|
|
628
629
|
const supported = SupportedAssets.find(
|
|
629
630
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
630
631
|
);
|
|
@@ -795,15 +796,15 @@ var readSession = (address, _chainId) => {
|
|
|
795
796
|
if (typeof window === "undefined") return null;
|
|
796
797
|
const key2 = buildKey(address);
|
|
797
798
|
const store = storage();
|
|
798
|
-
let
|
|
799
|
+
let raw2 = null;
|
|
799
800
|
try {
|
|
800
|
-
|
|
801
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
801
802
|
} catch {
|
|
802
|
-
|
|
803
|
+
raw2 = null;
|
|
803
804
|
}
|
|
804
|
-
if (
|
|
805
|
+
if (raw2) {
|
|
805
806
|
try {
|
|
806
|
-
const parsed = JSON.parse(
|
|
807
|
+
const parsed = JSON.parse(raw2);
|
|
807
808
|
if (isFreshSession(parsed)) return parsed;
|
|
808
809
|
} catch {
|
|
809
810
|
}
|
|
@@ -971,8 +972,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
971
972
|
}
|
|
972
973
|
return result;
|
|
973
974
|
}
|
|
974
|
-
function flattenAvailablePools(
|
|
975
|
-
const byChain =
|
|
975
|
+
function flattenAvailablePools(raw2) {
|
|
976
|
+
const byChain = raw2 ?? {};
|
|
976
977
|
const names = [];
|
|
977
978
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
978
979
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -1473,8 +1474,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1473
1474
|
const poolResults = await Promise.all(
|
|
1474
1475
|
universe.map(async (protocol) => {
|
|
1475
1476
|
try {
|
|
1476
|
-
const
|
|
1477
|
-
return [protocol.id, flattenAvailablePools(
|
|
1477
|
+
const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1478
|
+
return [protocol.id, flattenAvailablePools(raw2)];
|
|
1478
1479
|
} catch (error) {
|
|
1479
1480
|
console.warn(
|
|
1480
1481
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1540,14 +1541,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1540
1541
|
async readWalletState(ownerAddress) {
|
|
1541
1542
|
try {
|
|
1542
1543
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1543
|
-
const
|
|
1544
|
+
const raw2 = portfolio;
|
|
1544
1545
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1545
|
-
predeployed:
|
|
1546
|
-
hasActiveSessionKey:
|
|
1546
|
+
predeployed: raw2?.predeployed,
|
|
1547
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1547
1548
|
});
|
|
1548
1549
|
return {
|
|
1549
|
-
predeployed:
|
|
1550
|
-
hasActiveSessionKey:
|
|
1550
|
+
predeployed: raw2?.predeployed,
|
|
1551
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1551
1552
|
};
|
|
1552
1553
|
} catch (error) {
|
|
1553
1554
|
console.warn(
|
|
@@ -1827,14 +1828,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1827
1828
|
return { txHash, smartWallet, amount };
|
|
1828
1829
|
}
|
|
1829
1830
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1830
|
-
const
|
|
1831
|
+
const raw2 = await this.sdk.depositFunds(
|
|
1831
1832
|
this.getAddress(),
|
|
1832
1833
|
validChainId,
|
|
1833
1834
|
amount,
|
|
1834
1835
|
asset,
|
|
1835
1836
|
"aggressive"
|
|
1836
1837
|
);
|
|
1837
|
-
return mapDeposit(
|
|
1838
|
+
return mapDeposit(raw2);
|
|
1838
1839
|
} catch (error) {
|
|
1839
1840
|
throw error;
|
|
1840
1841
|
}
|
|
@@ -1843,27 +1844,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1843
1844
|
async withdraw(state, chainId, token, amount) {
|
|
1844
1845
|
const validChainId = isValidChainId(chainId);
|
|
1845
1846
|
await this.ensureConnected(state, validChainId);
|
|
1846
|
-
const
|
|
1847
|
+
const raw2 = await this.sdk.withdrawFunds(
|
|
1847
1848
|
this.getAddress(),
|
|
1848
1849
|
validChainId,
|
|
1849
1850
|
amount,
|
|
1850
1851
|
token
|
|
1851
1852
|
);
|
|
1852
|
-
if (!
|
|
1853
|
+
if (!raw2.success) {
|
|
1853
1854
|
throw new OwneyError(
|
|
1854
1855
|
"WITHDRAW_FAILED",
|
|
1855
|
-
|
|
1856
|
-
{ chainId: validChainId, token, amount, response:
|
|
1856
|
+
raw2.message || "Zyfai withdraw failed.",
|
|
1857
|
+
{ chainId: validChainId, token, amount, response: raw2 },
|
|
1857
1858
|
this.id
|
|
1858
1859
|
);
|
|
1859
1860
|
}
|
|
1860
|
-
return mapWithdraw(
|
|
1861
|
+
return mapWithdraw(raw2);
|
|
1861
1862
|
}
|
|
1862
1863
|
// --- IAgent: Portfolio reads ---
|
|
1863
1864
|
async getBalances(state, chainId) {
|
|
1864
1865
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1865
|
-
const
|
|
1866
|
-
return mapBalances(
|
|
1866
|
+
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
1867
|
+
return mapBalances(raw2, validChainId, smartWallet);
|
|
1867
1868
|
}
|
|
1868
1869
|
earningsKey(state, chainId, smartWallet) {
|
|
1869
1870
|
return JSON.stringify([
|
|
@@ -1876,11 +1877,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1876
1877
|
const existing = this.earningsReads.get(key2);
|
|
1877
1878
|
if (existing) return existing;
|
|
1878
1879
|
const generation = this.earningsGeneration;
|
|
1879
|
-
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((
|
|
1880
|
+
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw2) => {
|
|
1880
1881
|
if (generation === this.earningsGeneration) {
|
|
1881
|
-
this.earningsSnapshot = { key: key2, raw, at: Date.now() };
|
|
1882
|
+
this.earningsSnapshot = { key: key2, raw: raw2, at: Date.now() };
|
|
1882
1883
|
}
|
|
1883
|
-
return
|
|
1884
|
+
return raw2;
|
|
1884
1885
|
}).finally(() => {
|
|
1885
1886
|
if (this.earningsReads.get(key2) === pending)
|
|
1886
1887
|
this.earningsReads.delete(key2);
|
|
@@ -1890,11 +1891,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1890
1891
|
}
|
|
1891
1892
|
async getEarnings(state, chainId) {
|
|
1892
1893
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1893
|
-
const
|
|
1894
|
+
const raw2 = await this.readEarnings(
|
|
1894
1895
|
this.earningsKey(state, chainId, smartWallet),
|
|
1895
1896
|
smartWallet
|
|
1896
1897
|
);
|
|
1897
|
-
return mapEarnings(
|
|
1898
|
+
return mapEarnings(raw2, smartWallet);
|
|
1898
1899
|
}
|
|
1899
1900
|
async refreshEarnings(state, chainId) {
|
|
1900
1901
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -1921,8 +1922,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1921
1922
|
}
|
|
1922
1923
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1923
1924
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1924
|
-
const
|
|
1925
|
-
return mapApyHistory(
|
|
1925
|
+
const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
1926
|
+
return mapApyHistory(raw2, chainId, tokenSymbol);
|
|
1926
1927
|
}
|
|
1927
1928
|
/**
|
|
1928
1929
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -1957,7 +1958,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1957
1958
|
const matched = [];
|
|
1958
1959
|
let backendExhausted = false;
|
|
1959
1960
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
1960
|
-
const
|
|
1961
|
+
const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
1961
1962
|
limit: backendPageSize,
|
|
1962
1963
|
offset,
|
|
1963
1964
|
fromDate: options?.fromDate,
|
|
@@ -1968,13 +1969,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1968
1969
|
// asset's rows and handing back a page that filters to nothing.
|
|
1969
1970
|
assetType
|
|
1970
1971
|
});
|
|
1971
|
-
|
|
1972
|
+
raw2.data.forEach((entry, idx) => {
|
|
1972
1973
|
if (entry.chainId === validChainId) {
|
|
1973
1974
|
matched.push({ entry, rawIdx: offset + idx });
|
|
1974
1975
|
}
|
|
1975
1976
|
});
|
|
1976
|
-
offset +=
|
|
1977
|
-
if (
|
|
1977
|
+
offset += raw2.data.length;
|
|
1978
|
+
if (raw2.data.length < backendPageSize) {
|
|
1978
1979
|
backendExhausted = true;
|
|
1979
1980
|
break;
|
|
1980
1981
|
}
|
|
@@ -1996,18 +1997,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1996
1997
|
}
|
|
1997
1998
|
async getUserProfile(state, chainId) {
|
|
1998
1999
|
await this.connectAuth(state, chainId);
|
|
1999
|
-
const
|
|
2000
|
+
const raw2 = await this.sdk.getUserDetails();
|
|
2000
2001
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
2001
2002
|
asset: "USDC (default \u2014 no asset passed)",
|
|
2002
|
-
splitting:
|
|
2003
|
-
minSplits:
|
|
2004
|
-
strategy:
|
|
2005
|
-
chains:
|
|
2006
|
-
protocolCount:
|
|
2007
|
-
hasActiveSessionKey:
|
|
2008
|
-
smartWallet:
|
|
2003
|
+
splitting: raw2.splitting,
|
|
2004
|
+
minSplits: raw2.minSplits,
|
|
2005
|
+
strategy: raw2.strategy,
|
|
2006
|
+
chains: raw2.chains,
|
|
2007
|
+
protocolCount: raw2.protocols?.length,
|
|
2008
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey,
|
|
2009
|
+
smartWallet: raw2.smartWallet
|
|
2009
2010
|
});
|
|
2010
|
-
return mapUserProfile(
|
|
2011
|
+
return mapUserProfile(raw2, this.connectedAddress);
|
|
2011
2012
|
}
|
|
2012
2013
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
2013
2014
|
await this.connectAuth(state, chainId);
|
|
@@ -2026,236 +2027,58 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2026
2027
|
}
|
|
2027
2028
|
// --- IAgent: Discovery (no wallet required) ---
|
|
2028
2029
|
async getAgentApy(days, options) {
|
|
2029
|
-
const
|
|
2030
|
+
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
2030
2031
|
false,
|
|
2031
2032
|
DayFilterMapping[days],
|
|
2032
2033
|
"aggressive",
|
|
2033
2034
|
options?.chainId,
|
|
2034
2035
|
options?.tokenSymbol
|
|
2035
2036
|
);
|
|
2036
|
-
return mapApyByStrategy(
|
|
2037
|
+
return mapApyByStrategy(raw2);
|
|
2037
2038
|
}
|
|
2038
2039
|
};
|
|
2039
2040
|
|
|
2040
|
-
// src/
|
|
2041
|
-
var
|
|
2042
|
-
|
|
2043
|
-
|
|
2041
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2042
|
+
var import_viem6 = require("viem");
|
|
2043
|
+
var import_chains3 = require("viem/chains");
|
|
2044
|
+
|
|
2045
|
+
// src/lib/chain-guard.ts
|
|
2046
|
+
var CHAIN_NAMES = {
|
|
2047
|
+
1: "Ethereum",
|
|
2048
|
+
8453: "Base",
|
|
2049
|
+
42161: "Arbitrum"
|
|
2050
|
+
};
|
|
2051
|
+
function chainName(chainId) {
|
|
2052
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2053
|
+
}
|
|
2054
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2055
|
+
const actual = await pub.getChainId();
|
|
2056
|
+
if (actual === expected) return;
|
|
2044
2057
|
try {
|
|
2045
|
-
|
|
2046
|
-
method: "GET",
|
|
2047
|
-
headers: {
|
|
2048
|
-
"Content-Type": "application/json",
|
|
2049
|
-
"x-owney-api-key": `${apiKey}`
|
|
2050
|
-
}
|
|
2051
|
-
});
|
|
2052
|
-
if (!res.ok) {
|
|
2053
|
-
if (res.status !== 404) {
|
|
2054
|
-
console.warn(
|
|
2055
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
2056
|
-
);
|
|
2057
|
-
}
|
|
2058
|
-
return null;
|
|
2059
|
-
}
|
|
2060
|
-
const json = await res.json();
|
|
2061
|
-
const policy = json.success ? json.data ?? null : null;
|
|
2062
|
-
debugLog(
|
|
2063
|
-
"owney-sdk",
|
|
2064
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
2065
|
-
policy ?? void 0
|
|
2066
|
-
);
|
|
2067
|
-
return policy;
|
|
2058
|
+
await wallet.switchChain({ id: expected });
|
|
2068
2059
|
} catch (error) {
|
|
2069
|
-
console.warn(
|
|
2070
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
2071
|
-
error instanceof Error ? error.message : String(error)
|
|
2072
|
-
);
|
|
2073
|
-
return null;
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
2077
|
-
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
2078
|
-
const res = await fetch(url, {
|
|
2079
|
-
method: "GET",
|
|
2080
|
-
headers: {
|
|
2081
|
-
"Content-Type": "application/json",
|
|
2082
|
-
"x-owney-api-key": `${apiKey}`
|
|
2083
|
-
}
|
|
2084
|
-
});
|
|
2085
|
-
if (!res.ok) {
|
|
2086
|
-
const text = await res.text().catch(() => "");
|
|
2087
2060
|
throw new OwneyError(
|
|
2088
|
-
"
|
|
2089
|
-
`
|
|
2090
|
-
{
|
|
2061
|
+
"CHAIN_MISMATCH",
|
|
2062
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2063
|
+
{
|
|
2064
|
+
expectedChainId: expected,
|
|
2065
|
+
actualChainId: actual,
|
|
2066
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2067
|
+
}
|
|
2091
2068
|
);
|
|
2092
2069
|
}
|
|
2093
|
-
const
|
|
2094
|
-
if (
|
|
2070
|
+
const after = await pub.getChainId();
|
|
2071
|
+
if (after !== expected) {
|
|
2095
2072
|
throw new OwneyError(
|
|
2096
|
-
"
|
|
2097
|
-
`
|
|
2098
|
-
{
|
|
2099
|
-
);
|
|
2100
|
-
}
|
|
2101
|
-
return json.data;
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
// src/lib/health-report.ts
|
|
2105
|
-
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2106
|
-
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
|
|
2107
|
-
try {
|
|
2108
|
-
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
2109
|
-
method: "POST",
|
|
2110
|
-
headers: {
|
|
2111
|
-
"Content-Type": "application/json",
|
|
2112
|
-
"x-owney-api-key": apiKey
|
|
2113
|
-
},
|
|
2114
|
-
body: JSON.stringify({
|
|
2115
|
-
agent_type: agentType,
|
|
2116
|
-
error_code: errorCode,
|
|
2117
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2118
|
-
})
|
|
2119
|
-
});
|
|
2120
|
-
} catch (err) {
|
|
2121
|
-
console.warn(
|
|
2122
|
-
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
2123
|
-
err instanceof Error ? err.message : err
|
|
2124
|
-
);
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
2128
|
-
try {
|
|
2129
|
-
return await fn();
|
|
2130
|
-
} catch (err) {
|
|
2131
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
2132
|
-
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
2133
|
-
throw err;
|
|
2134
|
-
}
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
|
-
// src/lib/helpers/withdraw-helper.ts
|
|
2138
|
-
var import_viem2 = require("viem");
|
|
2139
|
-
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
2140
|
-
const target = asset.toUpperCase();
|
|
2141
|
-
return agents.map((agent) => {
|
|
2142
|
-
const agentBalance = aggregated[agent.id];
|
|
2143
|
-
const tokenBalance = agentBalance?.tokens.find(
|
|
2144
|
-
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
2073
|
+
"CHAIN_MISMATCH",
|
|
2074
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2075
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
2145
2076
|
);
|
|
2146
|
-
if (!tokenBalance) return { agent, balance: 0n };
|
|
2147
|
-
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
2148
|
-
});
|
|
2149
|
-
}
|
|
2150
|
-
function planProportionalShares(balances, requested, totalAvailable) {
|
|
2151
|
-
const plans = balances.map(({ agent, balance }) => ({
|
|
2152
|
-
agent,
|
|
2153
|
-
balance,
|
|
2154
|
-
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
2155
|
-
}));
|
|
2156
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
2157
|
-
let remainder = requested - assigned;
|
|
2158
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
2159
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
2160
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2161
|
-
});
|
|
2162
|
-
for (const p of byHeadroom) {
|
|
2163
|
-
if (remainder === 0n) break;
|
|
2164
|
-
const headroom = p.balance - p.planned;
|
|
2165
|
-
if (headroom <= 0n) continue;
|
|
2166
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
2167
|
-
p.planned += take;
|
|
2168
|
-
remainder -= take;
|
|
2169
|
-
}
|
|
2170
|
-
return plans;
|
|
2171
|
-
}
|
|
2172
|
-
function planDisabledDrain(disabled, requested) {
|
|
2173
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
2174
|
-
const plans = [];
|
|
2175
|
-
let remaining = requested;
|
|
2176
|
-
for (const { agent, balance } of sorted) {
|
|
2177
|
-
if (remaining === 0n) {
|
|
2178
|
-
plans.push({ agent, balance, planned: 0n });
|
|
2179
|
-
continue;
|
|
2180
|
-
}
|
|
2181
|
-
const take = balance < remaining ? balance : remaining;
|
|
2182
|
-
plans.push({ agent, balance, planned: take });
|
|
2183
|
-
remaining -= take;
|
|
2184
|
-
}
|
|
2185
|
-
return { plans, remaining };
|
|
2186
|
-
}
|
|
2187
|
-
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
2188
|
-
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
2189
|
-
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
2190
|
-
const totalHeadroom = candidates.reduce(
|
|
2191
|
-
(s, c) => s + (c.balance - c.planned),
|
|
2192
|
-
0n
|
|
2193
|
-
);
|
|
2194
|
-
if (totalHeadroom === 0n) return;
|
|
2195
|
-
let distributed = 0n;
|
|
2196
|
-
for (const c of candidates) {
|
|
2197
|
-
const headroom = c.balance - c.planned;
|
|
2198
|
-
const proportional = headroom * amount / totalHeadroom;
|
|
2199
|
-
const give = proportional > headroom ? headroom : proportional;
|
|
2200
|
-
c.planned += give;
|
|
2201
|
-
distributed += give;
|
|
2202
|
-
}
|
|
2203
|
-
let leftover = amount - distributed;
|
|
2204
|
-
for (const c of candidates) {
|
|
2205
|
-
if (leftover === 0n) break;
|
|
2206
|
-
const headroom = c.balance - c.planned;
|
|
2207
|
-
if (headroom <= 0n) continue;
|
|
2208
|
-
const take = headroom < leftover ? headroom : leftover;
|
|
2209
|
-
c.planned += take;
|
|
2210
|
-
leftover -= take;
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
function sumWithdrawnAmount(results) {
|
|
2214
|
-
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
// src/lib/helpers/account-apy-helper.ts
|
|
2218
|
-
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
2219
|
-
const sums = {};
|
|
2220
|
-
const weights = {};
|
|
2221
|
-
for (const id of Object.keys(agentApys)) {
|
|
2222
|
-
const cells = agentApys[id].apyByChainAndAsset;
|
|
2223
|
-
const balance = agentBalances[id] ?? 0;
|
|
2224
|
-
if (!cells || balance <= 0) continue;
|
|
2225
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
2226
|
-
if (!perAsset) continue;
|
|
2227
|
-
const chainId = Number(chainKey);
|
|
2228
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
2229
|
-
const apy = Number(apyValue ?? 0);
|
|
2230
|
-
if (apy === 0) continue;
|
|
2231
|
-
sums[chainId] ??= {};
|
|
2232
|
-
weights[chainId] ??= {};
|
|
2233
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
2234
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
const out = {};
|
|
2239
|
-
for (const chainKey of Object.keys(sums)) {
|
|
2240
|
-
const chainId = Number(chainKey);
|
|
2241
|
-
const perAssetOut = {};
|
|
2242
|
-
for (const asset of Object.keys(sums[chainId])) {
|
|
2243
|
-
const w = weights[chainId][asset];
|
|
2244
|
-
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
2245
|
-
}
|
|
2246
|
-
if (Object.keys(perAssetOut).length > 0) {
|
|
2247
|
-
out[chainId] = perAssetOut;
|
|
2248
|
-
}
|
|
2249
2077
|
}
|
|
2250
|
-
return out;
|
|
2251
2078
|
}
|
|
2252
2079
|
|
|
2253
|
-
// src/client.ts
|
|
2254
|
-
var import_viem6 = require("viem");
|
|
2255
|
-
var import_chains2 = require("viem/chains");
|
|
2256
|
-
|
|
2257
2080
|
// src/lib/transfer-auth.ts
|
|
2258
|
-
var
|
|
2081
|
+
var import_viem2 = require("viem");
|
|
2259
2082
|
var ERC20_META_ABI = [
|
|
2260
2083
|
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2261
2084
|
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
@@ -2287,28 +2110,29 @@ async function readTokenMeta(publicClient, token) {
|
|
|
2287
2110
|
function randomAuthNonce() {
|
|
2288
2111
|
const bytes = new Uint8Array(32);
|
|
2289
2112
|
globalThis.crypto.getRandomValues(bytes);
|
|
2290
|
-
return (0,
|
|
2113
|
+
return (0, import_viem2.bytesToHex)(bytes);
|
|
2291
2114
|
}
|
|
2292
2115
|
|
|
2293
2116
|
// src/lib/sponsor-client.ts
|
|
2294
|
-
var
|
|
2295
|
-
async function
|
|
2296
|
-
const
|
|
2117
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2118
|
+
async function postPaymasterIntent(input) {
|
|
2119
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2297
2120
|
let res;
|
|
2298
2121
|
try {
|
|
2299
|
-
res = await fetch(`${
|
|
2122
|
+
res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
|
|
2300
2123
|
method: "POST",
|
|
2301
2124
|
headers: {
|
|
2302
2125
|
"content-type": "application/json",
|
|
2303
|
-
"x-owney-api-key": input.apiKey
|
|
2126
|
+
"x-owney-api-key": input.apiKey,
|
|
2127
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
2304
2128
|
},
|
|
2305
2129
|
body: JSON.stringify(input.body)
|
|
2306
2130
|
});
|
|
2307
2131
|
} catch (networkError) {
|
|
2308
2132
|
throw new OwneyError(
|
|
2309
2133
|
"SPONSOR_REQUEST_FAILED",
|
|
2310
|
-
`
|
|
2311
|
-
{ cause: String(networkError) }
|
|
2134
|
+
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2135
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
2312
2136
|
);
|
|
2313
2137
|
}
|
|
2314
2138
|
const text = await res.text();
|
|
@@ -2317,30 +2141,29 @@ async function postSponsorTransferAuth(input) {
|
|
|
2317
2141
|
parsed = JSON.parse(text);
|
|
2318
2142
|
} catch {
|
|
2319
2143
|
}
|
|
2320
|
-
if (!res.ok || !parsed?.success ||
|
|
2144
|
+
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
2321
2145
|
throw new OwneyError(
|
|
2322
2146
|
"SPONSOR_REQUEST_FAILED",
|
|
2323
|
-
`
|
|
2147
|
+
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2324
2148
|
{
|
|
2325
2149
|
statusCode: res.status,
|
|
2326
2150
|
responseBody: text.slice(0, 500),
|
|
2327
|
-
|
|
2328
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2329
|
-
safeToFallback: res.status === 503
|
|
2151
|
+
safeToFallback: true
|
|
2330
2152
|
}
|
|
2331
2153
|
);
|
|
2332
2154
|
}
|
|
2333
2155
|
return parsed.data;
|
|
2334
2156
|
}
|
|
2335
|
-
async function
|
|
2336
|
-
const
|
|
2157
|
+
async function postSponsorTransferAuth(input) {
|
|
2158
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2337
2159
|
let res;
|
|
2338
2160
|
try {
|
|
2339
|
-
res = await fetch(`${
|
|
2161
|
+
res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2340
2162
|
method: "POST",
|
|
2341
2163
|
headers: {
|
|
2342
2164
|
"content-type": "application/json",
|
|
2343
|
-
"x-owney-api-key": input.apiKey
|
|
2165
|
+
"x-owney-api-key": input.apiKey,
|
|
2166
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2344
2167
|
},
|
|
2345
2168
|
body: JSON.stringify(input.body)
|
|
2346
2169
|
});
|
|
@@ -2348,7 +2171,7 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2348
2171
|
throw new OwneyError(
|
|
2349
2172
|
"SPONSOR_REQUEST_FAILED",
|
|
2350
2173
|
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2351
|
-
{ cause: String(networkError)
|
|
2174
|
+
{ cause: String(networkError) }
|
|
2352
2175
|
);
|
|
2353
2176
|
}
|
|
2354
2177
|
const text = await res.text();
|
|
@@ -2364,21 +2187,62 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2364
2187
|
{
|
|
2365
2188
|
statusCode: res.status,
|
|
2366
2189
|
responseBody: text.slice(0, 500),
|
|
2367
|
-
|
|
2190
|
+
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2191
|
+
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2192
|
+
safeToFallback: res.status === 503
|
|
2368
2193
|
}
|
|
2369
2194
|
);
|
|
2370
2195
|
}
|
|
2371
2196
|
return parsed.data;
|
|
2372
2197
|
}
|
|
2373
|
-
async function
|
|
2374
|
-
const
|
|
2198
|
+
async function postSponsorPermit2Transfer(input) {
|
|
2199
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2375
2200
|
let res;
|
|
2376
2201
|
try {
|
|
2377
|
-
res = await fetch(
|
|
2378
|
-
|
|
2379
|
-
{
|
|
2380
|
-
|
|
2381
|
-
|
|
2202
|
+
res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
|
|
2203
|
+
method: "POST",
|
|
2204
|
+
headers: {
|
|
2205
|
+
"content-type": "application/json",
|
|
2206
|
+
"x-owney-api-key": input.apiKey,
|
|
2207
|
+
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2208
|
+
},
|
|
2209
|
+
body: JSON.stringify(input.body)
|
|
2210
|
+
});
|
|
2211
|
+
} catch (networkError) {
|
|
2212
|
+
throw new OwneyError(
|
|
2213
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2214
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2215
|
+
{ cause: String(networkError), safeToFallback: false }
|
|
2216
|
+
);
|
|
2217
|
+
}
|
|
2218
|
+
const text = await res.text();
|
|
2219
|
+
let parsed = null;
|
|
2220
|
+
try {
|
|
2221
|
+
parsed = JSON.parse(text);
|
|
2222
|
+
} catch {
|
|
2223
|
+
}
|
|
2224
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2225
|
+
throw new OwneyError(
|
|
2226
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2227
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2228
|
+
{
|
|
2229
|
+
statusCode: res.status,
|
|
2230
|
+
responseBody: text.slice(0, 500),
|
|
2231
|
+
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
2232
|
+
}
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
return parsed.data;
|
|
2236
|
+
}
|
|
2237
|
+
async function getSponsorRelayerAddress(input) {
|
|
2238
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2239
|
+
let res;
|
|
2240
|
+
try {
|
|
2241
|
+
res = await fetch(
|
|
2242
|
+
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2243
|
+
{
|
|
2244
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
2245
|
+
}
|
|
2382
2246
|
);
|
|
2383
2247
|
} catch (networkError) {
|
|
2384
2248
|
throw new OwneyError(
|
|
@@ -2408,7 +2272,7 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2408
2272
|
}
|
|
2409
2273
|
|
|
2410
2274
|
// src/lib/permit2.ts
|
|
2411
|
-
var
|
|
2275
|
+
var import_viem3 = require("viem");
|
|
2412
2276
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2413
2277
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2414
2278
|
var ERC20_ALLOWANCE_ABI = [
|
|
@@ -2466,7 +2330,7 @@ function buildPermitTransferFromTypedData(input) {
|
|
|
2466
2330
|
function randomPermit2Nonce() {
|
|
2467
2331
|
const bytes = new Uint8Array(32);
|
|
2468
2332
|
globalThis.crypto.getRandomValues(bytes);
|
|
2469
|
-
return BigInt((0,
|
|
2333
|
+
return BigInt((0, import_viem3.bytesToHex)(bytes));
|
|
2470
2334
|
}
|
|
2471
2335
|
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2472
2336
|
return publicClient.readContract({
|
|
@@ -2476,213 +2340,1972 @@ async function readPermit2Allowance(publicClient, token, owner) {
|
|
|
2476
2340
|
args: [owner, PERMIT2_ADDRESS]
|
|
2477
2341
|
});
|
|
2478
2342
|
}
|
|
2479
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
2480
|
-
return publicClient.readContract({
|
|
2481
|
-
address: token,
|
|
2482
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2483
|
-
functionName: "balanceOf",
|
|
2484
|
-
args: [owner]
|
|
2343
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
2344
|
+
return publicClient.readContract({
|
|
2345
|
+
address: token,
|
|
2346
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2347
|
+
functionName: "balanceOf",
|
|
2348
|
+
args: [owner]
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
// src/lib/sponsored-deposit.ts
|
|
2353
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2354
|
+
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2355
|
+
function provideDepositVerificationContext(callback, context) {
|
|
2356
|
+
callback[verificationSetter]?.(context);
|
|
2357
|
+
}
|
|
2358
|
+
function makeVerificationAwareDepositCallback(implementation) {
|
|
2359
|
+
let nextVerification;
|
|
2360
|
+
const callback = async (smartWallet, chainId, amount) => {
|
|
2361
|
+
const verification = nextVerification;
|
|
2362
|
+
nextVerification = void 0;
|
|
2363
|
+
return implementation(smartWallet, chainId, amount, verification);
|
|
2364
|
+
};
|
|
2365
|
+
Object.defineProperty(callback, verificationSetter, {
|
|
2366
|
+
value: (context) => {
|
|
2367
|
+
nextVerification = context;
|
|
2368
|
+
}
|
|
2369
|
+
});
|
|
2370
|
+
return callback;
|
|
2371
|
+
}
|
|
2372
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2373
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2374
|
+
return makeVerificationAwareDepositCallback(
|
|
2375
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
2376
|
+
const cid = chainId;
|
|
2377
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2378
|
+
if (!token) {
|
|
2379
|
+
throw new OwneyError(
|
|
2380
|
+
"CHAIN_UNSUPPORTED",
|
|
2381
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2382
|
+
);
|
|
2383
|
+
}
|
|
2384
|
+
const pub = deps.getPublicClient(cid);
|
|
2385
|
+
const wallet = deps.getWalletClient(cid);
|
|
2386
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2387
|
+
try {
|
|
2388
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2389
|
+
if (balance < BigInt(amount)) {
|
|
2390
|
+
throw new OwneyError(
|
|
2391
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2392
|
+
"Insufficient balance for this deposit.",
|
|
2393
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
2396
|
+
} catch (err) {
|
|
2397
|
+
if (err instanceof OwneyError) throw err;
|
|
2398
|
+
console.warn(
|
|
2399
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2400
|
+
err instanceof Error ? err.message : String(err)
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2404
|
+
const validAfter = 0n;
|
|
2405
|
+
const validBefore = BigInt(
|
|
2406
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2407
|
+
);
|
|
2408
|
+
const nonce = randomAuthNonce();
|
|
2409
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2410
|
+
token,
|
|
2411
|
+
chainId: cid,
|
|
2412
|
+
tokenName,
|
|
2413
|
+
tokenVersion,
|
|
2414
|
+
message: {
|
|
2415
|
+
from: deps.ownerAddress,
|
|
2416
|
+
to: smartWallet,
|
|
2417
|
+
value: BigInt(amount),
|
|
2418
|
+
validAfter,
|
|
2419
|
+
validBefore,
|
|
2420
|
+
nonce
|
|
2421
|
+
}
|
|
2422
|
+
});
|
|
2423
|
+
const authSignature = await wallet.signTypedData({
|
|
2424
|
+
account: deps.ownerAddress,
|
|
2425
|
+
...typedData
|
|
2426
|
+
});
|
|
2427
|
+
deps.onApproved?.();
|
|
2428
|
+
const result = await post({
|
|
2429
|
+
baseUrl: deps.baseUrl,
|
|
2430
|
+
apiKey: deps.apiKey,
|
|
2431
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2432
|
+
body: {
|
|
2433
|
+
chainId: cid,
|
|
2434
|
+
token,
|
|
2435
|
+
from: deps.ownerAddress,
|
|
2436
|
+
to: smartWallet,
|
|
2437
|
+
value: amount,
|
|
2438
|
+
validAfter: validAfter.toString(),
|
|
2439
|
+
validBefore: validBefore.toString(),
|
|
2440
|
+
nonce,
|
|
2441
|
+
authSignature,
|
|
2442
|
+
tokenName,
|
|
2443
|
+
tokenVersion,
|
|
2444
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
2445
|
+
yieldseekerUserId: verification.userId,
|
|
2446
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
2447
|
+
} : {}
|
|
2448
|
+
}
|
|
2449
|
+
});
|
|
2450
|
+
return result.txHash;
|
|
2451
|
+
}
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2456
|
+
var import_siwe = require("siwe");
|
|
2457
|
+
var import_viem4 = require("viem");
|
|
2458
|
+
var import_chains2 = require("viem/chains");
|
|
2459
|
+
|
|
2460
|
+
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2461
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session.v5";
|
|
2462
|
+
var INVALIDATED_KEY_PREFIXES = [
|
|
2463
|
+
"owney.yieldseeker.session",
|
|
2464
|
+
"owney.yieldseeker.session.v3",
|
|
2465
|
+
"owney.yieldseeker.session.v4"
|
|
2466
|
+
];
|
|
2467
|
+
var storage2 = () => {
|
|
2468
|
+
if (typeof window === "undefined") return null;
|
|
2469
|
+
try {
|
|
2470
|
+
return window.localStorage;
|
|
2471
|
+
} catch {
|
|
2472
|
+
return null;
|
|
2473
|
+
}
|
|
2474
|
+
};
|
|
2475
|
+
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2476
|
+
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2477
|
+
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2478
|
+
);
|
|
2479
|
+
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2480
|
+
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2481
|
+
memorySessions2.delete(key2);
|
|
2482
|
+
try {
|
|
2483
|
+
store?.removeItem(key2);
|
|
2484
|
+
} catch {
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
};
|
|
2488
|
+
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2489
|
+
var isValidSession = (session) => {
|
|
2490
|
+
if (!session?.token) return false;
|
|
2491
|
+
try {
|
|
2492
|
+
const parsed = JSON.parse(atob(session.token));
|
|
2493
|
+
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2494
|
+
} catch {
|
|
2495
|
+
return false;
|
|
2496
|
+
}
|
|
2497
|
+
};
|
|
2498
|
+
var readYieldseekerSession = (address, chainId) => {
|
|
2499
|
+
if (typeof window === "undefined") return null;
|
|
2500
|
+
const key2 = buildKey2(address, chainId);
|
|
2501
|
+
const store = storage2();
|
|
2502
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2503
|
+
let raw2 = null;
|
|
2504
|
+
try {
|
|
2505
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
2506
|
+
} catch {
|
|
2507
|
+
raw2 = null;
|
|
2508
|
+
}
|
|
2509
|
+
if (raw2) {
|
|
2510
|
+
try {
|
|
2511
|
+
const parsed = JSON.parse(raw2);
|
|
2512
|
+
if (isValidSession(parsed)) return parsed.token;
|
|
2513
|
+
} catch {
|
|
2514
|
+
}
|
|
2515
|
+
memorySessions2.delete(key2);
|
|
2516
|
+
try {
|
|
2517
|
+
store?.removeItem(key2);
|
|
2518
|
+
} catch {
|
|
2519
|
+
}
|
|
2520
|
+
return null;
|
|
2521
|
+
}
|
|
2522
|
+
const cached = memorySessions2.get(key2);
|
|
2523
|
+
if (isValidSession(cached)) return cached.token;
|
|
2524
|
+
if (cached) memorySessions2.delete(key2);
|
|
2525
|
+
return null;
|
|
2526
|
+
};
|
|
2527
|
+
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2528
|
+
if (typeof window === "undefined") return;
|
|
2529
|
+
const session = { token };
|
|
2530
|
+
if (!isValidSession(session)) return;
|
|
2531
|
+
const key2 = buildKey2(address, chainId);
|
|
2532
|
+
memorySessions2.set(key2, session);
|
|
2533
|
+
const store = storage2();
|
|
2534
|
+
try {
|
|
2535
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2536
|
+
} catch {
|
|
2537
|
+
}
|
|
2538
|
+
};
|
|
2539
|
+
var clearYieldseekerSession = (address, chainId) => {
|
|
2540
|
+
const key2 = buildKey2(address, chainId);
|
|
2541
|
+
memorySessions2.delete(key2);
|
|
2542
|
+
const store = storage2();
|
|
2543
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2544
|
+
try {
|
|
2545
|
+
store?.removeItem(key2);
|
|
2546
|
+
} catch {
|
|
2547
|
+
}
|
|
2548
|
+
};
|
|
2549
|
+
|
|
2550
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2551
|
+
function resolveSiweOrigin(override) {
|
|
2552
|
+
const origin = override?.trim() || (typeof window !== "undefined" ? window.location?.origin : void 0);
|
|
2553
|
+
if (!origin || origin === "null") {
|
|
2554
|
+
throw new Error("Yieldseeker sign-in requires the requesting app's browser origin.");
|
|
2555
|
+
}
|
|
2556
|
+
const url = new URL(origin);
|
|
2557
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
2558
|
+
throw new Error("Yieldseeker sign-in requires an HTTP or HTTPS app origin.");
|
|
2559
|
+
}
|
|
2560
|
+
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
2561
|
+
throw new Error("Yieldseeker SIWE override must be an origin without credentials, path, query, or fragment.");
|
|
2562
|
+
}
|
|
2563
|
+
return url;
|
|
2564
|
+
}
|
|
2565
|
+
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2566
|
+
const url = resolveSiweOrigin(dependencies.origin);
|
|
2567
|
+
return new import_siwe.SiweMessage({
|
|
2568
|
+
scheme: url.protocol.slice(0, -1),
|
|
2569
|
+
domain: url.host,
|
|
2570
|
+
address: (0, import_viem4.getAddress)(address),
|
|
2571
|
+
uri: url.origin,
|
|
2572
|
+
version: "1",
|
|
2573
|
+
chainId,
|
|
2574
|
+
nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
|
|
2575
|
+
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2576
|
+
}).prepareMessage();
|
|
2577
|
+
}
|
|
2578
|
+
function encodeYieldseekerAuthToken(token) {
|
|
2579
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2580
|
+
let binary = "";
|
|
2581
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2582
|
+
return btoa(binary);
|
|
2583
|
+
}
|
|
2584
|
+
var YieldseekerAuth = class {
|
|
2585
|
+
constructor(dependencies = {}) {
|
|
2586
|
+
this.dependencies = dependencies;
|
|
2587
|
+
}
|
|
2588
|
+
dependencies;
|
|
2589
|
+
tokens = /* @__PURE__ */ new Map();
|
|
2590
|
+
pending = /* @__PURE__ */ new Map();
|
|
2591
|
+
scopes = /* @__PURE__ */ new Map();
|
|
2592
|
+
key(state, chainId) {
|
|
2593
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
|
|
2594
|
+
}
|
|
2595
|
+
async getToken(state, chainId) {
|
|
2596
|
+
const key2 = this.key(state, chainId);
|
|
2597
|
+
const scope = { address: state.walletAddress, chainId };
|
|
2598
|
+
this.scopes.set(key2, scope);
|
|
2599
|
+
const cached = this.tokens.get(key2);
|
|
2600
|
+
if (cached) return cached;
|
|
2601
|
+
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2602
|
+
if (persisted && this.matchesOrigin(persisted)) {
|
|
2603
|
+
this.tokens.set(key2, persisted);
|
|
2604
|
+
return persisted;
|
|
2605
|
+
}
|
|
2606
|
+
if (persisted) clearYieldseekerSession(scope.address, scope.chainId);
|
|
2607
|
+
const inFlight = this.pending.get(key2);
|
|
2608
|
+
if (inFlight) return inFlight;
|
|
2609
|
+
const request = this.sign(state, chainId).then((token) => {
|
|
2610
|
+
this.tokens.set(key2, token);
|
|
2611
|
+
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2612
|
+
return token;
|
|
2613
|
+
});
|
|
2614
|
+
this.pending.set(key2, request);
|
|
2615
|
+
try {
|
|
2616
|
+
return await request;
|
|
2617
|
+
} finally {
|
|
2618
|
+
this.pending.delete(key2);
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
matchesOrigin(token) {
|
|
2622
|
+
try {
|
|
2623
|
+
const message = new import_siwe.SiweMessage(JSON.parse(atob(token)).message);
|
|
2624
|
+
const url = resolveSiweOrigin(this.dependencies.origin);
|
|
2625
|
+
return message.domain === url.host && message.uri === url.origin && message.scheme === url.protocol.slice(0, -1);
|
|
2626
|
+
} catch {
|
|
2627
|
+
return false;
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
clear(state, chainId) {
|
|
2631
|
+
if (!state || chainId === void 0) {
|
|
2632
|
+
for (const scope of this.scopes.values()) {
|
|
2633
|
+
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2634
|
+
}
|
|
2635
|
+
this.tokens.clear();
|
|
2636
|
+
this.pending.clear();
|
|
2637
|
+
this.scopes.clear();
|
|
2638
|
+
return;
|
|
2639
|
+
}
|
|
2640
|
+
const key2 = this.key(state, chainId);
|
|
2641
|
+
this.tokens.delete(key2);
|
|
2642
|
+
this.pending.delete(key2);
|
|
2643
|
+
this.scopes.delete(key2);
|
|
2644
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2645
|
+
}
|
|
2646
|
+
async sign(state, chainId) {
|
|
2647
|
+
const account = (0, import_viem4.getAddress)(state.walletAddress);
|
|
2648
|
+
const publicClient = (0, import_viem4.createPublicClient)({
|
|
2649
|
+
chain: import_chains2.base,
|
|
2650
|
+
transport: (0, import_viem4.custom)(state.provider)
|
|
2651
|
+
});
|
|
2652
|
+
const walletClient = (0, import_viem4.createWalletClient)({
|
|
2653
|
+
account,
|
|
2654
|
+
chain: import_chains2.base,
|
|
2655
|
+
transport: (0, import_viem4.custom)(state.provider)
|
|
2656
|
+
});
|
|
2657
|
+
await ensureWalletOnChain(
|
|
2658
|
+
publicClient,
|
|
2659
|
+
walletClient,
|
|
2660
|
+
8453
|
|
2661
|
+
);
|
|
2662
|
+
const message = createYieldseekerSiweMessage(
|
|
2663
|
+
account,
|
|
2664
|
+
chainId,
|
|
2665
|
+
this.dependencies
|
|
2666
|
+
);
|
|
2667
|
+
const signature = await walletClient.signMessage({ account, message });
|
|
2668
|
+
return encodeYieldseekerAuthToken({ message, signature });
|
|
2669
|
+
}
|
|
2670
|
+
};
|
|
2671
|
+
|
|
2672
|
+
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2673
|
+
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2674
|
+
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2675
|
+
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2676
|
+
var storage3 = () => {
|
|
2677
|
+
if (typeof window === "undefined") return null;
|
|
2678
|
+
try {
|
|
2679
|
+
return window.localStorage;
|
|
2680
|
+
} catch {
|
|
2681
|
+
return null;
|
|
2682
|
+
}
|
|
2683
|
+
};
|
|
2684
|
+
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2685
|
+
function valid(value, walletAddress, chainId, now) {
|
|
2686
|
+
return Boolean(
|
|
2687
|
+
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
|
|
2688
|
+
);
|
|
2689
|
+
}
|
|
2690
|
+
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2691
|
+
if (typeof window === "undefined") return null;
|
|
2692
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2693
|
+
const store = storage3();
|
|
2694
|
+
let parsed = null;
|
|
2695
|
+
try {
|
|
2696
|
+
const raw2 = store?.getItem(key2);
|
|
2697
|
+
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2698
|
+
} catch {
|
|
2699
|
+
parsed = null;
|
|
2700
|
+
}
|
|
2701
|
+
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2702
|
+
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2703
|
+
memoryIdentities.set(key2, candidate);
|
|
2704
|
+
return { userId: candidate.userId };
|
|
2705
|
+
}
|
|
2706
|
+
memoryIdentities.delete(key2);
|
|
2707
|
+
try {
|
|
2708
|
+
store?.removeItem(key2);
|
|
2709
|
+
} catch {
|
|
2710
|
+
}
|
|
2711
|
+
return null;
|
|
2712
|
+
}
|
|
2713
|
+
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2714
|
+
if (typeof window === "undefined") return;
|
|
2715
|
+
const identity = {
|
|
2716
|
+
userId,
|
|
2717
|
+
walletAddress,
|
|
2718
|
+
chainId,
|
|
2719
|
+
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2720
|
+
};
|
|
2721
|
+
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2722
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2723
|
+
memoryIdentities.set(key2, identity);
|
|
2724
|
+
try {
|
|
2725
|
+
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2726
|
+
} catch {
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2730
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2731
|
+
memoryIdentities.delete(key2);
|
|
2732
|
+
try {
|
|
2733
|
+
storage3()?.removeItem(key2);
|
|
2734
|
+
} catch {
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2739
|
+
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2740
|
+
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2741
|
+
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2742
|
+
}
|
|
2743
|
+
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2744
|
+
var YieldseekerApiError = class extends Error {
|
|
2745
|
+
constructor(status, providerCode, responseFields) {
|
|
2746
|
+
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2747
|
+
this.status = status;
|
|
2748
|
+
this.providerCode = providerCode;
|
|
2749
|
+
this.responseFields = responseFields;
|
|
2750
|
+
this.name = "YieldseekerApiError";
|
|
2751
|
+
}
|
|
2752
|
+
status;
|
|
2753
|
+
providerCode;
|
|
2754
|
+
responseFields;
|
|
2755
|
+
get isAuthenticationError() {
|
|
2756
|
+
return this.status === 401 || this.status === 403;
|
|
2757
|
+
}
|
|
2758
|
+
};
|
|
2759
|
+
function providerError(body, fallback) {
|
|
2760
|
+
if (!body || typeof body !== "object") return { code: fallback };
|
|
2761
|
+
const record = body;
|
|
2762
|
+
return {
|
|
2763
|
+
code: typeof record.message === "string" ? record.message : fallback,
|
|
2764
|
+
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
var YieldseekerApiClient = class {
|
|
2768
|
+
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
|
|
2769
|
+
this.owneyApiKey = owneyApiKey;
|
|
2770
|
+
this.baseUrl = baseUrl;
|
|
2771
|
+
this.fetchFn = fetchFn;
|
|
2772
|
+
}
|
|
2773
|
+
owneyApiKey;
|
|
2774
|
+
baseUrl;
|
|
2775
|
+
fetchFn;
|
|
2776
|
+
async request(path, options = {}) {
|
|
2777
|
+
const controller = new AbortController();
|
|
2778
|
+
const timer = setTimeout(
|
|
2779
|
+
() => controller.abort(),
|
|
2780
|
+
options.timeoutMs ?? 15e3
|
|
2781
|
+
);
|
|
2782
|
+
try {
|
|
2783
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2784
|
+
method: options.method ?? "GET",
|
|
2785
|
+
headers: {
|
|
2786
|
+
"Content-Type": "application/json",
|
|
2787
|
+
"x-owney-api-key": this.owneyApiKey,
|
|
2788
|
+
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2789
|
+
},
|
|
2790
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2791
|
+
signal: controller.signal
|
|
2792
|
+
});
|
|
2793
|
+
const payload = await response.json().catch(() => null);
|
|
2794
|
+
if (!response.ok) {
|
|
2795
|
+
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2796
|
+
throw new YieldseekerApiError(
|
|
2797
|
+
response.status,
|
|
2798
|
+
error.code,
|
|
2799
|
+
error.fields
|
|
2800
|
+
);
|
|
2801
|
+
}
|
|
2802
|
+
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2803
|
+
return payload.data;
|
|
2804
|
+
}
|
|
2805
|
+
return payload;
|
|
2806
|
+
} catch (error) {
|
|
2807
|
+
if (error instanceof YieldseekerApiError) throw error;
|
|
2808
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2809
|
+
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2810
|
+
}
|
|
2811
|
+
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2812
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2813
|
+
});
|
|
2814
|
+
} finally {
|
|
2815
|
+
clearTimeout(timer);
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
|
|
2820
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2821
|
+
var import_viem5 = require("viem");
|
|
2822
|
+
|
|
2823
|
+
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2824
|
+
var YIELDSEEKER_ASSET_METADATA = {
|
|
2825
|
+
USDC: {
|
|
2826
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2827
|
+
decimals: 6
|
|
2828
|
+
},
|
|
2829
|
+
WETH: {
|
|
2830
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
2831
|
+
decimals: 18
|
|
2832
|
+
}
|
|
2833
|
+
};
|
|
2834
|
+
|
|
2835
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2836
|
+
function invalid(endpoint, detail) {
|
|
2837
|
+
throw new OwneyError(
|
|
2838
|
+
"AGENT_INVALID_RESPONSE",
|
|
2839
|
+
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2840
|
+
{ endpoint, detail },
|
|
2841
|
+
"yieldseeker"
|
|
2842
|
+
);
|
|
2843
|
+
}
|
|
2844
|
+
function raw(value, endpoint) {
|
|
2845
|
+
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2846
|
+
return invalid(endpoint, "expected a base-10 integer string");
|
|
2847
|
+
}
|
|
2848
|
+
return BigInt(value);
|
|
2849
|
+
}
|
|
2850
|
+
function decimal(value, decimals, endpoint) {
|
|
2851
|
+
return (0, import_viem5.formatUnits)(raw(value, endpoint), decimals);
|
|
2852
|
+
}
|
|
2853
|
+
function usd(rawAmount, decimals, price) {
|
|
2854
|
+
return Number((0, import_viem5.formatUnits)(rawAmount, decimals)) * price;
|
|
2855
|
+
}
|
|
2856
|
+
function percent(value) {
|
|
2857
|
+
const result = Number(value);
|
|
2858
|
+
return Number.isFinite(result) ? result * 100 : 0;
|
|
2859
|
+
}
|
|
2860
|
+
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2861
|
+
function publicApyAfterYieldseekerFee(value) {
|
|
2862
|
+
const grossPercent = percent(value);
|
|
2863
|
+
if (grossPercent <= 0) return grossPercent;
|
|
2864
|
+
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2865
|
+
return Math.round(netPercent * 1e12) / 1e12;
|
|
2866
|
+
}
|
|
2867
|
+
function riskAdjustedApyForDays(option, days) {
|
|
2868
|
+
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2869
|
+
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2870
|
+
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2871
|
+
}
|
|
2872
|
+
function assetAddressValue(record, address) {
|
|
2873
|
+
const entry = Object.entries(record).find(
|
|
2874
|
+
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2875
|
+
);
|
|
2876
|
+
return entry?.[1] ?? "0";
|
|
2877
|
+
}
|
|
2878
|
+
function position(value, asset, baseAssetDecimals) {
|
|
2879
|
+
const option = value?.yieldOption;
|
|
2880
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem5.isAddress)(option.address)) {
|
|
2881
|
+
return invalid("yield positions", "missing vault metadata");
|
|
2882
|
+
}
|
|
2883
|
+
return {
|
|
2884
|
+
chain: "BASE",
|
|
2885
|
+
protocol: option.provider,
|
|
2886
|
+
protocolId: option.address,
|
|
2887
|
+
pool: option.name,
|
|
2888
|
+
asset,
|
|
2889
|
+
// `assetsRaw` is the ERC-4626 vault-share quantity, whose decimals can
|
|
2890
|
+
// differ from the underlying asset. Yieldseeker already converts it to
|
|
2891
|
+
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
2892
|
+
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
2893
|
+
// share quantity separately because withdraw-from-position expects it.
|
|
2894
|
+
amount: decimal(
|
|
2895
|
+
value.assetsBase,
|
|
2896
|
+
baseAssetDecimals,
|
|
2897
|
+
"yield positions"
|
|
2898
|
+
),
|
|
2899
|
+
amountRaw: String(value.assetsRaw),
|
|
2900
|
+
apy: percent(option.riskAdjustedApy),
|
|
2901
|
+
tvl: Number(option.totalDepositsUsd),
|
|
2902
|
+
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
function mapYieldseekerBalances(contexts) {
|
|
2906
|
+
const tokens = [];
|
|
2907
|
+
const assetBalances = [];
|
|
2908
|
+
const positions = [];
|
|
2909
|
+
let totalUsd = 0;
|
|
2910
|
+
for (const context of contexts) {
|
|
2911
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2912
|
+
assetBalances.push({
|
|
2913
|
+
chain: "BASE",
|
|
2914
|
+
chainId: 8453,
|
|
2915
|
+
asset: context.asset,
|
|
2916
|
+
amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
|
|
2917
|
+
});
|
|
2918
|
+
const idle = assetAddressValue(
|
|
2919
|
+
context.snapshot.tokenBalances,
|
|
2920
|
+
metadata.address
|
|
2921
|
+
);
|
|
2922
|
+
tokens.push({
|
|
2923
|
+
chain: "BASE",
|
|
2924
|
+
chainId: 8453,
|
|
2925
|
+
asset: context.asset,
|
|
2926
|
+
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2927
|
+
});
|
|
2928
|
+
positions.push(
|
|
2929
|
+
...context.positions.map(
|
|
2930
|
+
(entry) => position(
|
|
2931
|
+
entry,
|
|
2932
|
+
context.asset,
|
|
2933
|
+
context.snapshot.baseAssetDecimals
|
|
2934
|
+
)
|
|
2935
|
+
)
|
|
2936
|
+
);
|
|
2937
|
+
totalUsd += usd(
|
|
2938
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2939
|
+
context.snapshot.baseAssetDecimals,
|
|
2940
|
+
context.snapshot.baseAssetPriceUsd
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2943
|
+
return {
|
|
2944
|
+
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2945
|
+
totalBalance: String(totalUsd),
|
|
2946
|
+
totalBalanceAsset: "usdc",
|
|
2947
|
+
assetBalances,
|
|
2948
|
+
tokens,
|
|
2949
|
+
positions
|
|
2950
|
+
};
|
|
2951
|
+
}
|
|
2952
|
+
function mapYieldseekerEarnings(contexts) {
|
|
2953
|
+
const tokens = [];
|
|
2954
|
+
let lifetimeEarnings = 0;
|
|
2955
|
+
for (const context of contexts) {
|
|
2956
|
+
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
2957
|
+
tokens.push({
|
|
2958
|
+
chain: "BASE",
|
|
2959
|
+
chainId: 8453,
|
|
2960
|
+
asset: context.asset,
|
|
2961
|
+
amount: (0, import_viem5.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
2962
|
+
});
|
|
2963
|
+
lifetimeEarnings += usd(
|
|
2964
|
+
amount,
|
|
2965
|
+
context.snapshot.baseAssetDecimals,
|
|
2966
|
+
context.snapshot.baseAssetPriceUsd
|
|
2967
|
+
);
|
|
2968
|
+
}
|
|
2969
|
+
return {
|
|
2970
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2971
|
+
lifetimeEarnings,
|
|
2972
|
+
tokens
|
|
2973
|
+
};
|
|
2974
|
+
}
|
|
2975
|
+
function apyForDays(snapshot, days) {
|
|
2976
|
+
if (days === "7D") return percent(snapshot.apy7d);
|
|
2977
|
+
if (days === "30D") return percent(snapshot.apy30d);
|
|
2978
|
+
return percent((snapshot.apy7d + snapshot.apy30d) / 2);
|
|
2979
|
+
}
|
|
2980
|
+
function dailyApy(point) {
|
|
2981
|
+
const total = raw(point.totalValueBase, "historic position");
|
|
2982
|
+
const earned = raw(point.dailyYieldBase, "historic position");
|
|
2983
|
+
const principal = total - earned;
|
|
2984
|
+
if (principal <= 0n || earned === 0n) return 0;
|
|
2985
|
+
return Number(earned) / Number(principal) * 365 * 100;
|
|
2986
|
+
}
|
|
2987
|
+
function aggregateHistory(contexts, dayCount) {
|
|
2988
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
2989
|
+
for (const context of contexts) {
|
|
2990
|
+
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
2991
|
+
for (const point of points.slice(-dayCount)) {
|
|
2992
|
+
const valueUsd = usd(
|
|
2993
|
+
raw(point.totalValueBase, "historic position"),
|
|
2994
|
+
point.baseAssetDecimals,
|
|
2995
|
+
point.baseAssetPriceUsd
|
|
2996
|
+
);
|
|
2997
|
+
const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
|
|
2998
|
+
current.weighted += dailyApy(point) * valueUsd;
|
|
2999
|
+
current.valueUsd += valueUsd;
|
|
3000
|
+
byDate.set(point.date, current);
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
3004
|
+
date,
|
|
3005
|
+
apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
|
|
3006
|
+
}));
|
|
3007
|
+
}
|
|
3008
|
+
function mapYieldseekerApy(walletAddress, contexts, days) {
|
|
3009
|
+
let weighted = 0;
|
|
3010
|
+
let totalUsd = 0;
|
|
3011
|
+
const byAsset = {};
|
|
3012
|
+
for (const context of contexts) {
|
|
3013
|
+
const valueUsd = usd(
|
|
3014
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
3015
|
+
context.snapshot.baseAssetDecimals,
|
|
3016
|
+
context.snapshot.baseAssetPriceUsd
|
|
3017
|
+
);
|
|
3018
|
+
const apy = apyForDays(context.snapshot, days);
|
|
3019
|
+
weighted += apy * valueUsd;
|
|
3020
|
+
totalUsd += valueUsd;
|
|
3021
|
+
byAsset[context.asset] = apy;
|
|
3022
|
+
}
|
|
3023
|
+
const dayCount = Number(days.slice(0, -1));
|
|
3024
|
+
return {
|
|
3025
|
+
walletAddress,
|
|
3026
|
+
weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
|
|
3027
|
+
apyByChainAndAsset: { 8453: byAsset },
|
|
3028
|
+
history: aggregateHistory(contexts, dayCount)
|
|
3029
|
+
};
|
|
3030
|
+
}
|
|
3031
|
+
function actionType(value) {
|
|
3032
|
+
const normalized = value.toLowerCase();
|
|
3033
|
+
if (normalized.includes("deposit")) return "Deposit";
|
|
3034
|
+
if (normalized.includes("withdraw")) return "Withdraw";
|
|
3035
|
+
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
3036
|
+
return "Earned";
|
|
3037
|
+
return "Rebalance";
|
|
3038
|
+
}
|
|
3039
|
+
function transactionHashes(details) {
|
|
3040
|
+
if (!details) return [];
|
|
3041
|
+
const values = [
|
|
3042
|
+
details.transactionHash,
|
|
3043
|
+
details.txHash,
|
|
3044
|
+
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
3045
|
+
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
3046
|
+
];
|
|
3047
|
+
return values.filter(
|
|
3048
|
+
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
3049
|
+
).filter((value, index, all) => all.indexOf(value) === index);
|
|
3050
|
+
}
|
|
3051
|
+
function actionEntry(action) {
|
|
3052
|
+
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
3053
|
+
return {
|
|
3054
|
+
agent: "yieldseeker",
|
|
3055
|
+
action: actionType(action.actionType),
|
|
3056
|
+
date: action.createdDate,
|
|
3057
|
+
oldApy: null,
|
|
3058
|
+
newApy: null,
|
|
3059
|
+
transactions: [
|
|
3060
|
+
{
|
|
3061
|
+
txHashes: transactionHashes(action.details),
|
|
3062
|
+
chainId: 8453
|
|
3063
|
+
}
|
|
3064
|
+
],
|
|
3065
|
+
rebalanceLog: []
|
|
3066
|
+
};
|
|
3067
|
+
}
|
|
3068
|
+
function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses) {
|
|
3069
|
+
const from = movement.fromAddress.toLowerCase();
|
|
3070
|
+
const to = movement.toAddress.toLowerCase();
|
|
3071
|
+
const owner = ownerAddress.toLowerCase();
|
|
3072
|
+
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
3073
|
+
const baseAsset = agent.assetAddress.toLowerCase();
|
|
3074
|
+
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
3075
|
+
return void 0;
|
|
3076
|
+
}
|
|
3077
|
+
let action;
|
|
3078
|
+
if (from === owner && to === agentWallet) {
|
|
3079
|
+
action = "Top up";
|
|
3080
|
+
} else if (from === agentWallet && to === owner) {
|
|
3081
|
+
action = "Withdraw";
|
|
3082
|
+
} else if (from === agentWallet && vaultAddresses.has(to)) {
|
|
3083
|
+
action = "Deposit";
|
|
3084
|
+
}
|
|
3085
|
+
if (!action) return void 0;
|
|
3086
|
+
return {
|
|
3087
|
+
agent: "yieldseeker",
|
|
3088
|
+
action,
|
|
3089
|
+
date: movement.blockDate,
|
|
3090
|
+
oldApy: null,
|
|
3091
|
+
newApy: null,
|
|
3092
|
+
transactions: [
|
|
3093
|
+
{
|
|
3094
|
+
txHashes: [movement.transactionHash],
|
|
3095
|
+
chainId: agent.chainId,
|
|
3096
|
+
tokenSymbol: asset,
|
|
3097
|
+
amount: decimal(
|
|
3098
|
+
movement.assetAmount,
|
|
3099
|
+
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
3100
|
+
"historic position"
|
|
3101
|
+
)
|
|
3102
|
+
}
|
|
3103
|
+
],
|
|
3104
|
+
rebalanceLog: []
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
function mapYieldseekerHistory(contexts, options) {
|
|
3108
|
+
const entries = contexts.flatMap((context) => {
|
|
3109
|
+
const vaultAddresses = new Set(
|
|
3110
|
+
context.positions.map(
|
|
3111
|
+
(position2) => position2.yieldOption.address.toLowerCase()
|
|
3112
|
+
)
|
|
3113
|
+
);
|
|
3114
|
+
return [
|
|
3115
|
+
...(context.historic?.movements ?? []).map(
|
|
3116
|
+
(movement) => movementEntry(
|
|
3117
|
+
movement,
|
|
3118
|
+
context.wallet,
|
|
3119
|
+
context.agent,
|
|
3120
|
+
context.asset,
|
|
3121
|
+
options.ownerAddress,
|
|
3122
|
+
vaultAddresses
|
|
3123
|
+
)
|
|
3124
|
+
),
|
|
3125
|
+
...(context.actions ?? []).map(actionEntry)
|
|
3126
|
+
].filter((entry) => entry !== void 0);
|
|
3127
|
+
});
|
|
3128
|
+
const filtered = entries.filter(
|
|
3129
|
+
(entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
|
|
3130
|
+
).filter((entry, index, all) => {
|
|
3131
|
+
const transactionHash = entry.transactions[0]?.txHashes[0];
|
|
3132
|
+
if (!transactionHash) return true;
|
|
3133
|
+
return all.findIndex(
|
|
3134
|
+
(candidate) => candidate.action === entry.action && candidate.transactions[0]?.txHashes[0] === transactionHash
|
|
3135
|
+
) === index;
|
|
3136
|
+
}).sort((left, right) => right.date.localeCompare(left.date));
|
|
3137
|
+
return {
|
|
3138
|
+
data: filtered.slice(0, options.limit),
|
|
3139
|
+
// v1 returns the whole action/movement collection and defines no cursor.
|
|
3140
|
+
// Report a terminal page so callers never loop over the same prefix.
|
|
3141
|
+
hasMore: false
|
|
3142
|
+
};
|
|
3143
|
+
}
|
|
3144
|
+
function mapYieldseekerProfile(address, contexts) {
|
|
3145
|
+
const protocols = /* @__PURE__ */ new Set();
|
|
3146
|
+
for (const context of contexts) {
|
|
3147
|
+
for (const current of context.positions) {
|
|
3148
|
+
if (current.yieldOption?.provider) {
|
|
3149
|
+
protocols.add(String(current.yieldOption.provider));
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
return {
|
|
3154
|
+
address,
|
|
3155
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
3156
|
+
chains: contexts.length > 0 ? [8453] : [],
|
|
3157
|
+
hasActiveSessionKey: contexts.some(
|
|
3158
|
+
(context) => context.wallet.initializedDate != null
|
|
3159
|
+
),
|
|
3160
|
+
protocols: [...protocols]
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
function mapYieldseekerAgentApy(options, days) {
|
|
3164
|
+
const perAsset = {};
|
|
3165
|
+
const all = [];
|
|
3166
|
+
for (const entry of options) {
|
|
3167
|
+
const apys = entry.yieldOptions.map(
|
|
3168
|
+
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
3169
|
+
).filter(Number.isFinite);
|
|
3170
|
+
if (apys.length === 0) continue;
|
|
3171
|
+
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
3172
|
+
perAsset[entry.asset] = average;
|
|
3173
|
+
all.push(average);
|
|
3174
|
+
}
|
|
3175
|
+
return {
|
|
3176
|
+
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
3177
|
+
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
3178
|
+
};
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3182
|
+
var OWNEY_AGENT_NAME = "owney";
|
|
3183
|
+
var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
3184
|
+
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3185
|
+
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3186
|
+
function generateYieldseekerUsername() {
|
|
3187
|
+
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3188
|
+
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
3189
|
+
}
|
|
3190
|
+
function isUsernameConflict(error) {
|
|
3191
|
+
if (!(error instanceof YieldseekerApiError)) return false;
|
|
3192
|
+
const code = error.providerCode.toUpperCase();
|
|
3193
|
+
return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
|
|
3194
|
+
}
|
|
3195
|
+
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
3196
|
+
{
|
|
3197
|
+
type: "function",
|
|
3198
|
+
name: "withdrawAssetToUser",
|
|
3199
|
+
stateMutability: "nonpayable",
|
|
3200
|
+
inputs: [
|
|
3201
|
+
{ name: "recipient", type: "address" },
|
|
3202
|
+
{ name: "asset", type: "address" },
|
|
3203
|
+
{ name: "amount", type: "uint256" }
|
|
3204
|
+
],
|
|
3205
|
+
outputs: []
|
|
3206
|
+
},
|
|
3207
|
+
{
|
|
3208
|
+
type: "function",
|
|
3209
|
+
name: "withdrawAllAssetToUser",
|
|
3210
|
+
stateMutability: "nonpayable",
|
|
3211
|
+
inputs: [
|
|
3212
|
+
{ name: "recipient", type: "address" },
|
|
3213
|
+
{ name: "asset", type: "address" }
|
|
3214
|
+
],
|
|
3215
|
+
outputs: []
|
|
3216
|
+
}
|
|
3217
|
+
];
|
|
3218
|
+
function query(params) {
|
|
3219
|
+
const search = new URLSearchParams();
|
|
3220
|
+
for (const [key2, value] of Object.entries(params)) {
|
|
3221
|
+
if (value !== void 0) search.set(key2, String(value));
|
|
3222
|
+
}
|
|
3223
|
+
const encoded = search.toString();
|
|
3224
|
+
return encoded ? `?${encoded}` : "";
|
|
3225
|
+
}
|
|
3226
|
+
var YieldseekerAgent = class {
|
|
3227
|
+
id = "yieldseeker";
|
|
3228
|
+
balanceComposition = "tokens-plus-positions";
|
|
3229
|
+
supportedChainIds = [8453];
|
|
3230
|
+
supportedAssets = [
|
|
3231
|
+
{
|
|
3232
|
+
chainId: 8453,
|
|
3233
|
+
chain: "BASE",
|
|
3234
|
+
assets: [
|
|
3235
|
+
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
3236
|
+
{ symbol: "WETH", minDepositAmount: "1" }
|
|
3237
|
+
]
|
|
3238
|
+
}
|
|
3239
|
+
];
|
|
3240
|
+
api;
|
|
3241
|
+
auth;
|
|
3242
|
+
transactionExecutor;
|
|
3243
|
+
unwindReceiptWaiter;
|
|
3244
|
+
agentContexts = /* @__PURE__ */ new Map();
|
|
3245
|
+
users = /* @__PURE__ */ new Map();
|
|
3246
|
+
pendingAgents = /* @__PURE__ */ new Map();
|
|
3247
|
+
constructor(owneyApiKey, options = {}) {
|
|
3248
|
+
this.api = new YieldseekerApiClient(
|
|
3249
|
+
owneyApiKey,
|
|
3250
|
+
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
3251
|
+
options.fetchFn
|
|
3252
|
+
);
|
|
3253
|
+
this.auth = new YieldseekerAuth(options.auth);
|
|
3254
|
+
this.transactionExecutor = options.transactionExecutor;
|
|
3255
|
+
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3256
|
+
}
|
|
3257
|
+
async disconnect() {
|
|
3258
|
+
this.auth.clear();
|
|
3259
|
+
for (const key2 of this.users.keys()) {
|
|
3260
|
+
const [walletAddress, chainId] = key2.split(":");
|
|
3261
|
+
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
3262
|
+
}
|
|
3263
|
+
this.users.clear();
|
|
3264
|
+
this.agentContexts.clear();
|
|
3265
|
+
this.pendingAgents.clear();
|
|
3266
|
+
}
|
|
3267
|
+
async activateAgent(state, chainId, asset) {
|
|
3268
|
+
this.assertChain(chainId);
|
|
3269
|
+
const targetAsset = asset ?? "USDC";
|
|
3270
|
+
this.assertAsset(targetAsset);
|
|
3271
|
+
await this.ensureAgent(state, chainId, targetAsset);
|
|
3272
|
+
}
|
|
3273
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3274
|
+
this.assertChain(chainId);
|
|
3275
|
+
this.assertAsset(asset);
|
|
3276
|
+
if (BigInt(amount) <= 0n) {
|
|
3277
|
+
throw new OwneyError(
|
|
3278
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
3279
|
+
"Yieldseeker deposits must be greater than zero.",
|
|
3280
|
+
{ amount, minDepositAmount: "1" },
|
|
3281
|
+
this.id
|
|
3282
|
+
);
|
|
3283
|
+
}
|
|
3284
|
+
const context = await this.ensureAgent(state, chainId, asset);
|
|
3285
|
+
let txHash;
|
|
3286
|
+
try {
|
|
3287
|
+
if (depositCallback) {
|
|
3288
|
+
provideDepositVerificationContext(depositCallback, {
|
|
3289
|
+
agentId: "yieldseeker",
|
|
3290
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3291
|
+
userId: context.user.userId,
|
|
3292
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3293
|
+
});
|
|
3294
|
+
txHash = await depositCallback(
|
|
3295
|
+
context.wallet.walletAddress,
|
|
3296
|
+
chainId,
|
|
3297
|
+
amount
|
|
3298
|
+
);
|
|
3299
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3300
|
+
} else {
|
|
3301
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3302
|
+
from: (0, import_viem6.getAddress)(state.walletAddress),
|
|
3303
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3304
|
+
data: (0, import_viem6.encodeFunctionData)({
|
|
3305
|
+
abi: import_viem6.erc20Abi,
|
|
3306
|
+
functionName: "transfer",
|
|
3307
|
+
args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
3308
|
+
}),
|
|
3309
|
+
value: "0",
|
|
3310
|
+
chainId
|
|
3311
|
+
});
|
|
3312
|
+
}
|
|
3313
|
+
await this.deployAfterFunding(state, chainId, context);
|
|
3314
|
+
} finally {
|
|
3315
|
+
await this.refreshSnapshotAfterMovement(
|
|
3316
|
+
state,
|
|
3317
|
+
chainId,
|
|
3318
|
+
context,
|
|
3319
|
+
"deposit"
|
|
3320
|
+
);
|
|
3321
|
+
}
|
|
3322
|
+
return {
|
|
3323
|
+
txHash,
|
|
3324
|
+
smartWallet: context.wallet.walletAddress,
|
|
3325
|
+
amount
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
async withdraw(state, chainId, asset, amount) {
|
|
3329
|
+
this.assertChain(chainId);
|
|
3330
|
+
this.assertAsset(asset);
|
|
3331
|
+
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
3332
|
+
throw new OwneyError(
|
|
3333
|
+
"WITHDRAW_FAILED",
|
|
3334
|
+
"Yieldseeker withdrawals must be greater than zero.",
|
|
3335
|
+
{ amount },
|
|
3336
|
+
this.id
|
|
3337
|
+
);
|
|
3338
|
+
}
|
|
3339
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
3340
|
+
if (!context) {
|
|
3341
|
+
throw new OwneyError(
|
|
3342
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3343
|
+
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3344
|
+
{ asset, available: "0" },
|
|
3345
|
+
this.id
|
|
3346
|
+
);
|
|
3347
|
+
}
|
|
3348
|
+
try {
|
|
3349
|
+
const portfolio = await this.loadPortfolioContext(
|
|
3350
|
+
state,
|
|
3351
|
+
chainId,
|
|
3352
|
+
context
|
|
3353
|
+
);
|
|
3354
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3355
|
+
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3356
|
+
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3357
|
+
);
|
|
3358
|
+
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3359
|
+
const deployed = portfolio.positions.reduce(
|
|
3360
|
+
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3361
|
+
0n
|
|
3362
|
+
);
|
|
3363
|
+
const totalAvailable = idle + deployed;
|
|
3364
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3365
|
+
if (requested > totalAvailable) {
|
|
3366
|
+
throw new OwneyError(
|
|
3367
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3368
|
+
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3369
|
+
{
|
|
3370
|
+
asset,
|
|
3371
|
+
requested: requested.toString(),
|
|
3372
|
+
available: totalAvailable.toString()
|
|
3373
|
+
},
|
|
3374
|
+
this.id
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3377
|
+
let remaining = requested > idle ? requested - idle : 0n;
|
|
3378
|
+
for (const position2 of portfolio.positions) {
|
|
3379
|
+
if (remaining === 0n) break;
|
|
3380
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3381
|
+
if (available <= 0n) continue;
|
|
3382
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
3383
|
+
const response = await this.walletRequest(
|
|
3384
|
+
state,
|
|
3385
|
+
chainId,
|
|
3386
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
3387
|
+
{
|
|
3388
|
+
method: "POST",
|
|
3389
|
+
body: {
|
|
3390
|
+
chainId,
|
|
3391
|
+
vaultAddress: position2.yieldOption.address,
|
|
3392
|
+
assetsRaw: assetsRaw.toString()
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
);
|
|
3396
|
+
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3397
|
+
throw this.invalidResponse("position withdrawal");
|
|
3398
|
+
}
|
|
3399
|
+
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3400
|
+
remaining -= assetsRaw;
|
|
3401
|
+
}
|
|
3402
|
+
if (remaining > 0n) {
|
|
3403
|
+
throw this.invalidResponse("yield positions", {
|
|
3404
|
+
reason: "Withdrawable positions could not cover the request.",
|
|
3405
|
+
remaining: remaining.toString()
|
|
3406
|
+
});
|
|
3407
|
+
}
|
|
3408
|
+
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3409
|
+
const txHash = await this.submitTransaction(state, chainId, {
|
|
3410
|
+
from: account,
|
|
3411
|
+
to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
|
|
3412
|
+
data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
|
|
3413
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3414
|
+
functionName: "withdrawAllAssetToUser",
|
|
3415
|
+
args: [account, metadata.address]
|
|
3416
|
+
}) : (0, import_viem6.encodeFunctionData)({
|
|
3417
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3418
|
+
functionName: "withdrawAssetToUser",
|
|
3419
|
+
args: [account, metadata.address, requested]
|
|
3420
|
+
}),
|
|
3421
|
+
value: "0",
|
|
3422
|
+
chainId
|
|
3423
|
+
});
|
|
3424
|
+
return {
|
|
3425
|
+
txHash,
|
|
3426
|
+
type: amount === void 0 ? "full" : "partial",
|
|
3427
|
+
amount: requested.toString()
|
|
3428
|
+
};
|
|
3429
|
+
} finally {
|
|
3430
|
+
await this.refreshSnapshotAfterMovement(
|
|
3431
|
+
state,
|
|
3432
|
+
chainId,
|
|
3433
|
+
context,
|
|
3434
|
+
"withdrawal"
|
|
3435
|
+
);
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
async getBalances(state, chainId) {
|
|
3439
|
+
this.assertChain(chainId);
|
|
3440
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3441
|
+
}
|
|
3442
|
+
async getEarnings(state, chainId) {
|
|
3443
|
+
this.assertChain(chainId);
|
|
3444
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3445
|
+
}
|
|
3446
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3447
|
+
this.assertChain(chainId);
|
|
3448
|
+
const asset = tokenSymbol?.toUpperCase();
|
|
3449
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3450
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3451
|
+
...asset ? { asset } : {},
|
|
3452
|
+
historic: true
|
|
3453
|
+
});
|
|
3454
|
+
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3455
|
+
}
|
|
3456
|
+
async getHistory(state, chainId, options) {
|
|
3457
|
+
this.assertChain(chainId);
|
|
3458
|
+
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3459
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3460
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3461
|
+
...asset ? { asset } : {},
|
|
3462
|
+
historic: true,
|
|
3463
|
+
actions: true
|
|
3464
|
+
});
|
|
3465
|
+
return mapYieldseekerHistory(contexts, {
|
|
3466
|
+
limit: options?.limit ?? 10,
|
|
3467
|
+
ownerAddress: state.walletAddress,
|
|
3468
|
+
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3469
|
+
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3470
|
+
});
|
|
3471
|
+
}
|
|
3472
|
+
async getUserProfile(state, chainId) {
|
|
3473
|
+
this.assertChain(chainId);
|
|
3474
|
+
return mapYieldseekerProfile(
|
|
3475
|
+
state.walletAddress,
|
|
3476
|
+
await this.loadPortfolio(state, chainId, {})
|
|
3477
|
+
);
|
|
3478
|
+
}
|
|
3479
|
+
async getAgentApy(days, options) {
|
|
3480
|
+
this.assertOptionalChain(options?.chainId);
|
|
3481
|
+
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3482
|
+
if (requested !== void 0) this.assertAsset(requested);
|
|
3483
|
+
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3484
|
+
const values = await Promise.all(
|
|
3485
|
+
assets.map(async (asset) => {
|
|
3486
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3487
|
+
const response = await this.api.request(
|
|
3488
|
+
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3489
|
+
);
|
|
3490
|
+
if (!Array.isArray(response?.yieldOptions)) {
|
|
3491
|
+
throw this.invalidResponse("yield options");
|
|
3492
|
+
}
|
|
3493
|
+
return { asset, yieldOptions: response.yieldOptions };
|
|
3494
|
+
})
|
|
3495
|
+
);
|
|
3496
|
+
return mapYieldseekerAgentApy(values, days);
|
|
3497
|
+
}
|
|
3498
|
+
userKey(state, chainId) {
|
|
3499
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3500
|
+
}
|
|
3501
|
+
contextKey(state, chainId, asset) {
|
|
3502
|
+
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3503
|
+
}
|
|
3504
|
+
async resolveUser(state, chainId) {
|
|
3505
|
+
const key2 = this.userKey(state, chainId);
|
|
3506
|
+
const inMemory = this.users.get(key2);
|
|
3507
|
+
if (inMemory) return inMemory;
|
|
3508
|
+
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3509
|
+
if (persisted) {
|
|
3510
|
+
this.users.set(key2, persisted);
|
|
3511
|
+
return persisted;
|
|
3512
|
+
}
|
|
3513
|
+
const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3514
|
+
let user = null;
|
|
3515
|
+
try {
|
|
3516
|
+
const login = await this.providerRequest(
|
|
3517
|
+
state,
|
|
3518
|
+
chainId,
|
|
3519
|
+
"/users/login-with-wallet",
|
|
3520
|
+
{ method: "POST", body: { walletAddress } }
|
|
3521
|
+
);
|
|
3522
|
+
user = login?.user ?? null;
|
|
3523
|
+
if (!user) {
|
|
3524
|
+
throw this.invalidResponse("wallet login", {
|
|
3525
|
+
reason: "A successful login returned no user."
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
} catch (error) {
|
|
3529
|
+
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3530
|
+
if (error instanceof OwneyError) throw error;
|
|
3531
|
+
throw this.mapApiError(error);
|
|
3532
|
+
}
|
|
3533
|
+
let created;
|
|
3534
|
+
for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
|
|
3535
|
+
try {
|
|
3536
|
+
created = await this.providerRequest(
|
|
3537
|
+
state,
|
|
3538
|
+
chainId,
|
|
3539
|
+
"/users",
|
|
3540
|
+
{
|
|
3541
|
+
method: "POST",
|
|
3542
|
+
body: {
|
|
3543
|
+
walletAddress,
|
|
3544
|
+
username: generateYieldseekerUsername()
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
);
|
|
3548
|
+
break;
|
|
3549
|
+
} catch (createError) {
|
|
3550
|
+
const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
|
|
3551
|
+
if (canRetry) continue;
|
|
3552
|
+
throw this.mapApiError(createError);
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
user = created?.user ?? null;
|
|
3556
|
+
}
|
|
3557
|
+
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3558
|
+
throw this.invalidResponse("wallet identity");
|
|
3559
|
+
}
|
|
3560
|
+
const resolved = { userId: user.userId };
|
|
3561
|
+
this.users.set(key2, resolved);
|
|
3562
|
+
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3563
|
+
return resolved;
|
|
3564
|
+
}
|
|
3565
|
+
forgetUser(state, chainId) {
|
|
3566
|
+
this.users.delete(this.userKey(state, chainId));
|
|
3567
|
+
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3568
|
+
}
|
|
3569
|
+
async ensureAgent(state, chainId, asset) {
|
|
3570
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3571
|
+
const cached = this.agentContexts.get(key2);
|
|
3572
|
+
if (cached) return cached;
|
|
3573
|
+
const pending = this.pendingAgents.get(key2);
|
|
3574
|
+
if (pending) return pending;
|
|
3575
|
+
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3576
|
+
(context) => {
|
|
3577
|
+
if (!context) throw this.invalidResponse("agent creation");
|
|
3578
|
+
this.agentContexts.set(key2, context);
|
|
3579
|
+
return context;
|
|
3580
|
+
}
|
|
3581
|
+
);
|
|
3582
|
+
this.pendingAgents.set(key2, request);
|
|
3583
|
+
try {
|
|
3584
|
+
return await request;
|
|
3585
|
+
} finally {
|
|
3586
|
+
this.pendingAgents.delete(key2);
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
async findAgent(state, chainId, asset) {
|
|
3590
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3591
|
+
const cached = this.agentContexts.get(key2);
|
|
3592
|
+
if (cached) return cached;
|
|
3593
|
+
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3594
|
+
if (context) this.agentContexts.set(key2, context);
|
|
3595
|
+
return context;
|
|
3596
|
+
}
|
|
3597
|
+
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3598
|
+
const user = await this.resolveUser(state, chainId);
|
|
3599
|
+
const response = await this.walletRequest(
|
|
3600
|
+
state,
|
|
3601
|
+
chainId,
|
|
3602
|
+
`/users/${user.userId}/agents`
|
|
3603
|
+
);
|
|
3604
|
+
if (!Array.isArray(response?.agents)) {
|
|
3605
|
+
throw this.invalidResponse("agent list");
|
|
3606
|
+
}
|
|
3607
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3608
|
+
let agent = response.agents.find(
|
|
3609
|
+
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3610
|
+
);
|
|
3611
|
+
if (!agent && createIfMissing) {
|
|
3612
|
+
const created = await this.walletRequest(
|
|
3613
|
+
state,
|
|
3614
|
+
chainId,
|
|
3615
|
+
`/users/${user.userId}/agents`,
|
|
3616
|
+
{
|
|
3617
|
+
method: "POST",
|
|
3618
|
+
body: {
|
|
3619
|
+
name: OWNEY_AGENT_NAME,
|
|
3620
|
+
emoji: "\u{1F989}",
|
|
3621
|
+
chainId,
|
|
3622
|
+
assetAddress: metadata.address,
|
|
3623
|
+
type: "vault",
|
|
3624
|
+
rulePreset: null
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
);
|
|
3628
|
+
agent = created?.agent;
|
|
3629
|
+
}
|
|
3630
|
+
if (!agent) return null;
|
|
3631
|
+
this.assertAgent(agent);
|
|
3632
|
+
const walletResponse = await this.walletRequest(
|
|
3633
|
+
state,
|
|
3634
|
+
chainId,
|
|
3635
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3636
|
+
);
|
|
3637
|
+
if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3638
|
+
throw this.invalidResponse("agent wallet");
|
|
3639
|
+
}
|
|
3640
|
+
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3641
|
+
}
|
|
3642
|
+
async loadPortfolio(state, chainId, options) {
|
|
3643
|
+
const user = await this.resolveUser(state, chainId);
|
|
3644
|
+
const response = await this.walletRequest(
|
|
3645
|
+
state,
|
|
3646
|
+
chainId,
|
|
3647
|
+
`/users/${user.userId}/agents`
|
|
3648
|
+
);
|
|
3649
|
+
if (!Array.isArray(response?.agents)) {
|
|
3650
|
+
throw this.invalidResponse("agent list");
|
|
3651
|
+
}
|
|
3652
|
+
const contexts = [];
|
|
3653
|
+
for (const agent of response.agents) {
|
|
3654
|
+
const asset = this.assetForAgent(agent);
|
|
3655
|
+
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3656
|
+
continue;
|
|
3657
|
+
}
|
|
3658
|
+
this.assertAgent(agent);
|
|
3659
|
+
const walletResponse = await this.walletRequest(
|
|
3660
|
+
state,
|
|
3661
|
+
chainId,
|
|
3662
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3663
|
+
);
|
|
3664
|
+
if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3665
|
+
throw this.invalidResponse("agent wallet");
|
|
3666
|
+
}
|
|
3667
|
+
const context = {
|
|
3668
|
+
user,
|
|
3669
|
+
agent,
|
|
3670
|
+
wallet: walletResponse.agentWallet,
|
|
3671
|
+
asset
|
|
3672
|
+
};
|
|
3673
|
+
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3674
|
+
contexts.push(context);
|
|
3675
|
+
}
|
|
3676
|
+
return Promise.all(
|
|
3677
|
+
contexts.map(
|
|
3678
|
+
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3679
|
+
)
|
|
3680
|
+
);
|
|
3681
|
+
}
|
|
3682
|
+
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3683
|
+
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3684
|
+
this.walletRequest(
|
|
3685
|
+
state,
|
|
3686
|
+
chainId,
|
|
3687
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3688
|
+
shouldOnlyUseRecentValue: true,
|
|
3689
|
+
shouldAllowStaleOnError: true
|
|
3690
|
+
})}`
|
|
3691
|
+
),
|
|
3692
|
+
this.walletRequest(
|
|
3693
|
+
state,
|
|
3694
|
+
chainId,
|
|
3695
|
+
this.agentPath(context, "yield-positions")
|
|
3696
|
+
),
|
|
3697
|
+
options.historic ? this.walletRequest(
|
|
3698
|
+
state,
|
|
3699
|
+
chainId,
|
|
3700
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3701
|
+
) : Promise.resolve(void 0),
|
|
3702
|
+
options.actions ? this.walletRequest(
|
|
3703
|
+
state,
|
|
3704
|
+
chainId,
|
|
3705
|
+
this.agentPath(context, "actions")
|
|
3706
|
+
) : Promise.resolve(void 0)
|
|
3707
|
+
]);
|
|
3708
|
+
if (!snapshot?.agentSnapshot) {
|
|
3709
|
+
throw this.invalidResponse("agent snapshot");
|
|
3710
|
+
}
|
|
3711
|
+
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3712
|
+
throw this.invalidResponse("yield positions");
|
|
3713
|
+
}
|
|
3714
|
+
return {
|
|
3715
|
+
...context,
|
|
3716
|
+
snapshot: snapshot.agentSnapshot,
|
|
3717
|
+
positions: positions.yieldPositions,
|
|
3718
|
+
...historic?.position ? { historic: historic.position } : {},
|
|
3719
|
+
...actions?.actions ? { actions: actions.actions } : {}
|
|
3720
|
+
};
|
|
3721
|
+
}
|
|
3722
|
+
async deployAfterFunding(state, chainId, context) {
|
|
3723
|
+
if (context.wallet.initializedDate != null) return;
|
|
3724
|
+
try {
|
|
3725
|
+
const deployed = await this.walletRequest(
|
|
3726
|
+
state,
|
|
3727
|
+
chainId,
|
|
3728
|
+
this.agentPath(context, "deploy"),
|
|
3729
|
+
{ method: "POST", body: {} }
|
|
3730
|
+
);
|
|
3731
|
+
if (deployed?.agentWallet) {
|
|
3732
|
+
context.wallet = deployed.agentWallet;
|
|
3733
|
+
}
|
|
3734
|
+
} catch (error) {
|
|
3735
|
+
console.warn(
|
|
3736
|
+
"[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
|
|
3737
|
+
error
|
|
3738
|
+
);
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3742
|
+
try {
|
|
3743
|
+
const response = await this.walletRequest(
|
|
3744
|
+
state,
|
|
3745
|
+
chainId,
|
|
3746
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3747
|
+
shouldForceRefresh: true
|
|
3748
|
+
})}`
|
|
3749
|
+
);
|
|
3750
|
+
if (!response?.agentSnapshot) {
|
|
3751
|
+
throw this.invalidResponse("agent snapshot refresh");
|
|
3752
|
+
}
|
|
3753
|
+
} catch (error) {
|
|
3754
|
+
console.warn(
|
|
3755
|
+
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3756
|
+
error
|
|
3757
|
+
);
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
agentPath(context, suffix) {
|
|
3761
|
+
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
3762
|
+
}
|
|
3763
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
3764
|
+
try {
|
|
3765
|
+
return await this.providerRequest(state, chainId, path, options);
|
|
3766
|
+
} catch (error) {
|
|
3767
|
+
throw this.mapApiError(error);
|
|
3768
|
+
}
|
|
3769
|
+
}
|
|
3770
|
+
async providerRequest(state, chainId, path, options = {}) {
|
|
3771
|
+
this.assertChain(chainId);
|
|
3772
|
+
const request = (signature2) => this.api.request(path, {
|
|
3773
|
+
...options,
|
|
3774
|
+
signature: signature2
|
|
3775
|
+
});
|
|
3776
|
+
let signature = await this.auth.getToken(state, chainId);
|
|
3777
|
+
try {
|
|
3778
|
+
return await request(signature);
|
|
3779
|
+
} catch (error) {
|
|
3780
|
+
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
3781
|
+
if (error.providerCode === "NO_USER") throw error;
|
|
3782
|
+
if (!error.isAuthenticationError) throw error;
|
|
3783
|
+
this.auth.clear(state, chainId);
|
|
3784
|
+
signature = await this.auth.getToken(state, chainId);
|
|
3785
|
+
try {
|
|
3786
|
+
return await request(signature);
|
|
3787
|
+
} catch (retryError) {
|
|
3788
|
+
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3789
|
+
this.forgetUser(state, chainId);
|
|
3790
|
+
}
|
|
3791
|
+
throw retryError;
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
mapApiError(error) {
|
|
3796
|
+
if (!(error instanceof YieldseekerApiError)) {
|
|
3797
|
+
return new OwneyError(
|
|
3798
|
+
"AGENT_API_ERROR",
|
|
3799
|
+
"Yieldseeker request failed.",
|
|
3800
|
+
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3801
|
+
this.id
|
|
3802
|
+
);
|
|
3803
|
+
}
|
|
3804
|
+
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3805
|
+
return new OwneyError(
|
|
3806
|
+
code,
|
|
3807
|
+
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3808
|
+
{
|
|
3809
|
+
statusCode: error.status,
|
|
3810
|
+
providerCode: error.providerCode,
|
|
3811
|
+
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3812
|
+
},
|
|
3813
|
+
this.id
|
|
3814
|
+
);
|
|
3815
|
+
}
|
|
3816
|
+
async submitTransaction(state, chainId, transaction) {
|
|
3817
|
+
if (this.transactionExecutor) {
|
|
3818
|
+
return this.transactionExecutor(state, chainId, transaction);
|
|
3819
|
+
}
|
|
3820
|
+
this.assertTransaction(transaction, state, chainId);
|
|
3821
|
+
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3822
|
+
const walletClient = (0, import_viem6.createWalletClient)({
|
|
3823
|
+
account,
|
|
3824
|
+
chain: import_chains3.base,
|
|
3825
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3826
|
+
});
|
|
3827
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3828
|
+
chain: import_chains3.base,
|
|
3829
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3830
|
+
});
|
|
3831
|
+
await ensureWalletOnChain(
|
|
3832
|
+
publicClient,
|
|
3833
|
+
walletClient,
|
|
3834
|
+
8453
|
|
3835
|
+
);
|
|
3836
|
+
const hash = await walletClient.sendTransaction({
|
|
3837
|
+
account,
|
|
3838
|
+
chain: import_chains3.base,
|
|
3839
|
+
to: (0, import_viem6.getAddress)(transaction.to),
|
|
3840
|
+
data: transaction.data,
|
|
3841
|
+
value: BigInt(transaction.value)
|
|
3842
|
+
});
|
|
3843
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3844
|
+
hash,
|
|
3845
|
+
confirmations: 1
|
|
3846
|
+
});
|
|
3847
|
+
if (receipt.status !== "success") {
|
|
3848
|
+
throw new OwneyError(
|
|
3849
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3850
|
+
`Yieldseeker transaction reverted (${hash}).`,
|
|
3851
|
+
{ transactionHash: hash },
|
|
3852
|
+
this.id
|
|
3853
|
+
);
|
|
3854
|
+
}
|
|
3855
|
+
return hash;
|
|
3856
|
+
}
|
|
3857
|
+
async waitForReceipt(state, chainId, transactionHash) {
|
|
3858
|
+
if (this.unwindReceiptWaiter) {
|
|
3859
|
+
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3860
|
+
return;
|
|
3861
|
+
}
|
|
3862
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3863
|
+
chain: import_chains3.base,
|
|
3864
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3865
|
+
});
|
|
3866
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3867
|
+
hash: transactionHash,
|
|
3868
|
+
confirmations: 1
|
|
3869
|
+
});
|
|
3870
|
+
if (receipt.status !== "success") {
|
|
3871
|
+
throw new OwneyError(
|
|
3872
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3873
|
+
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3874
|
+
{ transactionHash },
|
|
3875
|
+
this.id
|
|
3876
|
+
);
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
assertTransaction(transaction, state, chainId) {
|
|
3880
|
+
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem6.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem6.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem6.getAddress)(transaction.from) !== (0, import_viem6.getAddress)(state.walletAddress)) {
|
|
3881
|
+
throw this.invalidResponse("transaction");
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
assertAgent(agent) {
|
|
3885
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3886
|
+
throw this.invalidResponse("agent");
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
isOwneyAgent(agent) {
|
|
3890
|
+
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3891
|
+
}
|
|
3892
|
+
assetForAgent(agent) {
|
|
3893
|
+
for (const asset of ["USDC", "WETH"]) {
|
|
3894
|
+
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3895
|
+
return asset;
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
return null;
|
|
3899
|
+
}
|
|
3900
|
+
isTransactionHash(value) {
|
|
3901
|
+
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3902
|
+
}
|
|
3903
|
+
assertChain(chainId) {
|
|
3904
|
+
if (chainId !== 8453) {
|
|
3905
|
+
throw new OwneyError(
|
|
3906
|
+
"CHAIN_UNSUPPORTED",
|
|
3907
|
+
`Yieldseeker does not support chain ${chainId}.`,
|
|
3908
|
+
{ chainId, supportedChainIds: [8453] },
|
|
3909
|
+
this.id
|
|
3910
|
+
);
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
assertOptionalChain(chainId) {
|
|
3914
|
+
if (chainId !== void 0) this.assertChain(chainId);
|
|
3915
|
+
}
|
|
3916
|
+
assertAsset(asset) {
|
|
3917
|
+
if (asset !== "USDC" && asset !== "WETH") {
|
|
3918
|
+
throw new OwneyError(
|
|
3919
|
+
"ASSET_UNSUPPORTED",
|
|
3920
|
+
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
3921
|
+
{
|
|
3922
|
+
asset,
|
|
3923
|
+
supportedAssets: ["USDC", "WETH"],
|
|
3924
|
+
providerAlsoAdvertises: ["cbBTC"]
|
|
3925
|
+
},
|
|
3926
|
+
this.id
|
|
3927
|
+
);
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
invalidResponse(operation, details = {}) {
|
|
3931
|
+
return new OwneyError(
|
|
3932
|
+
"AGENT_INVALID_RESPONSE",
|
|
3933
|
+
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3934
|
+
details,
|
|
3935
|
+
this.id
|
|
3936
|
+
);
|
|
3937
|
+
}
|
|
3938
|
+
};
|
|
3939
|
+
|
|
3940
|
+
// src/lib/routing-api.ts
|
|
3941
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3942
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3943
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3944
|
+
try {
|
|
3945
|
+
const res = await fetch(url, {
|
|
3946
|
+
method: "GET",
|
|
3947
|
+
headers: {
|
|
3948
|
+
"Content-Type": "application/json",
|
|
3949
|
+
"x-owney-api-key": `${apiKey}`
|
|
3950
|
+
}
|
|
3951
|
+
});
|
|
3952
|
+
if (!res.ok) {
|
|
3953
|
+
if (res.status !== 404) {
|
|
3954
|
+
console.warn(
|
|
3955
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
3956
|
+
);
|
|
3957
|
+
}
|
|
3958
|
+
return null;
|
|
3959
|
+
}
|
|
3960
|
+
const json = await res.json();
|
|
3961
|
+
const policy = json.success ? json.data ?? null : null;
|
|
3962
|
+
debugLog(
|
|
3963
|
+
"owney-sdk",
|
|
3964
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
3965
|
+
policy ?? void 0
|
|
3966
|
+
);
|
|
3967
|
+
return policy;
|
|
3968
|
+
} catch (error) {
|
|
3969
|
+
console.warn(
|
|
3970
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
3971
|
+
error instanceof Error ? error.message : String(error)
|
|
3972
|
+
);
|
|
3973
|
+
return null;
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3977
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
3978
|
+
const res = await fetch(url, {
|
|
3979
|
+
method: "GET",
|
|
3980
|
+
headers: {
|
|
3981
|
+
"Content-Type": "application/json",
|
|
3982
|
+
"x-owney-api-key": `${apiKey}`
|
|
3983
|
+
}
|
|
3984
|
+
});
|
|
3985
|
+
if (!res.ok) {
|
|
3986
|
+
const text = await res.text().catch(() => "");
|
|
3987
|
+
throw new OwneyError(
|
|
3988
|
+
"API_ROUTING_ERROR",
|
|
3989
|
+
`Routing API error ${res.status}: ${text}`,
|
|
3990
|
+
{ statusCode: res.status, responseBody: text }
|
|
3991
|
+
);
|
|
3992
|
+
}
|
|
3993
|
+
const json = await res.json();
|
|
3994
|
+
if (!json.success) {
|
|
3995
|
+
throw new OwneyError(
|
|
3996
|
+
"API_ROUTING_FAILED",
|
|
3997
|
+
`Routing API request failed: ${json.message}`,
|
|
3998
|
+
{ message: json.message }
|
|
3999
|
+
);
|
|
4000
|
+
}
|
|
4001
|
+
return json.data;
|
|
4002
|
+
}
|
|
4003
|
+
|
|
4004
|
+
// src/lib/health-report.ts
|
|
4005
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
4006
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
4007
|
+
try {
|
|
4008
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
4009
|
+
method: "POST",
|
|
4010
|
+
headers: {
|
|
4011
|
+
"Content-Type": "application/json",
|
|
4012
|
+
"x-owney-api-key": apiKey
|
|
4013
|
+
},
|
|
4014
|
+
body: JSON.stringify({
|
|
4015
|
+
agent_type: agentType,
|
|
4016
|
+
error_code: errorCode,
|
|
4017
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4018
|
+
})
|
|
4019
|
+
});
|
|
4020
|
+
} catch (err) {
|
|
4021
|
+
console.warn(
|
|
4022
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
4023
|
+
err instanceof Error ? err.message : err
|
|
4024
|
+
);
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
4028
|
+
try {
|
|
4029
|
+
return await fn();
|
|
4030
|
+
} catch (err) {
|
|
4031
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
4032
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
4033
|
+
throw err;
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
4038
|
+
var import_viem7 = require("viem");
|
|
4039
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
4040
|
+
const target = asset.toUpperCase();
|
|
4041
|
+
return agents.map((agent) => {
|
|
4042
|
+
const agentBalance = aggregated[agent.id];
|
|
4043
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
4044
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4045
|
+
);
|
|
4046
|
+
let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
4047
|
+
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
4048
|
+
const chainNameById = {
|
|
4049
|
+
1: "ETHEREUM",
|
|
4050
|
+
8453: "BASE",
|
|
4051
|
+
42161: "ARBITRUM"
|
|
4052
|
+
};
|
|
4053
|
+
const targetChain = chainNameById[chainId];
|
|
4054
|
+
for (const position2 of agentBalance?.positions ?? []) {
|
|
4055
|
+
const positionChain = position2.chain.trim().toUpperCase();
|
|
4056
|
+
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
4057
|
+
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
4058
|
+
if (position2.amountRaw !== void 0) {
|
|
4059
|
+
try {
|
|
4060
|
+
balance += BigInt(position2.amountRaw);
|
|
4061
|
+
continue;
|
|
4062
|
+
} catch {
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
return { agent, balance };
|
|
4069
|
+
});
|
|
4070
|
+
}
|
|
4071
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
4072
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
4073
|
+
agent,
|
|
4074
|
+
balance,
|
|
4075
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
4076
|
+
}));
|
|
4077
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
4078
|
+
let remainder = requested - assigned;
|
|
4079
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
4080
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
4081
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2485
4082
|
});
|
|
4083
|
+
for (const p of byHeadroom) {
|
|
4084
|
+
if (remainder === 0n) break;
|
|
4085
|
+
const headroom = p.balance - p.planned;
|
|
4086
|
+
if (headroom <= 0n) continue;
|
|
4087
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
4088
|
+
p.planned += take;
|
|
4089
|
+
remainder -= take;
|
|
4090
|
+
}
|
|
4091
|
+
return plans;
|
|
2486
4092
|
}
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
}
|
|
2494
|
-
|
|
2495
|
-
|
|
4093
|
+
function planDisabledDrain(disabled, requested) {
|
|
4094
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
4095
|
+
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
4096
|
+
);
|
|
4097
|
+
const plans = [];
|
|
4098
|
+
let remaining = requested;
|
|
4099
|
+
for (const { agent, balance } of sorted) {
|
|
4100
|
+
if (remaining === 0n) {
|
|
4101
|
+
plans.push({ agent, balance, planned: 0n });
|
|
4102
|
+
continue;
|
|
4103
|
+
}
|
|
4104
|
+
const take = balance < remaining ? balance : remaining;
|
|
4105
|
+
plans.push({ agent, balance, planned: take });
|
|
4106
|
+
remaining -= take;
|
|
4107
|
+
}
|
|
4108
|
+
return { plans, remaining };
|
|
2496
4109
|
}
|
|
2497
|
-
|
|
2498
|
-
const
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
4110
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
4111
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
4112
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
4113
|
+
const totalHeadroom = candidates.reduce(
|
|
4114
|
+
(s, c) => s + (c.balance - c.planned),
|
|
4115
|
+
0n
|
|
4116
|
+
);
|
|
4117
|
+
if (totalHeadroom === 0n) return;
|
|
4118
|
+
let distributed = 0n;
|
|
4119
|
+
for (const c of candidates) {
|
|
4120
|
+
const headroom = c.balance - c.planned;
|
|
4121
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
4122
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
4123
|
+
c.planned += give;
|
|
4124
|
+
distributed += give;
|
|
2512
4125
|
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
4126
|
+
let leftover = amount - distributed;
|
|
4127
|
+
for (const c of candidates) {
|
|
4128
|
+
if (leftover === 0n) break;
|
|
4129
|
+
const headroom = c.balance - c.planned;
|
|
4130
|
+
if (headroom <= 0n) continue;
|
|
4131
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
4132
|
+
c.planned += take;
|
|
4133
|
+
leftover -= take;
|
|
2520
4134
|
}
|
|
2521
4135
|
}
|
|
4136
|
+
function sumWithdrawnAmount(results) {
|
|
4137
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
4138
|
+
}
|
|
2522
4139
|
|
|
2523
|
-
// src/lib/
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
4140
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
4141
|
+
function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
4142
|
+
if (!tokenSymbol) {
|
|
4143
|
+
const total = Number(balance.totalBalance);
|
|
4144
|
+
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
4145
|
+
}
|
|
4146
|
+
const normalizedToken = tokenSymbol.toUpperCase();
|
|
4147
|
+
return balance.tokens.reduce((total, token) => {
|
|
4148
|
+
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
4149
|
+
return total;
|
|
4150
|
+
}
|
|
4151
|
+
const amount = Number(token.amount);
|
|
4152
|
+
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
4153
|
+
}, 0);
|
|
4154
|
+
}
|
|
4155
|
+
function aggregateApyHistory(agentApys, agentBalances) {
|
|
4156
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
4157
|
+
for (const [id, accountApy] of Object.entries(agentApys)) {
|
|
4158
|
+
const balance = agentBalances[id] ?? 0;
|
|
4159
|
+
if (!Number.isFinite(balance) || balance <= 0) continue;
|
|
4160
|
+
for (const point of accountApy.history ?? []) {
|
|
4161
|
+
const apy = Number(point.apy);
|
|
4162
|
+
if (!point.date || !Number.isFinite(apy)) continue;
|
|
4163
|
+
const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
|
|
4164
|
+
current.weightedSum += apy * balance;
|
|
4165
|
+
current.weight += balance;
|
|
4166
|
+
byDate.set(point.date, current);
|
|
2535
4167
|
}
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
4168
|
+
}
|
|
4169
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
4170
|
+
date,
|
|
4171
|
+
apy: value.weightedSum / value.weight
|
|
4172
|
+
}));
|
|
4173
|
+
}
|
|
4174
|
+
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
4175
|
+
const sums = {};
|
|
4176
|
+
const weights = {};
|
|
4177
|
+
for (const id of Object.keys(agentApys)) {
|
|
4178
|
+
const cells = agentApys[id].apyByChainAndAsset;
|
|
4179
|
+
const balance = agentBalances[id] ?? 0;
|
|
4180
|
+
if (!cells || balance <= 0) continue;
|
|
4181
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
4182
|
+
if (!perAsset) continue;
|
|
4183
|
+
const chainId = Number(chainKey);
|
|
4184
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
4185
|
+
const apy = Number(apyValue ?? 0);
|
|
4186
|
+
if (apy === 0) continue;
|
|
4187
|
+
sums[chainId] ??= {};
|
|
4188
|
+
weights[chainId] ??= {};
|
|
4189
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
4190
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2547
4191
|
}
|
|
2548
|
-
} catch (err) {
|
|
2549
|
-
if (err instanceof OwneyError) throw err;
|
|
2550
|
-
console.warn(
|
|
2551
|
-
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2552
|
-
err instanceof Error ? err.message : String(err)
|
|
2553
|
-
);
|
|
2554
4192
|
}
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
const
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
value: BigInt(amount),
|
|
2570
|
-
validAfter,
|
|
2571
|
-
validBefore,
|
|
2572
|
-
nonce
|
|
2573
|
-
}
|
|
2574
|
-
});
|
|
2575
|
-
const authSignature = await wallet.signTypedData({
|
|
2576
|
-
account: deps.ownerAddress,
|
|
2577
|
-
...typedData
|
|
2578
|
-
});
|
|
2579
|
-
deps.onApproved?.();
|
|
2580
|
-
const result = await post({
|
|
2581
|
-
baseUrl: deps.baseUrl,
|
|
2582
|
-
apiKey: deps.apiKey,
|
|
2583
|
-
body: {
|
|
2584
|
-
chainId: cid,
|
|
2585
|
-
token,
|
|
2586
|
-
from: deps.ownerAddress,
|
|
2587
|
-
to: smartWallet,
|
|
2588
|
-
value: amount,
|
|
2589
|
-
validAfter: validAfter.toString(),
|
|
2590
|
-
validBefore: validBefore.toString(),
|
|
2591
|
-
nonce,
|
|
2592
|
-
authSignature,
|
|
2593
|
-
tokenName,
|
|
2594
|
-
tokenVersion
|
|
2595
|
-
}
|
|
2596
|
-
});
|
|
2597
|
-
return result.txHash;
|
|
2598
|
-
};
|
|
4193
|
+
}
|
|
4194
|
+
const out = {};
|
|
4195
|
+
for (const chainKey of Object.keys(sums)) {
|
|
4196
|
+
const chainId = Number(chainKey);
|
|
4197
|
+
const perAssetOut = {};
|
|
4198
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
4199
|
+
const w = weights[chainId][asset];
|
|
4200
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
4201
|
+
}
|
|
4202
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
4203
|
+
out[chainId] = perAssetOut;
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
return out;
|
|
2599
4207
|
}
|
|
2600
4208
|
|
|
4209
|
+
// src/client.ts
|
|
4210
|
+
var import_viem9 = require("viem");
|
|
4211
|
+
var import_chains4 = require("viem/chains");
|
|
4212
|
+
|
|
2601
4213
|
// src/lib/sponsored-weth-deposit.ts
|
|
2602
4214
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2603
4215
|
function makeSponsoredWethCallback(deps) {
|
|
2604
4216
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
2605
4217
|
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
2606
|
-
return
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
"CHAIN_UNSUPPORTED",
|
|
2612
|
-
`No sponsored WETH configured for chain ${chainId}`
|
|
2613
|
-
);
|
|
2614
|
-
}
|
|
2615
|
-
const amountWei = BigInt(amount);
|
|
2616
|
-
const pub = deps.getPublicClient(cid);
|
|
2617
|
-
const wallet = deps.getWalletClient(cid);
|
|
2618
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2619
|
-
try {
|
|
2620
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2621
|
-
if (balance < amountWei) {
|
|
4218
|
+
return makeVerificationAwareDepositCallback(
|
|
4219
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
4220
|
+
const cid = chainId;
|
|
4221
|
+
const token = deps.tokenAddressByChain[cid];
|
|
4222
|
+
if (!token) {
|
|
2622
4223
|
throw new OwneyError(
|
|
2623
|
-
"
|
|
2624
|
-
|
|
2625
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
4224
|
+
"CHAIN_UNSUPPORTED",
|
|
4225
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
2626
4226
|
);
|
|
2627
4227
|
}
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
)
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
});
|
|
2648
|
-
const nonce = randomPermit2Nonce();
|
|
2649
|
-
const deadline = BigInt(
|
|
2650
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2651
|
-
);
|
|
2652
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
2653
|
-
chainId: cid,
|
|
2654
|
-
message: {
|
|
2655
|
-
permitted: { token, amount: amountWei },
|
|
2656
|
-
spender: relayer,
|
|
2657
|
-
nonce,
|
|
2658
|
-
deadline
|
|
4228
|
+
const amountWei = BigInt(amount);
|
|
4229
|
+
const pub = deps.getPublicClient(cid);
|
|
4230
|
+
const wallet = deps.getWalletClient(cid);
|
|
4231
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
4232
|
+
try {
|
|
4233
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
4234
|
+
if (balance < amountWei) {
|
|
4235
|
+
throw new OwneyError(
|
|
4236
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4237
|
+
"Insufficient WETH balance for this deposit.",
|
|
4238
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
4239
|
+
);
|
|
4240
|
+
}
|
|
4241
|
+
} catch (err) {
|
|
4242
|
+
if (err instanceof OwneyError) throw err;
|
|
4243
|
+
console.warn(
|
|
4244
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
4245
|
+
err instanceof Error ? err.message : String(err)
|
|
4246
|
+
);
|
|
2659
4247
|
}
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
account: deps.ownerAddress,
|
|
2663
|
-
...typedData
|
|
2664
|
-
});
|
|
2665
|
-
deps.onApproved?.();
|
|
2666
|
-
const result = await post({
|
|
2667
|
-
baseUrl: deps.baseUrl,
|
|
2668
|
-
apiKey: deps.apiKey,
|
|
2669
|
-
body: {
|
|
2670
|
-
chainId: cid,
|
|
4248
|
+
const allowance = await readPermit2Allowance(
|
|
4249
|
+
pub,
|
|
2671
4250
|
token,
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
4251
|
+
deps.ownerAddress
|
|
4252
|
+
);
|
|
4253
|
+
if (allowance < amountWei) {
|
|
4254
|
+
throw new OwneyError(
|
|
4255
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
4256
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
4257
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
4258
|
+
);
|
|
2678
4259
|
}
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
4260
|
+
const relayer = await get({
|
|
4261
|
+
baseUrl: deps.baseUrl,
|
|
4262
|
+
apiKey: deps.apiKey,
|
|
4263
|
+
chainId: cid
|
|
4264
|
+
});
|
|
4265
|
+
const nonce = randomPermit2Nonce();
|
|
4266
|
+
const deadline = BigInt(
|
|
4267
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
4268
|
+
);
|
|
4269
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
4270
|
+
chainId: cid,
|
|
4271
|
+
message: {
|
|
4272
|
+
permitted: { token, amount: amountWei },
|
|
4273
|
+
spender: relayer,
|
|
4274
|
+
nonce,
|
|
4275
|
+
deadline
|
|
4276
|
+
}
|
|
4277
|
+
});
|
|
4278
|
+
const signature = await wallet.signTypedData({
|
|
4279
|
+
account: deps.ownerAddress,
|
|
4280
|
+
...typedData
|
|
4281
|
+
});
|
|
4282
|
+
deps.onApproved?.();
|
|
4283
|
+
const result = await post({
|
|
4284
|
+
baseUrl: deps.baseUrl,
|
|
4285
|
+
apiKey: deps.apiKey,
|
|
4286
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
4287
|
+
body: {
|
|
4288
|
+
chainId: cid,
|
|
4289
|
+
token,
|
|
4290
|
+
from: deps.ownerAddress,
|
|
4291
|
+
to: smartWallet,
|
|
4292
|
+
amount,
|
|
4293
|
+
nonce: nonce.toString(),
|
|
4294
|
+
deadline: deadline.toString(),
|
|
4295
|
+
signature,
|
|
4296
|
+
...verification?.agentId === "yieldseeker" ? {
|
|
4297
|
+
yieldseekerUserId: verification.userId,
|
|
4298
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4299
|
+
} : {}
|
|
4300
|
+
}
|
|
4301
|
+
});
|
|
4302
|
+
return result.txHash;
|
|
4303
|
+
}
|
|
4304
|
+
);
|
|
2682
4305
|
}
|
|
2683
4306
|
|
|
2684
4307
|
// src/lib/sponsored-calls-deposit.ts
|
|
2685
|
-
var
|
|
4308
|
+
var import_viem8 = require("viem");
|
|
2686
4309
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2687
4310
|
var DEFAULT_MAX_POLLS = 30;
|
|
2688
4311
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -2690,7 +4313,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
2690
4313
|
method: "wallet_getCapabilities",
|
|
2691
4314
|
params: [owner]
|
|
2692
4315
|
});
|
|
2693
|
-
const forChain = caps?.[(0,
|
|
4316
|
+
const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
2694
4317
|
return Boolean(forChain?.paymasterService?.supported);
|
|
2695
4318
|
}
|
|
2696
4319
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -2708,7 +4331,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2708
4331
|
}
|
|
2709
4332
|
return new URL(configured, origin).toString();
|
|
2710
4333
|
};
|
|
2711
|
-
return async (smartWallet, chainId, amount) => {
|
|
4334
|
+
return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
|
|
2712
4335
|
const cid = chainId;
|
|
2713
4336
|
const token = deps.tokenAddressByChain[cid];
|
|
2714
4337
|
if (!token) {
|
|
@@ -2724,22 +4347,48 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2724
4347
|
{ chainId }
|
|
2725
4348
|
);
|
|
2726
4349
|
}
|
|
2727
|
-
const data = (0,
|
|
2728
|
-
abi:
|
|
4350
|
+
const data = (0, import_viem8.encodeFunctionData)({
|
|
4351
|
+
abi: import_viem8.erc20Abi,
|
|
2729
4352
|
functionName: "transfer",
|
|
2730
4353
|
args: [smartWallet, BigInt(amount)]
|
|
2731
4354
|
});
|
|
4355
|
+
let paymasterUrl = absolutePaymasterUrl();
|
|
4356
|
+
if (verification?.agentId === "yieldseeker") {
|
|
4357
|
+
if (chainId !== 8453) {
|
|
4358
|
+
throw new OwneyError(
|
|
4359
|
+
"CHAIN_UNSUPPORTED",
|
|
4360
|
+
`Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
|
|
4361
|
+
);
|
|
4362
|
+
}
|
|
4363
|
+
const { intent } = await postPaymasterIntent({
|
|
4364
|
+
baseUrl: deps.routingApiBaseUrl,
|
|
4365
|
+
apiKey: deps.apiKey,
|
|
4366
|
+
yieldseekerSignature: verification.signature,
|
|
4367
|
+
body: {
|
|
4368
|
+
chainId,
|
|
4369
|
+
token,
|
|
4370
|
+
from: deps.ownerAddress,
|
|
4371
|
+
to: smartWallet,
|
|
4372
|
+
amount,
|
|
4373
|
+
yieldseekerUserId: verification.userId,
|
|
4374
|
+
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4375
|
+
}
|
|
4376
|
+
});
|
|
4377
|
+
const url = new URL(paymasterUrl);
|
|
4378
|
+
url.searchParams.set("owneyIntent", intent);
|
|
4379
|
+
paymasterUrl = url.toString();
|
|
4380
|
+
}
|
|
2732
4381
|
const sendResult = await deps.provider.request({
|
|
2733
4382
|
method: "wallet_sendCalls",
|
|
2734
4383
|
params: [
|
|
2735
4384
|
{
|
|
2736
4385
|
version: "2.0.0",
|
|
2737
4386
|
from: deps.ownerAddress,
|
|
2738
|
-
chainId: (0,
|
|
4387
|
+
chainId: (0, import_viem8.toHex)(chainId),
|
|
2739
4388
|
atomicRequired: false,
|
|
2740
4389
|
calls: [{ to: token, value: "0x0", data }],
|
|
2741
4390
|
capabilities: {
|
|
2742
|
-
paymasterService: { url:
|
|
4391
|
+
paymasterService: { url: paymasterUrl }
|
|
2743
4392
|
}
|
|
2744
4393
|
}
|
|
2745
4394
|
]
|
|
@@ -2769,7 +4418,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2769
4418
|
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
2770
4419
|
{ chainId, callsId }
|
|
2771
4420
|
);
|
|
2772
|
-
};
|
|
4421
|
+
});
|
|
2773
4422
|
}
|
|
2774
4423
|
|
|
2775
4424
|
// src/client.ts
|
|
@@ -2799,9 +4448,9 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2799
4448
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2800
4449
|
};
|
|
2801
4450
|
var VIEM_CHAIN2 = {
|
|
2802
|
-
8453:
|
|
2803
|
-
42161:
|
|
2804
|
-
1:
|
|
4451
|
+
8453: import_chains4.base,
|
|
4452
|
+
42161: import_chains4.arbitrum,
|
|
4453
|
+
1: import_chains4.mainnet
|
|
2805
4454
|
};
|
|
2806
4455
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
2807
4456
|
8453: "0x4200000000000000000000000000000000000006",
|
|
@@ -2829,6 +4478,8 @@ var OwneySDK = class {
|
|
|
2829
4478
|
orgAgentConfig;
|
|
2830
4479
|
orgAgentConfigPromise = null;
|
|
2831
4480
|
zyfaiRpcUrls;
|
|
4481
|
+
yieldseekerApiBaseUrl;
|
|
4482
|
+
yieldseekerSiweOrigin;
|
|
2832
4483
|
routingApiBaseUrl;
|
|
2833
4484
|
referralSource;
|
|
2834
4485
|
cachedSponsoredCallback = null;
|
|
@@ -2851,6 +4502,8 @@ var OwneySDK = class {
|
|
|
2851
4502
|
this.apiKey = config.apiKey;
|
|
2852
4503
|
if (config.debug) setOwneyDebug(true);
|
|
2853
4504
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
4505
|
+
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
4506
|
+
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
2854
4507
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2855
4508
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2856
4509
|
this.referralSource = config.referralSource;
|
|
@@ -2957,14 +4610,14 @@ var OwneySDK = class {
|
|
|
2957
4610
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2958
4611
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2959
4612
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2960
|
-
getPublicClient: (cid) => (0,
|
|
4613
|
+
getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
|
|
2961
4614
|
chain: VIEM_CHAIN2[cid],
|
|
2962
|
-
transport: (0,
|
|
4615
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2963
4616
|
}),
|
|
2964
|
-
getWalletClient: (cid) => (0,
|
|
4617
|
+
getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
|
|
2965
4618
|
account: owner,
|
|
2966
4619
|
chain: VIEM_CHAIN2[cid],
|
|
2967
|
-
transport: (0,
|
|
4620
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2968
4621
|
})
|
|
2969
4622
|
});
|
|
2970
4623
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -2981,6 +4634,8 @@ var OwneySDK = class {
|
|
|
2981
4634
|
if (!onApproved && cached) return cached;
|
|
2982
4635
|
const provider = this.requireConnectedProvider();
|
|
2983
4636
|
const callback = makeSponsoredCallsCallback({
|
|
4637
|
+
apiKey: this.apiKey,
|
|
4638
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
2984
4639
|
provider,
|
|
2985
4640
|
ownerAddress: this.state.walletAddress,
|
|
2986
4641
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
@@ -3010,14 +4665,14 @@ var OwneySDK = class {
|
|
|
3010
4665
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3011
4666
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3012
4667
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3013
|
-
getPublicClient: (cid) => (0,
|
|
4668
|
+
getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
|
|
3014
4669
|
chain: VIEM_CHAIN2[cid],
|
|
3015
|
-
transport: (0,
|
|
4670
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3016
4671
|
}),
|
|
3017
|
-
getWalletClient: (cid) => (0,
|
|
4672
|
+
getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
|
|
3018
4673
|
account: owner,
|
|
3019
4674
|
chain: VIEM_CHAIN2[cid],
|
|
3020
|
-
transport: (0,
|
|
4675
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3021
4676
|
})
|
|
3022
4677
|
});
|
|
3023
4678
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -3048,12 +4703,10 @@ var OwneySDK = class {
|
|
|
3048
4703
|
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
3049
4704
|
this.apiKey,
|
|
3050
4705
|
this.routingApiBaseUrl
|
|
3051
|
-
).then(
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
}
|
|
3056
|
-
);
|
|
4706
|
+
).then((config) => {
|
|
4707
|
+
this.orgAgentConfig = config;
|
|
4708
|
+
return config;
|
|
4709
|
+
});
|
|
3057
4710
|
}
|
|
3058
4711
|
return this.orgAgentConfigPromise;
|
|
3059
4712
|
}
|
|
@@ -3093,7 +4746,14 @@ var OwneySDK = class {
|
|
|
3093
4746
|
this.routingApiBaseUrl
|
|
3094
4747
|
);
|
|
3095
4748
|
this.disabledAgents.clear();
|
|
3096
|
-
for (const {
|
|
4749
|
+
for (const {
|
|
4750
|
+
key: key2,
|
|
4751
|
+
agent_type,
|
|
4752
|
+
is_enabled,
|
|
4753
|
+
is_configured
|
|
4754
|
+
} of agentKeys) {
|
|
4755
|
+
const configured = is_configured ?? Boolean(key2);
|
|
4756
|
+
if (!configured) continue;
|
|
3097
4757
|
const agent = this.createAgent(agent_type, key2);
|
|
3098
4758
|
if (!agent) continue;
|
|
3099
4759
|
this.agents.set(agent_type, agent);
|
|
@@ -3117,8 +4777,15 @@ var OwneySDK = class {
|
|
|
3117
4777
|
}
|
|
3118
4778
|
createAgent(agentId, key2) {
|
|
3119
4779
|
if (agentId === "zyfai") {
|
|
4780
|
+
if (!key2) return null;
|
|
3120
4781
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
3121
4782
|
}
|
|
4783
|
+
if (agentId === "yieldseeker") {
|
|
4784
|
+
return new YieldseekerAgent(this.apiKey, {
|
|
4785
|
+
auth: { origin: this.yieldseekerSiweOrigin },
|
|
4786
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
4787
|
+
});
|
|
4788
|
+
}
|
|
3122
4789
|
return null;
|
|
3123
4790
|
}
|
|
3124
4791
|
/**
|
|
@@ -3163,7 +4830,7 @@ var OwneySDK = class {
|
|
|
3163
4830
|
* If provided, ALL specified agents must support the chainId or the call
|
|
3164
4831
|
* throws before activating any agent.
|
|
3165
4832
|
*/
|
|
3166
|
-
async activateAgent(chainId, agentId) {
|
|
4833
|
+
async activateAgent(chainId, agentId, asset) {
|
|
3167
4834
|
const state = this.requireState();
|
|
3168
4835
|
await this.ensureAgentsInitialized();
|
|
3169
4836
|
if (agentId !== void 0) {
|
|
@@ -3199,7 +4866,7 @@ var OwneySDK = class {
|
|
|
3199
4866
|
this.activeAgents.add(id);
|
|
3200
4867
|
}
|
|
3201
4868
|
state.chainId = chainId;
|
|
3202
|
-
await this.activateAgentsInTurn(agents, state, chainId);
|
|
4869
|
+
await this.activateAgentsInTurn(agents, state, chainId, asset);
|
|
3203
4870
|
return;
|
|
3204
4871
|
}
|
|
3205
4872
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -3220,7 +4887,7 @@ var OwneySDK = class {
|
|
|
3220
4887
|
const enabledCompatible = compatible.filter(
|
|
3221
4888
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
3222
4889
|
);
|
|
3223
|
-
await this.activateAgentsInTurn(enabledCompatible, state, chainId);
|
|
4890
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
|
|
3224
4891
|
}
|
|
3225
4892
|
/**
|
|
3226
4893
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -3236,17 +4903,25 @@ var OwneySDK = class {
|
|
|
3236
4903
|
* at a time anyway.
|
|
3237
4904
|
*
|
|
3238
4905
|
* Every agent is attempted even if an earlier one fails, so one declined
|
|
3239
|
-
* signature can't deny the remaining agents their turn.
|
|
3240
|
-
*
|
|
3241
|
-
*
|
|
4906
|
+
* signature can't deny the remaining agents their turn. Once all agents have
|
|
4907
|
+
* had a chance, a partial failure identifies the agents that still need a
|
|
4908
|
+
* retry; if none activated, the original provider error is preserved.
|
|
3242
4909
|
*/
|
|
3243
|
-
async activateAgentsInTurn(agents, state, chainId) {
|
|
4910
|
+
async activateAgentsInTurn(agents, state, chainId, asset) {
|
|
3244
4911
|
let firstError = null;
|
|
4912
|
+
const activatedAgentIds = [];
|
|
4913
|
+
const failedAgents = [];
|
|
3245
4914
|
for (const agent of agents) {
|
|
3246
4915
|
try {
|
|
3247
|
-
await agent.activateAgent(state, chainId);
|
|
4916
|
+
await agent.activateAgent(state, chainId, asset);
|
|
3248
4917
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
4918
|
+
activatedAgentIds.push(agent.id);
|
|
3249
4919
|
} catch (error) {
|
|
4920
|
+
failedAgents.push({
|
|
4921
|
+
agentId: agent.id,
|
|
4922
|
+
code: error instanceof OwneyError ? error.code : void 0,
|
|
4923
|
+
message: error instanceof Error ? error.message : String(error)
|
|
4924
|
+
});
|
|
3250
4925
|
if (firstError === null) {
|
|
3251
4926
|
firstError = error;
|
|
3252
4927
|
} else {
|
|
@@ -3254,7 +4929,16 @@ var OwneySDK = class {
|
|
|
3254
4929
|
}
|
|
3255
4930
|
}
|
|
3256
4931
|
}
|
|
3257
|
-
if (firstError
|
|
4932
|
+
if (firstError === null) return;
|
|
4933
|
+
if (activatedAgentIds.length === 0) throw firstError;
|
|
4934
|
+
const failedAgentIds = failedAgents.map(({ agentId }) => agentId);
|
|
4935
|
+
const activeNames = activatedAgentIds.map(this.formatAgentName).join(", ");
|
|
4936
|
+
const remainingNames = failedAgentIds.map(this.formatAgentName).join(", ");
|
|
4937
|
+
throw new OwneyError(
|
|
4938
|
+
"AGENT_ACTIVATION_PARTIAL_FAILURE",
|
|
4939
|
+
`${activeNames} activated, but ${remainingNames} still needs activation. Try again and approve the remaining wallet request.`,
|
|
4940
|
+
{ activatedAgentIds, failedAgentIds, failures: failedAgents }
|
|
4941
|
+
);
|
|
3258
4942
|
}
|
|
3259
4943
|
/**
|
|
3260
4944
|
* Deposit into a specific agent, or distribute across all eligible agents.
|
|
@@ -3468,10 +5152,10 @@ var OwneySDK = class {
|
|
|
3468
5152
|
agent,
|
|
3469
5153
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
3470
5154
|
}));
|
|
3471
|
-
const
|
|
5155
|
+
const valid2 = splits.filter(
|
|
3472
5156
|
(s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
|
|
3473
5157
|
);
|
|
3474
|
-
if (
|
|
5158
|
+
if (valid2.length === agents.length) {
|
|
3475
5159
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
3476
5160
|
}
|
|
3477
5161
|
const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
|
|
@@ -3490,6 +5174,11 @@ var OwneySDK = class {
|
|
|
3490
5174
|
)
|
|
3491
5175
|
}));
|
|
3492
5176
|
}
|
|
5177
|
+
formatAgentName(agentId) {
|
|
5178
|
+
if (agentId === "zyfai") return "Zyfai";
|
|
5179
|
+
if (agentId === "yieldseeker") return "Yieldseeker";
|
|
5180
|
+
return agentId;
|
|
5181
|
+
}
|
|
3493
5182
|
async validateMinDepositAmount(agent, state, chainId, asset, amount) {
|
|
3494
5183
|
const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
|
|
3495
5184
|
const parsedAmount = BigInt(amount);
|
|
@@ -3521,12 +5210,12 @@ var OwneySDK = class {
|
|
|
3521
5210
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
3522
5211
|
);
|
|
3523
5212
|
const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
|
|
3524
|
-
const
|
|
5213
|
+
const position2 = (balance.positions ?? []).find((p) => {
|
|
3525
5214
|
const positionChain = p.chain.trim().toUpperCase();
|
|
3526
5215
|
const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
|
|
3527
5216
|
return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
|
|
3528
5217
|
});
|
|
3529
|
-
return !!token && Number(token.amount) > 0 || !!
|
|
5218
|
+
return !!token && Number(token.amount) > 0 || !!position2;
|
|
3530
5219
|
} catch (error) {
|
|
3531
5220
|
if (requireReliableRead) {
|
|
3532
5221
|
throw new OwneyError(
|
|
@@ -3641,6 +5330,10 @@ var OwneySDK = class {
|
|
|
3641
5330
|
}
|
|
3642
5331
|
const requested = BigInt(amount);
|
|
3643
5332
|
const aggregated = await this.getBalances();
|
|
5333
|
+
const unavailableAgents = eligibleAgents.filter(
|
|
5334
|
+
(agent) => !(agent.id in aggregated.agentBalances)
|
|
5335
|
+
);
|
|
5336
|
+
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
3644
5337
|
const balances = projectAgentBalancesForAsset(
|
|
3645
5338
|
eligibleAgents,
|
|
3646
5339
|
aggregated.agentBalances,
|
|
@@ -3649,7 +5342,18 @@ var OwneySDK = class {
|
|
|
3649
5342
|
assetInfo.decimals
|
|
3650
5343
|
);
|
|
3651
5344
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
3652
|
-
if (totalAvailable
|
|
5345
|
+
if (totalAvailable === 0n && unavailableAgents.length > 0) {
|
|
5346
|
+
throw new OwneyError(
|
|
5347
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5348
|
+
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
5349
|
+
{
|
|
5350
|
+
asset,
|
|
5351
|
+
unavailableAgents: unavailableAgentIds,
|
|
5352
|
+
agentErrors: aggregated.agentErrors
|
|
5353
|
+
}
|
|
5354
|
+
);
|
|
5355
|
+
}
|
|
5356
|
+
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3653
5357
|
throw new OwneyError(
|
|
3654
5358
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3655
5359
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -3660,6 +5364,7 @@ var OwneySDK = class {
|
|
|
3660
5364
|
}
|
|
3661
5365
|
);
|
|
3662
5366
|
}
|
|
5367
|
+
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
3663
5368
|
const disabledBalances = balances.filter(
|
|
3664
5369
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
3665
5370
|
);
|
|
@@ -3668,7 +5373,7 @@ var OwneySDK = class {
|
|
|
3668
5373
|
);
|
|
3669
5374
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
3670
5375
|
disabledBalances,
|
|
3671
|
-
|
|
5376
|
+
plannedTarget
|
|
3672
5377
|
);
|
|
3673
5378
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
3674
5379
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -3678,7 +5383,9 @@ var OwneySDK = class {
|
|
|
3678
5383
|
}));
|
|
3679
5384
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
3680
5385
|
const results = {};
|
|
3681
|
-
const agentErrors = {
|
|
5386
|
+
const agentErrors = {
|
|
5387
|
+
...aggregated.agentErrors ?? {}
|
|
5388
|
+
};
|
|
3682
5389
|
for (let i = 0; i < plans.length; i++) {
|
|
3683
5390
|
const p = plans[i];
|
|
3684
5391
|
if (p.planned === 0n) continue;
|
|
@@ -3725,7 +5432,8 @@ var OwneySDK = class {
|
|
|
3725
5432
|
requested: amount,
|
|
3726
5433
|
withdrawn: withdrawn.toString(),
|
|
3727
5434
|
partialResults: results,
|
|
3728
|
-
agentErrors
|
|
5435
|
+
agentErrors,
|
|
5436
|
+
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3729
5437
|
}
|
|
3730
5438
|
);
|
|
3731
5439
|
}
|
|
@@ -3743,7 +5451,10 @@ var OwneySDK = class {
|
|
|
3743
5451
|
if (agentId) {
|
|
3744
5452
|
const agent = this.getAgent(agentId);
|
|
3745
5453
|
const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
3746
|
-
return
|
|
5454
|
+
return {
|
|
5455
|
+
...result,
|
|
5456
|
+
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5457
|
+
};
|
|
3747
5458
|
}
|
|
3748
5459
|
let totalBalance = 0;
|
|
3749
5460
|
const results = {};
|
|
@@ -3751,7 +5462,13 @@ var OwneySDK = class {
|
|
|
3751
5462
|
const balanceResults = await Promise.allSettled(
|
|
3752
5463
|
entries.map(async ([id, agent]) => {
|
|
3753
5464
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
3754
|
-
return [
|
|
5465
|
+
return [
|
|
5466
|
+
id,
|
|
5467
|
+
{
|
|
5468
|
+
...b,
|
|
5469
|
+
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5470
|
+
}
|
|
5471
|
+
];
|
|
3755
5472
|
})
|
|
3756
5473
|
);
|
|
3757
5474
|
let successCount = 0;
|
|
@@ -3773,6 +5490,7 @@ var OwneySDK = class {
|
|
|
3773
5490
|
const retryDelay = rateLimitDelay(reason);
|
|
3774
5491
|
if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
|
|
3775
5492
|
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
5493
|
+
console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
|
|
3776
5494
|
}
|
|
3777
5495
|
if (successCount === 0) {
|
|
3778
5496
|
throw new OwneyError(
|
|
@@ -3885,7 +5603,10 @@ var OwneySDK = class {
|
|
|
3885
5603
|
Promise.all(
|
|
3886
5604
|
entries.map(async ([id, agent]) => {
|
|
3887
5605
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
3888
|
-
return [
|
|
5606
|
+
return [
|
|
5607
|
+
id,
|
|
5608
|
+
balanceForApyScope(b, chainId, tokenSymbol)
|
|
5609
|
+
];
|
|
3889
5610
|
})
|
|
3890
5611
|
)
|
|
3891
5612
|
]);
|
|
@@ -3913,10 +5634,12 @@ var OwneySDK = class {
|
|
|
3913
5634
|
}
|
|
3914
5635
|
}
|
|
3915
5636
|
const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
|
|
5637
|
+
const history = aggregateApyHistory(results, balances);
|
|
3916
5638
|
return {
|
|
3917
5639
|
totalApy: String(totalApy),
|
|
3918
5640
|
agentApy: results,
|
|
3919
|
-
apyByChainAndAsset
|
|
5641
|
+
apyByChainAndAsset,
|
|
5642
|
+
history
|
|
3920
5643
|
};
|
|
3921
5644
|
}
|
|
3922
5645
|
/**
|
|
@@ -4064,10 +5787,10 @@ var OwneySDK = class {
|
|
|
4064
5787
|
);
|
|
4065
5788
|
}
|
|
4066
5789
|
const provider = this.requireConnectedProvider();
|
|
4067
|
-
const wallet = (0,
|
|
5790
|
+
const wallet = (0, import_viem9.createWalletClient)({
|
|
4068
5791
|
account: state.walletAddress,
|
|
4069
5792
|
chain: VIEM_CHAIN2[chainId],
|
|
4070
|
-
transport: (0,
|
|
5793
|
+
transport: (0, import_viem9.custom)(provider)
|
|
4071
5794
|
});
|
|
4072
5795
|
const hash = await wallet.writeContract({
|
|
4073
5796
|
address: token,
|
|
@@ -4077,9 +5800,9 @@ var OwneySDK = class {
|
|
|
4077
5800
|
account: state.walletAddress,
|
|
4078
5801
|
chain: VIEM_CHAIN2[chainId]
|
|
4079
5802
|
});
|
|
4080
|
-
const publicClient = (0,
|
|
5803
|
+
const publicClient = (0, import_viem9.createPublicClient)({
|
|
4081
5804
|
chain: VIEM_CHAIN2[chainId],
|
|
4082
|
-
transport: (0,
|
|
5805
|
+
transport: (0, import_viem9.custom)(provider)
|
|
4083
5806
|
});
|
|
4084
5807
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
4085
5808
|
hash,
|
|
@@ -4116,7 +5839,9 @@ var OwneySDK = class {
|
|
|
4116
5839
|
return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
4117
5840
|
}
|
|
4118
5841
|
const results = {};
|
|
4119
|
-
const agentEntries = [...this.agents.entries()]
|
|
5842
|
+
const agentEntries = [...this.agents.entries()].filter(
|
|
5843
|
+
([id]) => !this.isAgentDisabled(id)
|
|
5844
|
+
);
|
|
4120
5845
|
const apyResults = await Promise.all(
|
|
4121
5846
|
agentEntries.map(async ([id, agent]) => {
|
|
4122
5847
|
const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
@@ -4191,13 +5916,13 @@ var OwneySDK = class {
|
|
|
4191
5916
|
};
|
|
4192
5917
|
|
|
4193
5918
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
4194
|
-
var
|
|
4195
|
-
var
|
|
5919
|
+
var import_viem10 = require("viem");
|
|
5920
|
+
var import_siwe2 = require("siwe");
|
|
4196
5921
|
var import_sdk2 = require("@zyfai/sdk");
|
|
4197
5922
|
|
|
4198
5923
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
4199
|
-
var
|
|
4200
|
-
var
|
|
5924
|
+
var KEY_PREFIX4 = "owney.siwx.session";
|
|
5925
|
+
var storage4 = () => {
|
|
4201
5926
|
if (typeof window === "undefined") return null;
|
|
4202
5927
|
try {
|
|
4203
5928
|
return window.localStorage;
|
|
@@ -4205,8 +5930,8 @@ var storage2 = () => {
|
|
|
4205
5930
|
return null;
|
|
4206
5931
|
}
|
|
4207
5932
|
};
|
|
4208
|
-
var
|
|
4209
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
5933
|
+
var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
|
|
5934
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
|
|
4210
5935
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
4211
5936
|
var readLegacySiwxSession = (store, address) => {
|
|
4212
5937
|
if (!store) return null;
|
|
@@ -4237,17 +5962,17 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
4237
5962
|
};
|
|
4238
5963
|
var readSiwxSession = (address, chainId) => {
|
|
4239
5964
|
if (typeof window === "undefined") return null;
|
|
4240
|
-
const key2 =
|
|
4241
|
-
const store =
|
|
4242
|
-
let
|
|
5965
|
+
const key2 = buildKey3(address);
|
|
5966
|
+
const store = storage4();
|
|
5967
|
+
let raw2 = null;
|
|
4243
5968
|
try {
|
|
4244
|
-
|
|
5969
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
4245
5970
|
} catch {
|
|
4246
|
-
|
|
5971
|
+
raw2 = null;
|
|
4247
5972
|
}
|
|
4248
|
-
if (
|
|
5973
|
+
if (raw2) {
|
|
4249
5974
|
try {
|
|
4250
|
-
return JSON.parse(
|
|
5975
|
+
return JSON.parse(raw2);
|
|
4251
5976
|
} catch {
|
|
4252
5977
|
memorySiwxSessions.delete(key2);
|
|
4253
5978
|
try {
|
|
@@ -4266,18 +5991,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
4266
5991
|
};
|
|
4267
5992
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
4268
5993
|
if (typeof window === "undefined") return;
|
|
4269
|
-
const key2 =
|
|
5994
|
+
const key2 = buildKey3(address);
|
|
4270
5995
|
memorySiwxSessions.set(key2, session);
|
|
4271
|
-
const store =
|
|
5996
|
+
const store = storage4();
|
|
4272
5997
|
try {
|
|
4273
5998
|
store?.setItem(key2, JSON.stringify(session));
|
|
4274
5999
|
} catch {
|
|
4275
6000
|
}
|
|
4276
6001
|
};
|
|
4277
6002
|
var clearSiwxSession = (address, _chainId) => {
|
|
4278
|
-
const key2 =
|
|
6003
|
+
const key2 = buildKey3(address);
|
|
4279
6004
|
memorySiwxSessions.delete(key2);
|
|
4280
|
-
const store =
|
|
6005
|
+
const store = storage4();
|
|
4281
6006
|
try {
|
|
4282
6007
|
store?.removeItem(key2);
|
|
4283
6008
|
} catch {
|
|
@@ -4317,8 +6042,8 @@ function buildSIWXConfig(deps) {
|
|
|
4317
6042
|
statement: STATEMENT,
|
|
4318
6043
|
issuedAt,
|
|
4319
6044
|
toString() {
|
|
4320
|
-
return new
|
|
4321
|
-
address: (0,
|
|
6045
|
+
return new import_siwe2.SiweMessage({
|
|
6046
|
+
address: (0, import_viem10.getAddress)(accountAddress),
|
|
4322
6047
|
chainId: numericChainId(chainId),
|
|
4323
6048
|
domain,
|
|
4324
6049
|
uri,
|
|
@@ -4360,7 +6085,7 @@ function buildSIWXConfig(deps) {
|
|
|
4360
6085
|
const persistSession = async (session) => {
|
|
4361
6086
|
const address = session.data.accountAddress;
|
|
4362
6087
|
const id = numericChainId(session.data.chainId);
|
|
4363
|
-
const message = new
|
|
6088
|
+
const message = new import_siwe2.SiweMessage(session.message);
|
|
4364
6089
|
const login = await post("/auth/login", {
|
|
4365
6090
|
message,
|
|
4366
6091
|
signature: session.signature,
|
|
@@ -4409,6 +6134,7 @@ function createOwneySIWX(config) {
|
|
|
4409
6134
|
NotConnectedError,
|
|
4410
6135
|
OwneyError,
|
|
4411
6136
|
OwneySDK,
|
|
6137
|
+
YieldseekerAgent,
|
|
4412
6138
|
createOwneySIWX,
|
|
4413
6139
|
setOwneyDebug
|
|
4414
6140
|
});
|