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