@owney/sdk 0.7.24 → 0.7.25-beta.1
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 +9 -7
- package/dist/index.cjs +2701 -775
- package/dist/index.d.cts +144 -33
- package/dist/index.d.ts +144 -33
- package/dist/index.js +2714 -772
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,11 +26,32 @@ __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
|
});
|
|
32
33
|
module.exports = __toCommonJS(index_exports);
|
|
33
34
|
|
|
35
|
+
// src/lib/deposit-batch-callback.ts
|
|
36
|
+
var batchCallbacks = /* @__PURE__ */ new WeakMap();
|
|
37
|
+
var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
|
|
38
|
+
function toBatchTransfer(to, amount, verification) {
|
|
39
|
+
return {
|
|
40
|
+
to,
|
|
41
|
+
amount,
|
|
42
|
+
...verification ? {
|
|
43
|
+
yieldseeker: {
|
|
44
|
+
signature: verification.signature,
|
|
45
|
+
userId: verification.userId,
|
|
46
|
+
agentId: verification.yieldseekerAgentId
|
|
47
|
+
}
|
|
48
|
+
} : {}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function registerDepositBatch(callback, transfer) {
|
|
52
|
+
batchCallbacks.set(callback, transfer);
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
// src/errors.ts
|
|
35
56
|
var OwneyError = class extends Error {
|
|
36
57
|
code;
|
|
@@ -310,18 +331,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
310
331
|
return explicit;
|
|
311
332
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
312
333
|
}
|
|
313
|
-
function mapDeposit(
|
|
334
|
+
function mapDeposit(raw2) {
|
|
314
335
|
return {
|
|
315
|
-
txHash:
|
|
316
|
-
smartWallet:
|
|
317
|
-
amount:
|
|
336
|
+
txHash: raw2.txHash,
|
|
337
|
+
smartWallet: raw2.smartWallet,
|
|
338
|
+
amount: raw2.amount
|
|
318
339
|
};
|
|
319
340
|
}
|
|
320
|
-
function mapWithdraw(
|
|
341
|
+
function mapWithdraw(raw2) {
|
|
321
342
|
return {
|
|
322
|
-
txHash:
|
|
323
|
-
type:
|
|
324
|
-
amount:
|
|
343
|
+
txHash: raw2.txHash,
|
|
344
|
+
type: raw2.type,
|
|
345
|
+
amount: raw2.amount
|
|
325
346
|
};
|
|
326
347
|
}
|
|
327
348
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -340,10 +361,10 @@ function resolveChainId(chain) {
|
|
|
340
361
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
341
362
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
342
363
|
}
|
|
343
|
-
function mapPendingAllocations(
|
|
344
|
-
if (!Array.isArray(
|
|
364
|
+
function mapPendingAllocations(raw2) {
|
|
365
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
345
366
|
const pending = [];
|
|
346
|
-
for (const entry of
|
|
367
|
+
for (const entry of raw2) {
|
|
347
368
|
if (typeof entry !== "object" || entry === null) continue;
|
|
348
369
|
const e = entry;
|
|
349
370
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -366,8 +387,8 @@ function mapPendingAllocations(raw) {
|
|
|
366
387
|
}
|
|
367
388
|
return pending.length > 0 ? pending : void 0;
|
|
368
389
|
}
|
|
369
|
-
function mapBalances(
|
|
370
|
-
const portfolio =
|
|
390
|
+
function mapBalances(raw2, _chainId, smartWallet) {
|
|
391
|
+
const portfolio = raw2.portfolio;
|
|
371
392
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
372
393
|
let totalBalance = 0;
|
|
373
394
|
const tokens = [];
|
|
@@ -436,8 +457,8 @@ function sumTokenValues(tokens) {
|
|
|
436
457
|
function sumTokenEarnings(tokens) {
|
|
437
458
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
438
459
|
}
|
|
439
|
-
function mapEarnings(
|
|
440
|
-
const totalEarningsByChain =
|
|
460
|
+
function mapEarnings(raw2, smartWallet) {
|
|
461
|
+
const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
|
|
441
462
|
const tokens = [];
|
|
442
463
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
443
464
|
totalEarningsByChain
|
|
@@ -456,15 +477,15 @@ function mapEarnings(raw, smartWallet) {
|
|
|
456
477
|
return {
|
|
457
478
|
smartWallet,
|
|
458
479
|
lifetimeEarnings: sumTokenEarnings(
|
|
459
|
-
|
|
480
|
+
raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
|
|
460
481
|
),
|
|
461
482
|
tokens
|
|
462
483
|
};
|
|
463
484
|
}
|
|
464
|
-
function mapWeightedApyByChain(
|
|
465
|
-
if (!
|
|
485
|
+
function mapWeightedApyByChain(raw2) {
|
|
486
|
+
if (!raw2) return void 0;
|
|
466
487
|
const out = {};
|
|
467
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
488
|
+
for (const [chainKey, tokenApy] of Object.entries(raw2)) {
|
|
468
489
|
const chainId = Number(chainKey);
|
|
469
490
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
470
491
|
const perAsset = {};
|
|
@@ -504,8 +525,8 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
504
525
|
}
|
|
505
526
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
506
527
|
}
|
|
507
|
-
function mapApyHistory(
|
|
508
|
-
const history = Object.entries(
|
|
528
|
+
function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
529
|
+
const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
|
|
509
530
|
date,
|
|
510
531
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol),
|
|
511
532
|
// Provider position balances are treated as decimal amounts of the
|
|
@@ -521,9 +542,9 @@ function mapApyHistory(raw, chainId, tokenSymbol) {
|
|
|
521
542
|
} : {}
|
|
522
543
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
523
544
|
return {
|
|
524
|
-
walletAddress:
|
|
525
|
-
weightedApyAfterFee:
|
|
526
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
545
|
+
walletAddress: raw2.walletAddress,
|
|
546
|
+
weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
|
|
547
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
|
|
527
548
|
history
|
|
528
549
|
};
|
|
529
550
|
}
|
|
@@ -619,23 +640,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
619
640
|
};
|
|
620
641
|
});
|
|
621
642
|
}
|
|
622
|
-
function mapUserProfile(
|
|
643
|
+
function mapUserProfile(raw2, userAddress) {
|
|
623
644
|
return {
|
|
624
645
|
address: userAddress,
|
|
625
|
-
smartWallet:
|
|
626
|
-
chains:
|
|
627
|
-
strategy:
|
|
628
|
-
hasActiveSessionKey:
|
|
629
|
-
protocols:
|
|
630
|
-
splitting:
|
|
631
|
-
minSplits:
|
|
646
|
+
smartWallet: raw2.smartWallet || "",
|
|
647
|
+
chains: raw2.chains || [],
|
|
648
|
+
strategy: raw2.strategy,
|
|
649
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey || false,
|
|
650
|
+
protocols: raw2.protocols || [],
|
|
651
|
+
splitting: raw2.splitting,
|
|
652
|
+
minSplits: raw2.minSplits
|
|
632
653
|
};
|
|
633
654
|
}
|
|
634
|
-
function mapApyByStrategy(
|
|
655
|
+
function mapApyByStrategy(raw2) {
|
|
635
656
|
const apyPerAsset = {};
|
|
636
657
|
let apySum = 0;
|
|
637
658
|
let apyCount = 0;
|
|
638
|
-
for (const entry of
|
|
659
|
+
for (const entry of raw2.data) {
|
|
639
660
|
const supported = SupportedAssets.find(
|
|
640
661
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
641
662
|
);
|
|
@@ -747,9 +768,9 @@ function netDeltaForSnapshot(entry, chainId, asset) {
|
|
|
747
768
|
debugLog("zyfai:earnings", "gross fallback for daily earnings", { gross });
|
|
748
769
|
return gross;
|
|
749
770
|
}
|
|
750
|
-
function mapDailyEarnings(
|
|
771
|
+
function mapDailyEarnings(raw2, chainId, tokenSymbol) {
|
|
751
772
|
const wanted = tokenSymbol?.toUpperCase();
|
|
752
|
-
const snapshots = [...
|
|
773
|
+
const snapshots = [...raw2.data ?? []].sort(
|
|
753
774
|
(a, b) => a.snapshot_date.localeCompare(b.snapshot_date)
|
|
754
775
|
);
|
|
755
776
|
const byAsset = /* @__PURE__ */ new Map();
|
|
@@ -764,7 +785,7 @@ function mapDailyEarnings(raw, chainId, tokenSymbol) {
|
|
|
764
785
|
}
|
|
765
786
|
}
|
|
766
787
|
const assets = [...byAsset.entries()].map(([asset, series]) => ({ asset, points: series.points })).sort((a, b) => a.asset.localeCompare(b.asset));
|
|
767
|
-
return { walletAddress:
|
|
788
|
+
return { walletAddress: raw2.walletAddress, chainId, assets };
|
|
768
789
|
}
|
|
769
790
|
|
|
770
791
|
// src/agents/zyfai/zyfai.withdraw-amount.ts
|
|
@@ -875,15 +896,15 @@ var readSession = (address, _chainId) => {
|
|
|
875
896
|
if (typeof window === "undefined") return null;
|
|
876
897
|
const key2 = buildKey(address);
|
|
877
898
|
const store = storage();
|
|
878
|
-
let
|
|
899
|
+
let raw2 = null;
|
|
879
900
|
try {
|
|
880
|
-
|
|
901
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
881
902
|
} catch {
|
|
882
|
-
|
|
903
|
+
raw2 = null;
|
|
883
904
|
}
|
|
884
|
-
if (
|
|
905
|
+
if (raw2) {
|
|
885
906
|
try {
|
|
886
|
-
const parsed = JSON.parse(
|
|
907
|
+
const parsed = JSON.parse(raw2);
|
|
887
908
|
if (isFreshSession(parsed)) return parsed;
|
|
888
909
|
} catch {
|
|
889
910
|
}
|
|
@@ -1051,8 +1072,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
1051
1072
|
}
|
|
1052
1073
|
return result;
|
|
1053
1074
|
}
|
|
1054
|
-
function flattenAvailablePools(
|
|
1055
|
-
const byChain =
|
|
1075
|
+
function flattenAvailablePools(raw2) {
|
|
1076
|
+
const byChain = raw2 ?? {};
|
|
1056
1077
|
const names = [];
|
|
1057
1078
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
1058
1079
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -1555,8 +1576,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1555
1576
|
const poolResults = await Promise.all(
|
|
1556
1577
|
universe.map(async (protocol) => {
|
|
1557
1578
|
try {
|
|
1558
|
-
const
|
|
1559
|
-
return [protocol.id, flattenAvailablePools(
|
|
1579
|
+
const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1580
|
+
return [protocol.id, flattenAvailablePools(raw2)];
|
|
1560
1581
|
} catch (error) {
|
|
1561
1582
|
console.warn(
|
|
1562
1583
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1622,14 +1643,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1622
1643
|
async readWalletState(ownerAddress) {
|
|
1623
1644
|
try {
|
|
1624
1645
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1625
|
-
const
|
|
1646
|
+
const raw2 = portfolio;
|
|
1626
1647
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1627
|
-
predeployed:
|
|
1628
|
-
hasActiveSessionKey:
|
|
1648
|
+
predeployed: raw2?.predeployed,
|
|
1649
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1629
1650
|
});
|
|
1630
1651
|
return {
|
|
1631
|
-
predeployed:
|
|
1632
|
-
hasActiveSessionKey:
|
|
1652
|
+
predeployed: raw2?.predeployed,
|
|
1653
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1633
1654
|
};
|
|
1634
1655
|
} catch (error) {
|
|
1635
1656
|
console.warn(
|
|
@@ -1915,14 +1936,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1915
1936
|
return { txHash, smartWallet, amount };
|
|
1916
1937
|
}
|
|
1917
1938
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1918
|
-
const
|
|
1939
|
+
const raw2 = await this.sdk.depositFunds(
|
|
1919
1940
|
this.getAddress(),
|
|
1920
1941
|
validChainId,
|
|
1921
1942
|
amount,
|
|
1922
1943
|
asset,
|
|
1923
1944
|
"aggressive"
|
|
1924
1945
|
);
|
|
1925
|
-
return mapDeposit(
|
|
1946
|
+
return mapDeposit(raw2);
|
|
1926
1947
|
} catch (error) {
|
|
1927
1948
|
throw error;
|
|
1928
1949
|
}
|
|
@@ -1931,27 +1952,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1931
1952
|
async withdraw(state, chainId, token, amount) {
|
|
1932
1953
|
const validChainId = isValidChainId(chainId);
|
|
1933
1954
|
await this.ensureConnected(state, validChainId);
|
|
1934
|
-
const
|
|
1955
|
+
const raw2 = await this.sdk.withdrawFunds(
|
|
1935
1956
|
this.getAddress(),
|
|
1936
1957
|
validChainId,
|
|
1937
1958
|
amount,
|
|
1938
1959
|
token
|
|
1939
1960
|
);
|
|
1940
|
-
if (!
|
|
1961
|
+
if (!raw2.success) {
|
|
1941
1962
|
throw new OwneyError(
|
|
1942
1963
|
"WITHDRAW_FAILED",
|
|
1943
|
-
|
|
1944
|
-
{ chainId: validChainId, token, amount, response:
|
|
1964
|
+
raw2.message || "Zyfai withdraw failed.",
|
|
1965
|
+
{ chainId: validChainId, token, amount, response: raw2 },
|
|
1945
1966
|
this.id
|
|
1946
1967
|
);
|
|
1947
1968
|
}
|
|
1948
|
-
return mapWithdraw(
|
|
1969
|
+
return mapWithdraw(raw2);
|
|
1949
1970
|
}
|
|
1950
1971
|
// --- IAgent: Portfolio reads ---
|
|
1951
1972
|
async getBalances(state, chainId) {
|
|
1952
1973
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1953
|
-
const
|
|
1954
|
-
return mapBalances(
|
|
1974
|
+
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
1975
|
+
return mapBalances(raw2, validChainId, smartWallet);
|
|
1955
1976
|
}
|
|
1956
1977
|
earningsKey(state, chainId, smartWallet) {
|
|
1957
1978
|
return JSON.stringify([
|
|
@@ -1964,11 +1985,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1964
1985
|
const existing = this.earningsReads.get(key2);
|
|
1965
1986
|
if (existing) return existing;
|
|
1966
1987
|
const generation = this.earningsGeneration;
|
|
1967
|
-
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((
|
|
1988
|
+
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw2) => {
|
|
1968
1989
|
if (generation === this.earningsGeneration) {
|
|
1969
|
-
this.earningsSnapshot = { key: key2, raw, at: Date.now() };
|
|
1990
|
+
this.earningsSnapshot = { key: key2, raw: raw2, at: Date.now() };
|
|
1970
1991
|
}
|
|
1971
|
-
return
|
|
1992
|
+
return raw2;
|
|
1972
1993
|
}).finally(() => {
|
|
1973
1994
|
if (this.earningsReads.get(key2) === pending)
|
|
1974
1995
|
this.earningsReads.delete(key2);
|
|
@@ -1978,11 +1999,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1978
1999
|
}
|
|
1979
2000
|
async getEarnings(state, chainId) {
|
|
1980
2001
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1981
|
-
const
|
|
2002
|
+
const raw2 = await this.readEarnings(
|
|
1982
2003
|
this.earningsKey(state, chainId, smartWallet),
|
|
1983
2004
|
smartWallet
|
|
1984
2005
|
);
|
|
1985
|
-
return mapEarnings(
|
|
2006
|
+
return mapEarnings(raw2, smartWallet);
|
|
1986
2007
|
}
|
|
1987
2008
|
async refreshEarnings(state, chainId) {
|
|
1988
2009
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -2009,17 +2030,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2009
2030
|
}
|
|
2010
2031
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
2011
2032
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
2012
|
-
const
|
|
2013
|
-
return mapApyHistory(
|
|
2033
|
+
const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
2034
|
+
return mapApyHistory(raw2, chainId, tokenSymbol);
|
|
2014
2035
|
}
|
|
2015
2036
|
async getDailyEarnings(state, chainId, days, tokenSymbol) {
|
|
2016
2037
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
2017
2038
|
const start = new Date(Date.now() - (DayFilterMapping[days] + 1) * 864e5);
|
|
2018
|
-
const
|
|
2039
|
+
const raw2 = await this.sdk.getDailyEarnings(
|
|
2019
2040
|
smartWallet,
|
|
2020
2041
|
start.toISOString().slice(0, 10)
|
|
2021
2042
|
);
|
|
2022
|
-
return mapDailyEarnings(
|
|
2043
|
+
return mapDailyEarnings(raw2, chainId, tokenSymbol);
|
|
2023
2044
|
}
|
|
2024
2045
|
/**
|
|
2025
2046
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -2054,7 +2075,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2054
2075
|
const matched = [];
|
|
2055
2076
|
let backendExhausted = false;
|
|
2056
2077
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
2057
|
-
const
|
|
2078
|
+
const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
2058
2079
|
limit: backendPageSize,
|
|
2059
2080
|
offset,
|
|
2060
2081
|
fromDate: options?.fromDate,
|
|
@@ -2065,13 +2086,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2065
2086
|
// asset's rows and handing back a page that filters to nothing.
|
|
2066
2087
|
assetType
|
|
2067
2088
|
});
|
|
2068
|
-
|
|
2089
|
+
raw2.data.forEach((entry, idx) => {
|
|
2069
2090
|
if (entry.chainId === validChainId) {
|
|
2070
2091
|
matched.push({ entry, rawIdx: offset + idx });
|
|
2071
2092
|
}
|
|
2072
2093
|
});
|
|
2073
|
-
offset +=
|
|
2074
|
-
if (
|
|
2094
|
+
offset += raw2.data.length;
|
|
2095
|
+
if (raw2.data.length < backendPageSize) {
|
|
2075
2096
|
backendExhausted = true;
|
|
2076
2097
|
break;
|
|
2077
2098
|
}
|
|
@@ -2093,18 +2114,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2093
2114
|
}
|
|
2094
2115
|
async getUserProfile(state, chainId) {
|
|
2095
2116
|
await this.connectAuth(state, chainId);
|
|
2096
|
-
const
|
|
2117
|
+
const raw2 = await this.sdk.getUserDetails();
|
|
2097
2118
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
2098
2119
|
asset: "USDC (default \u2014 no asset passed)",
|
|
2099
|
-
splitting:
|
|
2100
|
-
minSplits:
|
|
2101
|
-
strategy:
|
|
2102
|
-
chains:
|
|
2103
|
-
protocolCount:
|
|
2104
|
-
hasActiveSessionKey:
|
|
2105
|
-
smartWallet:
|
|
2120
|
+
splitting: raw2.splitting,
|
|
2121
|
+
minSplits: raw2.minSplits,
|
|
2122
|
+
strategy: raw2.strategy,
|
|
2123
|
+
chains: raw2.chains,
|
|
2124
|
+
protocolCount: raw2.protocols?.length,
|
|
2125
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey,
|
|
2126
|
+
smartWallet: raw2.smartWallet
|
|
2106
2127
|
});
|
|
2107
|
-
return mapUserProfile(
|
|
2128
|
+
return mapUserProfile(raw2, this.connectedAddress);
|
|
2108
2129
|
}
|
|
2109
2130
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
2110
2131
|
await this.connectAuth(state, chainId);
|
|
@@ -2123,201 +2144,2063 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2123
2144
|
}
|
|
2124
2145
|
// --- IAgent: Discovery (no wallet required) ---
|
|
2125
2146
|
async getAgentApy(days, options) {
|
|
2126
|
-
const
|
|
2147
|
+
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
2127
2148
|
false,
|
|
2128
2149
|
DayFilterMapping[days],
|
|
2129
2150
|
"aggressive",
|
|
2130
2151
|
options?.chainId,
|
|
2131
2152
|
options?.tokenSymbol
|
|
2132
2153
|
);
|
|
2133
|
-
return mapApyByStrategy(
|
|
2154
|
+
return mapApyByStrategy(raw2);
|
|
2134
2155
|
}
|
|
2135
2156
|
};
|
|
2136
2157
|
|
|
2137
|
-
// src/
|
|
2138
|
-
var
|
|
2139
|
-
|
|
2140
|
-
|
|
2158
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2159
|
+
var import_viem6 = require("viem");
|
|
2160
|
+
var import_chains3 = require("viem/chains");
|
|
2161
|
+
|
|
2162
|
+
// src/lib/chain-guard.ts
|
|
2163
|
+
var CHAIN_NAMES = {
|
|
2164
|
+
1: "Ethereum",
|
|
2165
|
+
8453: "Base",
|
|
2166
|
+
42161: "Arbitrum"
|
|
2167
|
+
};
|
|
2168
|
+
function chainName(chainId) {
|
|
2169
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2170
|
+
}
|
|
2171
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2172
|
+
const actual = await pub.getChainId();
|
|
2173
|
+
if (actual === expected) return;
|
|
2141
2174
|
try {
|
|
2142
|
-
|
|
2143
|
-
method: "GET",
|
|
2144
|
-
headers: {
|
|
2145
|
-
"Content-Type": "application/json",
|
|
2146
|
-
"x-owney-api-key": `${apiKey}`
|
|
2147
|
-
}
|
|
2148
|
-
});
|
|
2149
|
-
if (!res.ok) {
|
|
2150
|
-
if (res.status !== 404) {
|
|
2151
|
-
console.warn(
|
|
2152
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
2153
|
-
);
|
|
2154
|
-
}
|
|
2155
|
-
return null;
|
|
2156
|
-
}
|
|
2157
|
-
const json = await res.json();
|
|
2158
|
-
const policy = json.success ? json.data ?? null : null;
|
|
2159
|
-
debugLog(
|
|
2160
|
-
"owney-sdk",
|
|
2161
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
2162
|
-
policy ?? void 0
|
|
2163
|
-
);
|
|
2164
|
-
return policy;
|
|
2175
|
+
await wallet.switchChain({ id: expected });
|
|
2165
2176
|
} catch (error) {
|
|
2166
|
-
console.warn(
|
|
2167
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
2168
|
-
error instanceof Error ? error.message : String(error)
|
|
2169
|
-
);
|
|
2170
|
-
return null;
|
|
2171
|
-
}
|
|
2172
|
-
}
|
|
2173
|
-
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
2174
|
-
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
2175
|
-
const res = await fetch(url, {
|
|
2176
|
-
method: "GET",
|
|
2177
|
-
headers: {
|
|
2178
|
-
"Content-Type": "application/json",
|
|
2179
|
-
"x-owney-api-key": `${apiKey}`
|
|
2180
|
-
}
|
|
2181
|
-
});
|
|
2182
|
-
if (!res.ok) {
|
|
2183
|
-
const text = await res.text().catch(() => "");
|
|
2184
2177
|
throw new OwneyError(
|
|
2185
|
-
"
|
|
2186
|
-
`
|
|
2187
|
-
{
|
|
2178
|
+
"CHAIN_MISMATCH",
|
|
2179
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2180
|
+
{
|
|
2181
|
+
expectedChainId: expected,
|
|
2182
|
+
actualChainId: actual,
|
|
2183
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2184
|
+
}
|
|
2188
2185
|
);
|
|
2189
2186
|
}
|
|
2190
|
-
const
|
|
2191
|
-
if (
|
|
2187
|
+
const after = await pub.getChainId();
|
|
2188
|
+
if (after !== expected) {
|
|
2192
2189
|
throw new OwneyError(
|
|
2193
|
-
"
|
|
2194
|
-
`
|
|
2195
|
-
{
|
|
2190
|
+
"CHAIN_MISMATCH",
|
|
2191
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2192
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
2196
2193
|
);
|
|
2197
2194
|
}
|
|
2198
|
-
return json.data;
|
|
2199
2195
|
}
|
|
2200
2196
|
|
|
2201
|
-
// src/lib/
|
|
2202
|
-
var
|
|
2203
|
-
|
|
2197
|
+
// src/lib/transfer-auth.ts
|
|
2198
|
+
var import_viem2 = require("viem");
|
|
2199
|
+
|
|
2200
|
+
// src/lib/sponsor-client.ts
|
|
2201
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2202
|
+
async function postPaymasterIntent(input) {
|
|
2203
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2204
|
+
let res;
|
|
2204
2205
|
try {
|
|
2205
|
-
await fetch(`${
|
|
2206
|
+
res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
|
|
2206
2207
|
method: "POST",
|
|
2207
2208
|
headers: {
|
|
2208
|
-
"
|
|
2209
|
-
"x-owney-api-key": apiKey
|
|
2209
|
+
"content-type": "application/json",
|
|
2210
|
+
"x-owney-api-key": input.apiKey,
|
|
2211
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
2210
2212
|
},
|
|
2211
|
-
body: JSON.stringify(
|
|
2212
|
-
agent_type: agentType,
|
|
2213
|
-
error_code: errorCode,
|
|
2214
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2215
|
-
})
|
|
2213
|
+
body: JSON.stringify(input.body)
|
|
2216
2214
|
});
|
|
2217
|
-
} catch (
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2215
|
+
} catch (networkError) {
|
|
2216
|
+
throw new OwneyError(
|
|
2217
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2218
|
+
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2219
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
2221
2220
|
);
|
|
2222
2221
|
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2222
|
+
const text = await res.text();
|
|
2223
|
+
let parsed = null;
|
|
2225
2224
|
try {
|
|
2226
|
-
|
|
2227
|
-
} catch
|
|
2228
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
2229
|
-
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
2230
|
-
throw err;
|
|
2225
|
+
parsed = JSON.parse(text);
|
|
2226
|
+
} catch {
|
|
2231
2227
|
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
2228
|
+
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
2229
|
+
throw new OwneyError(
|
|
2230
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2231
|
+
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2232
|
+
{
|
|
2233
|
+
statusCode: res.status,
|
|
2234
|
+
responseBody: text.slice(0, 500),
|
|
2235
|
+
safeToFallback: true
|
|
2236
|
+
}
|
|
2242
2237
|
);
|
|
2243
|
-
if (!tokenBalance) return { agent, balance: 0n };
|
|
2244
|
-
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
2245
|
-
});
|
|
2246
|
-
}
|
|
2247
|
-
function planProportionalShares(balances, requested, totalAvailable) {
|
|
2248
|
-
const plans = balances.map(({ agent, balance }) => ({
|
|
2249
|
-
agent,
|
|
2250
|
-
balance,
|
|
2251
|
-
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
2252
|
-
}));
|
|
2253
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
2254
|
-
let remainder = requested - assigned;
|
|
2255
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
2256
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
2257
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2258
|
-
});
|
|
2259
|
-
for (const p of byHeadroom) {
|
|
2260
|
-
if (remainder === 0n) break;
|
|
2261
|
-
const headroom = p.balance - p.planned;
|
|
2262
|
-
if (headroom <= 0n) continue;
|
|
2263
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
2264
|
-
p.planned += take;
|
|
2265
|
-
remainder -= take;
|
|
2266
2238
|
}
|
|
2267
|
-
return
|
|
2239
|
+
return parsed.data;
|
|
2268
2240
|
}
|
|
2269
|
-
function
|
|
2270
|
-
const
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2241
|
+
async function getSponsorRelayerAddress(input) {
|
|
2242
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2243
|
+
let res;
|
|
2244
|
+
try {
|
|
2245
|
+
res = await fetch(
|
|
2246
|
+
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2247
|
+
{
|
|
2248
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
2249
|
+
}
|
|
2250
|
+
);
|
|
2251
|
+
} catch (networkError) {
|
|
2252
|
+
throw new OwneyError(
|
|
2253
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2254
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2255
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
2256
|
+
);
|
|
2281
2257
|
}
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
const totalHeadroom = candidates.reduce(
|
|
2288
|
-
(s, c) => s + (c.balance - c.planned),
|
|
2289
|
-
0n
|
|
2290
|
-
);
|
|
2291
|
-
if (totalHeadroom === 0n) return;
|
|
2292
|
-
let distributed = 0n;
|
|
2293
|
-
for (const c of candidates) {
|
|
2294
|
-
const headroom = c.balance - c.planned;
|
|
2295
|
-
const proportional = headroom * amount / totalHeadroom;
|
|
2296
|
-
const give = proportional > headroom ? headroom : proportional;
|
|
2297
|
-
c.planned += give;
|
|
2298
|
-
distributed += give;
|
|
2258
|
+
const text = await res.text();
|
|
2259
|
+
let parsed = null;
|
|
2260
|
+
try {
|
|
2261
|
+
parsed = JSON.parse(text);
|
|
2262
|
+
} catch {
|
|
2299
2263
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2264
|
+
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
2265
|
+
throw new OwneyError(
|
|
2266
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2267
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2268
|
+
{
|
|
2269
|
+
statusCode: res.status,
|
|
2270
|
+
responseBody: text.slice(0, 500),
|
|
2271
|
+
safeToFallback: true
|
|
2272
|
+
}
|
|
2273
|
+
);
|
|
2308
2274
|
}
|
|
2275
|
+
return parsed.data.relayer;
|
|
2309
2276
|
}
|
|
2310
|
-
function
|
|
2311
|
-
|
|
2277
|
+
async function postSponsorBatchTransfer(input) {
|
|
2278
|
+
let res;
|
|
2279
|
+
try {
|
|
2280
|
+
res = await fetch(
|
|
2281
|
+
`${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
|
|
2282
|
+
{
|
|
2283
|
+
method: "POST",
|
|
2284
|
+
headers: {
|
|
2285
|
+
"content-type": "application/json",
|
|
2286
|
+
"x-owney-api-key": input.apiKey
|
|
2287
|
+
},
|
|
2288
|
+
body: JSON.stringify(input.body)
|
|
2289
|
+
}
|
|
2290
|
+
);
|
|
2291
|
+
} catch {
|
|
2292
|
+
throw new OwneyError(
|
|
2293
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2294
|
+
"Deposit status is unknown. Retry the same amount to check it.",
|
|
2295
|
+
{ safeToFallback: false }
|
|
2296
|
+
);
|
|
2297
|
+
}
|
|
2298
|
+
const parsed = await res.json().catch(() => null);
|
|
2299
|
+
if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
|
|
2300
|
+
throw new OwneyError(
|
|
2301
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2302
|
+
"Deposit could not be confirmed. Retry the same amount to check its status.",
|
|
2303
|
+
{
|
|
2304
|
+
statusCode: res.status,
|
|
2305
|
+
safeToFallback: false,
|
|
2306
|
+
notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
|
|
2307
|
+
}
|
|
2308
|
+
);
|
|
2309
|
+
}
|
|
2310
|
+
return parsed.data;
|
|
2312
2311
|
}
|
|
2313
2312
|
|
|
2314
|
-
// src/lib/
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2313
|
+
// src/lib/permit2.ts
|
|
2314
|
+
var import_viem3 = require("viem");
|
|
2315
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2316
|
+
function permit2ApprovalAmount(requiredAmount) {
|
|
2317
|
+
if (requiredAmount <= 0n) {
|
|
2318
|
+
throw new Error("Permit2 approval requires a positive deposit amount");
|
|
2319
|
+
}
|
|
2320
|
+
return requiredAmount;
|
|
2321
|
+
}
|
|
2322
|
+
var ERC20_ALLOWANCE_ABI = [
|
|
2323
|
+
{
|
|
2324
|
+
type: "function",
|
|
2325
|
+
name: "allowance",
|
|
2326
|
+
stateMutability: "view",
|
|
2327
|
+
inputs: [
|
|
2328
|
+
{ name: "owner", type: "address" },
|
|
2329
|
+
{ name: "spender", type: "address" }
|
|
2330
|
+
],
|
|
2331
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
2332
|
+
},
|
|
2333
|
+
{
|
|
2334
|
+
type: "function",
|
|
2335
|
+
name: "approve",
|
|
2336
|
+
stateMutability: "nonpayable",
|
|
2337
|
+
inputs: [
|
|
2338
|
+
{ name: "spender", type: "address" },
|
|
2339
|
+
{ name: "amount", type: "uint256" }
|
|
2340
|
+
],
|
|
2341
|
+
outputs: [{ name: "", type: "bool" }]
|
|
2342
|
+
},
|
|
2343
|
+
{
|
|
2344
|
+
type: "function",
|
|
2345
|
+
name: "balanceOf",
|
|
2346
|
+
stateMutability: "view",
|
|
2347
|
+
inputs: [{ name: "account", type: "address" }],
|
|
2348
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
2349
|
+
}
|
|
2350
|
+
];
|
|
2351
|
+
function randomPermit2Nonce() {
|
|
2352
|
+
const bytes = new Uint8Array(32);
|
|
2353
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2354
|
+
return BigInt((0, import_viem3.bytesToHex)(bytes));
|
|
2355
|
+
}
|
|
2356
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2357
|
+
return publicClient.readContract({
|
|
2358
|
+
address: token,
|
|
2359
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2360
|
+
functionName: "allowance",
|
|
2361
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2364
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
2365
|
+
return publicClient.readContract({
|
|
2366
|
+
address: token,
|
|
2367
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2368
|
+
functionName: "balanceOf",
|
|
2369
|
+
args: [owner]
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
// src/lib/sponsored-deposit.ts
|
|
2374
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2375
|
+
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2376
|
+
function provideDepositVerificationContext(callback, context) {
|
|
2377
|
+
callback[verificationSetter]?.(context);
|
|
2378
|
+
}
|
|
2379
|
+
function makeVerificationAwareDepositCallback(implementation) {
|
|
2380
|
+
let nextVerification;
|
|
2381
|
+
const callback = async (smartWallet, chainId, amount) => {
|
|
2382
|
+
const verification = nextVerification;
|
|
2383
|
+
nextVerification = void 0;
|
|
2384
|
+
return implementation(smartWallet, chainId, amount, verification);
|
|
2385
|
+
};
|
|
2386
|
+
Object.defineProperty(callback, verificationSetter, {
|
|
2387
|
+
value: (context) => {
|
|
2388
|
+
nextVerification = context;
|
|
2389
|
+
}
|
|
2390
|
+
});
|
|
2391
|
+
return callback;
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2395
|
+
var import_siwe = require("siwe");
|
|
2396
|
+
var import_viem4 = require("viem");
|
|
2397
|
+
var import_chains2 = require("viem/chains");
|
|
2398
|
+
|
|
2399
|
+
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2400
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session.v5";
|
|
2401
|
+
var INVALIDATED_KEY_PREFIXES = [
|
|
2402
|
+
"owney.yieldseeker.session",
|
|
2403
|
+
"owney.yieldseeker.session.v3",
|
|
2404
|
+
"owney.yieldseeker.session.v4"
|
|
2405
|
+
];
|
|
2406
|
+
var storage2 = () => {
|
|
2407
|
+
if (typeof window === "undefined") return null;
|
|
2408
|
+
try {
|
|
2409
|
+
return window.localStorage;
|
|
2410
|
+
} catch {
|
|
2411
|
+
return null;
|
|
2412
|
+
}
|
|
2413
|
+
};
|
|
2414
|
+
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2415
|
+
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2416
|
+
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2417
|
+
);
|
|
2418
|
+
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2419
|
+
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2420
|
+
memorySessions2.delete(key2);
|
|
2421
|
+
try {
|
|
2422
|
+
store?.removeItem(key2);
|
|
2423
|
+
} catch {
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
};
|
|
2427
|
+
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2428
|
+
var isValidSession = (session) => {
|
|
2429
|
+
if (!session?.token) return false;
|
|
2430
|
+
try {
|
|
2431
|
+
const parsed = JSON.parse(atob(session.token));
|
|
2432
|
+
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2433
|
+
} catch {
|
|
2434
|
+
return false;
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
var readYieldseekerSession = (address, chainId) => {
|
|
2438
|
+
if (typeof window === "undefined") return null;
|
|
2439
|
+
const key2 = buildKey2(address, chainId);
|
|
2440
|
+
const store = storage2();
|
|
2441
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2442
|
+
let raw2 = null;
|
|
2443
|
+
try {
|
|
2444
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
2445
|
+
} catch {
|
|
2446
|
+
raw2 = null;
|
|
2447
|
+
}
|
|
2448
|
+
if (raw2) {
|
|
2449
|
+
try {
|
|
2450
|
+
const parsed = JSON.parse(raw2);
|
|
2451
|
+
if (isValidSession(parsed)) return parsed.token;
|
|
2452
|
+
} catch {
|
|
2453
|
+
}
|
|
2454
|
+
memorySessions2.delete(key2);
|
|
2455
|
+
try {
|
|
2456
|
+
store?.removeItem(key2);
|
|
2457
|
+
} catch {
|
|
2458
|
+
}
|
|
2459
|
+
return null;
|
|
2460
|
+
}
|
|
2461
|
+
const cached = memorySessions2.get(key2);
|
|
2462
|
+
if (isValidSession(cached)) return cached.token;
|
|
2463
|
+
if (cached) memorySessions2.delete(key2);
|
|
2464
|
+
return null;
|
|
2465
|
+
};
|
|
2466
|
+
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2467
|
+
if (typeof window === "undefined") return;
|
|
2468
|
+
const session = { token };
|
|
2469
|
+
if (!isValidSession(session)) return;
|
|
2470
|
+
const key2 = buildKey2(address, chainId);
|
|
2471
|
+
memorySessions2.set(key2, session);
|
|
2472
|
+
const store = storage2();
|
|
2473
|
+
try {
|
|
2474
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2475
|
+
} catch {
|
|
2476
|
+
}
|
|
2477
|
+
};
|
|
2478
|
+
var clearYieldseekerSession = (address, chainId) => {
|
|
2479
|
+
const key2 = buildKey2(address, chainId);
|
|
2480
|
+
memorySessions2.delete(key2);
|
|
2481
|
+
const store = storage2();
|
|
2482
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2483
|
+
try {
|
|
2484
|
+
store?.removeItem(key2);
|
|
2485
|
+
} catch {
|
|
2486
|
+
}
|
|
2487
|
+
};
|
|
2488
|
+
|
|
2489
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2490
|
+
function resolveSiweOrigin(override) {
|
|
2491
|
+
const origin = override?.trim() || (typeof window !== "undefined" ? window.location?.origin : void 0);
|
|
2492
|
+
if (!origin || origin === "null") {
|
|
2493
|
+
throw new Error("Yieldseeker sign-in requires the requesting app's browser origin.");
|
|
2494
|
+
}
|
|
2495
|
+
const url = new URL(origin);
|
|
2496
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
2497
|
+
throw new Error("Yieldseeker sign-in requires an HTTP or HTTPS app origin.");
|
|
2498
|
+
}
|
|
2499
|
+
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
2500
|
+
throw new Error("Yieldseeker SIWE override must be an origin without credentials, path, query, or fragment.");
|
|
2501
|
+
}
|
|
2502
|
+
return url;
|
|
2503
|
+
}
|
|
2504
|
+
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2505
|
+
const url = resolveSiweOrigin(dependencies.origin);
|
|
2506
|
+
return new import_siwe.SiweMessage({
|
|
2507
|
+
scheme: url.protocol.slice(0, -1),
|
|
2508
|
+
domain: url.host,
|
|
2509
|
+
address: (0, import_viem4.getAddress)(address),
|
|
2510
|
+
uri: url.origin,
|
|
2511
|
+
version: "1",
|
|
2512
|
+
chainId,
|
|
2513
|
+
nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
|
|
2514
|
+
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2515
|
+
}).prepareMessage();
|
|
2516
|
+
}
|
|
2517
|
+
function encodeYieldseekerAuthToken(token) {
|
|
2518
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2519
|
+
let binary = "";
|
|
2520
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2521
|
+
return btoa(binary);
|
|
2522
|
+
}
|
|
2523
|
+
var YieldseekerAuth = class {
|
|
2524
|
+
constructor(dependencies = {}) {
|
|
2525
|
+
this.dependencies = dependencies;
|
|
2526
|
+
}
|
|
2527
|
+
dependencies;
|
|
2528
|
+
tokens = /* @__PURE__ */ new Map();
|
|
2529
|
+
pending = /* @__PURE__ */ new Map();
|
|
2530
|
+
scopes = /* @__PURE__ */ new Map();
|
|
2531
|
+
key(state, chainId) {
|
|
2532
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
|
|
2533
|
+
}
|
|
2534
|
+
async getToken(state, chainId) {
|
|
2535
|
+
const key2 = this.key(state, chainId);
|
|
2536
|
+
const scope = { address: state.walletAddress, chainId };
|
|
2537
|
+
this.scopes.set(key2, scope);
|
|
2538
|
+
const cached = this.tokens.get(key2);
|
|
2539
|
+
if (cached) return cached;
|
|
2540
|
+
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2541
|
+
if (persisted && this.matchesOrigin(persisted)) {
|
|
2542
|
+
this.tokens.set(key2, persisted);
|
|
2543
|
+
return persisted;
|
|
2544
|
+
}
|
|
2545
|
+
if (persisted) clearYieldseekerSession(scope.address, scope.chainId);
|
|
2546
|
+
const inFlight = this.pending.get(key2);
|
|
2547
|
+
if (inFlight) return inFlight;
|
|
2548
|
+
const request = this.sign(state, chainId).then((token) => {
|
|
2549
|
+
if (this.pending.get(key2) !== request) throw new Error("Wallet sign-in session changed. Please try again.");
|
|
2550
|
+
this.tokens.set(key2, token);
|
|
2551
|
+
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2552
|
+
return token;
|
|
2553
|
+
});
|
|
2554
|
+
this.pending.set(key2, request);
|
|
2555
|
+
try {
|
|
2556
|
+
return await request;
|
|
2557
|
+
} finally {
|
|
2558
|
+
if (this.pending.get(key2) === request) this.pending.delete(key2);
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
async refreshToken(state, chainId, rejectedToken) {
|
|
2562
|
+
const key2 = this.key(state, chainId);
|
|
2563
|
+
if (this.tokens.get(key2) === rejectedToken) {
|
|
2564
|
+
this.tokens.delete(key2);
|
|
2565
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2566
|
+
}
|
|
2567
|
+
return this.getToken(state, chainId);
|
|
2568
|
+
}
|
|
2569
|
+
matchesOrigin(token) {
|
|
2570
|
+
try {
|
|
2571
|
+
const message = new import_siwe.SiweMessage(JSON.parse(atob(token)).message);
|
|
2572
|
+
const url = resolveSiweOrigin(this.dependencies.origin);
|
|
2573
|
+
return message.domain === url.host && message.uri === url.origin && message.scheme === url.protocol.slice(0, -1);
|
|
2574
|
+
} catch {
|
|
2575
|
+
return false;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
clear(state, chainId) {
|
|
2579
|
+
if (!state || chainId === void 0) {
|
|
2580
|
+
for (const scope of this.scopes.values()) {
|
|
2581
|
+
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2582
|
+
}
|
|
2583
|
+
this.tokens.clear();
|
|
2584
|
+
this.pending.clear();
|
|
2585
|
+
this.scopes.clear();
|
|
2586
|
+
return;
|
|
2587
|
+
}
|
|
2588
|
+
const key2 = this.key(state, chainId);
|
|
2589
|
+
this.tokens.delete(key2);
|
|
2590
|
+
this.pending.delete(key2);
|
|
2591
|
+
this.scopes.delete(key2);
|
|
2592
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2593
|
+
}
|
|
2594
|
+
async sign(state, chainId) {
|
|
2595
|
+
const account = (0, import_viem4.getAddress)(state.walletAddress);
|
|
2596
|
+
const publicClient = (0, import_viem4.createPublicClient)({
|
|
2597
|
+
chain: import_chains2.base,
|
|
2598
|
+
transport: (0, import_viem4.custom)(state.provider)
|
|
2599
|
+
});
|
|
2600
|
+
const walletClient = (0, import_viem4.createWalletClient)({
|
|
2601
|
+
account,
|
|
2602
|
+
chain: import_chains2.base,
|
|
2603
|
+
transport: (0, import_viem4.custom)(state.provider)
|
|
2604
|
+
});
|
|
2605
|
+
await ensureWalletOnChain(
|
|
2606
|
+
publicClient,
|
|
2607
|
+
walletClient,
|
|
2608
|
+
8453
|
|
2609
|
+
);
|
|
2610
|
+
const message = createYieldseekerSiweMessage(
|
|
2611
|
+
account,
|
|
2612
|
+
chainId,
|
|
2613
|
+
this.dependencies
|
|
2614
|
+
);
|
|
2615
|
+
const signature = await walletClient.signMessage({ account, message });
|
|
2616
|
+
return encodeYieldseekerAuthToken({ message, signature });
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2619
|
+
|
|
2620
|
+
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2621
|
+
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2622
|
+
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2623
|
+
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2624
|
+
var storage3 = () => {
|
|
2625
|
+
if (typeof window === "undefined") return null;
|
|
2626
|
+
try {
|
|
2627
|
+
return window.localStorage;
|
|
2628
|
+
} catch {
|
|
2629
|
+
return null;
|
|
2630
|
+
}
|
|
2631
|
+
};
|
|
2632
|
+
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2633
|
+
function valid(value, walletAddress, chainId, now) {
|
|
2634
|
+
return Boolean(
|
|
2635
|
+
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
|
|
2636
|
+
);
|
|
2637
|
+
}
|
|
2638
|
+
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2639
|
+
if (typeof window === "undefined") return null;
|
|
2640
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2641
|
+
const store = storage3();
|
|
2642
|
+
let parsed = null;
|
|
2643
|
+
try {
|
|
2644
|
+
const raw2 = store?.getItem(key2);
|
|
2645
|
+
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2646
|
+
} catch {
|
|
2647
|
+
parsed = null;
|
|
2648
|
+
}
|
|
2649
|
+
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2650
|
+
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2651
|
+
memoryIdentities.set(key2, candidate);
|
|
2652
|
+
return { userId: candidate.userId };
|
|
2653
|
+
}
|
|
2654
|
+
memoryIdentities.delete(key2);
|
|
2655
|
+
try {
|
|
2656
|
+
store?.removeItem(key2);
|
|
2657
|
+
} catch {
|
|
2658
|
+
}
|
|
2659
|
+
return null;
|
|
2660
|
+
}
|
|
2661
|
+
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2662
|
+
if (typeof window === "undefined") return;
|
|
2663
|
+
const identity = {
|
|
2664
|
+
userId,
|
|
2665
|
+
walletAddress,
|
|
2666
|
+
chainId,
|
|
2667
|
+
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2668
|
+
};
|
|
2669
|
+
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2670
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2671
|
+
memoryIdentities.set(key2, identity);
|
|
2672
|
+
try {
|
|
2673
|
+
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2674
|
+
} catch {
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2678
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2679
|
+
memoryIdentities.delete(key2);
|
|
2680
|
+
try {
|
|
2681
|
+
storage3()?.removeItem(key2);
|
|
2682
|
+
} catch {
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2687
|
+
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2688
|
+
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2689
|
+
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2690
|
+
}
|
|
2691
|
+
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2692
|
+
var YieldseekerApiError = class extends Error {
|
|
2693
|
+
constructor(status, providerCode, responseFields) {
|
|
2694
|
+
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2695
|
+
this.status = status;
|
|
2696
|
+
this.providerCode = providerCode;
|
|
2697
|
+
this.responseFields = responseFields;
|
|
2698
|
+
this.name = "YieldseekerApiError";
|
|
2699
|
+
}
|
|
2700
|
+
status;
|
|
2701
|
+
providerCode;
|
|
2702
|
+
responseFields;
|
|
2703
|
+
get isAuthenticationError() {
|
|
2704
|
+
return this.status === 401 || this.status === 403;
|
|
2705
|
+
}
|
|
2706
|
+
};
|
|
2707
|
+
function providerError(body, fallback) {
|
|
2708
|
+
if (!body || typeof body !== "object") return { code: fallback };
|
|
2709
|
+
const record = body;
|
|
2710
|
+
return {
|
|
2711
|
+
code: typeof record.message === "string" ? record.message : fallback,
|
|
2712
|
+
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
var YieldseekerApiClient = class {
|
|
2716
|
+
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch.bind(globalThis)) {
|
|
2717
|
+
this.owneyApiKey = owneyApiKey;
|
|
2718
|
+
this.baseUrl = baseUrl;
|
|
2719
|
+
this.fetchFn = fetchFn;
|
|
2720
|
+
}
|
|
2721
|
+
owneyApiKey;
|
|
2722
|
+
baseUrl;
|
|
2723
|
+
fetchFn;
|
|
2724
|
+
async request(path, options = {}) {
|
|
2725
|
+
const controller = new AbortController();
|
|
2726
|
+
const timer = setTimeout(
|
|
2727
|
+
() => controller.abort(),
|
|
2728
|
+
options.timeoutMs ?? 15e3
|
|
2729
|
+
);
|
|
2730
|
+
try {
|
|
2731
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2732
|
+
method: options.method ?? "GET",
|
|
2733
|
+
headers: {
|
|
2734
|
+
"Content-Type": "application/json",
|
|
2735
|
+
"x-owney-api-key": this.owneyApiKey,
|
|
2736
|
+
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2737
|
+
},
|
|
2738
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2739
|
+
signal: controller.signal
|
|
2740
|
+
});
|
|
2741
|
+
const payload = await response.json().catch(() => null);
|
|
2742
|
+
if (!response.ok) {
|
|
2743
|
+
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2744
|
+
throw new YieldseekerApiError(
|
|
2745
|
+
response.status,
|
|
2746
|
+
error.code,
|
|
2747
|
+
error.fields
|
|
2748
|
+
);
|
|
2749
|
+
}
|
|
2750
|
+
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2751
|
+
return payload.data;
|
|
2752
|
+
}
|
|
2753
|
+
return payload;
|
|
2754
|
+
} catch (error) {
|
|
2755
|
+
if (error instanceof YieldseekerApiError) throw error;
|
|
2756
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2757
|
+
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2758
|
+
}
|
|
2759
|
+
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2760
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2761
|
+
});
|
|
2762
|
+
} finally {
|
|
2763
|
+
clearTimeout(timer);
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
};
|
|
2767
|
+
|
|
2768
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2769
|
+
var import_viem5 = require("viem");
|
|
2770
|
+
|
|
2771
|
+
// src/lib/helpers/snapshot-apy.ts
|
|
2772
|
+
var DAY_MS = 864e5;
|
|
2773
|
+
function snapshotTime(date) {
|
|
2774
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return void 0;
|
|
2775
|
+
const time = Date.parse(date);
|
|
2776
|
+
return Number.isFinite(time) && new Date(time).toISOString().slice(0, 10) === date ? time : void 0;
|
|
2777
|
+
}
|
|
2778
|
+
function returnFactor(value) {
|
|
2779
|
+
if (typeof value !== "number" && typeof value !== "string") return void 0;
|
|
2780
|
+
if (typeof value === "string" && value.trim() === "") return void 0;
|
|
2781
|
+
const factor = Number(value);
|
|
2782
|
+
return Number.isFinite(factor) && factor >= 0 ? factor : void 0;
|
|
2783
|
+
}
|
|
2784
|
+
function calculateSnapshotApy(snapshots, lookbackDays, now = Date.now()) {
|
|
2785
|
+
if (!Number.isFinite(lookbackDays) || lookbackDays <= 0 || !Number.isFinite(now)) {
|
|
2786
|
+
return void 0;
|
|
2787
|
+
}
|
|
2788
|
+
const points = snapshots.flatMap((snapshot) => {
|
|
2789
|
+
const time = snapshotTime(snapshot.date);
|
|
2790
|
+
return time !== void 0 && time <= now ? [{ snapshot, time }] : [];
|
|
2791
|
+
}).sort((a, b) => a.time - b.time);
|
|
2792
|
+
const end = points.at(-1);
|
|
2793
|
+
if (!end) return void 0;
|
|
2794
|
+
const cutoff = end.time - lookbackDays * DAY_MS;
|
|
2795
|
+
const start = points.find((point) => point.time >= cutoff);
|
|
2796
|
+
const actualDays = (end.time - start.time) / DAY_MS;
|
|
2797
|
+
if (actualDays <= 0) return void 0;
|
|
2798
|
+
const startFactor = returnFactor(start.snapshot.cumulativeReturnFactor);
|
|
2799
|
+
const endFactor = returnFactor(end.snapshot.cumulativeReturnFactor);
|
|
2800
|
+
if (startFactor === void 0 || startFactor === 0 || endFactor === void 0) {
|
|
2801
|
+
return void 0;
|
|
2802
|
+
}
|
|
2803
|
+
const periodReturn = endFactor / startFactor - 1;
|
|
2804
|
+
const apy = Math.pow(1 + periodReturn, 365 / actualDays) - 1;
|
|
2805
|
+
return Number.isFinite(apy) ? apy : void 0;
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2809
|
+
var YIELDSEEKER_ASSET_METADATA = {
|
|
2810
|
+
USDC: {
|
|
2811
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2812
|
+
decimals: 6
|
|
2813
|
+
},
|
|
2814
|
+
WETH: {
|
|
2815
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
2816
|
+
decimals: 18
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
|
|
2820
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2821
|
+
function invalid(endpoint, detail) {
|
|
2822
|
+
throw new OwneyError(
|
|
2823
|
+
"AGENT_INVALID_RESPONSE",
|
|
2824
|
+
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2825
|
+
{ endpoint, detail },
|
|
2826
|
+
"yieldseeker"
|
|
2827
|
+
);
|
|
2828
|
+
}
|
|
2829
|
+
function raw(value, endpoint) {
|
|
2830
|
+
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2831
|
+
return invalid(endpoint, "expected a base-10 integer string");
|
|
2832
|
+
}
|
|
2833
|
+
return BigInt(value);
|
|
2834
|
+
}
|
|
2835
|
+
function decimal(value, decimals, endpoint) {
|
|
2836
|
+
return (0, import_viem5.formatUnits)(raw(value, endpoint), decimals);
|
|
2837
|
+
}
|
|
2838
|
+
function usd(rawAmount, decimals, price) {
|
|
2839
|
+
return Number((0, import_viem5.formatUnits)(rawAmount, decimals)) * price;
|
|
2840
|
+
}
|
|
2841
|
+
function percent(value) {
|
|
2842
|
+
const result = Number(value);
|
|
2843
|
+
return Number.isFinite(result) ? result * 100 : 0;
|
|
2844
|
+
}
|
|
2845
|
+
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2846
|
+
function publicApyAfterYieldseekerFee(value) {
|
|
2847
|
+
const grossPercent = percent(value);
|
|
2848
|
+
if (grossPercent <= 0) return grossPercent;
|
|
2849
|
+
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2850
|
+
return Math.round(netPercent * 1e12) / 1e12;
|
|
2851
|
+
}
|
|
2852
|
+
function riskAdjustedApyForDays(option, days) {
|
|
2853
|
+
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2854
|
+
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2855
|
+
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2856
|
+
}
|
|
2857
|
+
function assetAddressValue(record, address) {
|
|
2858
|
+
const entry = Object.entries(record).find(
|
|
2859
|
+
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2860
|
+
);
|
|
2861
|
+
return entry?.[1] ?? "0";
|
|
2862
|
+
}
|
|
2863
|
+
function position(value, asset, baseAssetDecimals) {
|
|
2864
|
+
const option = value?.yieldOption;
|
|
2865
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem5.isAddress)(option.address)) {
|
|
2866
|
+
return invalid("yield positions", "missing vault metadata");
|
|
2867
|
+
}
|
|
2868
|
+
return {
|
|
2869
|
+
chain: "BASE",
|
|
2870
|
+
protocol: option.provider,
|
|
2871
|
+
protocolId: option.address,
|
|
2872
|
+
pool: option.name,
|
|
2873
|
+
asset,
|
|
2874
|
+
// `assetsRaw` is the ERC-4626 vault-share quantity, whose decimals can
|
|
2875
|
+
// differ from the underlying asset. Yieldseeker already converts it to
|
|
2876
|
+
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
2877
|
+
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
2878
|
+
// share quantity separately because withdraw-from-position expects it.
|
|
2879
|
+
amount: decimal(
|
|
2880
|
+
value.assetsBase,
|
|
2881
|
+
baseAssetDecimals,
|
|
2882
|
+
"yield positions"
|
|
2883
|
+
),
|
|
2884
|
+
amountRaw: String(value.assetsRaw),
|
|
2885
|
+
apy: percent(option.riskAdjustedApy),
|
|
2886
|
+
tvl: Number(option.totalDepositsUsd),
|
|
2887
|
+
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2888
|
+
};
|
|
2889
|
+
}
|
|
2890
|
+
function mapYieldseekerBalances(contexts) {
|
|
2891
|
+
const tokens = [];
|
|
2892
|
+
const assetBalances = [];
|
|
2893
|
+
const positions = [];
|
|
2894
|
+
let totalUsd = 0;
|
|
2895
|
+
for (const context of contexts) {
|
|
2896
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2897
|
+
assetBalances.push({
|
|
2898
|
+
chain: "BASE",
|
|
2899
|
+
chainId: 8453,
|
|
2900
|
+
asset: context.asset,
|
|
2901
|
+
amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
|
|
2902
|
+
});
|
|
2903
|
+
const idle = assetAddressValue(
|
|
2904
|
+
context.snapshot.tokenBalances,
|
|
2905
|
+
metadata.address
|
|
2906
|
+
);
|
|
2907
|
+
tokens.push({
|
|
2908
|
+
chain: "BASE",
|
|
2909
|
+
chainId: 8453,
|
|
2910
|
+
asset: context.asset,
|
|
2911
|
+
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2912
|
+
});
|
|
2913
|
+
positions.push(
|
|
2914
|
+
...context.positions.map(
|
|
2915
|
+
(entry) => position(
|
|
2916
|
+
entry,
|
|
2917
|
+
context.asset,
|
|
2918
|
+
context.snapshot.baseAssetDecimals
|
|
2919
|
+
)
|
|
2920
|
+
)
|
|
2921
|
+
);
|
|
2922
|
+
totalUsd += usd(
|
|
2923
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2924
|
+
context.snapshot.baseAssetDecimals,
|
|
2925
|
+
context.snapshot.baseAssetPriceUsd
|
|
2926
|
+
);
|
|
2927
|
+
}
|
|
2928
|
+
return {
|
|
2929
|
+
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2930
|
+
totalBalance: String(totalUsd),
|
|
2931
|
+
totalBalanceAsset: "usdc",
|
|
2932
|
+
assetBalances,
|
|
2933
|
+
tokens,
|
|
2934
|
+
positions
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
function mapYieldseekerEarnings(contexts) {
|
|
2938
|
+
const tokens = [];
|
|
2939
|
+
let lifetimeEarnings = 0;
|
|
2940
|
+
for (const context of contexts) {
|
|
2941
|
+
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
2942
|
+
tokens.push({
|
|
2943
|
+
chain: "BASE",
|
|
2944
|
+
chainId: 8453,
|
|
2945
|
+
asset: context.asset,
|
|
2946
|
+
amount: (0, import_viem5.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
2947
|
+
});
|
|
2948
|
+
lifetimeEarnings += usd(
|
|
2949
|
+
amount,
|
|
2950
|
+
context.snapshot.baseAssetDecimals,
|
|
2951
|
+
context.snapshot.baseAssetPriceUsd
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
return {
|
|
2955
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2956
|
+
lifetimeEarnings,
|
|
2957
|
+
tokens
|
|
2958
|
+
};
|
|
2959
|
+
}
|
|
2960
|
+
function apyForDays(context, days, now) {
|
|
2961
|
+
if (days === "7D") return percent(context.snapshot.apy7d);
|
|
2962
|
+
if (days === "30D") return percent(context.snapshot.apy30d);
|
|
2963
|
+
const apy = calculateSnapshotApy(context.historic?.dailyYieldSnapshots ?? [], 14, now);
|
|
2964
|
+
const apyPercent = apy === void 0 ? void 0 : apy * 100;
|
|
2965
|
+
return apyPercent !== void 0 && Number.isFinite(apyPercent) ? apyPercent : void 0;
|
|
2966
|
+
}
|
|
2967
|
+
function dailyApy(point) {
|
|
2968
|
+
const total = raw(point.totalValueBase, "historic position");
|
|
2969
|
+
const earned = raw(point.dailyYieldBase, "historic position");
|
|
2970
|
+
const principal = total - earned;
|
|
2971
|
+
if (principal <= 0n || earned === 0n) return 0;
|
|
2972
|
+
return Number(earned) / Number(principal) * 365 * 100;
|
|
2973
|
+
}
|
|
2974
|
+
function aggregateHistory(contexts, dayCount, now) {
|
|
2975
|
+
const today = new Date(now).toISOString().slice(0, 10);
|
|
2976
|
+
const cutoff = new Date(Date.parse(today) - (dayCount - 1) * 864e5).toISOString().slice(0, 10);
|
|
2977
|
+
const assets = new Set(contexts.map((context) => context.asset.toUpperCase()));
|
|
2978
|
+
const unit = assets.size === 1 ? [...assets][0] : "USD";
|
|
2979
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
2980
|
+
for (const context of contexts) {
|
|
2981
|
+
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
2982
|
+
for (const point of points) {
|
|
2983
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(point.date) || !Number.isFinite(Date.parse(point.date)) || point.date < cutoff || point.date > today) continue;
|
|
2984
|
+
const tokens = Number(decimal(point.totalValueBase, point.baseAssetDecimals, "historic position"));
|
|
2985
|
+
const amount = assets.size === 1 ? tokens : tokens * point.baseAssetPriceUsd;
|
|
2986
|
+
if (!Number.isFinite(amount) || amount < 0) {
|
|
2987
|
+
invalid("historic position", "expected a finite non-negative balance");
|
|
2988
|
+
}
|
|
2989
|
+
const current = byDate.get(point.date) ?? { weighted: 0, amount: 0 };
|
|
2990
|
+
current.weighted += dailyApy(point) * amount;
|
|
2991
|
+
current.amount += amount;
|
|
2992
|
+
byDate.set(point.date, current);
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
2996
|
+
date,
|
|
2997
|
+
apy: value.amount > 0 ? value.weighted / value.amount : 0,
|
|
2998
|
+
historicalBalance: { amount: value.amount, unit }
|
|
2999
|
+
}));
|
|
3000
|
+
}
|
|
3001
|
+
function mapYieldseekerApy(walletAddress, contexts, days, now = Date.now()) {
|
|
3002
|
+
let weighted = 0;
|
|
3003
|
+
let totalUsd = 0;
|
|
3004
|
+
const byAsset = {};
|
|
3005
|
+
for (const context of contexts) {
|
|
3006
|
+
const valueUsd = usd(
|
|
3007
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
3008
|
+
context.snapshot.baseAssetDecimals,
|
|
3009
|
+
context.snapshot.baseAssetPriceUsd
|
|
3010
|
+
);
|
|
3011
|
+
const apy = apyForDays(context, days, now);
|
|
3012
|
+
if (apy === void 0) continue;
|
|
3013
|
+
weighted += apy * valueUsd;
|
|
3014
|
+
totalUsd += valueUsd;
|
|
3015
|
+
byAsset[context.asset] = apy;
|
|
3016
|
+
}
|
|
3017
|
+
const dayCount = Number(days.slice(0, -1));
|
|
3018
|
+
return {
|
|
3019
|
+
walletAddress,
|
|
3020
|
+
...days !== "14D" || Object.keys(byAsset).length > 0 ? { weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0 } : {},
|
|
3021
|
+
apyByChainAndAsset: { 8453: byAsset },
|
|
3022
|
+
history: aggregateHistory(contexts, dayCount, now)
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
function actionType(value) {
|
|
3026
|
+
const normalized = value.toLowerCase();
|
|
3027
|
+
if (normalized.includes("deposit")) return "Deposit";
|
|
3028
|
+
if (normalized.includes("withdraw")) return "Withdraw";
|
|
3029
|
+
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
3030
|
+
return "Earned";
|
|
3031
|
+
return "Rebalance";
|
|
3032
|
+
}
|
|
3033
|
+
function transactionHashes(details) {
|
|
3034
|
+
if (!details) return [];
|
|
3035
|
+
const values = [
|
|
3036
|
+
details.transactionHash,
|
|
3037
|
+
details.txHash,
|
|
3038
|
+
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
3039
|
+
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
3040
|
+
];
|
|
3041
|
+
return values.filter(
|
|
3042
|
+
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
3043
|
+
).filter((value, index, all) => all.indexOf(value) === index);
|
|
3044
|
+
}
|
|
3045
|
+
function actionEntry(action) {
|
|
3046
|
+
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
3047
|
+
return {
|
|
3048
|
+
agent: "yieldseeker",
|
|
3049
|
+
action: actionType(action.actionType),
|
|
3050
|
+
date: action.createdDate,
|
|
3051
|
+
oldApy: null,
|
|
3052
|
+
newApy: null,
|
|
3053
|
+
transactions: [
|
|
3054
|
+
{
|
|
3055
|
+
txHashes: transactionHashes(action.details),
|
|
3056
|
+
chainId: 8453
|
|
3057
|
+
}
|
|
3058
|
+
],
|
|
3059
|
+
rebalanceLog: []
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3062
|
+
function depositDestination(context, movement) {
|
|
3063
|
+
const to = movement.toAddress.toLowerCase();
|
|
3064
|
+
const option = context.positions.find(({ yieldOption }) => yieldOption.chainId === movement.chainId && yieldOption.assetAddress.toLowerCase() === context.agent.assetAddress.toLowerCase() && yieldOption.address.toLowerCase() === to)?.yieldOption;
|
|
3065
|
+
if (option) return { protocol: option.provider || "Vault", pool: option.name, tokenSymbol: context.asset };
|
|
3066
|
+
const receipt = context.historic?.assets.find((asset) => asset.chainId === movement.chainId && !asset.isSpam && asset.address.toLowerCase() === to && asset.address.toLowerCase() !== context.agent.assetAddress.toLowerCase());
|
|
3067
|
+
const minted = receipt && context.historic?.movements.some((candidate) => candidate.chainId === movement.chainId && candidate.transactionHash.toLowerCase() === movement.transactionHash.toLowerCase() && candidate.assetAddress.toLowerCase() === receipt.address.toLowerCase() && candidate.fromAddress.toLowerCase() === "0x0000000000000000000000000000000000000000" && candidate.toAddress.toLowerCase() === context.wallet.walletAddress.toLowerCase() && raw(candidate.assetAmount, "historic position") > 0n);
|
|
3068
|
+
if (receipt && minted) return { protocol: "Vault", pool: receipt.name || receipt.symbol, tokenSymbol: context.asset };
|
|
3069
|
+
return void 0;
|
|
3070
|
+
}
|
|
3071
|
+
function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses, destination) {
|
|
3072
|
+
const from = movement.fromAddress.toLowerCase();
|
|
3073
|
+
const to = movement.toAddress.toLowerCase();
|
|
3074
|
+
const owner = ownerAddress.toLowerCase();
|
|
3075
|
+
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
3076
|
+
const baseAsset = agent.assetAddress.toLowerCase();
|
|
3077
|
+
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
3078
|
+
return void 0;
|
|
3079
|
+
}
|
|
3080
|
+
let action;
|
|
3081
|
+
if (to === agentWallet && !vaultAddresses.has(from)) {
|
|
3082
|
+
action = "Top up";
|
|
3083
|
+
} else if (from === agentWallet && to === owner) {
|
|
3084
|
+
action = "Withdraw";
|
|
3085
|
+
} else if (from === agentWallet && destination) {
|
|
3086
|
+
action = "Deposit";
|
|
3087
|
+
}
|
|
3088
|
+
if (!action) return void 0;
|
|
3089
|
+
return {
|
|
3090
|
+
agent: "yieldseeker",
|
|
3091
|
+
action,
|
|
3092
|
+
...action === "Deposit" && destination ? { positions: [{
|
|
3093
|
+
...destination,
|
|
3094
|
+
amount: decimal(movement.assetAmount, YIELDSEEKER_ASSET_METADATA[asset].decimals, "historic position")
|
|
3095
|
+
}] } : {},
|
|
3096
|
+
date: movement.blockDate,
|
|
3097
|
+
oldApy: null,
|
|
3098
|
+
newApy: null,
|
|
3099
|
+
transactions: [
|
|
3100
|
+
{
|
|
3101
|
+
txHashes: [movement.transactionHash],
|
|
3102
|
+
chainId: agent.chainId,
|
|
3103
|
+
tokenSymbol: asset,
|
|
3104
|
+
amount: decimal(
|
|
3105
|
+
movement.assetAmount,
|
|
3106
|
+
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
3107
|
+
"historic position"
|
|
3108
|
+
)
|
|
3109
|
+
}
|
|
3110
|
+
],
|
|
3111
|
+
rebalanceLog: []
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
3114
|
+
function mapYieldseekerHistory(contexts, options) {
|
|
3115
|
+
const entries = contexts.flatMap((context) => {
|
|
3116
|
+
const seenMovements = /* @__PURE__ */ new Set();
|
|
3117
|
+
const movements = (context.historic?.movements ?? []).filter((movement) => {
|
|
3118
|
+
const key2 = `${movement.chainId}:${movement.transactionHash.toLowerCase()}:${movement.logIndex}`;
|
|
3119
|
+
if (seenMovements.has(key2)) return false;
|
|
3120
|
+
seenMovements.add(key2);
|
|
3121
|
+
return true;
|
|
3122
|
+
});
|
|
3123
|
+
return [
|
|
3124
|
+
...movements.map(
|
|
3125
|
+
(movement) => movementEntry(
|
|
3126
|
+
movement,
|
|
3127
|
+
context.wallet,
|
|
3128
|
+
context.agent,
|
|
3129
|
+
context.asset,
|
|
3130
|
+
options.ownerAddress,
|
|
3131
|
+
options.vaultAddresses,
|
|
3132
|
+
depositDestination(context, movement)
|
|
3133
|
+
)
|
|
3134
|
+
),
|
|
3135
|
+
...(context.actions ?? []).map(actionEntry)
|
|
3136
|
+
].filter((entry) => entry !== void 0);
|
|
3137
|
+
});
|
|
3138
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3139
|
+
const ungrouped = [];
|
|
3140
|
+
for (const entry of entries) {
|
|
3141
|
+
const tx = entry.transactions[0];
|
|
3142
|
+
const hash = tx?.txHashes[0];
|
|
3143
|
+
if (!hash) {
|
|
3144
|
+
ungrouped.push(entry);
|
|
3145
|
+
continue;
|
|
3146
|
+
}
|
|
3147
|
+
const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}`;
|
|
3148
|
+
const previous = grouped.get(key2);
|
|
3149
|
+
if (!previous) {
|
|
3150
|
+
grouped.set(key2, entry);
|
|
3151
|
+
continue;
|
|
3152
|
+
}
|
|
3153
|
+
if (entry.action === "Deposit" && entry.positions?.length) {
|
|
3154
|
+
if (!previous.positions?.length) {
|
|
3155
|
+
grouped.set(key2, entry);
|
|
3156
|
+
continue;
|
|
3157
|
+
}
|
|
3158
|
+
previous.positions.push(...entry.positions);
|
|
3159
|
+
previous.transactions.push(...entry.transactions);
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
const filtered = [...grouped.values(), ...ungrouped].filter((entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)).sort((left, right) => right.date.localeCompare(left.date));
|
|
3163
|
+
return {
|
|
3164
|
+
data: filtered.slice(0, options.limit),
|
|
3165
|
+
// v1 returns the whole action/movement collection and defines no cursor.
|
|
3166
|
+
// Report a terminal page so callers never loop over the same prefix.
|
|
3167
|
+
hasMore: false
|
|
3168
|
+
};
|
|
3169
|
+
}
|
|
3170
|
+
function mapYieldseekerProfile(address, contexts) {
|
|
3171
|
+
const protocols = /* @__PURE__ */ new Set();
|
|
3172
|
+
for (const context of contexts) {
|
|
3173
|
+
for (const current of context.positions) {
|
|
3174
|
+
if (current.yieldOption?.provider) {
|
|
3175
|
+
protocols.add(String(current.yieldOption.provider));
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
return {
|
|
3180
|
+
address,
|
|
3181
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
3182
|
+
chains: contexts.length > 0 ? [8453] : [],
|
|
3183
|
+
hasActiveSessionKey: contexts.some(
|
|
3184
|
+
(context) => context.wallet.initializedDate != null
|
|
3185
|
+
),
|
|
3186
|
+
protocols: [...protocols]
|
|
3187
|
+
};
|
|
3188
|
+
}
|
|
3189
|
+
function mapYieldseekerAgentApy(options, days) {
|
|
3190
|
+
const perAsset = {};
|
|
3191
|
+
const all = [];
|
|
3192
|
+
for (const entry of options) {
|
|
3193
|
+
const apys = entry.yieldOptions.map(
|
|
3194
|
+
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
3195
|
+
).filter(Number.isFinite);
|
|
3196
|
+
if (apys.length === 0) continue;
|
|
3197
|
+
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
3198
|
+
perAsset[entry.asset] = average;
|
|
3199
|
+
all.push(average);
|
|
3200
|
+
}
|
|
3201
|
+
return {
|
|
3202
|
+
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
3203
|
+
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
3204
|
+
};
|
|
3205
|
+
}
|
|
3206
|
+
|
|
3207
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3208
|
+
var OWNEY_AGENT_NAME = "owney";
|
|
3209
|
+
var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
3210
|
+
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3211
|
+
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3212
|
+
var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
|
|
3213
|
+
function generateYieldseekerUsername() {
|
|
3214
|
+
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3215
|
+
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
3216
|
+
}
|
|
3217
|
+
function isUsernameConflict(error) {
|
|
3218
|
+
if (!(error instanceof YieldseekerApiError)) return false;
|
|
3219
|
+
const code = error.providerCode.toUpperCase();
|
|
3220
|
+
return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
|
|
3221
|
+
}
|
|
3222
|
+
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
3223
|
+
{
|
|
3224
|
+
type: "function",
|
|
3225
|
+
name: "withdrawAssetToUser",
|
|
3226
|
+
stateMutability: "nonpayable",
|
|
3227
|
+
inputs: [
|
|
3228
|
+
{ name: "recipient", type: "address" },
|
|
3229
|
+
{ name: "asset", type: "address" },
|
|
3230
|
+
{ name: "amount", type: "uint256" }
|
|
3231
|
+
],
|
|
3232
|
+
outputs: []
|
|
3233
|
+
},
|
|
3234
|
+
{
|
|
3235
|
+
type: "function",
|
|
3236
|
+
name: "withdrawAllAssetToUser",
|
|
3237
|
+
stateMutability: "nonpayable",
|
|
3238
|
+
inputs: [
|
|
3239
|
+
{ name: "recipient", type: "address" },
|
|
3240
|
+
{ name: "asset", type: "address" }
|
|
3241
|
+
],
|
|
3242
|
+
outputs: []
|
|
3243
|
+
}
|
|
3244
|
+
];
|
|
3245
|
+
function query(params) {
|
|
3246
|
+
const search = new URLSearchParams();
|
|
3247
|
+
for (const [key2, value] of Object.entries(params)) {
|
|
3248
|
+
if (value !== void 0) search.set(key2, String(value));
|
|
3249
|
+
}
|
|
3250
|
+
const encoded = search.toString();
|
|
3251
|
+
return encoded ? `?${encoded}` : "";
|
|
3252
|
+
}
|
|
3253
|
+
var YieldseekerAgent = class {
|
|
3254
|
+
id = "yieldseeker";
|
|
3255
|
+
balanceComposition = "tokens-plus-positions";
|
|
3256
|
+
supportedChainIds = [8453];
|
|
3257
|
+
supportedAssets = [
|
|
3258
|
+
{
|
|
3259
|
+
chainId: 8453,
|
|
3260
|
+
chain: "BASE",
|
|
3261
|
+
assets: [
|
|
3262
|
+
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
3263
|
+
{ symbol: "WETH", minDepositAmount: "1" }
|
|
3264
|
+
]
|
|
3265
|
+
}
|
|
3266
|
+
];
|
|
3267
|
+
api;
|
|
3268
|
+
auth;
|
|
3269
|
+
transactionExecutor;
|
|
3270
|
+
unwindReceiptWaiter;
|
|
3271
|
+
agentContexts = /* @__PURE__ */ new Map();
|
|
3272
|
+
users = /* @__PURE__ */ new Map();
|
|
3273
|
+
pendingAgents = /* @__PURE__ */ new Map();
|
|
3274
|
+
yieldOptions = /* @__PURE__ */ new Map();
|
|
3275
|
+
pendingYieldOptions = /* @__PURE__ */ new Map();
|
|
3276
|
+
constructor(owneyApiKey, options = {}) {
|
|
3277
|
+
this.api = new YieldseekerApiClient(
|
|
3278
|
+
owneyApiKey,
|
|
3279
|
+
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
3280
|
+
options.fetchFn
|
|
3281
|
+
);
|
|
3282
|
+
this.auth = new YieldseekerAuth(options.auth);
|
|
3283
|
+
this.transactionExecutor = options.transactionExecutor;
|
|
3284
|
+
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3285
|
+
}
|
|
3286
|
+
async disconnect() {
|
|
3287
|
+
this.auth.clear();
|
|
3288
|
+
for (const key2 of this.users.keys()) {
|
|
3289
|
+
const [walletAddress, chainId] = key2.split(":");
|
|
3290
|
+
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
3291
|
+
}
|
|
3292
|
+
this.users.clear();
|
|
3293
|
+
this.agentContexts.clear();
|
|
3294
|
+
this.pendingAgents.clear();
|
|
3295
|
+
}
|
|
3296
|
+
async activateAgent(state, chainId, asset) {
|
|
3297
|
+
this.assertChain(chainId);
|
|
3298
|
+
const targetAsset = asset ?? "USDC";
|
|
3299
|
+
this.assertAsset(targetAsset);
|
|
3300
|
+
await this.ensureAgent(state, chainId, targetAsset);
|
|
3301
|
+
}
|
|
3302
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3303
|
+
this.assertChain(chainId);
|
|
3304
|
+
this.assertAsset(asset);
|
|
3305
|
+
if (BigInt(amount) <= 0n) {
|
|
3306
|
+
throw new OwneyError(
|
|
3307
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
3308
|
+
"Yieldseeker deposits must be greater than zero.",
|
|
3309
|
+
{ amount, minDepositAmount: "1" },
|
|
3310
|
+
this.id
|
|
3311
|
+
);
|
|
3312
|
+
}
|
|
3313
|
+
const context = await this.ensureAgent(state, chainId, asset);
|
|
3314
|
+
let txHash;
|
|
3315
|
+
try {
|
|
3316
|
+
if (depositCallback) {
|
|
3317
|
+
provideDepositVerificationContext(depositCallback, {
|
|
3318
|
+
agentId: "yieldseeker",
|
|
3319
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3320
|
+
userId: context.user.userId,
|
|
3321
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3322
|
+
});
|
|
3323
|
+
txHash = await depositCallback(
|
|
3324
|
+
context.wallet.walletAddress,
|
|
3325
|
+
chainId,
|
|
3326
|
+
amount
|
|
3327
|
+
);
|
|
3328
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3329
|
+
} else {
|
|
3330
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3331
|
+
from: (0, import_viem6.getAddress)(state.walletAddress),
|
|
3332
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3333
|
+
data: (0, import_viem6.encodeFunctionData)({
|
|
3334
|
+
abi: import_viem6.erc20Abi,
|
|
3335
|
+
functionName: "transfer",
|
|
3336
|
+
args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
3337
|
+
}),
|
|
3338
|
+
value: "0",
|
|
3339
|
+
chainId
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
} finally {
|
|
3343
|
+
await this.refreshSnapshotAfterMovement(
|
|
3344
|
+
state,
|
|
3345
|
+
chainId,
|
|
3346
|
+
context,
|
|
3347
|
+
"deposit"
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3350
|
+
return {
|
|
3351
|
+
txHash,
|
|
3352
|
+
smartWallet: context.wallet.walletAddress,
|
|
3353
|
+
amount
|
|
3354
|
+
};
|
|
3355
|
+
}
|
|
3356
|
+
async withdraw(state, chainId, asset, amount) {
|
|
3357
|
+
this.assertChain(chainId);
|
|
3358
|
+
this.assertAsset(asset);
|
|
3359
|
+
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
3360
|
+
throw new OwneyError(
|
|
3361
|
+
"WITHDRAW_FAILED",
|
|
3362
|
+
"Yieldseeker withdrawals must be greater than zero.",
|
|
3363
|
+
{ amount },
|
|
3364
|
+
this.id
|
|
3365
|
+
);
|
|
3366
|
+
}
|
|
3367
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
3368
|
+
if (!context) {
|
|
3369
|
+
throw new OwneyError(
|
|
3370
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3371
|
+
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3372
|
+
{ asset, available: "0" },
|
|
3373
|
+
this.id
|
|
3374
|
+
);
|
|
3375
|
+
}
|
|
3376
|
+
try {
|
|
3377
|
+
const portfolio = await this.loadPortfolioContext(
|
|
3378
|
+
state,
|
|
3379
|
+
chainId,
|
|
3380
|
+
context
|
|
3381
|
+
);
|
|
3382
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3383
|
+
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3384
|
+
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3385
|
+
);
|
|
3386
|
+
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3387
|
+
const deployed = portfolio.positions.reduce(
|
|
3388
|
+
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3389
|
+
0n
|
|
3390
|
+
);
|
|
3391
|
+
const totalAvailable = idle + deployed;
|
|
3392
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3393
|
+
if (requested > totalAvailable) {
|
|
3394
|
+
throw new OwneyError(
|
|
3395
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3396
|
+
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3397
|
+
{
|
|
3398
|
+
asset,
|
|
3399
|
+
requested: requested.toString(),
|
|
3400
|
+
available: totalAvailable.toString()
|
|
3401
|
+
},
|
|
3402
|
+
this.id
|
|
3403
|
+
);
|
|
3404
|
+
}
|
|
3405
|
+
let remaining = requested > idle ? requested - idle : 0n;
|
|
3406
|
+
for (const position2 of portfolio.positions) {
|
|
3407
|
+
if (remaining === 0n) break;
|
|
3408
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3409
|
+
if (available <= 0n) continue;
|
|
3410
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
3411
|
+
const response = await this.walletRequest(
|
|
3412
|
+
state,
|
|
3413
|
+
chainId,
|
|
3414
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
3415
|
+
{
|
|
3416
|
+
method: "POST",
|
|
3417
|
+
body: {
|
|
3418
|
+
chainId,
|
|
3419
|
+
vaultAddress: position2.yieldOption.address,
|
|
3420
|
+
assetsRaw: assetsRaw.toString()
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
);
|
|
3424
|
+
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3425
|
+
throw this.invalidResponse("position withdrawal");
|
|
3426
|
+
}
|
|
3427
|
+
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3428
|
+
remaining -= assetsRaw;
|
|
3429
|
+
}
|
|
3430
|
+
if (remaining > 0n) {
|
|
3431
|
+
throw this.invalidResponse("yield positions", {
|
|
3432
|
+
reason: "Withdrawable positions could not cover the request.",
|
|
3433
|
+
remaining: remaining.toString()
|
|
3434
|
+
});
|
|
3435
|
+
}
|
|
3436
|
+
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3437
|
+
const txHash = await this.submitTransaction(state, chainId, {
|
|
3438
|
+
from: account,
|
|
3439
|
+
to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
|
|
3440
|
+
data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
|
|
3441
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3442
|
+
functionName: "withdrawAllAssetToUser",
|
|
3443
|
+
args: [account, metadata.address]
|
|
3444
|
+
}) : (0, import_viem6.encodeFunctionData)({
|
|
3445
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3446
|
+
functionName: "withdrawAssetToUser",
|
|
3447
|
+
args: [account, metadata.address, requested]
|
|
3448
|
+
}),
|
|
3449
|
+
value: "0",
|
|
3450
|
+
chainId
|
|
3451
|
+
});
|
|
3452
|
+
return {
|
|
3453
|
+
txHash,
|
|
3454
|
+
type: amount === void 0 ? "full" : "partial",
|
|
3455
|
+
amount: requested.toString()
|
|
3456
|
+
};
|
|
3457
|
+
} finally {
|
|
3458
|
+
await this.refreshSnapshotAfterMovement(
|
|
3459
|
+
state,
|
|
3460
|
+
chainId,
|
|
3461
|
+
context,
|
|
3462
|
+
"withdrawal"
|
|
3463
|
+
);
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
async getBalances(state, chainId) {
|
|
3467
|
+
this.assertChain(chainId);
|
|
3468
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3469
|
+
}
|
|
3470
|
+
async getEarnings(state, chainId) {
|
|
3471
|
+
this.assertChain(chainId);
|
|
3472
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3473
|
+
}
|
|
3474
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3475
|
+
this.assertChain(chainId);
|
|
3476
|
+
const asset = tokenSymbol?.toUpperCase();
|
|
3477
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3478
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3479
|
+
...asset ? { asset } : {},
|
|
3480
|
+
historic: true
|
|
3481
|
+
});
|
|
3482
|
+
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3483
|
+
}
|
|
3484
|
+
async getHistory(state, chainId, options) {
|
|
3485
|
+
this.assertChain(chainId);
|
|
3486
|
+
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3487
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3488
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3489
|
+
...asset ? { asset } : {},
|
|
3490
|
+
historic: true,
|
|
3491
|
+
actions: true
|
|
3492
|
+
});
|
|
3493
|
+
const catalog = await Promise.all(
|
|
3494
|
+
[...new Set(contexts.map((context) => context.asset))].map(
|
|
3495
|
+
(contextAsset) => this.loadYieldOptions(contextAsset)
|
|
3496
|
+
)
|
|
3497
|
+
);
|
|
3498
|
+
const vaultAddresses = new Set(
|
|
3499
|
+
catalog.flat().filter(
|
|
3500
|
+
(yieldOption) => yieldOption.chainId === chainId && (0, import_viem6.isAddress)(yieldOption.address)
|
|
3501
|
+
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
3502
|
+
);
|
|
3503
|
+
return mapYieldseekerHistory(contexts, {
|
|
3504
|
+
limit: options?.limit ?? 10,
|
|
3505
|
+
ownerAddress: state.walletAddress,
|
|
3506
|
+
vaultAddresses,
|
|
3507
|
+
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3508
|
+
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3509
|
+
});
|
|
3510
|
+
}
|
|
3511
|
+
async getUserProfile(state, chainId) {
|
|
3512
|
+
this.assertChain(chainId);
|
|
3513
|
+
return mapYieldseekerProfile(
|
|
3514
|
+
state.walletAddress,
|
|
3515
|
+
await this.loadPortfolio(state, chainId, {})
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
3518
|
+
async getAgentApy(days, options) {
|
|
3519
|
+
this.assertOptionalChain(options?.chainId);
|
|
3520
|
+
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3521
|
+
if (requested !== void 0) this.assertAsset(requested);
|
|
3522
|
+
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3523
|
+
const values = await Promise.all(
|
|
3524
|
+
assets.map(async (asset) => {
|
|
3525
|
+
return { asset, yieldOptions: await this.loadYieldOptions(asset) };
|
|
3526
|
+
})
|
|
3527
|
+
);
|
|
3528
|
+
return mapYieldseekerAgentApy(values, days);
|
|
3529
|
+
}
|
|
3530
|
+
async loadYieldOptions(asset) {
|
|
3531
|
+
const cached = this.yieldOptions.get(asset);
|
|
3532
|
+
if (cached && cached.expiresAt > Date.now()) return cached.value;
|
|
3533
|
+
const pending = this.pendingYieldOptions.get(asset);
|
|
3534
|
+
if (pending) return pending;
|
|
3535
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3536
|
+
const request = this.api.request(
|
|
3537
|
+
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3538
|
+
).then((response) => {
|
|
3539
|
+
if (!Array.isArray(response?.yieldOptions)) {
|
|
3540
|
+
throw this.invalidResponse("yield options");
|
|
3541
|
+
}
|
|
3542
|
+
this.yieldOptions.set(asset, {
|
|
3543
|
+
expiresAt: Date.now() + YIELDSEEKER_YIELD_OPTIONS_CACHE_MS,
|
|
3544
|
+
value: response.yieldOptions
|
|
3545
|
+
});
|
|
3546
|
+
return response.yieldOptions;
|
|
3547
|
+
}).finally(() => this.pendingYieldOptions.delete(asset));
|
|
3548
|
+
this.pendingYieldOptions.set(asset, request);
|
|
3549
|
+
return request;
|
|
3550
|
+
}
|
|
3551
|
+
userKey(state, chainId) {
|
|
3552
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3553
|
+
}
|
|
3554
|
+
contextKey(state, chainId, asset) {
|
|
3555
|
+
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3556
|
+
}
|
|
3557
|
+
async resolveUser(state, chainId) {
|
|
3558
|
+
const key2 = this.userKey(state, chainId);
|
|
3559
|
+
const inMemory = this.users.get(key2);
|
|
3560
|
+
if (inMemory) return inMemory;
|
|
3561
|
+
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3562
|
+
if (persisted) {
|
|
3563
|
+
this.users.set(key2, persisted);
|
|
3564
|
+
return persisted;
|
|
3565
|
+
}
|
|
3566
|
+
const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3567
|
+
let user = null;
|
|
3568
|
+
try {
|
|
3569
|
+
const login = await this.providerRequest(
|
|
3570
|
+
state,
|
|
3571
|
+
chainId,
|
|
3572
|
+
"/users/login-with-wallet",
|
|
3573
|
+
{ method: "POST", body: { walletAddress } }
|
|
3574
|
+
);
|
|
3575
|
+
user = login?.user ?? null;
|
|
3576
|
+
if (!user) {
|
|
3577
|
+
throw this.invalidResponse("wallet login", {
|
|
3578
|
+
reason: "A successful login returned no user."
|
|
3579
|
+
});
|
|
3580
|
+
}
|
|
3581
|
+
} catch (error) {
|
|
3582
|
+
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3583
|
+
if (error instanceof OwneyError) throw error;
|
|
3584
|
+
throw this.mapApiError(error);
|
|
3585
|
+
}
|
|
3586
|
+
let created;
|
|
3587
|
+
for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
|
|
3588
|
+
try {
|
|
3589
|
+
created = await this.providerRequest(
|
|
3590
|
+
state,
|
|
3591
|
+
chainId,
|
|
3592
|
+
"/users",
|
|
3593
|
+
{
|
|
3594
|
+
method: "POST",
|
|
3595
|
+
body: {
|
|
3596
|
+
walletAddress,
|
|
3597
|
+
username: generateYieldseekerUsername()
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
);
|
|
3601
|
+
break;
|
|
3602
|
+
} catch (createError) {
|
|
3603
|
+
const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
|
|
3604
|
+
if (canRetry) continue;
|
|
3605
|
+
throw this.mapApiError(createError);
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
user = created?.user ?? null;
|
|
3609
|
+
}
|
|
3610
|
+
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3611
|
+
throw this.invalidResponse("wallet identity");
|
|
3612
|
+
}
|
|
3613
|
+
const resolved = { userId: user.userId };
|
|
3614
|
+
this.users.set(key2, resolved);
|
|
3615
|
+
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3616
|
+
return resolved;
|
|
3617
|
+
}
|
|
3618
|
+
forgetUser(state, chainId) {
|
|
3619
|
+
this.users.delete(this.userKey(state, chainId));
|
|
3620
|
+
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3621
|
+
}
|
|
3622
|
+
async ensureAgent(state, chainId, asset) {
|
|
3623
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3624
|
+
const cached = this.agentContexts.get(key2);
|
|
3625
|
+
if (cached) return cached;
|
|
3626
|
+
const pending = this.pendingAgents.get(key2);
|
|
3627
|
+
if (pending) return pending;
|
|
3628
|
+
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3629
|
+
async (context) => {
|
|
3630
|
+
if (!context) throw this.invalidResponse("agent creation");
|
|
3631
|
+
await this.deployAgent(state, chainId, context);
|
|
3632
|
+
this.agentContexts.set(key2, context);
|
|
3633
|
+
return context;
|
|
3634
|
+
}
|
|
3635
|
+
);
|
|
3636
|
+
this.pendingAgents.set(key2, request);
|
|
3637
|
+
try {
|
|
3638
|
+
return await request;
|
|
3639
|
+
} finally {
|
|
3640
|
+
this.pendingAgents.delete(key2);
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
async findAgent(state, chainId, asset) {
|
|
3644
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3645
|
+
const cached = this.agentContexts.get(key2);
|
|
3646
|
+
if (cached) return cached;
|
|
3647
|
+
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3648
|
+
if (context) this.agentContexts.set(key2, context);
|
|
3649
|
+
return context;
|
|
3650
|
+
}
|
|
3651
|
+
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3652
|
+
const user = await this.resolveUser(state, chainId);
|
|
3653
|
+
const response = await this.walletRequest(
|
|
3654
|
+
state,
|
|
3655
|
+
chainId,
|
|
3656
|
+
`/users/${user.userId}/agents`
|
|
3657
|
+
);
|
|
3658
|
+
if (!Array.isArray(response?.agents)) {
|
|
3659
|
+
throw this.invalidResponse("agent list");
|
|
3660
|
+
}
|
|
3661
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3662
|
+
let agent = response.agents.find(
|
|
3663
|
+
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3664
|
+
);
|
|
3665
|
+
if (!agent && createIfMissing) {
|
|
3666
|
+
const created = await this.walletRequest(
|
|
3667
|
+
state,
|
|
3668
|
+
chainId,
|
|
3669
|
+
`/users/${user.userId}/agents`,
|
|
3670
|
+
{
|
|
3671
|
+
method: "POST",
|
|
3672
|
+
body: {
|
|
3673
|
+
name: OWNEY_AGENT_NAME,
|
|
3674
|
+
emoji: "\u{1F989}",
|
|
3675
|
+
chainId,
|
|
3676
|
+
assetAddress: metadata.address,
|
|
3677
|
+
type: "vault",
|
|
3678
|
+
rulePreset: null
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
);
|
|
3682
|
+
agent = created?.agent;
|
|
3683
|
+
}
|
|
3684
|
+
if (!agent) return null;
|
|
3685
|
+
this.assertAgent(agent);
|
|
3686
|
+
const walletResponse = await this.walletRequest(
|
|
3687
|
+
state,
|
|
3688
|
+
chainId,
|
|
3689
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3690
|
+
);
|
|
3691
|
+
if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3692
|
+
throw this.invalidResponse("agent wallet");
|
|
3693
|
+
}
|
|
3694
|
+
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3695
|
+
}
|
|
3696
|
+
async loadPortfolio(state, chainId, options) {
|
|
3697
|
+
const user = await this.resolveUser(state, chainId);
|
|
3698
|
+
const response = await this.walletRequest(
|
|
3699
|
+
state,
|
|
3700
|
+
chainId,
|
|
3701
|
+
`/users/${user.userId}/agents`
|
|
3702
|
+
);
|
|
3703
|
+
if (!Array.isArray(response?.agents)) {
|
|
3704
|
+
throw this.invalidResponse("agent list");
|
|
3705
|
+
}
|
|
3706
|
+
const contexts = [];
|
|
3707
|
+
for (const agent of response.agents) {
|
|
3708
|
+
const asset = this.assetForAgent(agent);
|
|
3709
|
+
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3710
|
+
continue;
|
|
3711
|
+
}
|
|
3712
|
+
this.assertAgent(agent);
|
|
3713
|
+
const walletResponse = await this.walletRequest(
|
|
3714
|
+
state,
|
|
3715
|
+
chainId,
|
|
3716
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3717
|
+
);
|
|
3718
|
+
if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3719
|
+
throw this.invalidResponse("agent wallet");
|
|
3720
|
+
}
|
|
3721
|
+
const context = {
|
|
3722
|
+
user,
|
|
3723
|
+
agent,
|
|
3724
|
+
wallet: walletResponse.agentWallet,
|
|
3725
|
+
asset
|
|
3726
|
+
};
|
|
3727
|
+
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3728
|
+
contexts.push(context);
|
|
3729
|
+
}
|
|
3730
|
+
return Promise.all(
|
|
3731
|
+
contexts.map(
|
|
3732
|
+
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3733
|
+
)
|
|
3734
|
+
);
|
|
3735
|
+
}
|
|
3736
|
+
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3737
|
+
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3738
|
+
this.walletRequest(
|
|
3739
|
+
state,
|
|
3740
|
+
chainId,
|
|
3741
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3742
|
+
shouldOnlyUseRecentValue: true,
|
|
3743
|
+
shouldAllowStaleOnError: true
|
|
3744
|
+
})}`
|
|
3745
|
+
),
|
|
3746
|
+
this.walletRequest(
|
|
3747
|
+
state,
|
|
3748
|
+
chainId,
|
|
3749
|
+
this.agentPath(context, "yield-positions")
|
|
3750
|
+
),
|
|
3751
|
+
options.historic ? this.walletRequest(
|
|
3752
|
+
state,
|
|
3753
|
+
chainId,
|
|
3754
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3755
|
+
) : Promise.resolve(void 0),
|
|
3756
|
+
options.actions ? this.walletRequest(
|
|
3757
|
+
state,
|
|
3758
|
+
chainId,
|
|
3759
|
+
this.agentPath(context, "actions")
|
|
3760
|
+
) : Promise.resolve(void 0)
|
|
3761
|
+
]);
|
|
3762
|
+
if (!snapshot?.agentSnapshot) {
|
|
3763
|
+
throw this.invalidResponse("agent snapshot");
|
|
3764
|
+
}
|
|
3765
|
+
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3766
|
+
throw this.invalidResponse("yield positions");
|
|
3767
|
+
}
|
|
3768
|
+
return {
|
|
3769
|
+
...context,
|
|
3770
|
+
snapshot: snapshot.agentSnapshot,
|
|
3771
|
+
positions: positions.yieldPositions,
|
|
3772
|
+
...historic?.position ? { historic: historic.position } : {},
|
|
3773
|
+
...actions?.actions ? { actions: actions.actions } : {}
|
|
3774
|
+
};
|
|
3775
|
+
}
|
|
3776
|
+
async deployAgent(state, chainId, context) {
|
|
3777
|
+
if (context.wallet.initializedDate != null) return;
|
|
3778
|
+
const walletAddress = context.wallet.walletAddress.toLowerCase();
|
|
3779
|
+
const deployed = await this.walletRequest(
|
|
3780
|
+
state,
|
|
3781
|
+
chainId,
|
|
3782
|
+
this.agentPath(context, "deploy"),
|
|
3783
|
+
{ method: "POST", body: {} }
|
|
3784
|
+
);
|
|
3785
|
+
if (!deployed?.agentWallet || !(0, import_viem6.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
3786
|
+
throw this.invalidResponse("agent deployment", {
|
|
3787
|
+
reason: "Deploy did not return the expected Agent Wallet."
|
|
3788
|
+
});
|
|
3789
|
+
}
|
|
3790
|
+
context.wallet = deployed.agentWallet;
|
|
3791
|
+
}
|
|
3792
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3793
|
+
try {
|
|
3794
|
+
const response = await this.walletRequest(
|
|
3795
|
+
state,
|
|
3796
|
+
chainId,
|
|
3797
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3798
|
+
shouldForceRefresh: true
|
|
3799
|
+
})}`
|
|
3800
|
+
);
|
|
3801
|
+
if (!response?.agentSnapshot) {
|
|
3802
|
+
throw this.invalidResponse("agent snapshot refresh");
|
|
3803
|
+
}
|
|
3804
|
+
} catch (error) {
|
|
3805
|
+
console.warn(
|
|
3806
|
+
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3807
|
+
error
|
|
3808
|
+
);
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
agentPath(context, suffix) {
|
|
3812
|
+
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
3813
|
+
}
|
|
3814
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
3815
|
+
try {
|
|
3816
|
+
return await this.providerRequest(state, chainId, path, options);
|
|
3817
|
+
} catch (error) {
|
|
3818
|
+
throw this.mapApiError(error);
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
async providerRequest(state, chainId, path, options = {}) {
|
|
3822
|
+
this.assertChain(chainId);
|
|
3823
|
+
const request = (signature2) => this.api.request(path, {
|
|
3824
|
+
...options,
|
|
3825
|
+
signature: signature2
|
|
3826
|
+
});
|
|
3827
|
+
let signature = await this.auth.getToken(state, chainId);
|
|
3828
|
+
try {
|
|
3829
|
+
return await request(signature);
|
|
3830
|
+
} catch (error) {
|
|
3831
|
+
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
3832
|
+
if (error.providerCode === "NO_USER") throw error;
|
|
3833
|
+
if (!error.isAuthenticationError) throw error;
|
|
3834
|
+
signature = await this.auth.refreshToken(state, chainId, signature);
|
|
3835
|
+
try {
|
|
3836
|
+
return await request(signature);
|
|
3837
|
+
} catch (retryError) {
|
|
3838
|
+
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3839
|
+
this.forgetUser(state, chainId);
|
|
3840
|
+
}
|
|
3841
|
+
throw retryError;
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
mapApiError(error) {
|
|
3846
|
+
if (!(error instanceof YieldseekerApiError)) {
|
|
3847
|
+
return new OwneyError(
|
|
3848
|
+
"AGENT_API_ERROR",
|
|
3849
|
+
"Yieldseeker request failed.",
|
|
3850
|
+
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3851
|
+
this.id
|
|
3852
|
+
);
|
|
3853
|
+
}
|
|
3854
|
+
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3855
|
+
return new OwneyError(
|
|
3856
|
+
code,
|
|
3857
|
+
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3858
|
+
{
|
|
3859
|
+
statusCode: error.status,
|
|
3860
|
+
providerCode: error.providerCode,
|
|
3861
|
+
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3862
|
+
},
|
|
3863
|
+
this.id
|
|
3864
|
+
);
|
|
3865
|
+
}
|
|
3866
|
+
async submitTransaction(state, chainId, transaction) {
|
|
3867
|
+
if (this.transactionExecutor) {
|
|
3868
|
+
return this.transactionExecutor(state, chainId, transaction);
|
|
3869
|
+
}
|
|
3870
|
+
this.assertTransaction(transaction, state, chainId);
|
|
3871
|
+
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3872
|
+
const walletClient = (0, import_viem6.createWalletClient)({
|
|
3873
|
+
account,
|
|
3874
|
+
chain: import_chains3.base,
|
|
3875
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3876
|
+
});
|
|
3877
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3878
|
+
chain: import_chains3.base,
|
|
3879
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3880
|
+
});
|
|
3881
|
+
await ensureWalletOnChain(
|
|
3882
|
+
publicClient,
|
|
3883
|
+
walletClient,
|
|
3884
|
+
8453
|
|
3885
|
+
);
|
|
3886
|
+
const hash = await walletClient.sendTransaction({
|
|
3887
|
+
account,
|
|
3888
|
+
chain: import_chains3.base,
|
|
3889
|
+
to: (0, import_viem6.getAddress)(transaction.to),
|
|
3890
|
+
data: transaction.data,
|
|
3891
|
+
value: BigInt(transaction.value)
|
|
3892
|
+
});
|
|
3893
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3894
|
+
hash,
|
|
3895
|
+
confirmations: 1
|
|
3896
|
+
});
|
|
3897
|
+
if (receipt.status !== "success") {
|
|
3898
|
+
throw new OwneyError(
|
|
3899
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3900
|
+
`Yieldseeker transaction reverted (${hash}).`,
|
|
3901
|
+
{ transactionHash: hash },
|
|
3902
|
+
this.id
|
|
3903
|
+
);
|
|
3904
|
+
}
|
|
3905
|
+
return hash;
|
|
3906
|
+
}
|
|
3907
|
+
async waitForReceipt(state, chainId, transactionHash) {
|
|
3908
|
+
if (this.unwindReceiptWaiter) {
|
|
3909
|
+
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3910
|
+
return;
|
|
3911
|
+
}
|
|
3912
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3913
|
+
chain: import_chains3.base,
|
|
3914
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3915
|
+
});
|
|
3916
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3917
|
+
hash: transactionHash,
|
|
3918
|
+
confirmations: 1
|
|
3919
|
+
});
|
|
3920
|
+
if (receipt.status !== "success") {
|
|
3921
|
+
throw new OwneyError(
|
|
3922
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3923
|
+
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3924
|
+
{ transactionHash },
|
|
3925
|
+
this.id
|
|
3926
|
+
);
|
|
3927
|
+
}
|
|
3928
|
+
}
|
|
3929
|
+
assertTransaction(transaction, state, chainId) {
|
|
3930
|
+
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)) {
|
|
3931
|
+
throw this.invalidResponse("transaction");
|
|
3932
|
+
}
|
|
3933
|
+
}
|
|
3934
|
+
assertAgent(agent) {
|
|
3935
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3936
|
+
throw this.invalidResponse("agent");
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
isOwneyAgent(agent) {
|
|
3940
|
+
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3941
|
+
}
|
|
3942
|
+
assetForAgent(agent) {
|
|
3943
|
+
for (const asset of ["USDC", "WETH"]) {
|
|
3944
|
+
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3945
|
+
return asset;
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
return null;
|
|
3949
|
+
}
|
|
3950
|
+
isTransactionHash(value) {
|
|
3951
|
+
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3952
|
+
}
|
|
3953
|
+
assertChain(chainId) {
|
|
3954
|
+
if (chainId !== 8453) {
|
|
3955
|
+
throw new OwneyError(
|
|
3956
|
+
"CHAIN_UNSUPPORTED",
|
|
3957
|
+
`Yieldseeker does not support chain ${chainId}.`,
|
|
3958
|
+
{ chainId, supportedChainIds: [8453] },
|
|
3959
|
+
this.id
|
|
3960
|
+
);
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
assertOptionalChain(chainId) {
|
|
3964
|
+
if (chainId !== void 0) this.assertChain(chainId);
|
|
3965
|
+
}
|
|
3966
|
+
assertAsset(asset) {
|
|
3967
|
+
if (asset !== "USDC" && asset !== "WETH") {
|
|
3968
|
+
throw new OwneyError(
|
|
3969
|
+
"ASSET_UNSUPPORTED",
|
|
3970
|
+
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
3971
|
+
{
|
|
3972
|
+
asset,
|
|
3973
|
+
supportedAssets: ["USDC", "WETH"],
|
|
3974
|
+
providerAlsoAdvertises: ["cbBTC"]
|
|
3975
|
+
},
|
|
3976
|
+
this.id
|
|
3977
|
+
);
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
invalidResponse(operation, details = {}) {
|
|
3981
|
+
return new OwneyError(
|
|
3982
|
+
"AGENT_INVALID_RESPONSE",
|
|
3983
|
+
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3984
|
+
details,
|
|
3985
|
+
this.id
|
|
3986
|
+
);
|
|
3987
|
+
}
|
|
3988
|
+
};
|
|
3989
|
+
|
|
3990
|
+
// src/lib/routing-api.ts
|
|
3991
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3992
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3993
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3994
|
+
try {
|
|
3995
|
+
const res = await fetch(url, {
|
|
3996
|
+
method: "GET",
|
|
3997
|
+
headers: {
|
|
3998
|
+
"Content-Type": "application/json",
|
|
3999
|
+
"x-owney-api-key": `${apiKey}`
|
|
4000
|
+
}
|
|
4001
|
+
});
|
|
4002
|
+
if (!res.ok) {
|
|
4003
|
+
if (res.status !== 404) {
|
|
4004
|
+
console.warn(
|
|
4005
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
4006
|
+
);
|
|
4007
|
+
}
|
|
4008
|
+
return null;
|
|
4009
|
+
}
|
|
4010
|
+
const json = await res.json();
|
|
4011
|
+
const policy = json.success ? json.data ?? null : null;
|
|
4012
|
+
debugLog(
|
|
4013
|
+
"owney-sdk",
|
|
4014
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
4015
|
+
policy ?? void 0
|
|
4016
|
+
);
|
|
4017
|
+
return policy;
|
|
4018
|
+
} catch (error) {
|
|
4019
|
+
console.warn(
|
|
4020
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
4021
|
+
error instanceof Error ? error.message : String(error)
|
|
4022
|
+
);
|
|
4023
|
+
return null;
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
4026
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
4027
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
4028
|
+
const res = await fetch(url, {
|
|
4029
|
+
method: "GET",
|
|
4030
|
+
headers: {
|
|
4031
|
+
"Content-Type": "application/json",
|
|
4032
|
+
"x-owney-api-key": `${apiKey}`
|
|
4033
|
+
}
|
|
4034
|
+
});
|
|
4035
|
+
if (!res.ok) {
|
|
4036
|
+
const text = await res.text().catch(() => "");
|
|
4037
|
+
throw new OwneyError(
|
|
4038
|
+
"API_ROUTING_ERROR",
|
|
4039
|
+
`Routing API error ${res.status}: ${text}`,
|
|
4040
|
+
{ statusCode: res.status, responseBody: text }
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
4043
|
+
const json = await res.json();
|
|
4044
|
+
if (!json.success) {
|
|
4045
|
+
throw new OwneyError(
|
|
4046
|
+
"API_ROUTING_FAILED",
|
|
4047
|
+
`Routing API request failed: ${json.message}`,
|
|
4048
|
+
{ message: json.message }
|
|
4049
|
+
);
|
|
4050
|
+
}
|
|
4051
|
+
return json.data;
|
|
4052
|
+
}
|
|
4053
|
+
|
|
4054
|
+
// src/lib/health-report.ts
|
|
4055
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
4056
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
4057
|
+
try {
|
|
4058
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
4059
|
+
method: "POST",
|
|
4060
|
+
headers: {
|
|
4061
|
+
"Content-Type": "application/json",
|
|
4062
|
+
"x-owney-api-key": apiKey
|
|
4063
|
+
},
|
|
4064
|
+
body: JSON.stringify({
|
|
4065
|
+
agent_type: agentType,
|
|
4066
|
+
error_code: errorCode,
|
|
4067
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4068
|
+
})
|
|
4069
|
+
});
|
|
4070
|
+
} catch (err) {
|
|
4071
|
+
console.warn(
|
|
4072
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
4073
|
+
err instanceof Error ? err.message : err
|
|
4074
|
+
);
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
4078
|
+
try {
|
|
4079
|
+
return await fn();
|
|
4080
|
+
} catch (err) {
|
|
4081
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
4082
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
4083
|
+
throw err;
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
|
|
4087
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
4088
|
+
var import_viem7 = require("viem");
|
|
4089
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
4090
|
+
const target = asset.toUpperCase();
|
|
4091
|
+
return agents.map((agent) => {
|
|
4092
|
+
const agentBalance = aggregated[agent.id];
|
|
4093
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
4094
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4095
|
+
);
|
|
4096
|
+
let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
4097
|
+
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
4098
|
+
const chainNameById = {
|
|
4099
|
+
1: "ETHEREUM",
|
|
4100
|
+
8453: "BASE",
|
|
4101
|
+
42161: "ARBITRUM"
|
|
4102
|
+
};
|
|
4103
|
+
const targetChain = chainNameById[chainId];
|
|
4104
|
+
for (const position2 of agentBalance?.positions ?? []) {
|
|
4105
|
+
const positionChain = position2.chain.trim().toUpperCase();
|
|
4106
|
+
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
4107
|
+
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
4108
|
+
if (position2.amountRaw !== void 0) {
|
|
4109
|
+
try {
|
|
4110
|
+
balance += BigInt(position2.amountRaw);
|
|
4111
|
+
continue;
|
|
4112
|
+
} catch {
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
return { agent, balance };
|
|
4119
|
+
});
|
|
4120
|
+
}
|
|
4121
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
4122
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
4123
|
+
agent,
|
|
4124
|
+
balance,
|
|
4125
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
4126
|
+
}));
|
|
4127
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
4128
|
+
let remainder = requested - assigned;
|
|
4129
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
4130
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
4131
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
4132
|
+
});
|
|
4133
|
+
for (const p of byHeadroom) {
|
|
4134
|
+
if (remainder === 0n) break;
|
|
4135
|
+
const headroom = p.balance - p.planned;
|
|
4136
|
+
if (headroom <= 0n) continue;
|
|
4137
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
4138
|
+
p.planned += take;
|
|
4139
|
+
remainder -= take;
|
|
4140
|
+
}
|
|
4141
|
+
return plans;
|
|
4142
|
+
}
|
|
4143
|
+
function planDisabledDrain(disabled, requested) {
|
|
4144
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
4145
|
+
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
4146
|
+
);
|
|
4147
|
+
const plans = [];
|
|
4148
|
+
let remaining = requested;
|
|
4149
|
+
for (const { agent, balance } of sorted) {
|
|
4150
|
+
if (remaining === 0n) {
|
|
4151
|
+
plans.push({ agent, balance, planned: 0n });
|
|
4152
|
+
continue;
|
|
4153
|
+
}
|
|
4154
|
+
const take = balance < remaining ? balance : remaining;
|
|
4155
|
+
plans.push({ agent, balance, planned: take });
|
|
4156
|
+
remaining -= take;
|
|
4157
|
+
}
|
|
4158
|
+
return { plans, remaining };
|
|
4159
|
+
}
|
|
4160
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
4161
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
4162
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
4163
|
+
const totalHeadroom = candidates.reduce(
|
|
4164
|
+
(s, c) => s + (c.balance - c.planned),
|
|
4165
|
+
0n
|
|
4166
|
+
);
|
|
4167
|
+
if (totalHeadroom === 0n) return;
|
|
4168
|
+
let distributed = 0n;
|
|
4169
|
+
for (const c of candidates) {
|
|
4170
|
+
const headroom = c.balance - c.planned;
|
|
4171
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
4172
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
4173
|
+
c.planned += give;
|
|
4174
|
+
distributed += give;
|
|
4175
|
+
}
|
|
4176
|
+
let leftover = amount - distributed;
|
|
4177
|
+
for (const c of candidates) {
|
|
4178
|
+
if (leftover === 0n) break;
|
|
4179
|
+
const headroom = c.balance - c.planned;
|
|
4180
|
+
if (headroom <= 0n) continue;
|
|
4181
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
4182
|
+
c.planned += take;
|
|
4183
|
+
leftover -= take;
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
function sumWithdrawnAmount(results) {
|
|
4187
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
4191
|
+
function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
4192
|
+
if (!tokenSymbol) {
|
|
4193
|
+
const total = Number(balance.totalBalance);
|
|
4194
|
+
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
2319
4195
|
}
|
|
2320
4196
|
const normalizedToken = tokenSymbol.toUpperCase();
|
|
4197
|
+
const snapshots = balance.assetBalances?.filter(
|
|
4198
|
+
(token) => Number(token.chainId) === chainId && String(token.asset).toUpperCase() === normalizedToken
|
|
4199
|
+
);
|
|
4200
|
+
if (snapshots?.length) {
|
|
4201
|
+
const amount = snapshots.reduce((sum, token) => sum + Number(token.amount), 0);
|
|
4202
|
+
if (Number.isFinite(amount)) return Math.max(0, amount);
|
|
4203
|
+
}
|
|
2321
4204
|
return balance.tokens.reduce((total, token) => {
|
|
2322
4205
|
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
2323
4206
|
return total;
|
|
@@ -2357,469 +4240,336 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
2357
4240
|
const weights = {};
|
|
2358
4241
|
for (const id of Object.keys(agentApys)) {
|
|
2359
4242
|
const cells = agentApys[id].apyByChainAndAsset;
|
|
2360
|
-
const balance = agentBalances[id] ?? 0;
|
|
2361
|
-
if (!cells || balance <= 0) continue;
|
|
2362
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
2363
|
-
if (!perAsset) continue;
|
|
2364
|
-
const chainId = Number(chainKey);
|
|
2365
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
2366
|
-
const apy = Number(apyValue ?? 0);
|
|
2367
|
-
if (apy === 0) continue;
|
|
2368
|
-
sums[chainId] ??= {};
|
|
2369
|
-
weights[chainId] ??= {};
|
|
2370
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
2371
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2375
|
-
const out = {};
|
|
2376
|
-
for (const chainKey of Object.keys(sums)) {
|
|
2377
|
-
const chainId = Number(chainKey);
|
|
2378
|
-
const perAssetOut = {};
|
|
2379
|
-
for (const asset of Object.keys(sums[chainId])) {
|
|
2380
|
-
const w = weights[chainId][asset];
|
|
2381
|
-
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
2382
|
-
}
|
|
2383
|
-
if (Object.keys(perAssetOut).length > 0) {
|
|
2384
|
-
out[chainId] = perAssetOut;
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
return out;
|
|
2388
|
-
}
|
|
2389
|
-
|
|
2390
|
-
// src/client.ts
|
|
2391
|
-
var import_viem6 = require("viem");
|
|
2392
|
-
var import_chains2 = require("viem/chains");
|
|
2393
|
-
|
|
2394
|
-
// src/lib/transfer-auth.ts
|
|
2395
|
-
var import_viem3 = require("viem");
|
|
2396
|
-
var ERC20_META_ABI = [
|
|
2397
|
-
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2398
|
-
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
2399
|
-
];
|
|
2400
|
-
function buildTransferWithAuthorizationTypedData(input) {
|
|
2401
|
-
return {
|
|
2402
|
-
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
2403
|
-
types: {
|
|
2404
|
-
TransferWithAuthorization: [
|
|
2405
|
-
{ name: "from", type: "address" },
|
|
2406
|
-
{ name: "to", type: "address" },
|
|
2407
|
-
{ name: "value", type: "uint256" },
|
|
2408
|
-
{ name: "validAfter", type: "uint256" },
|
|
2409
|
-
{ name: "validBefore", type: "uint256" },
|
|
2410
|
-
{ name: "nonce", type: "bytes32" }
|
|
2411
|
-
]
|
|
2412
|
-
},
|
|
2413
|
-
primaryType: "TransferWithAuthorization",
|
|
2414
|
-
message: input.message
|
|
2415
|
-
};
|
|
2416
|
-
}
|
|
2417
|
-
async function readTokenMeta(publicClient, token) {
|
|
2418
|
-
const [tokenName, tokenVersion] = await Promise.all([
|
|
2419
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
2420
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
2421
|
-
]);
|
|
2422
|
-
return { tokenName, tokenVersion };
|
|
2423
|
-
}
|
|
2424
|
-
function randomAuthNonce() {
|
|
2425
|
-
const bytes = new Uint8Array(32);
|
|
2426
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2427
|
-
return (0, import_viem3.bytesToHex)(bytes);
|
|
2428
|
-
}
|
|
2429
|
-
|
|
2430
|
-
// src/lib/sponsor-client.ts
|
|
2431
|
-
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2432
|
-
async function postSponsorTransferAuth(input) {
|
|
2433
|
-
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2434
|
-
let res;
|
|
2435
|
-
try {
|
|
2436
|
-
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2437
|
-
method: "POST",
|
|
2438
|
-
headers: {
|
|
2439
|
-
"content-type": "application/json",
|
|
2440
|
-
"x-owney-api-key": input.apiKey
|
|
2441
|
-
},
|
|
2442
|
-
body: JSON.stringify(input.body)
|
|
2443
|
-
});
|
|
2444
|
-
} catch (networkError) {
|
|
2445
|
-
throw new OwneyError(
|
|
2446
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2447
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2448
|
-
{ cause: String(networkError) }
|
|
2449
|
-
);
|
|
2450
|
-
}
|
|
2451
|
-
const text = await res.text();
|
|
2452
|
-
let parsed = null;
|
|
2453
|
-
try {
|
|
2454
|
-
parsed = JSON.parse(text);
|
|
2455
|
-
} catch {
|
|
2456
|
-
}
|
|
2457
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2458
|
-
throw new OwneyError(
|
|
2459
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2460
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2461
|
-
{
|
|
2462
|
-
statusCode: res.status,
|
|
2463
|
-
responseBody: text.slice(0, 500),
|
|
2464
|
-
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2465
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2466
|
-
safeToFallback: res.status === 503
|
|
2467
|
-
}
|
|
2468
|
-
);
|
|
2469
|
-
}
|
|
2470
|
-
return parsed.data;
|
|
2471
|
-
}
|
|
2472
|
-
async function postSponsorPermit2Transfer(input) {
|
|
2473
|
-
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2474
|
-
let res;
|
|
2475
|
-
try {
|
|
2476
|
-
res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
|
|
2477
|
-
method: "POST",
|
|
2478
|
-
headers: {
|
|
2479
|
-
"content-type": "application/json",
|
|
2480
|
-
"x-owney-api-key": input.apiKey
|
|
2481
|
-
},
|
|
2482
|
-
body: JSON.stringify(input.body)
|
|
2483
|
-
});
|
|
2484
|
-
} catch (networkError) {
|
|
2485
|
-
throw new OwneyError(
|
|
2486
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2487
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2488
|
-
{ cause: String(networkError), safeToFallback: false }
|
|
2489
|
-
);
|
|
2490
|
-
}
|
|
2491
|
-
const text = await res.text();
|
|
2492
|
-
let parsed = null;
|
|
2493
|
-
try {
|
|
2494
|
-
parsed = JSON.parse(text);
|
|
2495
|
-
} catch {
|
|
2496
|
-
}
|
|
2497
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2498
|
-
throw new OwneyError(
|
|
2499
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2500
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2501
|
-
{
|
|
2502
|
-
statusCode: res.status,
|
|
2503
|
-
responseBody: text.slice(0, 500),
|
|
2504
|
-
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
2505
|
-
}
|
|
2506
|
-
);
|
|
2507
|
-
}
|
|
2508
|
-
return parsed.data;
|
|
2509
|
-
}
|
|
2510
|
-
async function getSponsorRelayerAddress(input) {
|
|
2511
|
-
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2512
|
-
let res;
|
|
2513
|
-
try {
|
|
2514
|
-
res = await fetch(
|
|
2515
|
-
`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2516
|
-
{
|
|
2517
|
-
headers: { "x-owney-api-key": input.apiKey }
|
|
2518
|
-
}
|
|
2519
|
-
);
|
|
2520
|
-
} catch (networkError) {
|
|
2521
|
-
throw new OwneyError(
|
|
2522
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2523
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2524
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
2525
|
-
);
|
|
2526
|
-
}
|
|
2527
|
-
const text = await res.text();
|
|
2528
|
-
let parsed = null;
|
|
2529
|
-
try {
|
|
2530
|
-
parsed = JSON.parse(text);
|
|
2531
|
-
} catch {
|
|
2532
|
-
}
|
|
2533
|
-
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
2534
|
-
throw new OwneyError(
|
|
2535
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2536
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2537
|
-
{
|
|
2538
|
-
statusCode: res.status,
|
|
2539
|
-
responseBody: text.slice(0, 500),
|
|
2540
|
-
safeToFallback: true
|
|
2541
|
-
}
|
|
2542
|
-
);
|
|
2543
|
-
}
|
|
2544
|
-
return parsed.data.relayer;
|
|
2545
|
-
}
|
|
2546
|
-
|
|
2547
|
-
// src/lib/permit2.ts
|
|
2548
|
-
var import_viem4 = require("viem");
|
|
2549
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2550
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2551
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
2552
|
-
{
|
|
2553
|
-
type: "function",
|
|
2554
|
-
name: "allowance",
|
|
2555
|
-
stateMutability: "view",
|
|
2556
|
-
inputs: [
|
|
2557
|
-
{ name: "owner", type: "address" },
|
|
2558
|
-
{ name: "spender", type: "address" }
|
|
2559
|
-
],
|
|
2560
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2561
|
-
},
|
|
2562
|
-
{
|
|
2563
|
-
type: "function",
|
|
2564
|
-
name: "approve",
|
|
2565
|
-
stateMutability: "nonpayable",
|
|
2566
|
-
inputs: [
|
|
2567
|
-
{ name: "spender", type: "address" },
|
|
2568
|
-
{ name: "amount", type: "uint256" }
|
|
2569
|
-
],
|
|
2570
|
-
outputs: [{ name: "", type: "bool" }]
|
|
2571
|
-
},
|
|
2572
|
-
{
|
|
2573
|
-
type: "function",
|
|
2574
|
-
name: "balanceOf",
|
|
2575
|
-
stateMutability: "view",
|
|
2576
|
-
inputs: [{ name: "account", type: "address" }],
|
|
2577
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
4243
|
+
const balance = agentBalances[id] ?? 0;
|
|
4244
|
+
if (!cells || balance <= 0) continue;
|
|
4245
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
4246
|
+
if (!perAsset) continue;
|
|
4247
|
+
const chainId = Number(chainKey);
|
|
4248
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
4249
|
+
const apy = Number(apyValue ?? 0);
|
|
4250
|
+
if (apy === 0) continue;
|
|
4251
|
+
sums[chainId] ??= {};
|
|
4252
|
+
weights[chainId] ??= {};
|
|
4253
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
4254
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
2578
4257
|
}
|
|
2579
|
-
|
|
2580
|
-
|
|
4258
|
+
const out = {};
|
|
4259
|
+
for (const chainKey of Object.keys(sums)) {
|
|
4260
|
+
const chainId = Number(chainKey);
|
|
4261
|
+
const perAssetOut = {};
|
|
4262
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
4263
|
+
const w = weights[chainId][asset];
|
|
4264
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
4265
|
+
}
|
|
4266
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
4267
|
+
out[chainId] = perAssetOut;
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
return out;
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4273
|
+
// src/client.ts
|
|
4274
|
+
var import_viem11 = require("viem");
|
|
4275
|
+
var import_chains4 = require("viem/chains");
|
|
4276
|
+
|
|
4277
|
+
// src/lib/sponsored-token-batch.ts
|
|
4278
|
+
var import_viem9 = require("viem");
|
|
4279
|
+
|
|
4280
|
+
// src/lib/permit2-batch.ts
|
|
4281
|
+
var import_viem8 = require("viem");
|
|
4282
|
+
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4283
|
+
var PERMIT_BATCH_TYPES = {
|
|
4284
|
+
PermitBatchWitnessTransferFrom: [
|
|
4285
|
+
{ name: "permitted", type: "TokenPermissions[]" },
|
|
4286
|
+
{ name: "spender", type: "address" },
|
|
4287
|
+
{ name: "nonce", type: "uint256" },
|
|
4288
|
+
{ name: "deadline", type: "uint256" },
|
|
4289
|
+
{ name: "witness", type: "Deposit" }
|
|
4290
|
+
],
|
|
4291
|
+
Deposit: [{ name: "recipients", type: "address[]" }],
|
|
4292
|
+
TokenPermissions: [
|
|
4293
|
+
{ name: "token", type: "address" },
|
|
4294
|
+
{ name: "amount", type: "uint256" }
|
|
4295
|
+
]
|
|
4296
|
+
};
|
|
4297
|
+
var PERMIT2_BATCH_ABI = (0, import_viem8.parseAbi)([
|
|
4298
|
+
"struct TokenPermissions { address token; uint256 amount; }",
|
|
4299
|
+
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
4300
|
+
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
4301
|
+
"function permitWitnessTransferFrom(PermitBatchTransferFrom permit,SignatureTransferDetails[] transferDetails,address owner,bytes32 witness,string witnessTypeString,bytes signature)",
|
|
4302
|
+
"function nonceBitmap(address owner,uint256 wordPos) view returns (uint256)"
|
|
4303
|
+
]);
|
|
4304
|
+
function batchPermit(b) {
|
|
4305
|
+
return {
|
|
4306
|
+
permitted: b.transfers.map((t) => ({
|
|
4307
|
+
token: b.token,
|
|
4308
|
+
amount: BigInt(t.amount)
|
|
4309
|
+
})),
|
|
4310
|
+
nonce: BigInt(b.nonce),
|
|
4311
|
+
deadline: BigInt(b.deadline)
|
|
4312
|
+
};
|
|
4313
|
+
}
|
|
4314
|
+
function batchTypedData(b, spender) {
|
|
2581
4315
|
return {
|
|
2582
4316
|
domain: {
|
|
2583
4317
|
name: "Permit2",
|
|
2584
|
-
chainId:
|
|
2585
|
-
verifyingContract:
|
|
2586
|
-
},
|
|
2587
|
-
types: {
|
|
2588
|
-
PermitTransferFrom: [
|
|
2589
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
2590
|
-
{ name: "spender", type: "address" },
|
|
2591
|
-
{ name: "nonce", type: "uint256" },
|
|
2592
|
-
{ name: "deadline", type: "uint256" }
|
|
2593
|
-
],
|
|
2594
|
-
TokenPermissions: [
|
|
2595
|
-
{ name: "token", type: "address" },
|
|
2596
|
-
{ name: "amount", type: "uint256" }
|
|
2597
|
-
]
|
|
4318
|
+
chainId: b.chainId,
|
|
4319
|
+
verifyingContract: BATCH_PERMIT2_ADDRESS
|
|
2598
4320
|
},
|
|
2599
|
-
|
|
2600
|
-
|
|
4321
|
+
types: PERMIT_BATCH_TYPES,
|
|
4322
|
+
primaryType: "PermitBatchWitnessTransferFrom",
|
|
4323
|
+
message: {
|
|
4324
|
+
...batchPermit(b),
|
|
4325
|
+
spender,
|
|
4326
|
+
witness: { recipients: b.transfers.map((t) => t.to) }
|
|
4327
|
+
}
|
|
2601
4328
|
};
|
|
2602
4329
|
}
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
4330
|
+
|
|
4331
|
+
// src/lib/sponsored-token-batch.ts
|
|
4332
|
+
var memory = /* @__PURE__ */ new Map();
|
|
4333
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
4334
|
+
var planOf = (transfers) => JSON.stringify(
|
|
4335
|
+
transfers.map((t) => ({
|
|
4336
|
+
to: t.to.toLowerCase(),
|
|
4337
|
+
amount: BigInt(t.amount).toString()
|
|
4338
|
+
}))
|
|
4339
|
+
);
|
|
4340
|
+
function read(key2) {
|
|
4341
|
+
return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
|
|
2607
4342
|
}
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
functionName: "allowance",
|
|
2613
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
4343
|
+
function save(key2, body) {
|
|
4344
|
+
const value = JSON.stringify({
|
|
4345
|
+
...body,
|
|
4346
|
+
transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
|
|
2614
4347
|
});
|
|
4348
|
+
if (typeof window === "undefined") memory.set(key2, value);
|
|
4349
|
+
else window.localStorage.setItem(key2, value);
|
|
2615
4350
|
}
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2620
|
-
functionName: "balanceOf",
|
|
2621
|
-
args: [owner]
|
|
2622
|
-
});
|
|
4351
|
+
function clear(key2) {
|
|
4352
|
+
if (typeof window === "undefined") memory.delete(key2);
|
|
4353
|
+
else window.localStorage.removeItem(key2);
|
|
2623
4354
|
}
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
4355
|
+
function sponsorTokenBatch(i) {
|
|
4356
|
+
const key2 = `owney.token-batch.v1:${(0, import_viem9.keccak256)((0, import_viem9.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
|
|
4357
|
+
const plan = planOf(i.transfers);
|
|
4358
|
+
const active = inflight.get(key2);
|
|
4359
|
+
if (active) {
|
|
4360
|
+
if (active.plan !== plan)
|
|
4361
|
+
return Promise.reject(
|
|
4362
|
+
new Error(
|
|
4363
|
+
"A token deposit is already in progress. Wait for its result before depositing again."
|
|
4364
|
+
)
|
|
4365
|
+
);
|
|
4366
|
+
return active.promise;
|
|
4367
|
+
}
|
|
4368
|
+
const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
|
|
4369
|
+
inflight.set(key2, { plan, promise });
|
|
4370
|
+
return promise;
|
|
2633
4371
|
}
|
|
2634
|
-
async function
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
{
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
4372
|
+
async function execute(i, key2, plan) {
|
|
4373
|
+
if (!i.transfers.length || i.transfers.length > 16 || i.transfers.some(
|
|
4374
|
+
(t) => BigInt(t.amount) <= 0n || BigInt(t.amount) >= 1n << 256n
|
|
4375
|
+
) || new Set(i.transfers.map((t) => t.to.toLowerCase())).size !== i.transfers.length)
|
|
4376
|
+
throw new Error("Invalid token deposit shares.");
|
|
4377
|
+
const send = async (initial) => {
|
|
4378
|
+
let body = initial;
|
|
4379
|
+
save(key2, body);
|
|
4380
|
+
try {
|
|
4381
|
+
if (!body.serializedTransaction) {
|
|
4382
|
+
const prepared = await postSponsorBatchTransfer({
|
|
4383
|
+
apiKey: i.apiKey,
|
|
4384
|
+
baseUrl: i.baseUrl,
|
|
4385
|
+
body
|
|
4386
|
+
});
|
|
4387
|
+
if (!prepared.serializedTransaction || (0, import_viem9.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
|
|
4388
|
+
throw new Error(
|
|
4389
|
+
"Sponsorship API did not return a valid prepared transaction."
|
|
4390
|
+
);
|
|
4391
|
+
body = {
|
|
4392
|
+
...body,
|
|
4393
|
+
serializedTransaction: prepared.serializedTransaction
|
|
4394
|
+
};
|
|
4395
|
+
save(key2, body);
|
|
2647
4396
|
}
|
|
2648
|
-
|
|
4397
|
+
const result = await postSponsorBatchTransfer({
|
|
4398
|
+
apiKey: i.apiKey,
|
|
4399
|
+
baseUrl: i.baseUrl,
|
|
4400
|
+
body
|
|
4401
|
+
});
|
|
4402
|
+
if (result.txHash !== (0, import_viem9.keccak256)(body.serializedTransaction))
|
|
4403
|
+
throw new Error(
|
|
4404
|
+
"Sponsorship receipt does not match the pending transaction."
|
|
4405
|
+
);
|
|
4406
|
+
clear(key2);
|
|
4407
|
+
return result.txHash;
|
|
4408
|
+
} catch (error) {
|
|
4409
|
+
if (!body.serializedTransaction || error instanceof OwneyError && error.details?.notSubmitted === true)
|
|
4410
|
+
clear(key2);
|
|
4411
|
+
throw error;
|
|
4412
|
+
}
|
|
4413
|
+
};
|
|
4414
|
+
const saved = read(key2);
|
|
4415
|
+
if (saved) {
|
|
4416
|
+
const previous = JSON.parse(saved);
|
|
4417
|
+
if (previous.chainId !== i.chainId || !(0, import_viem9.isAddressEqual)(previous.from, i.owner) || !(0, import_viem9.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
4418
|
+
throw new Error(
|
|
4419
|
+
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
4420
|
+
);
|
|
4421
|
+
i.onApproved?.();
|
|
4422
|
+
return send({ ...previous, transfers: i.transfers });
|
|
2649
4423
|
}
|
|
2650
|
-
const
|
|
2651
|
-
|
|
4424
|
+
const total = i.transfers.reduce((sum, t) => sum + BigInt(t.amount), 0n);
|
|
4425
|
+
const [balance, allowance] = await Promise.all([
|
|
4426
|
+
readErc20Balance(i.pub, i.token, i.owner),
|
|
4427
|
+
readPermit2Allowance(i.pub, i.token, i.owner)
|
|
4428
|
+
]);
|
|
4429
|
+
if (balance < total)
|
|
2652
4430
|
throw new OwneyError(
|
|
2653
|
-
"
|
|
2654
|
-
|
|
2655
|
-
{ expectedChainId: expected, actualChainId: after }
|
|
4431
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4432
|
+
"Insufficient token balance for this deposit."
|
|
2656
4433
|
);
|
|
2657
|
-
|
|
4434
|
+
if (allowance < total)
|
|
4435
|
+
throw new OwneyError(
|
|
4436
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
4437
|
+
"token deposits need a one-time Permit2 approval."
|
|
4438
|
+
);
|
|
4439
|
+
const relayer = await getSponsorRelayerAddress({
|
|
4440
|
+
apiKey: i.apiKey,
|
|
4441
|
+
baseUrl: i.baseUrl,
|
|
4442
|
+
chainId: i.chainId
|
|
4443
|
+
});
|
|
4444
|
+
const now = (await i.pub.getBlock()).timestamp;
|
|
4445
|
+
const unsigned = {
|
|
4446
|
+
chainId: i.chainId,
|
|
4447
|
+
token: i.token,
|
|
4448
|
+
from: i.owner,
|
|
4449
|
+
transfers: i.transfers,
|
|
4450
|
+
nonce: randomPermit2Nonce().toString(),
|
|
4451
|
+
deadline: (now + 900n).toString()
|
|
4452
|
+
};
|
|
4453
|
+
const signature = await i.wallet.signTypedData({
|
|
4454
|
+
account: i.owner,
|
|
4455
|
+
...batchTypedData(unsigned, relayer)
|
|
4456
|
+
});
|
|
4457
|
+
i.onApproved?.();
|
|
4458
|
+
return send({ ...unsigned, signature });
|
|
2658
4459
|
}
|
|
2659
4460
|
|
|
2660
|
-
// src/lib/sponsored-deposit.ts
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
return async (smartWallet, chainId, amount) => {
|
|
2665
|
-
const cid = chainId;
|
|
2666
|
-
const token = deps.tokenAddressByChain[cid];
|
|
2667
|
-
if (!token) {
|
|
4461
|
+
// src/lib/sponsored-token-deposit.ts
|
|
4462
|
+
function makeSponsoredTokenCallback(deps) {
|
|
4463
|
+
const batch = async (chainId, transfers) => {
|
|
4464
|
+
if (chainId !== 8453 && chainId !== 42161 && chainId !== 1)
|
|
2668
4465
|
throw new OwneyError(
|
|
2669
4466
|
"CHAIN_UNSUPPORTED",
|
|
2670
4467
|
`No sponsored token configured for chain ${chainId}`
|
|
2671
4468
|
);
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2678
|
-
if (balance < BigInt(amount)) {
|
|
2679
|
-
throw new OwneyError(
|
|
2680
|
-
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2681
|
-
"Insufficient balance for this deposit.",
|
|
2682
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2683
|
-
);
|
|
2684
|
-
}
|
|
2685
|
-
} catch (err) {
|
|
2686
|
-
if (err instanceof OwneyError) throw err;
|
|
2687
|
-
console.warn(
|
|
2688
|
-
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2689
|
-
err instanceof Error ? err.message : String(err)
|
|
4469
|
+
const token = deps.tokenAddressByChain[chainId];
|
|
4470
|
+
if (!token)
|
|
4471
|
+
throw new OwneyError(
|
|
4472
|
+
"CHAIN_UNSUPPORTED",
|
|
4473
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2690
4474
|
);
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
const validBefore = BigInt(
|
|
2695
|
-
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2696
|
-
);
|
|
2697
|
-
const nonce = randomAuthNonce();
|
|
2698
|
-
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2699
|
-
token,
|
|
2700
|
-
chainId: cid,
|
|
2701
|
-
tokenName,
|
|
2702
|
-
tokenVersion,
|
|
2703
|
-
message: {
|
|
2704
|
-
from: deps.ownerAddress,
|
|
2705
|
-
to: smartWallet,
|
|
2706
|
-
value: BigInt(amount),
|
|
2707
|
-
validAfter,
|
|
2708
|
-
validBefore,
|
|
2709
|
-
nonce
|
|
2710
|
-
}
|
|
2711
|
-
});
|
|
2712
|
-
const authSignature = await wallet.signTypedData({
|
|
2713
|
-
account: deps.ownerAddress,
|
|
2714
|
-
...typedData
|
|
2715
|
-
});
|
|
2716
|
-
deps.onApproved?.();
|
|
2717
|
-
const result = await post({
|
|
2718
|
-
baseUrl: deps.baseUrl,
|
|
4475
|
+
const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
|
|
4476
|
+
await ensureWalletOnChain(pub, wallet, chainId);
|
|
4477
|
+
return sponsorTokenBatch({
|
|
2719
4478
|
apiKey: deps.apiKey,
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
nonce,
|
|
2729
|
-
authSignature,
|
|
2730
|
-
tokenName,
|
|
2731
|
-
tokenVersion
|
|
2732
|
-
}
|
|
4479
|
+
baseUrl: deps.baseUrl,
|
|
4480
|
+
owner: deps.ownerAddress,
|
|
4481
|
+
token,
|
|
4482
|
+
chainId,
|
|
4483
|
+
transfers,
|
|
4484
|
+
pub,
|
|
4485
|
+
wallet,
|
|
4486
|
+
onApproved: deps.onApproved
|
|
2733
4487
|
});
|
|
2734
|
-
return result.txHash;
|
|
2735
4488
|
};
|
|
4489
|
+
const callback = makeVerificationAwareDepositCallback(
|
|
4490
|
+
(to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
|
|
4491
|
+
);
|
|
4492
|
+
registerDepositBatch(callback, batch);
|
|
4493
|
+
return callback;
|
|
2736
4494
|
}
|
|
2737
4495
|
|
|
2738
|
-
// src/lib/
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
const
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
4496
|
+
// src/lib/agent-deposit-batch.ts
|
|
4497
|
+
function deferred() {
|
|
4498
|
+
let resolve, reject;
|
|
4499
|
+
const promise = new Promise((yes, no) => {
|
|
4500
|
+
resolve = yes;
|
|
4501
|
+
reject = no;
|
|
4502
|
+
});
|
|
4503
|
+
void promise.catch(() => {
|
|
4504
|
+
});
|
|
4505
|
+
return { promise, resolve, reject };
|
|
4506
|
+
}
|
|
4507
|
+
async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
4508
|
+
const funding = deferred();
|
|
4509
|
+
const tasks = [];
|
|
4510
|
+
const transfers = [];
|
|
4511
|
+
try {
|
|
4512
|
+
for (const leg of legs) {
|
|
4513
|
+
const ready = deferred();
|
|
4514
|
+
let entered = false;
|
|
4515
|
+
const callback = makeVerificationAwareDepositCallback(
|
|
4516
|
+
(to, cid, amount, verification) => {
|
|
4517
|
+
if (entered || cid !== chainId || BigInt(amount) !== BigInt(leg.amount)) {
|
|
4518
|
+
const error = new Error(
|
|
4519
|
+
"Agent changed its prepared deposit share."
|
|
4520
|
+
);
|
|
4521
|
+
ready.reject(error);
|
|
4522
|
+
throw error;
|
|
4523
|
+
}
|
|
4524
|
+
entered = true;
|
|
4525
|
+
ready.resolve(toBatchTransfer(to, amount, verification));
|
|
4526
|
+
return funding.promise;
|
|
4527
|
+
}
|
|
2750
4528
|
);
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
"Insufficient WETH balance for this deposit.",
|
|
2762
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2763
|
-
);
|
|
2764
|
-
}
|
|
2765
|
-
} catch (err) {
|
|
2766
|
-
if (err instanceof OwneyError) throw err;
|
|
2767
|
-
console.warn(
|
|
2768
|
-
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
2769
|
-
err instanceof Error ? err.message : String(err)
|
|
4529
|
+
const task = Promise.resolve().then(() => leg.run(callback));
|
|
4530
|
+
tasks.push(task);
|
|
4531
|
+
void task.then(
|
|
4532
|
+
() => {
|
|
4533
|
+
if (!entered)
|
|
4534
|
+
ready.reject(
|
|
4535
|
+
new Error("Agent did not prepare a deposit transfer.")
|
|
4536
|
+
);
|
|
4537
|
+
},
|
|
4538
|
+
(error) => ready.reject(error)
|
|
2770
4539
|
);
|
|
4540
|
+
transfers.push(await ready.promise);
|
|
4541
|
+
}
|
|
4542
|
+
const txHash = await transfer(chainId, transfers);
|
|
4543
|
+
funding.resolve(txHash);
|
|
4544
|
+
const settled = await Promise.allSettled(tasks);
|
|
4545
|
+
const agentResults = {};
|
|
4546
|
+
const failures = [];
|
|
4547
|
+
for (const [index, result] of settled.entries()) {
|
|
4548
|
+
if (result.status === "fulfilled")
|
|
4549
|
+
agentResults[legs[index].id] = result.value;
|
|
4550
|
+
else failures.push(legs[index].id);
|
|
2771
4551
|
}
|
|
2772
|
-
|
|
2773
|
-
if (allowance < amountWei) {
|
|
4552
|
+
if (failures.length)
|
|
2774
4553
|
throw new OwneyError(
|
|
2775
|
-
"
|
|
2776
|
-
"
|
|
2777
|
-
{
|
|
4554
|
+
"DEPOSIT_PARTIAL_FAILURE",
|
|
4555
|
+
"The deposit was sent to all agents, but some agent updates could not be confirmed. Check activity before depositing again.",
|
|
4556
|
+
{
|
|
4557
|
+
txHash,
|
|
4558
|
+
fundsSubmitted: true,
|
|
4559
|
+
agentResults,
|
|
4560
|
+
failedAgentIds: failures
|
|
4561
|
+
}
|
|
2778
4562
|
);
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
const nonce = randomPermit2Nonce();
|
|
2786
|
-
const deadline = BigInt(
|
|
2787
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2788
|
-
);
|
|
2789
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
2790
|
-
chainId: cid,
|
|
2791
|
-
message: {
|
|
2792
|
-
permitted: { token, amount: amountWei },
|
|
2793
|
-
spender: relayer,
|
|
2794
|
-
nonce,
|
|
2795
|
-
deadline
|
|
2796
|
-
}
|
|
2797
|
-
});
|
|
2798
|
-
const signature = await wallet.signTypedData({
|
|
2799
|
-
account: deps.ownerAddress,
|
|
2800
|
-
...typedData
|
|
2801
|
-
});
|
|
2802
|
-
deps.onApproved?.();
|
|
2803
|
-
const result = await post({
|
|
2804
|
-
baseUrl: deps.baseUrl,
|
|
2805
|
-
apiKey: deps.apiKey,
|
|
2806
|
-
body: {
|
|
2807
|
-
chainId: cid,
|
|
2808
|
-
token,
|
|
2809
|
-
from: deps.ownerAddress,
|
|
2810
|
-
to: smartWallet,
|
|
2811
|
-
amount,
|
|
2812
|
-
nonce: nonce.toString(),
|
|
2813
|
-
deadline: deadline.toString(),
|
|
2814
|
-
signature
|
|
2815
|
-
}
|
|
2816
|
-
});
|
|
2817
|
-
return result.txHash;
|
|
2818
|
-
};
|
|
4563
|
+
return { agentResults };
|
|
4564
|
+
} catch (error) {
|
|
4565
|
+
funding.reject(error);
|
|
4566
|
+
await Promise.allSettled(tasks);
|
|
4567
|
+
throw error;
|
|
4568
|
+
}
|
|
2819
4569
|
}
|
|
2820
4570
|
|
|
2821
4571
|
// src/lib/sponsored-calls-deposit.ts
|
|
2822
|
-
var
|
|
4572
|
+
var import_viem10 = require("viem");
|
|
2823
4573
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2824
4574
|
var DEFAULT_MAX_POLLS = 30;
|
|
2825
4575
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -2827,7 +4577,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
2827
4577
|
method: "wallet_getCapabilities",
|
|
2828
4578
|
params: [owner]
|
|
2829
4579
|
});
|
|
2830
|
-
const forChain = caps?.[(0,
|
|
4580
|
+
const forChain = caps?.[(0, import_viem10.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
2831
4581
|
return Boolean(forChain?.paymasterService?.supported);
|
|
2832
4582
|
}
|
|
2833
4583
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -2845,7 +4595,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2845
4595
|
}
|
|
2846
4596
|
return new URL(configured, origin).toString();
|
|
2847
4597
|
};
|
|
2848
|
-
|
|
4598
|
+
const batch = async (chainId, transfers) => {
|
|
2849
4599
|
const cid = chainId;
|
|
2850
4600
|
const token = deps.tokenAddressByChain[cid];
|
|
2851
4601
|
if (!token) {
|
|
@@ -2861,22 +4611,53 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2861
4611
|
{ chainId }
|
|
2862
4612
|
);
|
|
2863
4613
|
}
|
|
2864
|
-
const
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
4614
|
+
const calls = transfers.map((transfer) => ({
|
|
4615
|
+
to: token,
|
|
4616
|
+
value: "0x0",
|
|
4617
|
+
data: (0, import_viem10.encodeFunctionData)({
|
|
4618
|
+
abi: import_viem10.erc20Abi,
|
|
4619
|
+
functionName: "transfer",
|
|
4620
|
+
args: [transfer.to, BigInt(transfer.amount)]
|
|
4621
|
+
})
|
|
4622
|
+
}));
|
|
4623
|
+
let paymasterUrl = absolutePaymasterUrl();
|
|
4624
|
+
for (const transfer of transfers) {
|
|
4625
|
+
const verification = transfer.yieldseeker;
|
|
4626
|
+
if (!verification) continue;
|
|
4627
|
+
if (chainId !== 8453)
|
|
4628
|
+
throw new OwneyError(
|
|
4629
|
+
"CHAIN_UNSUPPORTED",
|
|
4630
|
+
`Yieldseeker sponsorship is not available on chain ${chainId}.`
|
|
4631
|
+
);
|
|
4632
|
+
const { intent } = await postPaymasterIntent({
|
|
4633
|
+
baseUrl: deps.routingApiBaseUrl,
|
|
4634
|
+
apiKey: deps.apiKey,
|
|
4635
|
+
yieldseekerSignature: verification.signature,
|
|
4636
|
+
body: {
|
|
4637
|
+
chainId,
|
|
4638
|
+
token,
|
|
4639
|
+
from: deps.ownerAddress,
|
|
4640
|
+
to: transfer.to,
|
|
4641
|
+
amount: transfer.amount,
|
|
4642
|
+
yieldseekerUserId: verification.userId,
|
|
4643
|
+
yieldseekerAgentId: verification.agentId
|
|
4644
|
+
}
|
|
4645
|
+
});
|
|
4646
|
+
const url = new URL(paymasterUrl);
|
|
4647
|
+
url.searchParams.append("owneyIntent", intent);
|
|
4648
|
+
paymasterUrl = url.toString();
|
|
4649
|
+
}
|
|
2869
4650
|
const sendResult = await deps.provider.request({
|
|
2870
4651
|
method: "wallet_sendCalls",
|
|
2871
4652
|
params: [
|
|
2872
4653
|
{
|
|
2873
4654
|
version: "2.0.0",
|
|
2874
4655
|
from: deps.ownerAddress,
|
|
2875
|
-
chainId: (0,
|
|
2876
|
-
atomicRequired:
|
|
2877
|
-
calls
|
|
4656
|
+
chainId: (0, import_viem10.toHex)(chainId),
|
|
4657
|
+
atomicRequired: transfers.length > 1,
|
|
4658
|
+
calls,
|
|
2878
4659
|
capabilities: {
|
|
2879
|
-
paymasterService: { url:
|
|
4660
|
+
paymasterService: { url: paymasterUrl }
|
|
2880
4661
|
}
|
|
2881
4662
|
}
|
|
2882
4663
|
]
|
|
@@ -2896,7 +4677,24 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2896
4677
|
params: [callsId]
|
|
2897
4678
|
});
|
|
2898
4679
|
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
2899
|
-
if (
|
|
4680
|
+
if (status?.receipts?.some((receipt) => receipt.status === "0x0") || typeof status?.status === "number" && status.status >= 400) {
|
|
4681
|
+
throw new OwneyError(
|
|
4682
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4683
|
+
"The sponsored deposit did not complete successfully.",
|
|
4684
|
+
{ chainId, callsId, safeToFallback: false }
|
|
4685
|
+
);
|
|
4686
|
+
}
|
|
4687
|
+
if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
|
|
4688
|
+
if (status?.receipts?.some(
|
|
4689
|
+
(receipt) => receipt.transactionHash !== txHash
|
|
4690
|
+
))
|
|
4691
|
+
throw new OwneyError(
|
|
4692
|
+
"SPONSORED_CALLS_NO_RECEIPT",
|
|
4693
|
+
"The wallet returned multiple transactions for an atomic deposit. Check activity before trying again.",
|
|
4694
|
+
{ chainId, callsId }
|
|
4695
|
+
);
|
|
4696
|
+
return txHash;
|
|
4697
|
+
}
|
|
2900
4698
|
if (pollIntervalMs > 0) {
|
|
2901
4699
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
2902
4700
|
}
|
|
@@ -2907,6 +4705,11 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2907
4705
|
{ chainId, callsId }
|
|
2908
4706
|
);
|
|
2909
4707
|
};
|
|
4708
|
+
const callback = makeVerificationAwareDepositCallback(
|
|
4709
|
+
(to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
|
|
4710
|
+
);
|
|
4711
|
+
registerDepositBatch(callback, batch);
|
|
4712
|
+
return callback;
|
|
2910
4713
|
}
|
|
2911
4714
|
|
|
2912
4715
|
// src/client.ts
|
|
@@ -2936,15 +4739,20 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2936
4739
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2937
4740
|
};
|
|
2938
4741
|
var VIEM_CHAIN2 = {
|
|
2939
|
-
8453:
|
|
2940
|
-
42161:
|
|
2941
|
-
1:
|
|
4742
|
+
8453: import_chains4.base,
|
|
4743
|
+
42161: import_chains4.arbitrum,
|
|
4744
|
+
1: import_chains4.mainnet
|
|
2942
4745
|
};
|
|
2943
4746
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
2944
4747
|
8453: "0x4200000000000000000000000000000000000006",
|
|
2945
4748
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
2946
4749
|
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
2947
4750
|
};
|
|
4751
|
+
var SPONSORED_TOKENS_BY_ASSET = { USDC: SPONSORED_USDC_BY_CHAIN, WETH: SPONSORED_WETH_BY_CHAIN };
|
|
4752
|
+
function sponsoredTokensFor(asset) {
|
|
4753
|
+
if (!Object.prototype.hasOwnProperty.call(SPONSORED_TOKENS_BY_ASSET, asset)) throw new OwneyError("ASSET_UNSUPPORTED", `No sponsored token configured for ${asset}`);
|
|
4754
|
+
return SPONSORED_TOKENS_BY_ASSET[asset];
|
|
4755
|
+
}
|
|
2948
4756
|
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
2949
4757
|
return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
2950
4758
|
}
|
|
@@ -2966,6 +4774,8 @@ var OwneySDK = class {
|
|
|
2966
4774
|
orgAgentConfig;
|
|
2967
4775
|
orgAgentConfigPromise = null;
|
|
2968
4776
|
zyfaiRpcUrls;
|
|
4777
|
+
yieldseekerApiBaseUrl;
|
|
4778
|
+
yieldseekerSiweOrigin;
|
|
2969
4779
|
routingApiBaseUrl;
|
|
2970
4780
|
referralSource;
|
|
2971
4781
|
cachedSponsoredCallback = null;
|
|
@@ -2988,6 +4798,8 @@ var OwneySDK = class {
|
|
|
2988
4798
|
this.apiKey = config.apiKey;
|
|
2989
4799
|
if (config.debug) setOwneyDebug(true);
|
|
2990
4800
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
4801
|
+
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
4802
|
+
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
2991
4803
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2992
4804
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2993
4805
|
this.referralSource = config.referralSource;
|
|
@@ -3021,6 +4833,7 @@ var OwneySDK = class {
|
|
|
3021
4833
|
* After calling this, `connect()` must be called again before using agent methods.
|
|
3022
4834
|
*/
|
|
3023
4835
|
async disconnect() {
|
|
4836
|
+
this.state = null;
|
|
3024
4837
|
for (const agent of this.agents.values()) {
|
|
3025
4838
|
await agent.disconnect();
|
|
3026
4839
|
}
|
|
@@ -3074,18 +4887,13 @@ var OwneySDK = class {
|
|
|
3074
4887
|
}
|
|
3075
4888
|
return this.state.provider;
|
|
3076
4889
|
}
|
|
3077
|
-
/**
|
|
3078
|
-
* Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
|
|
3079
|
-
* used when the caller omits `depositCallback`. Wraps the connected EIP-1193
|
|
3080
|
-
* provider with viem `custom(provider)` to read token meta and sign the
|
|
3081
|
-
* `TransferWithAuthorization`, then POSTs to the sponsor API.
|
|
3082
|
-
*/
|
|
4890
|
+
/** Builds the default USDC batch callback for the connected wallet. */
|
|
3083
4891
|
getDefaultSponsoredCallback(onApproved) {
|
|
3084
4892
|
if (!onApproved && this.cachedSponsoredCallback)
|
|
3085
4893
|
return this.cachedSponsoredCallback;
|
|
3086
4894
|
const provider = this.requireConnectedProvider();
|
|
3087
4895
|
const owner = this.state.walletAddress;
|
|
3088
|
-
const callback =
|
|
4896
|
+
const callback = makeSponsoredTokenCallback({
|
|
3089
4897
|
apiKey: this.apiKey,
|
|
3090
4898
|
baseUrl: this.routingApiBaseUrl,
|
|
3091
4899
|
ownerAddress: owner,
|
|
@@ -3094,35 +4902,32 @@ var OwneySDK = class {
|
|
|
3094
4902
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3095
4903
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3096
4904
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3097
|
-
getPublicClient: (cid) => (0,
|
|
4905
|
+
getPublicClient: (cid) => (0, import_viem11.createPublicClient)({
|
|
3098
4906
|
chain: VIEM_CHAIN2[cid],
|
|
3099
|
-
transport: (0,
|
|
4907
|
+
transport: (0, import_viem11.custom)(provider)
|
|
3100
4908
|
}),
|
|
3101
|
-
getWalletClient: (cid) => (0,
|
|
4909
|
+
getWalletClient: (cid) => (0, import_viem11.createWalletClient)({
|
|
3102
4910
|
account: owner,
|
|
3103
4911
|
chain: VIEM_CHAIN2[cid],
|
|
3104
|
-
transport: (0,
|
|
4912
|
+
transport: (0, import_viem11.custom)(provider)
|
|
3105
4913
|
})
|
|
3106
4914
|
});
|
|
3107
4915
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
3108
4916
|
return callback;
|
|
3109
4917
|
}
|
|
3110
|
-
/**
|
|
3111
|
-
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
3112
|
-
* callback used when the caller omits `depositCallback` for a WETH
|
|
3113
|
-
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
3114
|
-
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
3115
|
-
*/
|
|
4918
|
+
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
3116
4919
|
getDefaultSponsoredCallsCallback(asset, onApproved) {
|
|
3117
4920
|
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
3118
4921
|
if (!onApproved && cached) return cached;
|
|
3119
4922
|
const provider = this.requireConnectedProvider();
|
|
3120
4923
|
const callback = makeSponsoredCallsCallback({
|
|
4924
|
+
apiKey: this.apiKey,
|
|
4925
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
3121
4926
|
provider,
|
|
3122
4927
|
ownerAddress: this.state.walletAddress,
|
|
3123
4928
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
3124
4929
|
onApproved,
|
|
3125
|
-
tokenAddressByChain: asset
|
|
4930
|
+
tokenAddressByChain: sponsoredTokensFor(asset)
|
|
3126
4931
|
});
|
|
3127
4932
|
if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
3128
4933
|
return callback;
|
|
@@ -3131,14 +4936,14 @@ var OwneySDK = class {
|
|
|
3131
4936
|
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
3132
4937
|
* callback used when the caller omits `depositCallback` for a WETH deposit.
|
|
3133
4938
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
3134
|
-
*
|
|
4939
|
+
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
3135
4940
|
*/
|
|
3136
4941
|
getDefaultWethSponsoredCallback(onApproved) {
|
|
3137
4942
|
if (!onApproved && this.cachedWethSponsoredCallback)
|
|
3138
4943
|
return this.cachedWethSponsoredCallback;
|
|
3139
4944
|
const provider = this.requireConnectedProvider();
|
|
3140
4945
|
const owner = this.state.walletAddress;
|
|
3141
|
-
const callback =
|
|
4946
|
+
const callback = makeSponsoredTokenCallback({
|
|
3142
4947
|
apiKey: this.apiKey,
|
|
3143
4948
|
baseUrl: this.routingApiBaseUrl,
|
|
3144
4949
|
ownerAddress: owner,
|
|
@@ -3147,14 +4952,14 @@ var OwneySDK = class {
|
|
|
3147
4952
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3148
4953
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3149
4954
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3150
|
-
getPublicClient: (cid) => (0,
|
|
4955
|
+
getPublicClient: (cid) => (0, import_viem11.createPublicClient)({
|
|
3151
4956
|
chain: VIEM_CHAIN2[cid],
|
|
3152
|
-
transport: (0,
|
|
4957
|
+
transport: (0, import_viem11.custom)(provider)
|
|
3153
4958
|
}),
|
|
3154
|
-
getWalletClient: (cid) => (0,
|
|
4959
|
+
getWalletClient: (cid) => (0, import_viem11.createWalletClient)({
|
|
3155
4960
|
account: owner,
|
|
3156
4961
|
chain: VIEM_CHAIN2[cid],
|
|
3157
|
-
transport: (0,
|
|
4962
|
+
transport: (0, import_viem11.custom)(provider)
|
|
3158
4963
|
})
|
|
3159
4964
|
});
|
|
3160
4965
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -3185,12 +4990,10 @@ var OwneySDK = class {
|
|
|
3185
4990
|
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
3186
4991
|
this.apiKey,
|
|
3187
4992
|
this.routingApiBaseUrl
|
|
3188
|
-
).then(
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
}
|
|
3193
|
-
);
|
|
4993
|
+
).then((config) => {
|
|
4994
|
+
this.orgAgentConfig = config;
|
|
4995
|
+
return config;
|
|
4996
|
+
});
|
|
3194
4997
|
}
|
|
3195
4998
|
return this.orgAgentConfigPromise;
|
|
3196
4999
|
}
|
|
@@ -3230,7 +5033,14 @@ var OwneySDK = class {
|
|
|
3230
5033
|
this.routingApiBaseUrl
|
|
3231
5034
|
);
|
|
3232
5035
|
this.disabledAgents.clear();
|
|
3233
|
-
for (const {
|
|
5036
|
+
for (const {
|
|
5037
|
+
key: key2,
|
|
5038
|
+
agent_type,
|
|
5039
|
+
is_enabled,
|
|
5040
|
+
is_configured
|
|
5041
|
+
} of agentKeys) {
|
|
5042
|
+
const configured = is_configured ?? Boolean(key2);
|
|
5043
|
+
if (!configured) continue;
|
|
3234
5044
|
const agent = this.createAgent(agent_type, key2);
|
|
3235
5045
|
if (!agent) continue;
|
|
3236
5046
|
this.agents.set(agent_type, agent);
|
|
@@ -3254,8 +5064,15 @@ var OwneySDK = class {
|
|
|
3254
5064
|
}
|
|
3255
5065
|
createAgent(agentId, key2) {
|
|
3256
5066
|
if (agentId === "zyfai") {
|
|
5067
|
+
if (!key2) return null;
|
|
3257
5068
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
3258
5069
|
}
|
|
5070
|
+
if (agentId === "yieldseeker") {
|
|
5071
|
+
return new YieldseekerAgent(this.apiKey, {
|
|
5072
|
+
auth: { origin: this.yieldseekerSiweOrigin },
|
|
5073
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
5074
|
+
});
|
|
5075
|
+
}
|
|
3259
5076
|
return null;
|
|
3260
5077
|
}
|
|
3261
5078
|
/**
|
|
@@ -3300,9 +5117,10 @@ var OwneySDK = class {
|
|
|
3300
5117
|
* If provided, ALL specified agents must support the chainId or the call
|
|
3301
5118
|
* throws before activating any agent.
|
|
3302
5119
|
*/
|
|
3303
|
-
async activateAgent(chainId, agentId) {
|
|
5120
|
+
async activateAgent(chainId, agentId, asset) {
|
|
3304
5121
|
const state = this.requireState();
|
|
3305
5122
|
await this.ensureAgentsInitialized();
|
|
5123
|
+
this.assertActivationSession(state);
|
|
3306
5124
|
if (agentId !== void 0) {
|
|
3307
5125
|
if (agentId.length === 0) {
|
|
3308
5126
|
throw new OwneyError(
|
|
@@ -3336,7 +5154,7 @@ var OwneySDK = class {
|
|
|
3336
5154
|
this.activeAgents.add(id);
|
|
3337
5155
|
}
|
|
3338
5156
|
state.chainId = chainId;
|
|
3339
|
-
await this.activateAgentsInTurn(agents, state, chainId);
|
|
5157
|
+
await this.activateAgentsInTurn(agents, state, chainId, asset);
|
|
3340
5158
|
return;
|
|
3341
5159
|
}
|
|
3342
5160
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -3357,7 +5175,12 @@ var OwneySDK = class {
|
|
|
3357
5175
|
const enabledCompatible = compatible.filter(
|
|
3358
5176
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
3359
5177
|
);
|
|
3360
|
-
await this.activateAgentsInTurn(enabledCompatible, state, chainId);
|
|
5178
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
|
|
5179
|
+
}
|
|
5180
|
+
assertActivationSession(state) {
|
|
5181
|
+
if (this.state !== state) {
|
|
5182
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed. Please try again.");
|
|
5183
|
+
}
|
|
3361
5184
|
}
|
|
3362
5185
|
/**
|
|
3363
5186
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -3372,26 +5195,51 @@ var OwneySDK = class {
|
|
|
3372
5195
|
* Serializing costs no real wall-clock: the user can only approve one prompt
|
|
3373
5196
|
* at a time anyway.
|
|
3374
5197
|
*
|
|
3375
|
-
*
|
|
3376
|
-
*
|
|
3377
|
-
*
|
|
3378
|
-
* have had a chance to activate.
|
|
5198
|
+
* Stop at the first failure so a canceled sign-in does not open another
|
|
5199
|
+
* agent's wallet prompt. Report any earlier successes for diagnostics; the
|
|
5200
|
+
* app discards the session when the complete sign-in does not succeed.
|
|
3379
5201
|
*/
|
|
3380
|
-
async activateAgentsInTurn(agents, state, chainId) {
|
|
5202
|
+
async activateAgentsInTurn(agents, state, chainId, asset) {
|
|
3381
5203
|
let firstError = null;
|
|
5204
|
+
const activatedAgentIds = [];
|
|
5205
|
+
const failedAgents = [];
|
|
3382
5206
|
for (const agent of agents) {
|
|
5207
|
+
this.assertActivationSession(state);
|
|
3383
5208
|
try {
|
|
3384
|
-
await agent.activateAgent(state, chainId);
|
|
5209
|
+
await agent.activateAgent(state, chainId, asset);
|
|
5210
|
+
this.assertActivationSession(state);
|
|
3385
5211
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
5212
|
+
this.assertActivationSession(state);
|
|
5213
|
+
activatedAgentIds.push(agent.id);
|
|
3386
5214
|
} catch (error) {
|
|
5215
|
+
this.assertActivationSession(state);
|
|
5216
|
+
const message = error !== null && typeof error === "object" && "message" in error && typeof error.message === "string" ? error.message : typeof error === "string" ? error : "No activation error message was returned.";
|
|
5217
|
+
failedAgents.push({
|
|
5218
|
+
agentId: agent.id,
|
|
5219
|
+
code: error instanceof OwneyError ? error.code : void 0,
|
|
5220
|
+
message,
|
|
5221
|
+
...error instanceof OwneyError && error.details ? { details: error.details } : {}
|
|
5222
|
+
});
|
|
3387
5223
|
if (firstError === null) {
|
|
3388
5224
|
firstError = error;
|
|
3389
5225
|
} else {
|
|
3390
5226
|
console.error(`activateAgent(${agent.id}) failed:`, error);
|
|
3391
5227
|
}
|
|
5228
|
+
break;
|
|
3392
5229
|
}
|
|
3393
5230
|
}
|
|
3394
|
-
if (firstError
|
|
5231
|
+
if (firstError === null) return;
|
|
5232
|
+
if (activatedAgentIds.length === 0) throw firstError;
|
|
5233
|
+
const failedAgentIds = failedAgents.map(({ agentId }) => agentId);
|
|
5234
|
+
const activeNames = activatedAgentIds.map(this.formatAgentName).join(", ");
|
|
5235
|
+
const failureMessages = failedAgents.map(
|
|
5236
|
+
({ agentId, message }) => `${this.formatAgentName(agentId)} activation failed: ${message}`
|
|
5237
|
+
).join(" ");
|
|
5238
|
+
throw new OwneyError(
|
|
5239
|
+
"AGENT_ACTIVATION_PARTIAL_FAILURE",
|
|
5240
|
+
`${activeNames} activated. ${failureMessages}`,
|
|
5241
|
+
{ activatedAgentIds, failedAgentIds, failures: failedAgents }
|
|
5242
|
+
);
|
|
3395
5243
|
}
|
|
3396
5244
|
/**
|
|
3397
5245
|
* Deposit into a specific agent, or distribute across all eligible agents.
|
|
@@ -3401,7 +5249,8 @@ var OwneySDK = class {
|
|
|
3401
5249
|
* @param options.asset - Asset symbol to deposit (e.g. "USDC")
|
|
3402
5250
|
* @param options.depositCallback - Callback that performs the token transfer and returns a tx hash.
|
|
3403
5251
|
* When agentId is omitted, this callback is invoked once per eligible agent with that agent's
|
|
3404
|
-
* split amount and smart wallet address
|
|
5252
|
+
* split amount and smart wallet address. Default sponsored deposits batch
|
|
5253
|
+
* all shares into one signature; custom callbacks still run once per agent.
|
|
3405
5254
|
* @param options.agentId - Optional explicit target. Otherwise split equally,
|
|
3406
5255
|
* or fund remaining agents when a recovery deposit cannot meet every minimum.
|
|
3407
5256
|
* @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
|
|
@@ -3486,6 +5335,39 @@ var OwneySDK = class {
|
|
|
3486
5335
|
}
|
|
3487
5336
|
);
|
|
3488
5337
|
}
|
|
5338
|
+
const batchTransfer = getDepositBatchTransfer(effectiveCallback);
|
|
5339
|
+
if (!depositCallback && batchTransfer) {
|
|
5340
|
+
return runAgentDepositBatch(
|
|
5341
|
+
chainId,
|
|
5342
|
+
agentAmounts.map(({ agent, amount: amount2 }) => ({
|
|
5343
|
+
id: agent.id,
|
|
5344
|
+
amount: amount2,
|
|
5345
|
+
run: (callback) => withFailureReporting(
|
|
5346
|
+
this.apiKey,
|
|
5347
|
+
agent.id,
|
|
5348
|
+
() => agent.deposit(state, chainId, amount2, asset, callback),
|
|
5349
|
+
this.routingApiBaseUrl
|
|
5350
|
+
)
|
|
5351
|
+
})),
|
|
5352
|
+
async (cid, transfers) => {
|
|
5353
|
+
try {
|
|
5354
|
+
return await batchTransfer(cid, transfers);
|
|
5355
|
+
} catch (error) {
|
|
5356
|
+
if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
|
|
5357
|
+
throw error;
|
|
5358
|
+
const requiredAmount = transfers.reduce(
|
|
5359
|
+
(sum, transfer) => sum + BigInt(transfer.amount),
|
|
5360
|
+
0n
|
|
5361
|
+
);
|
|
5362
|
+
await this.approvePermit2(
|
|
5363
|
+
asset,
|
|
5364
|
+
requiredAmount
|
|
5365
|
+
);
|
|
5366
|
+
return batchTransfer(cid, transfers);
|
|
5367
|
+
}
|
|
5368
|
+
}
|
|
5369
|
+
);
|
|
5370
|
+
}
|
|
3489
5371
|
const agentResults = {};
|
|
3490
5372
|
for (const [
|
|
3491
5373
|
index,
|
|
@@ -3525,7 +5407,7 @@ var OwneySDK = class {
|
|
|
3525
5407
|
*
|
|
3526
5408
|
* 1. Missing Permit2 allowance: when the app did not supply its own
|
|
3527
5409
|
* callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
|
|
3528
|
-
*
|
|
5410
|
+
* token deposit, this is the wallet's first Permit2 deposit for that token. We send
|
|
3529
5411
|
* the one-time (user-paid) Permit2 approval via `approvePermit2()` and
|
|
3530
5412
|
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
3531
5413
|
* per call so a wallet/agent that keeps reporting the allowance as
|
|
@@ -3562,12 +5444,15 @@ var OwneySDK = class {
|
|
|
3562
5444
|
try {
|
|
3563
5445
|
return await attempt(effectiveCallback);
|
|
3564
5446
|
} catch (error) {
|
|
3565
|
-
if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
5447
|
+
if (!approvalAttempted && appCallback === void 0 && (asset === "WETH" || asset === "USDC") && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
3566
5448
|
approvalAttempted = true;
|
|
3567
5449
|
console.warn(
|
|
3568
|
-
"[owney-sdk] First
|
|
5450
|
+
"[owney-sdk] First token deposit: sending one-time Permit2 approval..."
|
|
5451
|
+
);
|
|
5452
|
+
await this.approvePermit2(
|
|
5453
|
+
asset,
|
|
5454
|
+
BigInt(amount)
|
|
3569
5455
|
);
|
|
3570
|
-
await this.approvePermit2();
|
|
3571
5456
|
continue;
|
|
3572
5457
|
}
|
|
3573
5458
|
if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
|
|
@@ -3605,10 +5490,10 @@ var OwneySDK = class {
|
|
|
3605
5490
|
agent,
|
|
3606
5491
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
3607
5492
|
}));
|
|
3608
|
-
const
|
|
5493
|
+
const valid2 = splits.filter(
|
|
3609
5494
|
(s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
|
|
3610
5495
|
);
|
|
3611
|
-
if (
|
|
5496
|
+
if (valid2.length === agents.length) {
|
|
3612
5497
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
3613
5498
|
}
|
|
3614
5499
|
const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
|
|
@@ -3627,6 +5512,11 @@ var OwneySDK = class {
|
|
|
3627
5512
|
)
|
|
3628
5513
|
}));
|
|
3629
5514
|
}
|
|
5515
|
+
formatAgentName(agentId) {
|
|
5516
|
+
if (agentId === "zyfai") return "Zyfai";
|
|
5517
|
+
if (agentId === "yieldseeker") return "Yieldseeker";
|
|
5518
|
+
return agentId;
|
|
5519
|
+
}
|
|
3630
5520
|
async validateMinDepositAmount(agent, state, chainId, asset, amount) {
|
|
3631
5521
|
const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
|
|
3632
5522
|
const parsedAmount = BigInt(amount);
|
|
@@ -3658,12 +5548,12 @@ var OwneySDK = class {
|
|
|
3658
5548
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
3659
5549
|
);
|
|
3660
5550
|
const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
|
|
3661
|
-
const
|
|
5551
|
+
const position2 = (balance.positions ?? []).find((p) => {
|
|
3662
5552
|
const positionChain = p.chain.trim().toUpperCase();
|
|
3663
5553
|
const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
|
|
3664
5554
|
return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
|
|
3665
5555
|
});
|
|
3666
|
-
return !!token && Number(token.amount) > 0 || !!
|
|
5556
|
+
return !!token && Number(token.amount) > 0 || !!position2;
|
|
3667
5557
|
} catch (error) {
|
|
3668
5558
|
if (requireReliableRead) {
|
|
3669
5559
|
throw new OwneyError(
|
|
@@ -3778,6 +5668,10 @@ var OwneySDK = class {
|
|
|
3778
5668
|
}
|
|
3779
5669
|
const requested = BigInt(amount);
|
|
3780
5670
|
const aggregated = await this.getBalances();
|
|
5671
|
+
const unavailableAgents = eligibleAgents.filter(
|
|
5672
|
+
(agent) => !(agent.id in aggregated.agentBalances)
|
|
5673
|
+
);
|
|
5674
|
+
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
3781
5675
|
const balances = projectAgentBalancesForAsset(
|
|
3782
5676
|
eligibleAgents,
|
|
3783
5677
|
aggregated.agentBalances,
|
|
@@ -3786,7 +5680,18 @@ var OwneySDK = class {
|
|
|
3786
5680
|
assetInfo.decimals
|
|
3787
5681
|
);
|
|
3788
5682
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
3789
|
-
if (totalAvailable
|
|
5683
|
+
if (totalAvailable === 0n && unavailableAgents.length > 0) {
|
|
5684
|
+
throw new OwneyError(
|
|
5685
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5686
|
+
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
5687
|
+
{
|
|
5688
|
+
asset,
|
|
5689
|
+
unavailableAgents: unavailableAgentIds,
|
|
5690
|
+
agentErrors: aggregated.agentErrors
|
|
5691
|
+
}
|
|
5692
|
+
);
|
|
5693
|
+
}
|
|
5694
|
+
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3790
5695
|
throw new OwneyError(
|
|
3791
5696
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3792
5697
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -3797,6 +5702,7 @@ var OwneySDK = class {
|
|
|
3797
5702
|
}
|
|
3798
5703
|
);
|
|
3799
5704
|
}
|
|
5705
|
+
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
3800
5706
|
const disabledBalances = balances.filter(
|
|
3801
5707
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
3802
5708
|
);
|
|
@@ -3805,7 +5711,7 @@ var OwneySDK = class {
|
|
|
3805
5711
|
);
|
|
3806
5712
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
3807
5713
|
disabledBalances,
|
|
3808
|
-
|
|
5714
|
+
plannedTarget
|
|
3809
5715
|
);
|
|
3810
5716
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
3811
5717
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -3815,7 +5721,9 @@ var OwneySDK = class {
|
|
|
3815
5721
|
}));
|
|
3816
5722
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
3817
5723
|
const results = {};
|
|
3818
|
-
const agentErrors = {
|
|
5724
|
+
const agentErrors = {
|
|
5725
|
+
...aggregated.agentErrors ?? {}
|
|
5726
|
+
};
|
|
3819
5727
|
for (let i = 0; i < plans.length; i++) {
|
|
3820
5728
|
const p = plans[i];
|
|
3821
5729
|
if (p.planned === 0n) continue;
|
|
@@ -3862,7 +5770,8 @@ var OwneySDK = class {
|
|
|
3862
5770
|
requested: amount,
|
|
3863
5771
|
withdrawn: withdrawn.toString(),
|
|
3864
5772
|
partialResults: results,
|
|
3865
|
-
agentErrors
|
|
5773
|
+
agentErrors,
|
|
5774
|
+
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3866
5775
|
}
|
|
3867
5776
|
);
|
|
3868
5777
|
}
|
|
@@ -3880,7 +5789,10 @@ var OwneySDK = class {
|
|
|
3880
5789
|
if (agentId) {
|
|
3881
5790
|
const agent = this.getAgent(agentId);
|
|
3882
5791
|
const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
3883
|
-
return
|
|
5792
|
+
return {
|
|
5793
|
+
...result,
|
|
5794
|
+
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5795
|
+
};
|
|
3884
5796
|
}
|
|
3885
5797
|
let totalBalance = 0;
|
|
3886
5798
|
const results = {};
|
|
@@ -3888,7 +5800,13 @@ var OwneySDK = class {
|
|
|
3888
5800
|
const balanceResults = await Promise.allSettled(
|
|
3889
5801
|
entries.map(async ([id, agent]) => {
|
|
3890
5802
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
3891
|
-
return [
|
|
5803
|
+
return [
|
|
5804
|
+
id,
|
|
5805
|
+
{
|
|
5806
|
+
...b,
|
|
5807
|
+
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5808
|
+
}
|
|
5809
|
+
];
|
|
3892
5810
|
})
|
|
3893
5811
|
);
|
|
3894
5812
|
let successCount = 0;
|
|
@@ -3910,6 +5828,7 @@ var OwneySDK = class {
|
|
|
3910
5828
|
const retryDelay = rateLimitDelay(reason);
|
|
3911
5829
|
if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
|
|
3912
5830
|
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
5831
|
+
console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
|
|
3913
5832
|
}
|
|
3914
5833
|
if (successCount === 0) {
|
|
3915
5834
|
throw new OwneyError(
|
|
@@ -4088,7 +6007,10 @@ var OwneySDK = class {
|
|
|
4088
6007
|
Promise.all(
|
|
4089
6008
|
entries.map(async ([id, agent]) => {
|
|
4090
6009
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
4091
|
-
return [
|
|
6010
|
+
return [
|
|
6011
|
+
id,
|
|
6012
|
+
balanceForApyScope(b, chainId, tokenSymbol)
|
|
6013
|
+
];
|
|
4092
6014
|
})
|
|
4093
6015
|
)
|
|
4094
6016
|
]);
|
|
@@ -4249,43 +6171,44 @@ var OwneySDK = class {
|
|
|
4249
6171
|
return pending;
|
|
4250
6172
|
}
|
|
4251
6173
|
/**
|
|
4252
|
-
*
|
|
4253
|
-
*
|
|
4254
|
-
*
|
|
4255
|
-
*
|
|
4256
|
-
*
|
|
6174
|
+
* User-paid approval of Permit2 on the selected token for the active chain.
|
|
6175
|
+
* Approves exactly the pending deposit amount. Another approval is required
|
|
6176
|
+
* for a later deposit once this allowance has been consumed. Resolves after
|
|
6177
|
+
* one confirmation so the subsequent deposit attempt sees the new allowance.
|
|
6178
|
+
*
|
|
6179
|
+
* @param requiredAmount Raw base-unit amount the pending deposit must cover.
|
|
4257
6180
|
* @returns the approval transaction hash.
|
|
4258
6181
|
*/
|
|
4259
|
-
async approvePermit2(asset = "WETH") {
|
|
4260
|
-
void asset;
|
|
6182
|
+
async approvePermit2(asset = "WETH", requiredAmount = 0n) {
|
|
4261
6183
|
const state = this.requireState();
|
|
4262
6184
|
const chainId = this.requireChainId();
|
|
4263
|
-
this.
|
|
4264
|
-
const token =
|
|
6185
|
+
this.getEligibleAgents(chainId, asset, { excludeDisabled: true });
|
|
6186
|
+
const token = sponsoredTokensFor(asset)[chainId];
|
|
4265
6187
|
if (!token) {
|
|
4266
6188
|
throw new OwneyError(
|
|
4267
6189
|
"CHAIN_UNSUPPORTED",
|
|
4268
|
-
`No sponsored
|
|
6190
|
+
`No sponsored token on chain ${chainId}`
|
|
4269
6191
|
);
|
|
4270
6192
|
}
|
|
4271
6193
|
const provider = this.requireConnectedProvider();
|
|
4272
|
-
const
|
|
6194
|
+
const publicClient = (0, import_viem11.createPublicClient)({
|
|
6195
|
+
chain: VIEM_CHAIN2[chainId],
|
|
6196
|
+
transport: (0, import_viem11.custom)(provider)
|
|
6197
|
+
});
|
|
6198
|
+
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
6199
|
+
const wallet = (0, import_viem11.createWalletClient)({
|
|
4273
6200
|
account: state.walletAddress,
|
|
4274
6201
|
chain: VIEM_CHAIN2[chainId],
|
|
4275
|
-
transport: (0,
|
|
6202
|
+
transport: (0, import_viem11.custom)(provider)
|
|
4276
6203
|
});
|
|
4277
6204
|
const hash = await wallet.writeContract({
|
|
4278
6205
|
address: token,
|
|
4279
6206
|
abi: ERC20_ALLOWANCE_ABI,
|
|
4280
6207
|
functionName: "approve",
|
|
4281
|
-
args: [PERMIT2_ADDRESS,
|
|
6208
|
+
args: [PERMIT2_ADDRESS, approvalAmount],
|
|
4282
6209
|
account: state.walletAddress,
|
|
4283
6210
|
chain: VIEM_CHAIN2[chainId]
|
|
4284
6211
|
});
|
|
4285
|
-
const publicClient = (0, import_viem6.createPublicClient)({
|
|
4286
|
-
chain: VIEM_CHAIN2[chainId],
|
|
4287
|
-
transport: (0, import_viem6.custom)(provider)
|
|
4288
|
-
});
|
|
4289
6212
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
4290
6213
|
hash,
|
|
4291
6214
|
confirmations: 1
|
|
@@ -4321,7 +6244,9 @@ var OwneySDK = class {
|
|
|
4321
6244
|
return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
4322
6245
|
}
|
|
4323
6246
|
const results = {};
|
|
4324
|
-
const agentEntries = [...this.agents.entries()]
|
|
6247
|
+
const agentEntries = [...this.agents.entries()].filter(
|
|
6248
|
+
([id]) => !this.isAgentDisabled(id)
|
|
6249
|
+
);
|
|
4325
6250
|
const apyResults = await Promise.all(
|
|
4326
6251
|
agentEntries.map(async ([id, agent]) => {
|
|
4327
6252
|
const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
@@ -4396,13 +6321,13 @@ var OwneySDK = class {
|
|
|
4396
6321
|
};
|
|
4397
6322
|
|
|
4398
6323
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
4399
|
-
var
|
|
4400
|
-
var
|
|
6324
|
+
var import_viem12 = require("viem");
|
|
6325
|
+
var import_siwe2 = require("siwe");
|
|
4401
6326
|
var import_sdk2 = require("@zyfai/sdk");
|
|
4402
6327
|
|
|
4403
6328
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
4404
|
-
var
|
|
4405
|
-
var
|
|
6329
|
+
var KEY_PREFIX4 = "owney.siwx.session";
|
|
6330
|
+
var storage4 = () => {
|
|
4406
6331
|
if (typeof window === "undefined") return null;
|
|
4407
6332
|
try {
|
|
4408
6333
|
return window.localStorage;
|
|
@@ -4410,8 +6335,8 @@ var storage2 = () => {
|
|
|
4410
6335
|
return null;
|
|
4411
6336
|
}
|
|
4412
6337
|
};
|
|
4413
|
-
var
|
|
4414
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
6338
|
+
var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
|
|
6339
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
|
|
4415
6340
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
4416
6341
|
var readLegacySiwxSession = (store, address) => {
|
|
4417
6342
|
if (!store) return null;
|
|
@@ -4442,17 +6367,17 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
4442
6367
|
};
|
|
4443
6368
|
var readSiwxSession = (address, chainId) => {
|
|
4444
6369
|
if (typeof window === "undefined") return null;
|
|
4445
|
-
const key2 =
|
|
4446
|
-
const store =
|
|
4447
|
-
let
|
|
6370
|
+
const key2 = buildKey3(address);
|
|
6371
|
+
const store = storage4();
|
|
6372
|
+
let raw2 = null;
|
|
4448
6373
|
try {
|
|
4449
|
-
|
|
6374
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
4450
6375
|
} catch {
|
|
4451
|
-
|
|
6376
|
+
raw2 = null;
|
|
4452
6377
|
}
|
|
4453
|
-
if (
|
|
6378
|
+
if (raw2) {
|
|
4454
6379
|
try {
|
|
4455
|
-
return JSON.parse(
|
|
6380
|
+
return JSON.parse(raw2);
|
|
4456
6381
|
} catch {
|
|
4457
6382
|
memorySiwxSessions.delete(key2);
|
|
4458
6383
|
try {
|
|
@@ -4471,18 +6396,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
4471
6396
|
};
|
|
4472
6397
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
4473
6398
|
if (typeof window === "undefined") return;
|
|
4474
|
-
const key2 =
|
|
6399
|
+
const key2 = buildKey3(address);
|
|
4475
6400
|
memorySiwxSessions.set(key2, session);
|
|
4476
|
-
const store =
|
|
6401
|
+
const store = storage4();
|
|
4477
6402
|
try {
|
|
4478
6403
|
store?.setItem(key2, JSON.stringify(session));
|
|
4479
6404
|
} catch {
|
|
4480
6405
|
}
|
|
4481
6406
|
};
|
|
4482
6407
|
var clearSiwxSession = (address, _chainId) => {
|
|
4483
|
-
const key2 =
|
|
6408
|
+
const key2 = buildKey3(address);
|
|
4484
6409
|
memorySiwxSessions.delete(key2);
|
|
4485
|
-
const store =
|
|
6410
|
+
const store = storage4();
|
|
4486
6411
|
try {
|
|
4487
6412
|
store?.removeItem(key2);
|
|
4488
6413
|
} catch {
|
|
@@ -4522,8 +6447,8 @@ function buildSIWXConfig(deps) {
|
|
|
4522
6447
|
statement: STATEMENT,
|
|
4523
6448
|
issuedAt,
|
|
4524
6449
|
toString() {
|
|
4525
|
-
return new
|
|
4526
|
-
address: (0,
|
|
6450
|
+
return new import_siwe2.SiweMessage({
|
|
6451
|
+
address: (0, import_viem12.getAddress)(accountAddress),
|
|
4527
6452
|
chainId: numericChainId(chainId),
|
|
4528
6453
|
domain,
|
|
4529
6454
|
uri,
|
|
@@ -4565,7 +6490,7 @@ function buildSIWXConfig(deps) {
|
|
|
4565
6490
|
const persistSession = async (session) => {
|
|
4566
6491
|
const address = session.data.accountAddress;
|
|
4567
6492
|
const id = numericChainId(session.data.chainId);
|
|
4568
|
-
const message = new
|
|
6493
|
+
const message = new import_siwe2.SiweMessage(session.message);
|
|
4569
6494
|
const login = await post("/auth/login", {
|
|
4570
6495
|
message,
|
|
4571
6496
|
signature: session.signature,
|
|
@@ -4615,6 +6540,7 @@ function createOwneySIWX(config) {
|
|
|
4615
6540
|
NotConnectedError,
|
|
4616
6541
|
OwneyError,
|
|
4617
6542
|
OwneySDK,
|
|
6543
|
+
YieldseekerAgent,
|
|
4618
6544
|
createOwneySIWX,
|
|
4619
6545
|
setOwneyDebug
|
|
4620
6546
|
});
|