@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.js
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
// src/lib/deposit-batch-callback.ts
|
|
2
|
+
var batchCallbacks = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
|
|
4
|
+
function toBatchTransfer(to, amount, verification) {
|
|
5
|
+
return {
|
|
6
|
+
to,
|
|
7
|
+
amount,
|
|
8
|
+
...verification ? {
|
|
9
|
+
yieldseeker: {
|
|
10
|
+
signature: verification.signature,
|
|
11
|
+
userId: verification.userId,
|
|
12
|
+
agentId: verification.yieldseekerAgentId
|
|
13
|
+
}
|
|
14
|
+
} : {}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function registerDepositBatch(callback, transfer) {
|
|
18
|
+
batchCallbacks.set(callback, transfer);
|
|
19
|
+
}
|
|
20
|
+
|
|
1
21
|
// src/errors.ts
|
|
2
22
|
var OwneyError = class extends Error {
|
|
3
23
|
code;
|
|
@@ -277,18 +297,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
277
297
|
return explicit;
|
|
278
298
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
279
299
|
}
|
|
280
|
-
function mapDeposit(
|
|
300
|
+
function mapDeposit(raw2) {
|
|
281
301
|
return {
|
|
282
|
-
txHash:
|
|
283
|
-
smartWallet:
|
|
284
|
-
amount:
|
|
302
|
+
txHash: raw2.txHash,
|
|
303
|
+
smartWallet: raw2.smartWallet,
|
|
304
|
+
amount: raw2.amount
|
|
285
305
|
};
|
|
286
306
|
}
|
|
287
|
-
function mapWithdraw(
|
|
307
|
+
function mapWithdraw(raw2) {
|
|
288
308
|
return {
|
|
289
|
-
txHash:
|
|
290
|
-
type:
|
|
291
|
-
amount:
|
|
309
|
+
txHash: raw2.txHash,
|
|
310
|
+
type: raw2.type,
|
|
311
|
+
amount: raw2.amount
|
|
292
312
|
};
|
|
293
313
|
}
|
|
294
314
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -307,10 +327,10 @@ function resolveChainId(chain) {
|
|
|
307
327
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
308
328
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
309
329
|
}
|
|
310
|
-
function mapPendingAllocations(
|
|
311
|
-
if (!Array.isArray(
|
|
330
|
+
function mapPendingAllocations(raw2) {
|
|
331
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
312
332
|
const pending = [];
|
|
313
|
-
for (const entry of
|
|
333
|
+
for (const entry of raw2) {
|
|
314
334
|
if (typeof entry !== "object" || entry === null) continue;
|
|
315
335
|
const e = entry;
|
|
316
336
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -333,8 +353,8 @@ function mapPendingAllocations(raw) {
|
|
|
333
353
|
}
|
|
334
354
|
return pending.length > 0 ? pending : void 0;
|
|
335
355
|
}
|
|
336
|
-
function mapBalances(
|
|
337
|
-
const portfolio =
|
|
356
|
+
function mapBalances(raw2, _chainId, smartWallet) {
|
|
357
|
+
const portfolio = raw2.portfolio;
|
|
338
358
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
339
359
|
let totalBalance = 0;
|
|
340
360
|
const tokens = [];
|
|
@@ -403,8 +423,8 @@ function sumTokenValues(tokens) {
|
|
|
403
423
|
function sumTokenEarnings(tokens) {
|
|
404
424
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
405
425
|
}
|
|
406
|
-
function mapEarnings(
|
|
407
|
-
const totalEarningsByChain =
|
|
426
|
+
function mapEarnings(raw2, smartWallet) {
|
|
427
|
+
const totalEarningsByChain = raw2.data.totalEarningsByChainWithFee ?? raw2.data.totalEarningsByChain ?? {};
|
|
408
428
|
const tokens = [];
|
|
409
429
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
410
430
|
totalEarningsByChain
|
|
@@ -423,15 +443,15 @@ function mapEarnings(raw, smartWallet) {
|
|
|
423
443
|
return {
|
|
424
444
|
smartWallet,
|
|
425
445
|
lifetimeEarnings: sumTokenEarnings(
|
|
426
|
-
|
|
446
|
+
raw2.data.totalEarningsByTokenWithFee ?? raw2.data.totalEarningsByToken
|
|
427
447
|
),
|
|
428
448
|
tokens
|
|
429
449
|
};
|
|
430
450
|
}
|
|
431
|
-
function mapWeightedApyByChain(
|
|
432
|
-
if (!
|
|
451
|
+
function mapWeightedApyByChain(raw2) {
|
|
452
|
+
if (!raw2) return void 0;
|
|
433
453
|
const out = {};
|
|
434
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
454
|
+
for (const [chainKey, tokenApy] of Object.entries(raw2)) {
|
|
435
455
|
const chainId = Number(chainKey);
|
|
436
456
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
437
457
|
const perAsset = {};
|
|
@@ -471,8 +491,8 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
471
491
|
}
|
|
472
492
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
473
493
|
}
|
|
474
|
-
function mapApyHistory(
|
|
475
|
-
const history = Object.entries(
|
|
494
|
+
function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
495
|
+
const history = Object.entries(raw2.history ?? {}).map(([date, entry]) => ({
|
|
476
496
|
date,
|
|
477
497
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol),
|
|
478
498
|
// Provider position balances are treated as decimal amounts of the
|
|
@@ -488,9 +508,9 @@ function mapApyHistory(raw, chainId, tokenSymbol) {
|
|
|
488
508
|
} : {}
|
|
489
509
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
490
510
|
return {
|
|
491
|
-
walletAddress:
|
|
492
|
-
weightedApyAfterFee:
|
|
493
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
511
|
+
walletAddress: raw2.walletAddress,
|
|
512
|
+
weightedApyAfterFee: raw2.weightedApyAfterFee ? sumTokenValues(raw2.weightedApyAfterFee) : void 0,
|
|
513
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw2.weightedApyAfterFeeByChain),
|
|
494
514
|
history
|
|
495
515
|
};
|
|
496
516
|
}
|
|
@@ -586,23 +606,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
586
606
|
};
|
|
587
607
|
});
|
|
588
608
|
}
|
|
589
|
-
function mapUserProfile(
|
|
609
|
+
function mapUserProfile(raw2, userAddress) {
|
|
590
610
|
return {
|
|
591
611
|
address: userAddress,
|
|
592
|
-
smartWallet:
|
|
593
|
-
chains:
|
|
594
|
-
strategy:
|
|
595
|
-
hasActiveSessionKey:
|
|
596
|
-
protocols:
|
|
597
|
-
splitting:
|
|
598
|
-
minSplits:
|
|
612
|
+
smartWallet: raw2.smartWallet || "",
|
|
613
|
+
chains: raw2.chains || [],
|
|
614
|
+
strategy: raw2.strategy,
|
|
615
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey || false,
|
|
616
|
+
protocols: raw2.protocols || [],
|
|
617
|
+
splitting: raw2.splitting,
|
|
618
|
+
minSplits: raw2.minSplits
|
|
599
619
|
};
|
|
600
620
|
}
|
|
601
|
-
function mapApyByStrategy(
|
|
621
|
+
function mapApyByStrategy(raw2) {
|
|
602
622
|
const apyPerAsset = {};
|
|
603
623
|
let apySum = 0;
|
|
604
624
|
let apyCount = 0;
|
|
605
|
-
for (const entry of
|
|
625
|
+
for (const entry of raw2.data) {
|
|
606
626
|
const supported = SupportedAssets.find(
|
|
607
627
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
608
628
|
);
|
|
@@ -714,9 +734,9 @@ function netDeltaForSnapshot(entry, chainId, asset) {
|
|
|
714
734
|
debugLog("zyfai:earnings", "gross fallback for daily earnings", { gross });
|
|
715
735
|
return gross;
|
|
716
736
|
}
|
|
717
|
-
function mapDailyEarnings(
|
|
737
|
+
function mapDailyEarnings(raw2, chainId, tokenSymbol) {
|
|
718
738
|
const wanted = tokenSymbol?.toUpperCase();
|
|
719
|
-
const snapshots = [...
|
|
739
|
+
const snapshots = [...raw2.data ?? []].sort(
|
|
720
740
|
(a, b) => a.snapshot_date.localeCompare(b.snapshot_date)
|
|
721
741
|
);
|
|
722
742
|
const byAsset = /* @__PURE__ */ new Map();
|
|
@@ -731,7 +751,7 @@ function mapDailyEarnings(raw, chainId, tokenSymbol) {
|
|
|
731
751
|
}
|
|
732
752
|
}
|
|
733
753
|
const assets = [...byAsset.entries()].map(([asset, series]) => ({ asset, points: series.points })).sort((a, b) => a.asset.localeCompare(b.asset));
|
|
734
|
-
return { walletAddress:
|
|
754
|
+
return { walletAddress: raw2.walletAddress, chainId, assets };
|
|
735
755
|
}
|
|
736
756
|
|
|
737
757
|
// src/agents/zyfai/zyfai.withdraw-amount.ts
|
|
@@ -842,15 +862,15 @@ var readSession = (address, _chainId) => {
|
|
|
842
862
|
if (typeof window === "undefined") return null;
|
|
843
863
|
const key2 = buildKey(address);
|
|
844
864
|
const store = storage();
|
|
845
|
-
let
|
|
865
|
+
let raw2 = null;
|
|
846
866
|
try {
|
|
847
|
-
|
|
867
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
848
868
|
} catch {
|
|
849
|
-
|
|
869
|
+
raw2 = null;
|
|
850
870
|
}
|
|
851
|
-
if (
|
|
871
|
+
if (raw2) {
|
|
852
872
|
try {
|
|
853
|
-
const parsed = JSON.parse(
|
|
873
|
+
const parsed = JSON.parse(raw2);
|
|
854
874
|
if (isFreshSession(parsed)) return parsed;
|
|
855
875
|
} catch {
|
|
856
876
|
}
|
|
@@ -1018,8 +1038,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
1018
1038
|
}
|
|
1019
1039
|
return result;
|
|
1020
1040
|
}
|
|
1021
|
-
function flattenAvailablePools(
|
|
1022
|
-
const byChain =
|
|
1041
|
+
function flattenAvailablePools(raw2) {
|
|
1042
|
+
const byChain = raw2 ?? {};
|
|
1023
1043
|
const names = [];
|
|
1024
1044
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
1025
1045
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -1522,8 +1542,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1522
1542
|
const poolResults = await Promise.all(
|
|
1523
1543
|
universe.map(async (protocol) => {
|
|
1524
1544
|
try {
|
|
1525
|
-
const
|
|
1526
|
-
return [protocol.id, flattenAvailablePools(
|
|
1545
|
+
const raw2 = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1546
|
+
return [protocol.id, flattenAvailablePools(raw2)];
|
|
1527
1547
|
} catch (error) {
|
|
1528
1548
|
console.warn(
|
|
1529
1549
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1589,14 +1609,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1589
1609
|
async readWalletState(ownerAddress) {
|
|
1590
1610
|
try {
|
|
1591
1611
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1592
|
-
const
|
|
1612
|
+
const raw2 = portfolio;
|
|
1593
1613
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1594
|
-
predeployed:
|
|
1595
|
-
hasActiveSessionKey:
|
|
1614
|
+
predeployed: raw2?.predeployed,
|
|
1615
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1596
1616
|
});
|
|
1597
1617
|
return {
|
|
1598
|
-
predeployed:
|
|
1599
|
-
hasActiveSessionKey:
|
|
1618
|
+
predeployed: raw2?.predeployed,
|
|
1619
|
+
hasActiveSessionKey: raw2?.hasActiveSessionKey
|
|
1600
1620
|
};
|
|
1601
1621
|
} catch (error) {
|
|
1602
1622
|
console.warn(
|
|
@@ -1882,14 +1902,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1882
1902
|
return { txHash, smartWallet, amount };
|
|
1883
1903
|
}
|
|
1884
1904
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1885
|
-
const
|
|
1905
|
+
const raw2 = await this.sdk.depositFunds(
|
|
1886
1906
|
this.getAddress(),
|
|
1887
1907
|
validChainId,
|
|
1888
1908
|
amount,
|
|
1889
1909
|
asset,
|
|
1890
1910
|
"aggressive"
|
|
1891
1911
|
);
|
|
1892
|
-
return mapDeposit(
|
|
1912
|
+
return mapDeposit(raw2);
|
|
1893
1913
|
} catch (error) {
|
|
1894
1914
|
throw error;
|
|
1895
1915
|
}
|
|
@@ -1898,27 +1918,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1898
1918
|
async withdraw(state, chainId, token, amount) {
|
|
1899
1919
|
const validChainId = isValidChainId(chainId);
|
|
1900
1920
|
await this.ensureConnected(state, validChainId);
|
|
1901
|
-
const
|
|
1921
|
+
const raw2 = await this.sdk.withdrawFunds(
|
|
1902
1922
|
this.getAddress(),
|
|
1903
1923
|
validChainId,
|
|
1904
1924
|
amount,
|
|
1905
1925
|
token
|
|
1906
1926
|
);
|
|
1907
|
-
if (!
|
|
1927
|
+
if (!raw2.success) {
|
|
1908
1928
|
throw new OwneyError(
|
|
1909
1929
|
"WITHDRAW_FAILED",
|
|
1910
|
-
|
|
1911
|
-
{ chainId: validChainId, token, amount, response:
|
|
1930
|
+
raw2.message || "Zyfai withdraw failed.",
|
|
1931
|
+
{ chainId: validChainId, token, amount, response: raw2 },
|
|
1912
1932
|
this.id
|
|
1913
1933
|
);
|
|
1914
1934
|
}
|
|
1915
|
-
return mapWithdraw(
|
|
1935
|
+
return mapWithdraw(raw2);
|
|
1916
1936
|
}
|
|
1917
1937
|
// --- IAgent: Portfolio reads ---
|
|
1918
1938
|
async getBalances(state, chainId) {
|
|
1919
1939
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1920
|
-
const
|
|
1921
|
-
return mapBalances(
|
|
1940
|
+
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
1941
|
+
return mapBalances(raw2, validChainId, smartWallet);
|
|
1922
1942
|
}
|
|
1923
1943
|
earningsKey(state, chainId, smartWallet) {
|
|
1924
1944
|
return JSON.stringify([
|
|
@@ -1931,11 +1951,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1931
1951
|
const existing = this.earningsReads.get(key2);
|
|
1932
1952
|
if (existing) return existing;
|
|
1933
1953
|
const generation = this.earningsGeneration;
|
|
1934
|
-
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((
|
|
1954
|
+
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw2) => {
|
|
1935
1955
|
if (generation === this.earningsGeneration) {
|
|
1936
|
-
this.earningsSnapshot = { key: key2, raw, at: Date.now() };
|
|
1956
|
+
this.earningsSnapshot = { key: key2, raw: raw2, at: Date.now() };
|
|
1937
1957
|
}
|
|
1938
|
-
return
|
|
1958
|
+
return raw2;
|
|
1939
1959
|
}).finally(() => {
|
|
1940
1960
|
if (this.earningsReads.get(key2) === pending)
|
|
1941
1961
|
this.earningsReads.delete(key2);
|
|
@@ -1945,11 +1965,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1945
1965
|
}
|
|
1946
1966
|
async getEarnings(state, chainId) {
|
|
1947
1967
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1948
|
-
const
|
|
1968
|
+
const raw2 = await this.readEarnings(
|
|
1949
1969
|
this.earningsKey(state, chainId, smartWallet),
|
|
1950
1970
|
smartWallet
|
|
1951
1971
|
);
|
|
1952
|
-
return mapEarnings(
|
|
1972
|
+
return mapEarnings(raw2, smartWallet);
|
|
1953
1973
|
}
|
|
1954
1974
|
async refreshEarnings(state, chainId) {
|
|
1955
1975
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -1976,17 +1996,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1976
1996
|
}
|
|
1977
1997
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1978
1998
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1979
|
-
const
|
|
1980
|
-
return mapApyHistory(
|
|
1999
|
+
const raw2 = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
2000
|
+
return mapApyHistory(raw2, chainId, tokenSymbol);
|
|
1981
2001
|
}
|
|
1982
2002
|
async getDailyEarnings(state, chainId, days, tokenSymbol) {
|
|
1983
2003
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1984
2004
|
const start = new Date(Date.now() - (DayFilterMapping[days] + 1) * 864e5);
|
|
1985
|
-
const
|
|
2005
|
+
const raw2 = await this.sdk.getDailyEarnings(
|
|
1986
2006
|
smartWallet,
|
|
1987
2007
|
start.toISOString().slice(0, 10)
|
|
1988
2008
|
);
|
|
1989
|
-
return mapDailyEarnings(
|
|
2009
|
+
return mapDailyEarnings(raw2, chainId, tokenSymbol);
|
|
1990
2010
|
}
|
|
1991
2011
|
/**
|
|
1992
2012
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -2021,7 +2041,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2021
2041
|
const matched = [];
|
|
2022
2042
|
let backendExhausted = false;
|
|
2023
2043
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
2024
|
-
const
|
|
2044
|
+
const raw2 = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
2025
2045
|
limit: backendPageSize,
|
|
2026
2046
|
offset,
|
|
2027
2047
|
fromDate: options?.fromDate,
|
|
@@ -2032,13 +2052,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2032
2052
|
// asset's rows and handing back a page that filters to nothing.
|
|
2033
2053
|
assetType
|
|
2034
2054
|
});
|
|
2035
|
-
|
|
2055
|
+
raw2.data.forEach((entry, idx) => {
|
|
2036
2056
|
if (entry.chainId === validChainId) {
|
|
2037
2057
|
matched.push({ entry, rawIdx: offset + idx });
|
|
2038
2058
|
}
|
|
2039
2059
|
});
|
|
2040
|
-
offset +=
|
|
2041
|
-
if (
|
|
2060
|
+
offset += raw2.data.length;
|
|
2061
|
+
if (raw2.data.length < backendPageSize) {
|
|
2042
2062
|
backendExhausted = true;
|
|
2043
2063
|
break;
|
|
2044
2064
|
}
|
|
@@ -2060,18 +2080,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2060
2080
|
}
|
|
2061
2081
|
async getUserProfile(state, chainId) {
|
|
2062
2082
|
await this.connectAuth(state, chainId);
|
|
2063
|
-
const
|
|
2083
|
+
const raw2 = await this.sdk.getUserDetails();
|
|
2064
2084
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
2065
2085
|
asset: "USDC (default \u2014 no asset passed)",
|
|
2066
|
-
splitting:
|
|
2067
|
-
minSplits:
|
|
2068
|
-
strategy:
|
|
2069
|
-
chains:
|
|
2070
|
-
protocolCount:
|
|
2071
|
-
hasActiveSessionKey:
|
|
2072
|
-
smartWallet:
|
|
2086
|
+
splitting: raw2.splitting,
|
|
2087
|
+
minSplits: raw2.minSplits,
|
|
2088
|
+
strategy: raw2.strategy,
|
|
2089
|
+
chains: raw2.chains,
|
|
2090
|
+
protocolCount: raw2.protocols?.length,
|
|
2091
|
+
hasActiveSessionKey: raw2.hasActiveSessionKey,
|
|
2092
|
+
smartWallet: raw2.smartWallet
|
|
2073
2093
|
});
|
|
2074
|
-
return mapUserProfile(
|
|
2094
|
+
return mapUserProfile(raw2, this.connectedAddress);
|
|
2075
2095
|
}
|
|
2076
2096
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
2077
2097
|
await this.connectAuth(state, chainId);
|
|
@@ -2090,80 +2110,28 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2090
2110
|
}
|
|
2091
2111
|
// --- IAgent: Discovery (no wallet required) ---
|
|
2092
2112
|
async getAgentApy(days, options) {
|
|
2093
|
-
const
|
|
2113
|
+
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
2094
2114
|
false,
|
|
2095
2115
|
DayFilterMapping[days],
|
|
2096
2116
|
"aggressive",
|
|
2097
2117
|
options?.chainId,
|
|
2098
2118
|
options?.tokenSymbol
|
|
2099
2119
|
);
|
|
2100
|
-
return mapApyByStrategy(
|
|
2120
|
+
return mapApyByStrategy(raw2);
|
|
2101
2121
|
}
|
|
2102
2122
|
};
|
|
2103
2123
|
|
|
2104
|
-
// src/
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
});
|
|
2116
|
-
if (!res.ok) {
|
|
2117
|
-
if (res.status !== 404) {
|
|
2118
|
-
console.warn(
|
|
2119
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
2120
|
-
);
|
|
2121
|
-
}
|
|
2122
|
-
return null;
|
|
2123
|
-
}
|
|
2124
|
-
const json = await res.json();
|
|
2125
|
-
const policy = json.success ? json.data ?? null : null;
|
|
2126
|
-
debugLog(
|
|
2127
|
-
"owney-sdk",
|
|
2128
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
2129
|
-
policy ?? void 0
|
|
2130
|
-
);
|
|
2131
|
-
return policy;
|
|
2132
|
-
} catch (error) {
|
|
2133
|
-
console.warn(
|
|
2134
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
2135
|
-
error instanceof Error ? error.message : String(error)
|
|
2136
|
-
);
|
|
2137
|
-
return null;
|
|
2138
|
-
}
|
|
2139
|
-
}
|
|
2140
|
-
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
2141
|
-
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
2142
|
-
const res = await fetch(url, {
|
|
2143
|
-
method: "GET",
|
|
2144
|
-
headers: {
|
|
2145
|
-
"Content-Type": "application/json",
|
|
2146
|
-
"x-owney-api-key": `${apiKey}`
|
|
2147
|
-
}
|
|
2148
|
-
});
|
|
2149
|
-
if (!res.ok) {
|
|
2150
|
-
const text = await res.text().catch(() => "");
|
|
2151
|
-
throw new OwneyError(
|
|
2152
|
-
"API_ROUTING_ERROR",
|
|
2153
|
-
`Routing API error ${res.status}: ${text}`,
|
|
2154
|
-
{ statusCode: res.status, responseBody: text }
|
|
2155
|
-
);
|
|
2156
|
-
}
|
|
2157
|
-
const json = await res.json();
|
|
2158
|
-
if (!json.success) {
|
|
2159
|
-
throw new OwneyError(
|
|
2160
|
-
"API_ROUTING_FAILED",
|
|
2161
|
-
`Routing API request failed: ${json.message}`,
|
|
2162
|
-
{ message: json.message }
|
|
2163
|
-
);
|
|
2164
|
-
}
|
|
2165
|
-
return json.data;
|
|
2166
|
-
}
|
|
2124
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2125
|
+
import {
|
|
2126
|
+
createPublicClient as createPublicClient3,
|
|
2127
|
+
createWalletClient as createWalletClient2,
|
|
2128
|
+
custom as custom2,
|
|
2129
|
+
encodeFunctionData,
|
|
2130
|
+
erc20Abi,
|
|
2131
|
+
getAddress as getAddress2,
|
|
2132
|
+
isAddress as isAddress2
|
|
2133
|
+
} from "viem";
|
|
2134
|
+
import { base as base3 } from "viem/chains";
|
|
2167
2135
|
|
|
2168
2136
|
// src/lib/chain-guard.ts
|
|
2169
2137
|
var CHAIN_NAMES = {
|
|
@@ -2200,148 +2168,132 @@ async function ensureWalletOnChain(pub, wallet, expected) {
|
|
|
2200
2168
|
}
|
|
2201
2169
|
}
|
|
2202
2170
|
|
|
2203
|
-
// src/lib/
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
}
|
|
2223
|
-
if (res.status === 403) {
|
|
2224
|
-
throw new OwneyError(
|
|
2225
|
-
"SWAP_DISABLED",
|
|
2226
|
-
"Swap is not enabled for this organization",
|
|
2227
|
-
{ statusCode: res.status }
|
|
2228
|
-
);
|
|
2229
|
-
}
|
|
2171
|
+
// src/lib/transfer-auth.ts
|
|
2172
|
+
import { bytesToHex } from "viem";
|
|
2173
|
+
|
|
2174
|
+
// src/lib/sponsor-client.ts
|
|
2175
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2176
|
+
async function postPaymasterIntent(input) {
|
|
2177
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2178
|
+
let res;
|
|
2179
|
+
try {
|
|
2180
|
+
res = await fetch(`${base5}/api/v1/sponsor/paymaster-intent`, {
|
|
2181
|
+
method: "POST",
|
|
2182
|
+
headers: {
|
|
2183
|
+
"content-type": "application/json",
|
|
2184
|
+
"x-owney-api-key": input.apiKey,
|
|
2185
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
2186
|
+
},
|
|
2187
|
+
body: JSON.stringify(input.body)
|
|
2188
|
+
});
|
|
2189
|
+
} catch (networkError) {
|
|
2230
2190
|
throw new OwneyError(
|
|
2231
|
-
"
|
|
2232
|
-
`
|
|
2233
|
-
{
|
|
2191
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2192
|
+
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2193
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
2234
2194
|
);
|
|
2235
2195
|
}
|
|
2236
|
-
const
|
|
2237
|
-
|
|
2196
|
+
const text = await res.text();
|
|
2197
|
+
let parsed = null;
|
|
2198
|
+
try {
|
|
2199
|
+
parsed = JSON.parse(text);
|
|
2200
|
+
} catch {
|
|
2201
|
+
}
|
|
2202
|
+
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
2238
2203
|
throw new OwneyError(
|
|
2239
|
-
"
|
|
2240
|
-
`
|
|
2241
|
-
{
|
|
2204
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2205
|
+
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2206
|
+
{
|
|
2207
|
+
statusCode: res.status,
|
|
2208
|
+
responseBody: text.slice(0, 500),
|
|
2209
|
+
safeToFallback: true
|
|
2210
|
+
}
|
|
2242
2211
|
);
|
|
2243
2212
|
}
|
|
2244
|
-
return
|
|
2213
|
+
return parsed.data;
|
|
2245
2214
|
}
|
|
2246
|
-
function
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
*/
|
|
2255
|
-
quote: (params) => request(baseUrl, apiKey, "/quote", {
|
|
2256
|
-
method: "POST",
|
|
2257
|
-
body: {
|
|
2258
|
-
srcChainId: params.from.chainId,
|
|
2259
|
-
srcSymbol: params.from.symbol,
|
|
2260
|
-
dstChainId: params.to.chainId,
|
|
2261
|
-
dstSymbol: params.to.symbol,
|
|
2262
|
-
amount: params.from.amount,
|
|
2263
|
-
walletAddress: params.walletAddress,
|
|
2264
|
-
...params.direction ? { direction: params.direction } : {}
|
|
2265
|
-
}
|
|
2266
|
-
}),
|
|
2267
|
-
/** Ready-to-send calldata for a same-chain swap. */
|
|
2268
|
-
swapTx: (params) => request(baseUrl, apiKey, "/tx", {
|
|
2269
|
-
method: "POST",
|
|
2270
|
-
body: {
|
|
2271
|
-
srcChainId: params.from.chainId,
|
|
2272
|
-
srcSymbol: params.from.symbol,
|
|
2273
|
-
dstChainId: params.to.chainId,
|
|
2274
|
-
dstSymbol: params.to.symbol,
|
|
2275
|
-
amount: params.from.amount,
|
|
2276
|
-
walletAddress: params.walletAddress,
|
|
2277
|
-
slippage: params.slippage,
|
|
2278
|
-
...params.direction ? { direction: params.direction } : {}
|
|
2215
|
+
async function getSponsorRelayerAddress(input) {
|
|
2216
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2217
|
+
let res;
|
|
2218
|
+
try {
|
|
2219
|
+
res = await fetch(
|
|
2220
|
+
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2221
|
+
{
|
|
2222
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
2279
2223
|
}
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2224
|
+
);
|
|
2225
|
+
} catch (networkError) {
|
|
2226
|
+
throw new OwneyError(
|
|
2227
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2228
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2229
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
2230
|
+
);
|
|
2231
|
+
}
|
|
2232
|
+
const text = await res.text();
|
|
2233
|
+
let parsed = null;
|
|
2234
|
+
try {
|
|
2235
|
+
parsed = JSON.parse(text);
|
|
2236
|
+
} catch {
|
|
2237
|
+
}
|
|
2238
|
+
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
2239
|
+
throw new OwneyError(
|
|
2240
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2241
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2242
|
+
{
|
|
2243
|
+
statusCode: res.status,
|
|
2244
|
+
responseBody: text.slice(0, 500),
|
|
2245
|
+
safeToFallback: true
|
|
2299
2246
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
* Only call once `readyForSecrets` reports the escrow deployed. Publishing
|
|
2304
|
-
* earlier hands a resolver the preimage while the user's funds are locked
|
|
2305
|
-
* and nothing has been posted on the destination chain.
|
|
2306
|
-
*/
|
|
2307
|
-
submitSecret: (orderHash, secret) => request(baseUrl, apiKey, "/order/secret", {
|
|
2308
|
-
method: "POST",
|
|
2309
|
-
body: { orderHash, secret }
|
|
2310
|
-
}),
|
|
2311
|
-
orderStatus: (orderHash) => request(baseUrl, apiKey, `/order/${orderHash}`),
|
|
2312
|
-
readyForSecrets: (orderHash) => request(
|
|
2313
|
-
baseUrl,
|
|
2314
|
-
apiKey,
|
|
2315
|
-
`/order/${orderHash}/ready-for-secrets`
|
|
2316
|
-
)
|
|
2317
|
-
};
|
|
2318
|
-
}
|
|
2319
|
-
|
|
2320
|
-
// src/lib/swap/swap.rpc.ts
|
|
2321
|
-
import { fallback, http as http2 } from "viem";
|
|
2322
|
-
var DEFAULT_RPC_URLS = {
|
|
2323
|
-
1: ["https://cloudflare-eth.com", "https://ethereum-rpc.publicnode.com"],
|
|
2324
|
-
8453: ["https://mainnet.base.org", "https://base-rpc.publicnode.com"],
|
|
2325
|
-
42161: [
|
|
2326
|
-
"https://arb1.arbitrum.io/rpc",
|
|
2327
|
-
"https://arbitrum-one-rpc.publicnode.com"
|
|
2328
|
-
]
|
|
2329
|
-
};
|
|
2330
|
-
function swapReadTransport(chainId, overrides) {
|
|
2331
|
-
const override = overrides?.[chainId];
|
|
2332
|
-
if (override) return http2(override);
|
|
2333
|
-
const urls = DEFAULT_RPC_URLS[chainId];
|
|
2334
|
-
if (!urls || urls.length === 0) return http2();
|
|
2335
|
-
return fallback(urls.map((url) => http2(url)));
|
|
2247
|
+
);
|
|
2248
|
+
}
|
|
2249
|
+
return parsed.data.relayer;
|
|
2336
2250
|
}
|
|
2337
|
-
function
|
|
2338
|
-
|
|
2251
|
+
async function postSponsorBatchTransfer(input) {
|
|
2252
|
+
let res;
|
|
2253
|
+
try {
|
|
2254
|
+
res = await fetch(
|
|
2255
|
+
`${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
|
|
2256
|
+
{
|
|
2257
|
+
method: "POST",
|
|
2258
|
+
headers: {
|
|
2259
|
+
"content-type": "application/json",
|
|
2260
|
+
"x-owney-api-key": input.apiKey
|
|
2261
|
+
},
|
|
2262
|
+
body: JSON.stringify(input.body)
|
|
2263
|
+
}
|
|
2264
|
+
);
|
|
2265
|
+
} catch {
|
|
2266
|
+
throw new OwneyError(
|
|
2267
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2268
|
+
"Deposit status is unknown. Retry the same amount to check it.",
|
|
2269
|
+
{ safeToFallback: false }
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
const parsed = await res.json().catch(() => null);
|
|
2273
|
+
if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
|
|
2274
|
+
throw new OwneyError(
|
|
2275
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2276
|
+
"Deposit could not be confirmed. Retry the same amount to check its status.",
|
|
2277
|
+
{
|
|
2278
|
+
statusCode: res.status,
|
|
2279
|
+
safeToFallback: false,
|
|
2280
|
+
notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
|
|
2281
|
+
}
|
|
2282
|
+
);
|
|
2283
|
+
}
|
|
2284
|
+
return parsed.data;
|
|
2339
2285
|
}
|
|
2340
2286
|
|
|
2341
2287
|
// src/lib/permit2.ts
|
|
2342
|
-
import { bytesToHex } from "viem";
|
|
2288
|
+
import { bytesToHex as bytesToHex2 } from "viem";
|
|
2343
2289
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2344
2290
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2291
|
+
function permit2ApprovalAmount(requiredAmount) {
|
|
2292
|
+
if (requiredAmount <= 0n) {
|
|
2293
|
+
throw new Error("Permit2 approval requires a positive deposit amount");
|
|
2294
|
+
}
|
|
2295
|
+
return MAX_UINT256;
|
|
2296
|
+
}
|
|
2345
2297
|
var ERC20_ALLOWANCE_ABI = [
|
|
2346
2298
|
{
|
|
2347
2299
|
type: "function",
|
|
@@ -2371,33 +2323,10 @@ var ERC20_ALLOWANCE_ABI = [
|
|
|
2371
2323
|
outputs: [{ name: "", type: "uint256" }]
|
|
2372
2324
|
}
|
|
2373
2325
|
];
|
|
2374
|
-
function buildPermitTransferFromTypedData(input) {
|
|
2375
|
-
return {
|
|
2376
|
-
domain: {
|
|
2377
|
-
name: "Permit2",
|
|
2378
|
-
chainId: input.chainId,
|
|
2379
|
-
verifyingContract: PERMIT2_ADDRESS
|
|
2380
|
-
},
|
|
2381
|
-
types: {
|
|
2382
|
-
PermitTransferFrom: [
|
|
2383
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
2384
|
-
{ name: "spender", type: "address" },
|
|
2385
|
-
{ name: "nonce", type: "uint256" },
|
|
2386
|
-
{ name: "deadline", type: "uint256" }
|
|
2387
|
-
],
|
|
2388
|
-
TokenPermissions: [
|
|
2389
|
-
{ name: "token", type: "address" },
|
|
2390
|
-
{ name: "amount", type: "uint256" }
|
|
2391
|
-
]
|
|
2392
|
-
},
|
|
2393
|
-
primaryType: "PermitTransferFrom",
|
|
2394
|
-
message: input.message
|
|
2395
|
-
};
|
|
2396
|
-
}
|
|
2397
2326
|
function randomPermit2Nonce() {
|
|
2398
2327
|
const bytes = new Uint8Array(32);
|
|
2399
2328
|
globalThis.crypto.getRandomValues(bytes);
|
|
2400
|
-
return BigInt(
|
|
2329
|
+
return BigInt(bytesToHex2(bytes));
|
|
2401
2330
|
}
|
|
2402
2331
|
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2403
2332
|
return publicClient.readContract({
|
|
@@ -2416,122 +2345,44 @@ async function readErc20Balance(publicClient, token, owner) {
|
|
|
2416
2345
|
});
|
|
2417
2346
|
}
|
|
2418
2347
|
|
|
2419
|
-
// src/lib/
|
|
2420
|
-
|
|
2421
|
-
var
|
|
2422
|
-
function
|
|
2423
|
-
|
|
2424
|
-
const cryptoObj = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
|
|
2425
|
-
if (!cryptoObj?.getRandomValues) {
|
|
2426
|
-
throw new Error(
|
|
2427
|
-
"[owney-sdk] Secure randomness is unavailable, so a swap secret cannot be generated safely."
|
|
2428
|
-
);
|
|
2429
|
-
}
|
|
2430
|
-
cryptoObj.getRandomValues(bytes);
|
|
2431
|
-
return bytes;
|
|
2432
|
-
}
|
|
2433
|
-
function mintSecrets(count) {
|
|
2434
|
-
if (!Number.isInteger(count) || count < 1) {
|
|
2435
|
-
throw new Error(
|
|
2436
|
-
`[owney-sdk] A swap needs at least one secret, got ${String(count)}.`
|
|
2437
|
-
);
|
|
2438
|
-
}
|
|
2439
|
-
const secrets = [];
|
|
2440
|
-
const secretHashes = [];
|
|
2441
|
-
for (let i = 0; i < count; i++) {
|
|
2442
|
-
const secret = toHex(randomBytes(SECRET_BYTES));
|
|
2443
|
-
secrets.push(secret);
|
|
2444
|
-
secretHashes.push(keccak256(secret));
|
|
2445
|
-
}
|
|
2446
|
-
return { secrets, secretHashes };
|
|
2348
|
+
// src/lib/sponsored-deposit.ts
|
|
2349
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2350
|
+
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2351
|
+
function provideDepositVerificationContext(callback, context) {
|
|
2352
|
+
callback[verificationSetter]?.(context);
|
|
2447
2353
|
}
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
// src/lib/swap/swap.order-runner.ts
|
|
2459
|
-
var DEFAULT_POLL_MS = 5e3;
|
|
2460
|
-
var MAX_BACKOFF_MS = 3e4;
|
|
2461
|
-
var backoffFor = (failures, base3) => Math.min(base3 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS);
|
|
2462
|
-
var DEFAULT_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
2463
|
-
async function runFusionOrder(deps, options) {
|
|
2464
|
-
const {
|
|
2465
|
-
orderHash,
|
|
2466
|
-
secrets,
|
|
2467
|
-
onStage,
|
|
2468
|
-
pollIntervalMs = DEFAULT_POLL_MS,
|
|
2469
|
-
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
2470
|
-
} = options;
|
|
2471
|
-
const deadline = deps.now() + timeoutMs;
|
|
2472
|
-
let failures = 0;
|
|
2473
|
-
const published = /* @__PURE__ */ new Set();
|
|
2474
|
-
onStage?.("swapping");
|
|
2475
|
-
for (; ; ) {
|
|
2476
|
-
if (deps.now() >= deadline) {
|
|
2477
|
-
throw new OwneyError(
|
|
2478
|
-
"SWAP_REQUEST_FAILED",
|
|
2479
|
-
"Timed out waiting for the swap to settle. It may still complete \u2014 check the order status before retrying.",
|
|
2480
|
-
{ orderHash }
|
|
2481
|
-
);
|
|
2482
|
-
}
|
|
2483
|
-
let ready;
|
|
2484
|
-
try {
|
|
2485
|
-
ready = await deps.readyForSecrets(orderHash);
|
|
2486
|
-
} catch {
|
|
2487
|
-
ready = {};
|
|
2488
|
-
}
|
|
2489
|
-
for (const fill of ready.fills ?? []) {
|
|
2490
|
-
if (published.has(fill.idx)) continue;
|
|
2491
|
-
const secret = secrets[fill.idx];
|
|
2492
|
-
if (secret === void 0) {
|
|
2493
|
-
throw new OwneyError(
|
|
2494
|
-
"SWAP_REQUEST_FAILED",
|
|
2495
|
-
`Swap needs a secret for fill ${fill.idx} that this session does not have. The order will refund once its timelock expires.`,
|
|
2496
|
-
{ orderHash, fillIndex: fill.idx }
|
|
2497
|
-
);
|
|
2498
|
-
}
|
|
2499
|
-
try {
|
|
2500
|
-
await deps.submitSecret(orderHash, secret);
|
|
2501
|
-
published.add(fill.idx);
|
|
2502
|
-
} catch {
|
|
2503
|
-
failures += 1;
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
let status;
|
|
2507
|
-
try {
|
|
2508
|
-
({ status } = await deps.orderStatus(orderHash));
|
|
2509
|
-
failures = 0;
|
|
2510
|
-
} catch {
|
|
2511
|
-
failures += 1;
|
|
2512
|
-
await deps.sleep(backoffFor(failures, pollIntervalMs));
|
|
2513
|
-
continue;
|
|
2514
|
-
}
|
|
2515
|
-
if (status === "refunding") onStage?.("refunding");
|
|
2516
|
-
if (isSwapTerminal(status)) {
|
|
2517
|
-
if (status === "executed") {
|
|
2518
|
-
onStage?.("swapped");
|
|
2519
|
-
return { status, filled: true };
|
|
2520
|
-
}
|
|
2521
|
-
if (status === "refunded") onStage?.("refunded");
|
|
2522
|
-
throw new OwneyError(
|
|
2523
|
-
status === "refunded" ? "SWAP_ORDER_REFUNDED" : status === "cancelled" ? "SWAP_ORDER_CANCELLED" : "SWAP_ORDER_EXPIRED",
|
|
2524
|
-
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.",
|
|
2525
|
-
{ orderHash, status }
|
|
2526
|
-
);
|
|
2354
|
+
function makeVerificationAwareDepositCallback(implementation) {
|
|
2355
|
+
let nextVerification;
|
|
2356
|
+
const callback = async (smartWallet, chainId, amount) => {
|
|
2357
|
+
const verification = nextVerification;
|
|
2358
|
+
nextVerification = void 0;
|
|
2359
|
+
return implementation(smartWallet, chainId, amount, verification);
|
|
2360
|
+
};
|
|
2361
|
+
Object.defineProperty(callback, verificationSetter, {
|
|
2362
|
+
value: (context) => {
|
|
2363
|
+
nextVerification = context;
|
|
2527
2364
|
}
|
|
2528
|
-
|
|
2529
|
-
|
|
2365
|
+
});
|
|
2366
|
+
return callback;
|
|
2530
2367
|
}
|
|
2531
2368
|
|
|
2532
|
-
// src/
|
|
2533
|
-
|
|
2534
|
-
|
|
2369
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2370
|
+
import { SiweMessage, generateNonce } from "siwe";
|
|
2371
|
+
import {
|
|
2372
|
+
createPublicClient as createPublicClient2,
|
|
2373
|
+
createWalletClient,
|
|
2374
|
+
custom,
|
|
2375
|
+
getAddress
|
|
2376
|
+
} from "viem";
|
|
2377
|
+
import { base as base2 } from "viem/chains";
|
|
2378
|
+
|
|
2379
|
+
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2380
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session.v5";
|
|
2381
|
+
var INVALIDATED_KEY_PREFIXES = [
|
|
2382
|
+
"owney.yieldseeker.session",
|
|
2383
|
+
"owney.yieldseeker.session.v3",
|
|
2384
|
+
"owney.yieldseeker.session.v4"
|
|
2385
|
+
];
|
|
2535
2386
|
var storage2 = () => {
|
|
2536
2387
|
if (typeof window === "undefined") return null;
|
|
2537
2388
|
try {
|
|
@@ -2540,281 +2391,1649 @@ var storage2 = () => {
|
|
|
2540
2391
|
return null;
|
|
2541
2392
|
}
|
|
2542
2393
|
};
|
|
2543
|
-
var
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2394
|
+
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2395
|
+
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2396
|
+
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2397
|
+
);
|
|
2398
|
+
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2399
|
+
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2400
|
+
memorySessions2.delete(key2);
|
|
2401
|
+
try {
|
|
2402
|
+
store?.removeItem(key2);
|
|
2403
|
+
} catch {
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
};
|
|
2407
|
+
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2408
|
+
var isValidSession = (session) => {
|
|
2409
|
+
if (!session?.token) return false;
|
|
2547
2410
|
try {
|
|
2548
|
-
|
|
2411
|
+
const parsed = JSON.parse(atob(session.token));
|
|
2412
|
+
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2549
2413
|
} catch {
|
|
2414
|
+
return false;
|
|
2550
2415
|
}
|
|
2551
|
-
}
|
|
2552
|
-
|
|
2416
|
+
};
|
|
2417
|
+
var readYieldseekerSession = (address, chainId) => {
|
|
2418
|
+
if (typeof window === "undefined") return null;
|
|
2419
|
+
const key2 = buildKey2(address, chainId);
|
|
2553
2420
|
const store = storage2();
|
|
2554
|
-
|
|
2421
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2422
|
+
let raw2 = null;
|
|
2555
2423
|
try {
|
|
2556
|
-
store
|
|
2424
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
2557
2425
|
} catch {
|
|
2426
|
+
raw2 = null;
|
|
2558
2427
|
}
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
try {
|
|
2565
|
-
const keys = [];
|
|
2566
|
-
for (let i = 0; i < store.length; i++) {
|
|
2567
|
-
const key2 = store.key(i);
|
|
2568
|
-
if (key2?.startsWith(`${KEY_PREFIX2}.`)) keys.push(key2);
|
|
2428
|
+
if (raw2) {
|
|
2429
|
+
try {
|
|
2430
|
+
const parsed = JSON.parse(raw2);
|
|
2431
|
+
if (isValidSession(parsed)) return parsed.token;
|
|
2432
|
+
} catch {
|
|
2569
2433
|
}
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2434
|
+
memorySessions2.delete(key2);
|
|
2435
|
+
try {
|
|
2436
|
+
store?.removeItem(key2);
|
|
2437
|
+
} catch {
|
|
2438
|
+
}
|
|
2439
|
+
return null;
|
|
2440
|
+
}
|
|
2441
|
+
const cached = memorySessions2.get(key2);
|
|
2442
|
+
if (isValidSession(cached)) return cached.token;
|
|
2443
|
+
if (cached) memorySessions2.delete(key2);
|
|
2444
|
+
return null;
|
|
2445
|
+
};
|
|
2446
|
+
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2447
|
+
if (typeof window === "undefined") return;
|
|
2448
|
+
const session = { token };
|
|
2449
|
+
if (!isValidSession(session)) return;
|
|
2450
|
+
const key2 = buildKey2(address, chainId);
|
|
2451
|
+
memorySessions2.set(key2, session);
|
|
2452
|
+
const store = storage2();
|
|
2453
|
+
try {
|
|
2454
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2455
|
+
} catch {
|
|
2456
|
+
}
|
|
2457
|
+
};
|
|
2458
|
+
var clearYieldseekerSession = (address, chainId) => {
|
|
2459
|
+
const key2 = buildKey2(address, chainId);
|
|
2460
|
+
memorySessions2.delete(key2);
|
|
2461
|
+
const store = storage2();
|
|
2462
|
+
clearInvalidatedSessions(store, address, chainId);
|
|
2463
|
+
try {
|
|
2464
|
+
store?.removeItem(key2);
|
|
2465
|
+
} catch {
|
|
2466
|
+
}
|
|
2467
|
+
};
|
|
2468
|
+
|
|
2469
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2470
|
+
function resolveSiweOrigin(override) {
|
|
2471
|
+
const origin = override?.trim() || (typeof window !== "undefined" ? window.location?.origin : void 0);
|
|
2472
|
+
if (!origin || origin === "null") {
|
|
2473
|
+
throw new Error("Yieldseeker sign-in requires the requesting app's browser origin.");
|
|
2474
|
+
}
|
|
2475
|
+
const url = new URL(origin);
|
|
2476
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
2477
|
+
throw new Error("Yieldseeker sign-in requires an HTTP or HTTPS app origin.");
|
|
2478
|
+
}
|
|
2479
|
+
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
2480
|
+
throw new Error("Yieldseeker SIWE override must be an origin without credentials, path, query, or fragment.");
|
|
2481
|
+
}
|
|
2482
|
+
return url;
|
|
2483
|
+
}
|
|
2484
|
+
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2485
|
+
const url = resolveSiweOrigin(dependencies.origin);
|
|
2486
|
+
return new SiweMessage({
|
|
2487
|
+
scheme: url.protocol.slice(0, -1),
|
|
2488
|
+
domain: url.host,
|
|
2489
|
+
address: getAddress(address),
|
|
2490
|
+
uri: url.origin,
|
|
2491
|
+
version: "1",
|
|
2492
|
+
chainId,
|
|
2493
|
+
nonce: (dependencies.nonce ?? generateNonce)(),
|
|
2494
|
+
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2495
|
+
}).prepareMessage();
|
|
2496
|
+
}
|
|
2497
|
+
function encodeYieldseekerAuthToken(token) {
|
|
2498
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2499
|
+
let binary = "";
|
|
2500
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2501
|
+
return btoa(binary);
|
|
2502
|
+
}
|
|
2503
|
+
var YieldseekerAuth = class {
|
|
2504
|
+
constructor(dependencies = {}) {
|
|
2505
|
+
this.dependencies = dependencies;
|
|
2506
|
+
}
|
|
2507
|
+
dependencies;
|
|
2508
|
+
tokens = /* @__PURE__ */ new Map();
|
|
2509
|
+
pending = /* @__PURE__ */ new Map();
|
|
2510
|
+
scopes = /* @__PURE__ */ new Map();
|
|
2511
|
+
key(state, chainId) {
|
|
2512
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
|
|
2513
|
+
}
|
|
2514
|
+
async getToken(state, chainId) {
|
|
2515
|
+
const key2 = this.key(state, chainId);
|
|
2516
|
+
const scope = { address: state.walletAddress, chainId };
|
|
2517
|
+
this.scopes.set(key2, scope);
|
|
2518
|
+
const cached = this.tokens.get(key2);
|
|
2519
|
+
if (cached) return cached;
|
|
2520
|
+
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2521
|
+
if (persisted && this.matchesOrigin(persisted)) {
|
|
2522
|
+
this.tokens.set(key2, persisted);
|
|
2523
|
+
return persisted;
|
|
2524
|
+
}
|
|
2525
|
+
if (persisted) clearYieldseekerSession(scope.address, scope.chainId);
|
|
2526
|
+
const inFlight = this.pending.get(key2);
|
|
2527
|
+
if (inFlight) return inFlight;
|
|
2528
|
+
const request = this.sign(state, chainId).then((token) => {
|
|
2529
|
+
if (this.pending.get(key2) !== request) throw new Error("Wallet sign-in session changed. Please try again.");
|
|
2530
|
+
this.tokens.set(key2, token);
|
|
2531
|
+
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2532
|
+
return token;
|
|
2533
|
+
});
|
|
2534
|
+
this.pending.set(key2, request);
|
|
2535
|
+
try {
|
|
2536
|
+
return await request;
|
|
2537
|
+
} finally {
|
|
2538
|
+
if (this.pending.get(key2) === request) this.pending.delete(key2);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
async refreshToken(state, chainId, rejectedToken) {
|
|
2542
|
+
const key2 = this.key(state, chainId);
|
|
2543
|
+
if (this.tokens.get(key2) === rejectedToken) {
|
|
2544
|
+
this.tokens.delete(key2);
|
|
2545
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2546
|
+
}
|
|
2547
|
+
return this.getToken(state, chainId);
|
|
2548
|
+
}
|
|
2549
|
+
matchesOrigin(token) {
|
|
2550
|
+
try {
|
|
2551
|
+
const message = new SiweMessage(JSON.parse(atob(token)).message);
|
|
2552
|
+
const url = resolveSiweOrigin(this.dependencies.origin);
|
|
2553
|
+
return message.domain === url.host && message.uri === url.origin && message.scheme === url.protocol.slice(0, -1);
|
|
2554
|
+
} catch {
|
|
2555
|
+
return false;
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
clear(state, chainId) {
|
|
2559
|
+
if (!state || chainId === void 0) {
|
|
2560
|
+
for (const scope of this.scopes.values()) {
|
|
2561
|
+
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2584
2562
|
}
|
|
2563
|
+
this.tokens.clear();
|
|
2564
|
+
this.pending.clear();
|
|
2565
|
+
this.scopes.clear();
|
|
2566
|
+
return;
|
|
2585
2567
|
}
|
|
2568
|
+
const key2 = this.key(state, chainId);
|
|
2569
|
+
this.tokens.delete(key2);
|
|
2570
|
+
this.pending.delete(key2);
|
|
2571
|
+
this.scopes.delete(key2);
|
|
2572
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2573
|
+
}
|
|
2574
|
+
async sign(state, chainId) {
|
|
2575
|
+
const account = getAddress(state.walletAddress);
|
|
2576
|
+
const publicClient = createPublicClient2({
|
|
2577
|
+
chain: base2,
|
|
2578
|
+
transport: custom(state.provider)
|
|
2579
|
+
});
|
|
2580
|
+
const walletClient = createWalletClient({
|
|
2581
|
+
account,
|
|
2582
|
+
chain: base2,
|
|
2583
|
+
transport: custom(state.provider)
|
|
2584
|
+
});
|
|
2585
|
+
await ensureWalletOnChain(
|
|
2586
|
+
publicClient,
|
|
2587
|
+
walletClient,
|
|
2588
|
+
8453
|
|
2589
|
+
);
|
|
2590
|
+
const message = createYieldseekerSiweMessage(
|
|
2591
|
+
account,
|
|
2592
|
+
chainId,
|
|
2593
|
+
this.dependencies
|
|
2594
|
+
);
|
|
2595
|
+
const signature = await walletClient.signMessage({ account, message });
|
|
2596
|
+
return encodeYieldseekerAuthToken({ message, signature });
|
|
2597
|
+
}
|
|
2598
|
+
};
|
|
2599
|
+
|
|
2600
|
+
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2601
|
+
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2602
|
+
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2603
|
+
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2604
|
+
var storage3 = () => {
|
|
2605
|
+
if (typeof window === "undefined") return null;
|
|
2606
|
+
try {
|
|
2607
|
+
return window.localStorage;
|
|
2608
|
+
} catch {
|
|
2609
|
+
return null;
|
|
2610
|
+
}
|
|
2611
|
+
};
|
|
2612
|
+
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2613
|
+
function valid(value, walletAddress, chainId, now) {
|
|
2614
|
+
return Boolean(
|
|
2615
|
+
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
|
|
2616
|
+
);
|
|
2617
|
+
}
|
|
2618
|
+
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2619
|
+
if (typeof window === "undefined") return null;
|
|
2620
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2621
|
+
const store = storage3();
|
|
2622
|
+
let parsed = null;
|
|
2623
|
+
try {
|
|
2624
|
+
const raw2 = store?.getItem(key2);
|
|
2625
|
+
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2626
|
+
} catch {
|
|
2627
|
+
parsed = null;
|
|
2628
|
+
}
|
|
2629
|
+
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2630
|
+
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2631
|
+
memoryIdentities.set(key2, candidate);
|
|
2632
|
+
return { userId: candidate.userId };
|
|
2633
|
+
}
|
|
2634
|
+
memoryIdentities.delete(key2);
|
|
2635
|
+
try {
|
|
2636
|
+
store?.removeItem(key2);
|
|
2637
|
+
} catch {
|
|
2638
|
+
}
|
|
2639
|
+
return null;
|
|
2640
|
+
}
|
|
2641
|
+
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2642
|
+
if (typeof window === "undefined") return;
|
|
2643
|
+
const identity = {
|
|
2644
|
+
userId,
|
|
2645
|
+
walletAddress,
|
|
2646
|
+
chainId,
|
|
2647
|
+
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2648
|
+
};
|
|
2649
|
+
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2650
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2651
|
+
memoryIdentities.set(key2, identity);
|
|
2652
|
+
try {
|
|
2653
|
+
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2654
|
+
} catch {
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2658
|
+
const key2 = keyFor(walletAddress, chainId);
|
|
2659
|
+
memoryIdentities.delete(key2);
|
|
2660
|
+
try {
|
|
2661
|
+
storage3()?.removeItem(key2);
|
|
2586
2662
|
} catch {
|
|
2587
|
-
return out;
|
|
2588
2663
|
}
|
|
2589
|
-
return out.sort((a, b) => b.createdAt - a.createdAt);
|
|
2590
2664
|
}
|
|
2591
2665
|
|
|
2592
|
-
// src/
|
|
2593
|
-
var
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
if (balance >= quoted) return quoted;
|
|
2597
|
-
debugLog("owney-sdk", "swap: trimming to the current source balance", {
|
|
2598
|
-
quoted: quoted.toString(),
|
|
2599
|
-
balance: balance.toString(),
|
|
2600
|
-
short: (quoted - balance).toString()
|
|
2601
|
-
});
|
|
2602
|
-
return balance;
|
|
2666
|
+
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2667
|
+
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2668
|
+
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2669
|
+
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2603
2670
|
}
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
}
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
}
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2671
|
+
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2672
|
+
var YieldseekerApiError = class extends Error {
|
|
2673
|
+
constructor(status, providerCode, responseFields) {
|
|
2674
|
+
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2675
|
+
this.status = status;
|
|
2676
|
+
this.providerCode = providerCode;
|
|
2677
|
+
this.responseFields = responseFields;
|
|
2678
|
+
this.name = "YieldseekerApiError";
|
|
2679
|
+
}
|
|
2680
|
+
status;
|
|
2681
|
+
providerCode;
|
|
2682
|
+
responseFields;
|
|
2683
|
+
get isAuthenticationError() {
|
|
2684
|
+
return this.status === 401 || this.status === 403;
|
|
2685
|
+
}
|
|
2686
|
+
};
|
|
2687
|
+
function providerError(body, fallback) {
|
|
2688
|
+
if (!body || typeof body !== "object") return { code: fallback };
|
|
2689
|
+
const record = body;
|
|
2690
|
+
return {
|
|
2691
|
+
code: typeof record.message === "string" ? record.message : fallback,
|
|
2692
|
+
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
var YieldseekerApiClient = class {
|
|
2696
|
+
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch.bind(globalThis)) {
|
|
2697
|
+
this.owneyApiKey = owneyApiKey;
|
|
2698
|
+
this.baseUrl = baseUrl;
|
|
2699
|
+
this.fetchFn = fetchFn;
|
|
2700
|
+
}
|
|
2701
|
+
owneyApiKey;
|
|
2702
|
+
baseUrl;
|
|
2703
|
+
fetchFn;
|
|
2704
|
+
async request(path, options = {}) {
|
|
2705
|
+
const controller = new AbortController();
|
|
2706
|
+
const timer = setTimeout(
|
|
2707
|
+
() => controller.abort(),
|
|
2708
|
+
options.timeoutMs ?? 15e3
|
|
2627
2709
|
);
|
|
2710
|
+
try {
|
|
2711
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2712
|
+
method: options.method ?? "GET",
|
|
2713
|
+
headers: {
|
|
2714
|
+
"Content-Type": "application/json",
|
|
2715
|
+
"x-owney-api-key": this.owneyApiKey,
|
|
2716
|
+
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2717
|
+
},
|
|
2718
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2719
|
+
signal: controller.signal
|
|
2720
|
+
});
|
|
2721
|
+
const payload = await response.json().catch(() => null);
|
|
2722
|
+
if (!response.ok) {
|
|
2723
|
+
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2724
|
+
throw new YieldseekerApiError(
|
|
2725
|
+
response.status,
|
|
2726
|
+
error.code,
|
|
2727
|
+
error.fields
|
|
2728
|
+
);
|
|
2729
|
+
}
|
|
2730
|
+
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2731
|
+
return payload.data;
|
|
2732
|
+
}
|
|
2733
|
+
return payload;
|
|
2734
|
+
} catch (error) {
|
|
2735
|
+
if (error instanceof YieldseekerApiError) throw error;
|
|
2736
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2737
|
+
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2738
|
+
}
|
|
2739
|
+
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2740
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2741
|
+
});
|
|
2742
|
+
} finally {
|
|
2743
|
+
clearTimeout(timer);
|
|
2744
|
+
}
|
|
2628
2745
|
}
|
|
2629
|
-
|
|
2746
|
+
};
|
|
2747
|
+
|
|
2748
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2749
|
+
import { formatUnits, isAddress } from "viem";
|
|
2750
|
+
|
|
2751
|
+
// src/lib/helpers/snapshot-apy.ts
|
|
2752
|
+
var DAY_MS = 864e5;
|
|
2753
|
+
function snapshotTime(date) {
|
|
2754
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return void 0;
|
|
2755
|
+
const time = Date.parse(date);
|
|
2756
|
+
return Number.isFinite(time) && new Date(time).toISOString().slice(0, 10) === date ? time : void 0;
|
|
2630
2757
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2758
|
+
function returnFactor(value) {
|
|
2759
|
+
if (typeof value !== "number" && typeof value !== "string") return void 0;
|
|
2760
|
+
if (typeof value === "string" && value.trim() === "") return void 0;
|
|
2761
|
+
const factor = Number(value);
|
|
2762
|
+
return Number.isFinite(factor) && factor >= 0 ? factor : void 0;
|
|
2763
|
+
}
|
|
2764
|
+
function calculateSnapshotApy(snapshots, lookbackDays, now = Date.now()) {
|
|
2765
|
+
if (!Number.isFinite(lookbackDays) || lookbackDays <= 0 || !Number.isFinite(now)) {
|
|
2766
|
+
return void 0;
|
|
2767
|
+
}
|
|
2768
|
+
const points = snapshots.flatMap((snapshot) => {
|
|
2769
|
+
const time = snapshotTime(snapshot.date);
|
|
2770
|
+
return time !== void 0 && time <= now ? [{ snapshot, time }] : [];
|
|
2771
|
+
}).sort((a, b) => a.time - b.time);
|
|
2772
|
+
const end = points.at(-1);
|
|
2773
|
+
if (!end) return void 0;
|
|
2774
|
+
const cutoff = end.time - lookbackDays * DAY_MS;
|
|
2775
|
+
const start = points.find((point) => point.time >= cutoff);
|
|
2776
|
+
const actualDays = (end.time - start.time) / DAY_MS;
|
|
2777
|
+
if (actualDays <= 0) return void 0;
|
|
2778
|
+
const startFactor = returnFactor(start.snapshot.cumulativeReturnFactor);
|
|
2779
|
+
const endFactor = returnFactor(end.snapshot.cumulativeReturnFactor);
|
|
2780
|
+
if (startFactor === void 0 || startFactor === 0 || endFactor === void 0) {
|
|
2781
|
+
return void 0;
|
|
2782
|
+
}
|
|
2783
|
+
const periodReturn = endFactor / startFactor - 1;
|
|
2784
|
+
const apy = Math.pow(1 + periodReturn, 365 / actualDays) - 1;
|
|
2785
|
+
return Number.isFinite(apy) ? apy : void 0;
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2789
|
+
var YIELDSEEKER_ASSET_METADATA = {
|
|
2790
|
+
USDC: {
|
|
2791
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2792
|
+
decimals: 6
|
|
2793
|
+
},
|
|
2794
|
+
WETH: {
|
|
2795
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
2796
|
+
decimals: 18
|
|
2797
|
+
}
|
|
2798
|
+
};
|
|
2799
|
+
|
|
2800
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2801
|
+
function invalid(endpoint, detail) {
|
|
2802
|
+
throw new OwneyError(
|
|
2803
|
+
"AGENT_INVALID_RESPONSE",
|
|
2804
|
+
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2805
|
+
{ endpoint, detail },
|
|
2806
|
+
"yieldseeker"
|
|
2807
|
+
);
|
|
2808
|
+
}
|
|
2809
|
+
function raw(value, endpoint) {
|
|
2810
|
+
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2811
|
+
return invalid(endpoint, "expected a base-10 integer string");
|
|
2812
|
+
}
|
|
2813
|
+
return BigInt(value);
|
|
2814
|
+
}
|
|
2815
|
+
function decimal(value, decimals, endpoint) {
|
|
2816
|
+
return formatUnits(raw(value, endpoint), decimals);
|
|
2817
|
+
}
|
|
2818
|
+
function usd(rawAmount, decimals, price) {
|
|
2819
|
+
return Number(formatUnits(rawAmount, decimals)) * price;
|
|
2820
|
+
}
|
|
2821
|
+
function percent(value) {
|
|
2822
|
+
const result = Number(value);
|
|
2823
|
+
return Number.isFinite(result) ? result * 100 : 0;
|
|
2824
|
+
}
|
|
2825
|
+
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2826
|
+
function publicApyAfterYieldseekerFee(value) {
|
|
2827
|
+
const grossPercent = percent(value);
|
|
2828
|
+
if (grossPercent <= 0) return grossPercent;
|
|
2829
|
+
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2830
|
+
return Math.round(netPercent * 1e12) / 1e12;
|
|
2831
|
+
}
|
|
2832
|
+
function riskAdjustedApyForDays(option, days) {
|
|
2833
|
+
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2834
|
+
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2835
|
+
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2836
|
+
}
|
|
2837
|
+
function assetAddressValue(record, address) {
|
|
2838
|
+
const entry = Object.entries(record).find(
|
|
2839
|
+
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2840
|
+
);
|
|
2841
|
+
return entry?.[1] ?? "0";
|
|
2842
|
+
}
|
|
2843
|
+
function position(value, asset, baseAssetDecimals) {
|
|
2844
|
+
const option = value?.yieldOption;
|
|
2845
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress(option.address)) {
|
|
2846
|
+
return invalid("yield positions", "missing vault metadata");
|
|
2847
|
+
}
|
|
2848
|
+
return {
|
|
2849
|
+
chain: "BASE",
|
|
2850
|
+
protocol: option.provider,
|
|
2851
|
+
protocolId: option.address,
|
|
2852
|
+
pool: option.name,
|
|
2853
|
+
asset,
|
|
2854
|
+
// `assetsRaw` is the ERC-4626 vault-share quantity, whose decimals can
|
|
2855
|
+
// differ from the underlying asset. Yieldseeker already converts it to
|
|
2856
|
+
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
2857
|
+
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
2858
|
+
// share quantity separately because withdraw-from-position expects it.
|
|
2859
|
+
amount: decimal(
|
|
2860
|
+
value.assetsBase,
|
|
2861
|
+
baseAssetDecimals,
|
|
2862
|
+
"yield positions"
|
|
2863
|
+
),
|
|
2864
|
+
amountRaw: String(value.assetsRaw),
|
|
2865
|
+
apy: percent(option.riskAdjustedApy),
|
|
2866
|
+
tvl: Number(option.totalDepositsUsd),
|
|
2867
|
+
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
function mapYieldseekerBalances(contexts) {
|
|
2871
|
+
const tokens = [];
|
|
2872
|
+
const assetBalances = [];
|
|
2873
|
+
const positions = [];
|
|
2874
|
+
let totalUsd = 0;
|
|
2875
|
+
for (const context of contexts) {
|
|
2876
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2877
|
+
assetBalances.push({
|
|
2878
|
+
chain: "BASE",
|
|
2879
|
+
chainId: 8453,
|
|
2880
|
+
asset: context.asset,
|
|
2881
|
+
amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
|
|
2662
2882
|
});
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2883
|
+
const idle = assetAddressValue(
|
|
2884
|
+
context.snapshot.tokenBalances,
|
|
2885
|
+
metadata.address
|
|
2886
|
+
);
|
|
2887
|
+
tokens.push({
|
|
2888
|
+
chain: "BASE",
|
|
2889
|
+
chainId: 8453,
|
|
2890
|
+
asset: context.asset,
|
|
2891
|
+
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2892
|
+
});
|
|
2893
|
+
positions.push(
|
|
2894
|
+
...context.positions.map(
|
|
2895
|
+
(entry) => position(
|
|
2896
|
+
entry,
|
|
2897
|
+
context.asset,
|
|
2898
|
+
context.snapshot.baseAssetDecimals
|
|
2899
|
+
)
|
|
2900
|
+
)
|
|
2901
|
+
);
|
|
2902
|
+
totalUsd += usd(
|
|
2903
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2904
|
+
context.snapshot.baseAssetDecimals,
|
|
2905
|
+
context.snapshot.baseAssetPriceUsd
|
|
2906
|
+
);
|
|
2907
|
+
}
|
|
2908
|
+
return {
|
|
2909
|
+
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2910
|
+
totalBalance: String(totalUsd),
|
|
2911
|
+
totalBalanceAsset: "usdc",
|
|
2912
|
+
assetBalances,
|
|
2913
|
+
tokens,
|
|
2914
|
+
positions
|
|
2915
|
+
};
|
|
2679
2916
|
}
|
|
2680
|
-
|
|
2681
|
-
const
|
|
2682
|
-
|
|
2683
|
-
const
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
needed: needed.toString()
|
|
2917
|
+
function mapYieldseekerEarnings(contexts) {
|
|
2918
|
+
const tokens = [];
|
|
2919
|
+
let lifetimeEarnings = 0;
|
|
2920
|
+
for (const context of contexts) {
|
|
2921
|
+
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
2922
|
+
tokens.push({
|
|
2923
|
+
chain: "BASE",
|
|
2924
|
+
chainId: 8453,
|
|
2925
|
+
asset: context.asset,
|
|
2926
|
+
amount: formatUnits(amount, context.snapshot.baseAssetDecimals)
|
|
2691
2927
|
});
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2928
|
+
lifetimeEarnings += usd(
|
|
2929
|
+
amount,
|
|
2930
|
+
context.snapshot.baseAssetDecimals,
|
|
2931
|
+
context.snapshot.baseAssetPriceUsd
|
|
2932
|
+
);
|
|
2933
|
+
}
|
|
2934
|
+
return {
|
|
2935
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2936
|
+
lifetimeEarnings,
|
|
2937
|
+
tokens
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
function apyForDays(context, days, now) {
|
|
2941
|
+
if (days === "7D") return percent(context.snapshot.apy7d);
|
|
2942
|
+
if (days === "30D") return percent(context.snapshot.apy30d);
|
|
2943
|
+
const apy = calculateSnapshotApy(context.historic?.dailyYieldSnapshots ?? [], 14, now);
|
|
2944
|
+
const apyPercent = apy === void 0 ? void 0 : apy * 100;
|
|
2945
|
+
return apyPercent !== void 0 && Number.isFinite(apyPercent) ? apyPercent : void 0;
|
|
2946
|
+
}
|
|
2947
|
+
function dailyApy(point) {
|
|
2948
|
+
const total = raw(point.totalValueBase, "historic position");
|
|
2949
|
+
const earned = raw(point.dailyYieldBase, "historic position");
|
|
2950
|
+
const principal = total - earned;
|
|
2951
|
+
if (principal <= 0n || earned === 0n) return 0;
|
|
2952
|
+
return Number(earned) / Number(principal) * 365 * 100;
|
|
2953
|
+
}
|
|
2954
|
+
function aggregateHistory(contexts, dayCount, now) {
|
|
2955
|
+
const today = new Date(now).toISOString().slice(0, 10);
|
|
2956
|
+
const cutoff = new Date(Date.parse(today) - (dayCount - 1) * 864e5).toISOString().slice(0, 10);
|
|
2957
|
+
const assets = new Set(contexts.map((context) => context.asset.toUpperCase()));
|
|
2958
|
+
const unit = assets.size === 1 ? [...assets][0] : "USD";
|
|
2959
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
2960
|
+
for (const context of contexts) {
|
|
2961
|
+
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
2962
|
+
for (const point of points) {
|
|
2963
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(point.date) || !Number.isFinite(Date.parse(point.date)) || point.date < cutoff || point.date > today) continue;
|
|
2964
|
+
const tokens = Number(decimal(point.totalValueBase, point.baseAssetDecimals, "historic position"));
|
|
2965
|
+
const amount = assets.size === 1 ? tokens : tokens * point.baseAssetPriceUsd;
|
|
2966
|
+
if (!Number.isFinite(amount) || amount < 0) {
|
|
2967
|
+
invalid("historic position", "expected a finite non-negative balance");
|
|
2968
|
+
}
|
|
2969
|
+
const current = byDate.get(point.date) ?? { weighted: 0, amount: 0 };
|
|
2970
|
+
current.weighted += dailyApy(point) * amount;
|
|
2971
|
+
current.amount += amount;
|
|
2972
|
+
byDate.set(point.date, current);
|
|
2697
2973
|
}
|
|
2698
2974
|
}
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
});
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2975
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
2976
|
+
date,
|
|
2977
|
+
apy: value.amount > 0 ? value.weighted / value.amount : 0,
|
|
2978
|
+
historicalBalance: { amount: value.amount, unit }
|
|
2979
|
+
}));
|
|
2980
|
+
}
|
|
2981
|
+
function mapYieldseekerApy(walletAddress, contexts, days, now = Date.now()) {
|
|
2982
|
+
let weighted = 0;
|
|
2983
|
+
let totalUsd = 0;
|
|
2984
|
+
const byAsset = {};
|
|
2985
|
+
for (const context of contexts) {
|
|
2986
|
+
const valueUsd = usd(
|
|
2987
|
+
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2988
|
+
context.snapshot.baseAssetDecimals,
|
|
2989
|
+
context.snapshot.baseAssetPriceUsd
|
|
2990
|
+
);
|
|
2991
|
+
const apy = apyForDays(context, days, now);
|
|
2992
|
+
if (apy === void 0) continue;
|
|
2993
|
+
weighted += apy * valueUsd;
|
|
2994
|
+
totalUsd += valueUsd;
|
|
2995
|
+
byAsset[context.asset] = apy;
|
|
2996
|
+
}
|
|
2997
|
+
const dayCount = Number(days.slice(0, -1));
|
|
2998
|
+
return {
|
|
2712
2999
|
walletAddress,
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
3000
|
+
...days !== "14D" || Object.keys(byAsset).length > 0 ? { weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0 } : {},
|
|
3001
|
+
apyByChainAndAsset: { 8453: byAsset },
|
|
3002
|
+
history: aggregateHistory(contexts, dayCount, now)
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
function actionType(value) {
|
|
3006
|
+
const normalized = value.toLowerCase();
|
|
3007
|
+
if (normalized.includes("deposit")) return "Deposit";
|
|
3008
|
+
if (normalized.includes("withdraw")) return "Withdraw";
|
|
3009
|
+
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
3010
|
+
return "Earned";
|
|
3011
|
+
return "Rebalance";
|
|
3012
|
+
}
|
|
3013
|
+
function transactionHashes(details) {
|
|
3014
|
+
if (!details) return [];
|
|
3015
|
+
const values = [
|
|
3016
|
+
details.transactionHash,
|
|
3017
|
+
details.txHash,
|
|
3018
|
+
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
3019
|
+
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
3020
|
+
];
|
|
3021
|
+
return values.filter(
|
|
3022
|
+
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
3023
|
+
).filter((value, index, all) => all.indexOf(value) === index);
|
|
3024
|
+
}
|
|
3025
|
+
function actionEntry(action) {
|
|
3026
|
+
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
3027
|
+
return {
|
|
3028
|
+
agent: "yieldseeker",
|
|
3029
|
+
action: actionType(action.actionType),
|
|
3030
|
+
date: action.createdDate,
|
|
3031
|
+
oldApy: null,
|
|
3032
|
+
newApy: null,
|
|
3033
|
+
transactions: [
|
|
3034
|
+
{
|
|
3035
|
+
txHashes: transactionHashes(action.details),
|
|
3036
|
+
chainId: 8453
|
|
3037
|
+
}
|
|
3038
|
+
],
|
|
3039
|
+
rebalanceLog: []
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
function depositDestination(context, movement) {
|
|
3043
|
+
const to = movement.toAddress.toLowerCase();
|
|
3044
|
+
const option = context.positions.find(({ yieldOption }) => yieldOption.chainId === movement.chainId && yieldOption.assetAddress.toLowerCase() === context.agent.assetAddress.toLowerCase() && yieldOption.address.toLowerCase() === to)?.yieldOption;
|
|
3045
|
+
if (option) return { protocol: option.provider || "Vault", pool: option.name, tokenSymbol: context.asset };
|
|
3046
|
+
const receipt = context.historic?.assets.find((asset) => asset.chainId === movement.chainId && !asset.isSpam && asset.address.toLowerCase() === to && asset.address.toLowerCase() !== context.agent.assetAddress.toLowerCase());
|
|
3047
|
+
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);
|
|
3048
|
+
if (receipt && minted) return { protocol: "Vault", pool: receipt.name || receipt.symbol, tokenSymbol: context.asset };
|
|
3049
|
+
return void 0;
|
|
3050
|
+
}
|
|
3051
|
+
function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses, destination) {
|
|
3052
|
+
const from = movement.fromAddress.toLowerCase();
|
|
3053
|
+
const to = movement.toAddress.toLowerCase();
|
|
3054
|
+
const owner = ownerAddress.toLowerCase();
|
|
3055
|
+
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
3056
|
+
const baseAsset = agent.assetAddress.toLowerCase();
|
|
3057
|
+
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
3058
|
+
return void 0;
|
|
3059
|
+
}
|
|
3060
|
+
let action;
|
|
3061
|
+
if (to === agentWallet && !vaultAddresses.has(from)) {
|
|
3062
|
+
action = "Top up";
|
|
3063
|
+
} else if (from === agentWallet && to === owner) {
|
|
3064
|
+
action = "Withdraw";
|
|
3065
|
+
} else if (from === agentWallet && destination) {
|
|
3066
|
+
action = "Deposit";
|
|
3067
|
+
}
|
|
3068
|
+
if (!action) return void 0;
|
|
3069
|
+
return {
|
|
3070
|
+
agent: "yieldseeker",
|
|
3071
|
+
action,
|
|
3072
|
+
...action === "Deposit" && destination ? { positions: [{
|
|
3073
|
+
...destination,
|
|
3074
|
+
amount: decimal(movement.assetAmount, YIELDSEEKER_ASSET_METADATA[asset].decimals, "historic position")
|
|
3075
|
+
}] } : {},
|
|
3076
|
+
date: movement.blockDate,
|
|
3077
|
+
oldApy: null,
|
|
3078
|
+
newApy: null,
|
|
3079
|
+
transactions: [
|
|
3080
|
+
{
|
|
3081
|
+
txHashes: [movement.transactionHash],
|
|
3082
|
+
chainId: agent.chainId,
|
|
3083
|
+
tokenSymbol: asset,
|
|
3084
|
+
amount: decimal(
|
|
3085
|
+
movement.assetAmount,
|
|
3086
|
+
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
3087
|
+
"historic position"
|
|
3088
|
+
)
|
|
3089
|
+
}
|
|
3090
|
+
],
|
|
3091
|
+
rebalanceLog: []
|
|
3092
|
+
};
|
|
3093
|
+
}
|
|
3094
|
+
function mapYieldseekerHistory(contexts, options) {
|
|
3095
|
+
const entries = contexts.flatMap((context) => {
|
|
3096
|
+
const seenMovements = /* @__PURE__ */ new Set();
|
|
3097
|
+
const movements = (context.historic?.movements ?? []).filter((movement) => {
|
|
3098
|
+
const key2 = `${movement.chainId}:${movement.transactionHash.toLowerCase()}:${movement.logIndex}`;
|
|
3099
|
+
if (seenMovements.has(key2)) return false;
|
|
3100
|
+
seenMovements.add(key2);
|
|
3101
|
+
return true;
|
|
3102
|
+
});
|
|
3103
|
+
return [
|
|
3104
|
+
...movements.map(
|
|
3105
|
+
(movement) => movementEntry(
|
|
3106
|
+
movement,
|
|
3107
|
+
context.wallet,
|
|
3108
|
+
context.agent,
|
|
3109
|
+
context.asset,
|
|
3110
|
+
options.ownerAddress,
|
|
3111
|
+
options.vaultAddresses,
|
|
3112
|
+
depositDestination(context, movement)
|
|
3113
|
+
)
|
|
3114
|
+
),
|
|
3115
|
+
...(context.actions ?? []).map(actionEntry)
|
|
3116
|
+
].filter((entry) => entry !== void 0);
|
|
2747
3117
|
});
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
3118
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3119
|
+
const ungrouped = [];
|
|
3120
|
+
for (const entry of entries) {
|
|
3121
|
+
const tx = entry.transactions[0];
|
|
3122
|
+
const hash = tx?.txHashes[0];
|
|
3123
|
+
if (!hash) {
|
|
3124
|
+
ungrouped.push(entry);
|
|
3125
|
+
continue;
|
|
3126
|
+
}
|
|
3127
|
+
const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}`;
|
|
3128
|
+
const previous = grouped.get(key2);
|
|
3129
|
+
if (!previous) {
|
|
3130
|
+
grouped.set(key2, entry);
|
|
3131
|
+
continue;
|
|
3132
|
+
}
|
|
3133
|
+
if (entry.action === "Deposit" && entry.positions?.length) {
|
|
3134
|
+
if (!previous.positions?.length) {
|
|
3135
|
+
grouped.set(key2, entry);
|
|
3136
|
+
continue;
|
|
3137
|
+
}
|
|
3138
|
+
previous.positions.push(...entry.positions);
|
|
3139
|
+
previous.transactions.push(...entry.transactions);
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
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));
|
|
3143
|
+
return {
|
|
3144
|
+
data: filtered.slice(0, options.limit),
|
|
3145
|
+
// v1 returns the whole action/movement collection and defines no cursor.
|
|
3146
|
+
// Report a terminal page so callers never loop over the same prefix.
|
|
3147
|
+
hasMore: false
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
3150
|
+
function mapYieldseekerProfile(address, contexts) {
|
|
3151
|
+
const protocols = /* @__PURE__ */ new Set();
|
|
3152
|
+
for (const context of contexts) {
|
|
3153
|
+
for (const current of context.positions) {
|
|
3154
|
+
if (current.yieldOption?.provider) {
|
|
3155
|
+
protocols.add(String(current.yieldOption.provider));
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
return {
|
|
3160
|
+
address,
|
|
3161
|
+
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
3162
|
+
chains: contexts.length > 0 ? [8453] : [],
|
|
3163
|
+
hasActiveSessionKey: contexts.some(
|
|
3164
|
+
(context) => context.wallet.initializedDate != null
|
|
3165
|
+
),
|
|
3166
|
+
protocols: [...protocols]
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3169
|
+
function mapYieldseekerAgentApy(options, days) {
|
|
3170
|
+
const perAsset = {};
|
|
3171
|
+
const all = [];
|
|
3172
|
+
for (const entry of options) {
|
|
3173
|
+
const apys = entry.yieldOptions.map(
|
|
3174
|
+
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
3175
|
+
).filter(Number.isFinite);
|
|
3176
|
+
if (apys.length === 0) continue;
|
|
3177
|
+
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
3178
|
+
perAsset[entry.asset] = average;
|
|
3179
|
+
all.push(average);
|
|
3180
|
+
}
|
|
3181
|
+
return {
|
|
3182
|
+
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
3183
|
+
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3188
|
+
var OWNEY_AGENT_NAME = "owney";
|
|
3189
|
+
var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
3190
|
+
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3191
|
+
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3192
|
+
var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
|
|
3193
|
+
function generateYieldseekerUsername() {
|
|
3194
|
+
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3195
|
+
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
3196
|
+
}
|
|
3197
|
+
function isUsernameConflict(error) {
|
|
3198
|
+
if (!(error instanceof YieldseekerApiError)) return false;
|
|
3199
|
+
const code = error.providerCode.toUpperCase();
|
|
3200
|
+
return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
|
|
3201
|
+
}
|
|
3202
|
+
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
3203
|
+
{
|
|
3204
|
+
type: "function",
|
|
3205
|
+
name: "withdrawAssetToUser",
|
|
3206
|
+
stateMutability: "nonpayable",
|
|
3207
|
+
inputs: [
|
|
3208
|
+
{ name: "recipient", type: "address" },
|
|
3209
|
+
{ name: "asset", type: "address" },
|
|
3210
|
+
{ name: "amount", type: "uint256" }
|
|
3211
|
+
],
|
|
3212
|
+
outputs: []
|
|
3213
|
+
},
|
|
3214
|
+
{
|
|
3215
|
+
type: "function",
|
|
3216
|
+
name: "withdrawAllAssetToUser",
|
|
3217
|
+
stateMutability: "nonpayable",
|
|
3218
|
+
inputs: [
|
|
3219
|
+
{ name: "recipient", type: "address" },
|
|
3220
|
+
{ name: "asset", type: "address" }
|
|
3221
|
+
],
|
|
3222
|
+
outputs: []
|
|
3223
|
+
}
|
|
3224
|
+
];
|
|
3225
|
+
function query(params) {
|
|
3226
|
+
const search = new URLSearchParams();
|
|
3227
|
+
for (const [key2, value] of Object.entries(params)) {
|
|
3228
|
+
if (value !== void 0) search.set(key2, String(value));
|
|
3229
|
+
}
|
|
3230
|
+
const encoded = search.toString();
|
|
3231
|
+
return encoded ? `?${encoded}` : "";
|
|
3232
|
+
}
|
|
3233
|
+
var YieldseekerAgent = class {
|
|
3234
|
+
id = "yieldseeker";
|
|
3235
|
+
balanceComposition = "tokens-plus-positions";
|
|
3236
|
+
supportedChainIds = [8453];
|
|
3237
|
+
supportedAssets = [
|
|
3238
|
+
{
|
|
3239
|
+
chainId: 8453,
|
|
3240
|
+
chain: "BASE",
|
|
3241
|
+
assets: [
|
|
3242
|
+
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
3243
|
+
{ symbol: "WETH", minDepositAmount: "1" }
|
|
3244
|
+
]
|
|
3245
|
+
}
|
|
3246
|
+
];
|
|
3247
|
+
api;
|
|
3248
|
+
auth;
|
|
3249
|
+
transactionExecutor;
|
|
3250
|
+
unwindReceiptWaiter;
|
|
3251
|
+
agentContexts = /* @__PURE__ */ new Map();
|
|
3252
|
+
users = /* @__PURE__ */ new Map();
|
|
3253
|
+
pendingAgents = /* @__PURE__ */ new Map();
|
|
3254
|
+
yieldOptions = /* @__PURE__ */ new Map();
|
|
3255
|
+
pendingYieldOptions = /* @__PURE__ */ new Map();
|
|
3256
|
+
constructor(owneyApiKey, options = {}) {
|
|
3257
|
+
this.api = new YieldseekerApiClient(
|
|
3258
|
+
owneyApiKey,
|
|
3259
|
+
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
3260
|
+
options.fetchFn
|
|
3261
|
+
);
|
|
3262
|
+
this.auth = new YieldseekerAuth(options.auth);
|
|
3263
|
+
this.transactionExecutor = options.transactionExecutor;
|
|
3264
|
+
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3265
|
+
}
|
|
3266
|
+
async disconnect() {
|
|
3267
|
+
this.auth.clear();
|
|
3268
|
+
for (const key2 of this.users.keys()) {
|
|
3269
|
+
const [walletAddress, chainId] = key2.split(":");
|
|
3270
|
+
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
3271
|
+
}
|
|
3272
|
+
this.users.clear();
|
|
3273
|
+
this.agentContexts.clear();
|
|
3274
|
+
this.pendingAgents.clear();
|
|
3275
|
+
}
|
|
3276
|
+
async activateAgent(state, chainId, asset) {
|
|
3277
|
+
this.assertChain(chainId);
|
|
3278
|
+
const targetAsset = asset ?? "USDC";
|
|
3279
|
+
this.assertAsset(targetAsset);
|
|
3280
|
+
await this.ensureAgent(state, chainId, targetAsset);
|
|
3281
|
+
}
|
|
3282
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3283
|
+
this.assertChain(chainId);
|
|
3284
|
+
this.assertAsset(asset);
|
|
3285
|
+
if (BigInt(amount) <= 0n) {
|
|
3286
|
+
throw new OwneyError(
|
|
3287
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
3288
|
+
"Yieldseeker deposits must be greater than zero.",
|
|
3289
|
+
{ amount, minDepositAmount: "1" },
|
|
3290
|
+
this.id
|
|
3291
|
+
);
|
|
3292
|
+
}
|
|
3293
|
+
const context = await this.ensureAgent(state, chainId, asset);
|
|
3294
|
+
let txHash;
|
|
3295
|
+
try {
|
|
3296
|
+
if (depositCallback) {
|
|
3297
|
+
provideDepositVerificationContext(depositCallback, {
|
|
3298
|
+
agentId: "yieldseeker",
|
|
3299
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3300
|
+
userId: context.user.userId,
|
|
3301
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3302
|
+
});
|
|
3303
|
+
txHash = await depositCallback(
|
|
3304
|
+
context.wallet.walletAddress,
|
|
3305
|
+
chainId,
|
|
3306
|
+
amount
|
|
3307
|
+
);
|
|
3308
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3309
|
+
} else {
|
|
3310
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3311
|
+
from: getAddress2(state.walletAddress),
|
|
3312
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3313
|
+
data: encodeFunctionData({
|
|
3314
|
+
abi: erc20Abi,
|
|
3315
|
+
functionName: "transfer",
|
|
3316
|
+
args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
|
|
3317
|
+
}),
|
|
3318
|
+
value: "0",
|
|
3319
|
+
chainId
|
|
3320
|
+
});
|
|
3321
|
+
}
|
|
3322
|
+
} finally {
|
|
3323
|
+
await this.refreshSnapshotAfterMovement(
|
|
3324
|
+
state,
|
|
3325
|
+
chainId,
|
|
3326
|
+
context,
|
|
3327
|
+
"deposit"
|
|
3328
|
+
);
|
|
3329
|
+
}
|
|
3330
|
+
return {
|
|
3331
|
+
txHash,
|
|
3332
|
+
smartWallet: context.wallet.walletAddress,
|
|
3333
|
+
amount
|
|
3334
|
+
};
|
|
3335
|
+
}
|
|
3336
|
+
async withdraw(state, chainId, asset, amount) {
|
|
3337
|
+
this.assertChain(chainId);
|
|
3338
|
+
this.assertAsset(asset);
|
|
3339
|
+
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
3340
|
+
throw new OwneyError(
|
|
3341
|
+
"WITHDRAW_FAILED",
|
|
3342
|
+
"Yieldseeker withdrawals must be greater than zero.",
|
|
3343
|
+
{ amount },
|
|
3344
|
+
this.id
|
|
3345
|
+
);
|
|
3346
|
+
}
|
|
3347
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
3348
|
+
if (!context) {
|
|
3349
|
+
throw new OwneyError(
|
|
3350
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3351
|
+
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3352
|
+
{ asset, available: "0" },
|
|
3353
|
+
this.id
|
|
3354
|
+
);
|
|
3355
|
+
}
|
|
3356
|
+
try {
|
|
3357
|
+
const portfolio = await this.loadPortfolioContext(
|
|
3358
|
+
state,
|
|
3359
|
+
chainId,
|
|
3360
|
+
context
|
|
3361
|
+
);
|
|
3362
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3363
|
+
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3364
|
+
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3365
|
+
);
|
|
3366
|
+
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3367
|
+
const deployed = portfolio.positions.reduce(
|
|
3368
|
+
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3369
|
+
0n
|
|
3370
|
+
);
|
|
3371
|
+
const totalAvailable = idle + deployed;
|
|
3372
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3373
|
+
if (requested > totalAvailable) {
|
|
3374
|
+
throw new OwneyError(
|
|
3375
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3376
|
+
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3377
|
+
{
|
|
3378
|
+
asset,
|
|
3379
|
+
requested: requested.toString(),
|
|
3380
|
+
available: totalAvailable.toString()
|
|
3381
|
+
},
|
|
3382
|
+
this.id
|
|
3383
|
+
);
|
|
3384
|
+
}
|
|
3385
|
+
let remaining = requested > idle ? requested - idle : 0n;
|
|
3386
|
+
for (const position2 of portfolio.positions) {
|
|
3387
|
+
if (remaining === 0n) break;
|
|
3388
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3389
|
+
if (available <= 0n) continue;
|
|
3390
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
3391
|
+
const response = await this.walletRequest(
|
|
3392
|
+
state,
|
|
3393
|
+
chainId,
|
|
3394
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
3395
|
+
{
|
|
3396
|
+
method: "POST",
|
|
3397
|
+
body: {
|
|
3398
|
+
chainId,
|
|
3399
|
+
vaultAddress: position2.yieldOption.address,
|
|
3400
|
+
assetsRaw: assetsRaw.toString()
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
);
|
|
3404
|
+
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3405
|
+
throw this.invalidResponse("position withdrawal");
|
|
3406
|
+
}
|
|
3407
|
+
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3408
|
+
remaining -= assetsRaw;
|
|
3409
|
+
}
|
|
3410
|
+
if (remaining > 0n) {
|
|
3411
|
+
throw this.invalidResponse("yield positions", {
|
|
3412
|
+
reason: "Withdrawable positions could not cover the request.",
|
|
3413
|
+
remaining: remaining.toString()
|
|
3414
|
+
});
|
|
3415
|
+
}
|
|
3416
|
+
const account = getAddress2(state.walletAddress);
|
|
3417
|
+
const txHash = await this.submitTransaction(state, chainId, {
|
|
3418
|
+
from: account,
|
|
3419
|
+
to: getAddress2(context.wallet.walletAddress),
|
|
3420
|
+
data: amount === void 0 ? encodeFunctionData({
|
|
3421
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3422
|
+
functionName: "withdrawAllAssetToUser",
|
|
3423
|
+
args: [account, metadata.address]
|
|
3424
|
+
}) : encodeFunctionData({
|
|
3425
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3426
|
+
functionName: "withdrawAssetToUser",
|
|
3427
|
+
args: [account, metadata.address, requested]
|
|
3428
|
+
}),
|
|
3429
|
+
value: "0",
|
|
3430
|
+
chainId
|
|
3431
|
+
});
|
|
3432
|
+
return {
|
|
3433
|
+
txHash,
|
|
3434
|
+
type: amount === void 0 ? "full" : "partial",
|
|
3435
|
+
amount: requested.toString()
|
|
3436
|
+
};
|
|
3437
|
+
} finally {
|
|
3438
|
+
await this.refreshSnapshotAfterMovement(
|
|
3439
|
+
state,
|
|
3440
|
+
chainId,
|
|
3441
|
+
context,
|
|
3442
|
+
"withdrawal"
|
|
3443
|
+
);
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
async getBalances(state, chainId) {
|
|
3447
|
+
this.assertChain(chainId);
|
|
3448
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3449
|
+
}
|
|
3450
|
+
async getEarnings(state, chainId) {
|
|
3451
|
+
this.assertChain(chainId);
|
|
3452
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3453
|
+
}
|
|
3454
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3455
|
+
this.assertChain(chainId);
|
|
3456
|
+
const asset = tokenSymbol?.toUpperCase();
|
|
3457
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3458
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3459
|
+
...asset ? { asset } : {},
|
|
3460
|
+
historic: true
|
|
2754
3461
|
});
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
3462
|
+
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3463
|
+
}
|
|
3464
|
+
async getHistory(state, chainId, options) {
|
|
3465
|
+
this.assertChain(chainId);
|
|
3466
|
+
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3467
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
3468
|
+
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3469
|
+
...asset ? { asset } : {},
|
|
3470
|
+
historic: true,
|
|
3471
|
+
actions: true
|
|
3472
|
+
});
|
|
3473
|
+
const catalog = await Promise.all(
|
|
3474
|
+
[...new Set(contexts.map((context) => context.asset))].map(
|
|
3475
|
+
(contextAsset) => this.loadYieldOptions(contextAsset)
|
|
3476
|
+
)
|
|
3477
|
+
);
|
|
3478
|
+
const vaultAddresses = new Set(
|
|
3479
|
+
catalog.flat().filter(
|
|
3480
|
+
(yieldOption) => yieldOption.chainId === chainId && isAddress2(yieldOption.address)
|
|
3481
|
+
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
3482
|
+
);
|
|
3483
|
+
return mapYieldseekerHistory(contexts, {
|
|
3484
|
+
limit: options?.limit ?? 10,
|
|
3485
|
+
ownerAddress: state.walletAddress,
|
|
3486
|
+
vaultAddresses,
|
|
3487
|
+
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3488
|
+
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3489
|
+
});
|
|
3490
|
+
}
|
|
3491
|
+
async getUserProfile(state, chainId) {
|
|
3492
|
+
this.assertChain(chainId);
|
|
3493
|
+
return mapYieldseekerProfile(
|
|
3494
|
+
state.walletAddress,
|
|
3495
|
+
await this.loadPortfolio(state, chainId, {})
|
|
3496
|
+
);
|
|
3497
|
+
}
|
|
3498
|
+
async getAgentApy(days, options) {
|
|
3499
|
+
this.assertOptionalChain(options?.chainId);
|
|
3500
|
+
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3501
|
+
if (requested !== void 0) this.assertAsset(requested);
|
|
3502
|
+
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3503
|
+
const values = await Promise.all(
|
|
3504
|
+
assets.map(async (asset) => {
|
|
3505
|
+
return { asset, yieldOptions: await this.loadYieldOptions(asset) };
|
|
3506
|
+
})
|
|
3507
|
+
);
|
|
3508
|
+
return mapYieldseekerAgentApy(values, days);
|
|
3509
|
+
}
|
|
3510
|
+
async loadYieldOptions(asset) {
|
|
3511
|
+
const cached = this.yieldOptions.get(asset);
|
|
3512
|
+
if (cached && cached.expiresAt > Date.now()) return cached.value;
|
|
3513
|
+
const pending = this.pendingYieldOptions.get(asset);
|
|
3514
|
+
if (pending) return pending;
|
|
3515
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3516
|
+
const request = this.api.request(
|
|
3517
|
+
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3518
|
+
).then((response) => {
|
|
3519
|
+
if (!Array.isArray(response?.yieldOptions)) {
|
|
3520
|
+
throw this.invalidResponse("yield options");
|
|
3521
|
+
}
|
|
3522
|
+
this.yieldOptions.set(asset, {
|
|
3523
|
+
expiresAt: Date.now() + YIELDSEEKER_YIELD_OPTIONS_CACHE_MS,
|
|
3524
|
+
value: response.yieldOptions
|
|
3525
|
+
});
|
|
3526
|
+
return response.yieldOptions;
|
|
3527
|
+
}).finally(() => this.pendingYieldOptions.delete(asset));
|
|
3528
|
+
this.pendingYieldOptions.set(asset, request);
|
|
3529
|
+
return request;
|
|
3530
|
+
}
|
|
3531
|
+
userKey(state, chainId) {
|
|
3532
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3533
|
+
}
|
|
3534
|
+
contextKey(state, chainId, asset) {
|
|
3535
|
+
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3536
|
+
}
|
|
3537
|
+
async resolveUser(state, chainId) {
|
|
3538
|
+
const key2 = this.userKey(state, chainId);
|
|
3539
|
+
const inMemory = this.users.get(key2);
|
|
3540
|
+
if (inMemory) return inMemory;
|
|
3541
|
+
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3542
|
+
if (persisted) {
|
|
3543
|
+
this.users.set(key2, persisted);
|
|
3544
|
+
return persisted;
|
|
3545
|
+
}
|
|
3546
|
+
const walletAddress = getAddress2(state.walletAddress);
|
|
3547
|
+
let user = null;
|
|
3548
|
+
try {
|
|
3549
|
+
const login = await this.providerRequest(
|
|
3550
|
+
state,
|
|
3551
|
+
chainId,
|
|
3552
|
+
"/users/login-with-wallet",
|
|
3553
|
+
{ method: "POST", body: { walletAddress } }
|
|
3554
|
+
);
|
|
3555
|
+
user = login?.user ?? null;
|
|
3556
|
+
if (!user) {
|
|
3557
|
+
throw this.invalidResponse("wallet login", {
|
|
3558
|
+
reason: "A successful login returned no user."
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
} catch (error) {
|
|
3562
|
+
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3563
|
+
if (error instanceof OwneyError) throw error;
|
|
3564
|
+
throw this.mapApiError(error);
|
|
3565
|
+
}
|
|
3566
|
+
let created;
|
|
3567
|
+
for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
|
|
3568
|
+
try {
|
|
3569
|
+
created = await this.providerRequest(
|
|
3570
|
+
state,
|
|
3571
|
+
chainId,
|
|
3572
|
+
"/users",
|
|
3573
|
+
{
|
|
3574
|
+
method: "POST",
|
|
3575
|
+
body: {
|
|
3576
|
+
walletAddress,
|
|
3577
|
+
username: generateYieldseekerUsername()
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
);
|
|
3581
|
+
break;
|
|
3582
|
+
} catch (createError) {
|
|
3583
|
+
const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
|
|
3584
|
+
if (canRetry) continue;
|
|
3585
|
+
throw this.mapApiError(createError);
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
user = created?.user ?? null;
|
|
3589
|
+
}
|
|
3590
|
+
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3591
|
+
throw this.invalidResponse("wallet identity");
|
|
3592
|
+
}
|
|
3593
|
+
const resolved = { userId: user.userId };
|
|
3594
|
+
this.users.set(key2, resolved);
|
|
3595
|
+
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3596
|
+
return resolved;
|
|
3597
|
+
}
|
|
3598
|
+
forgetUser(state, chainId) {
|
|
3599
|
+
this.users.delete(this.userKey(state, chainId));
|
|
3600
|
+
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3601
|
+
}
|
|
3602
|
+
async ensureAgent(state, chainId, asset) {
|
|
3603
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
3604
|
+
const cached = this.agentContexts.get(key2);
|
|
3605
|
+
if (cached) return cached;
|
|
3606
|
+
const pending = this.pendingAgents.get(key2);
|
|
3607
|
+
if (pending) return pending;
|
|
3608
|
+
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3609
|
+
async (context) => {
|
|
3610
|
+
if (!context) throw this.invalidResponse("agent creation");
|
|
3611
|
+
await this.deployAgent(state, chainId, context);
|
|
3612
|
+
this.agentContexts.set(key2, context);
|
|
3613
|
+
return context;
|
|
3614
|
+
}
|
|
3615
|
+
);
|
|
3616
|
+
this.pendingAgents.set(key2, request);
|
|
3617
|
+
try {
|
|
3618
|
+
return await request;
|
|
3619
|
+
} finally {
|
|
3620
|
+
this.pendingAgents.delete(key2);
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
async findAgent(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 context = await this.resolveAgent(state, chainId, asset, false);
|
|
3628
|
+
if (context) this.agentContexts.set(key2, context);
|
|
3629
|
+
return context;
|
|
3630
|
+
}
|
|
3631
|
+
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3632
|
+
const user = await this.resolveUser(state, chainId);
|
|
3633
|
+
const response = await this.walletRequest(
|
|
3634
|
+
state,
|
|
3635
|
+
chainId,
|
|
3636
|
+
`/users/${user.userId}/agents`
|
|
3637
|
+
);
|
|
3638
|
+
if (!Array.isArray(response?.agents)) {
|
|
3639
|
+
throw this.invalidResponse("agent list");
|
|
3640
|
+
}
|
|
3641
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3642
|
+
let agent = response.agents.find(
|
|
3643
|
+
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3644
|
+
);
|
|
3645
|
+
if (!agent && createIfMissing) {
|
|
3646
|
+
const created = await this.walletRequest(
|
|
3647
|
+
state,
|
|
3648
|
+
chainId,
|
|
3649
|
+
`/users/${user.userId}/agents`,
|
|
3650
|
+
{
|
|
3651
|
+
method: "POST",
|
|
3652
|
+
body: {
|
|
3653
|
+
name: OWNEY_AGENT_NAME,
|
|
3654
|
+
emoji: "\u{1F989}",
|
|
3655
|
+
chainId,
|
|
3656
|
+
assetAddress: metadata.address,
|
|
3657
|
+
type: "vault",
|
|
3658
|
+
rulePreset: null
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
);
|
|
3662
|
+
agent = created?.agent;
|
|
3663
|
+
}
|
|
3664
|
+
if (!agent) return null;
|
|
3665
|
+
this.assertAgent(agent);
|
|
3666
|
+
const walletResponse = await this.walletRequest(
|
|
3667
|
+
state,
|
|
3668
|
+
chainId,
|
|
3669
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3670
|
+
);
|
|
3671
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3672
|
+
throw this.invalidResponse("agent wallet");
|
|
3673
|
+
}
|
|
3674
|
+
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3675
|
+
}
|
|
3676
|
+
async loadPortfolio(state, chainId, options) {
|
|
3677
|
+
const user = await this.resolveUser(state, chainId);
|
|
3678
|
+
const response = await this.walletRequest(
|
|
3679
|
+
state,
|
|
3680
|
+
chainId,
|
|
3681
|
+
`/users/${user.userId}/agents`
|
|
3682
|
+
);
|
|
3683
|
+
if (!Array.isArray(response?.agents)) {
|
|
3684
|
+
throw this.invalidResponse("agent list");
|
|
3685
|
+
}
|
|
3686
|
+
const contexts = [];
|
|
3687
|
+
for (const agent of response.agents) {
|
|
3688
|
+
const asset = this.assetForAgent(agent);
|
|
3689
|
+
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3690
|
+
continue;
|
|
3691
|
+
}
|
|
3692
|
+
this.assertAgent(agent);
|
|
3693
|
+
const walletResponse = await this.walletRequest(
|
|
3694
|
+
state,
|
|
3695
|
+
chainId,
|
|
3696
|
+
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3697
|
+
);
|
|
3698
|
+
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3699
|
+
throw this.invalidResponse("agent wallet");
|
|
3700
|
+
}
|
|
3701
|
+
const context = {
|
|
3702
|
+
user,
|
|
3703
|
+
agent,
|
|
3704
|
+
wallet: walletResponse.agentWallet,
|
|
3705
|
+
asset
|
|
3706
|
+
};
|
|
3707
|
+
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3708
|
+
contexts.push(context);
|
|
3709
|
+
}
|
|
3710
|
+
return Promise.all(
|
|
3711
|
+
contexts.map(
|
|
3712
|
+
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3713
|
+
)
|
|
3714
|
+
);
|
|
3715
|
+
}
|
|
3716
|
+
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3717
|
+
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3718
|
+
this.walletRequest(
|
|
3719
|
+
state,
|
|
3720
|
+
chainId,
|
|
3721
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3722
|
+
shouldOnlyUseRecentValue: true,
|
|
3723
|
+
shouldAllowStaleOnError: true
|
|
3724
|
+
})}`
|
|
3725
|
+
),
|
|
3726
|
+
this.walletRequest(
|
|
3727
|
+
state,
|
|
3728
|
+
chainId,
|
|
3729
|
+
this.agentPath(context, "yield-positions")
|
|
3730
|
+
),
|
|
3731
|
+
options.historic ? this.walletRequest(
|
|
3732
|
+
state,
|
|
3733
|
+
chainId,
|
|
3734
|
+
this.agentPath(context, "wallet/historic-position")
|
|
3735
|
+
) : Promise.resolve(void 0),
|
|
3736
|
+
options.actions ? this.walletRequest(
|
|
3737
|
+
state,
|
|
3738
|
+
chainId,
|
|
3739
|
+
this.agentPath(context, "actions")
|
|
3740
|
+
) : Promise.resolve(void 0)
|
|
3741
|
+
]);
|
|
3742
|
+
if (!snapshot?.agentSnapshot) {
|
|
3743
|
+
throw this.invalidResponse("agent snapshot");
|
|
3744
|
+
}
|
|
3745
|
+
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3746
|
+
throw this.invalidResponse("yield positions");
|
|
3747
|
+
}
|
|
3748
|
+
return {
|
|
3749
|
+
...context,
|
|
3750
|
+
snapshot: snapshot.agentSnapshot,
|
|
3751
|
+
positions: positions.yieldPositions,
|
|
3752
|
+
...historic?.position ? { historic: historic.position } : {},
|
|
3753
|
+
...actions?.actions ? { actions: actions.actions } : {}
|
|
3754
|
+
};
|
|
3755
|
+
}
|
|
3756
|
+
async deployAgent(state, chainId, context) {
|
|
3757
|
+
if (context.wallet.initializedDate != null) return;
|
|
3758
|
+
const walletAddress = context.wallet.walletAddress.toLowerCase();
|
|
3759
|
+
const deployed = await this.walletRequest(
|
|
3760
|
+
state,
|
|
3761
|
+
chainId,
|
|
3762
|
+
this.agentPath(context, "deploy"),
|
|
3763
|
+
{ method: "POST", body: {} }
|
|
3764
|
+
);
|
|
3765
|
+
if (!deployed?.agentWallet || !isAddress2(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
3766
|
+
throw this.invalidResponse("agent deployment", {
|
|
3767
|
+
reason: "Deploy did not return the expected Agent Wallet."
|
|
3768
|
+
});
|
|
3769
|
+
}
|
|
3770
|
+
context.wallet = deployed.agentWallet;
|
|
3771
|
+
}
|
|
3772
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3773
|
+
try {
|
|
3774
|
+
const response = await this.walletRequest(
|
|
3775
|
+
state,
|
|
3776
|
+
chainId,
|
|
3777
|
+
`${this.agentPath(context, "snapshot")}${query({
|
|
3778
|
+
shouldForceRefresh: true
|
|
3779
|
+
})}`
|
|
3780
|
+
);
|
|
3781
|
+
if (!response?.agentSnapshot) {
|
|
3782
|
+
throw this.invalidResponse("agent snapshot refresh");
|
|
3783
|
+
}
|
|
3784
|
+
} catch (error) {
|
|
3785
|
+
console.warn(
|
|
3786
|
+
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3787
|
+
error
|
|
3788
|
+
);
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
agentPath(context, suffix) {
|
|
3792
|
+
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
3793
|
+
}
|
|
3794
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
3795
|
+
try {
|
|
3796
|
+
return await this.providerRequest(state, chainId, path, options);
|
|
3797
|
+
} catch (error) {
|
|
3798
|
+
throw this.mapApiError(error);
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
async providerRequest(state, chainId, path, options = {}) {
|
|
3802
|
+
this.assertChain(chainId);
|
|
3803
|
+
const request = (signature2) => this.api.request(path, {
|
|
3804
|
+
...options,
|
|
3805
|
+
signature: signature2
|
|
3806
|
+
});
|
|
3807
|
+
let signature = await this.auth.getToken(state, chainId);
|
|
3808
|
+
try {
|
|
3809
|
+
return await request(signature);
|
|
3810
|
+
} catch (error) {
|
|
3811
|
+
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
3812
|
+
if (error.providerCode === "NO_USER") throw error;
|
|
3813
|
+
if (!error.isAuthenticationError) throw error;
|
|
3814
|
+
signature = await this.auth.refreshToken(state, chainId, signature);
|
|
3815
|
+
try {
|
|
3816
|
+
return await request(signature);
|
|
3817
|
+
} catch (retryError) {
|
|
3818
|
+
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3819
|
+
this.forgetUser(state, chainId);
|
|
3820
|
+
}
|
|
3821
|
+
throw retryError;
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
mapApiError(error) {
|
|
3826
|
+
if (!(error instanceof YieldseekerApiError)) {
|
|
3827
|
+
return new OwneyError(
|
|
3828
|
+
"AGENT_API_ERROR",
|
|
3829
|
+
"Yieldseeker request failed.",
|
|
3830
|
+
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3831
|
+
this.id
|
|
3832
|
+
);
|
|
3833
|
+
}
|
|
3834
|
+
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3835
|
+
return new OwneyError(
|
|
3836
|
+
code,
|
|
3837
|
+
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3838
|
+
{
|
|
3839
|
+
statusCode: error.status,
|
|
3840
|
+
providerCode: error.providerCode,
|
|
3841
|
+
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3842
|
+
},
|
|
3843
|
+
this.id
|
|
3844
|
+
);
|
|
3845
|
+
}
|
|
3846
|
+
async submitTransaction(state, chainId, transaction) {
|
|
3847
|
+
if (this.transactionExecutor) {
|
|
3848
|
+
return this.transactionExecutor(state, chainId, transaction);
|
|
3849
|
+
}
|
|
3850
|
+
this.assertTransaction(transaction, state, chainId);
|
|
3851
|
+
const account = getAddress2(state.walletAddress);
|
|
3852
|
+
const walletClient = createWalletClient2({
|
|
3853
|
+
account,
|
|
3854
|
+
chain: base3,
|
|
3855
|
+
transport: custom2(state.provider)
|
|
3856
|
+
});
|
|
3857
|
+
const publicClient = createPublicClient3({
|
|
3858
|
+
chain: base3,
|
|
3859
|
+
transport: custom2(state.provider)
|
|
3860
|
+
});
|
|
3861
|
+
await ensureWalletOnChain(
|
|
3862
|
+
publicClient,
|
|
3863
|
+
walletClient,
|
|
3864
|
+
8453
|
|
3865
|
+
);
|
|
3866
|
+
const hash = await walletClient.sendTransaction({
|
|
3867
|
+
account,
|
|
3868
|
+
chain: base3,
|
|
3869
|
+
to: getAddress2(transaction.to),
|
|
3870
|
+
data: transaction.data,
|
|
3871
|
+
value: BigInt(transaction.value)
|
|
3872
|
+
});
|
|
3873
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3874
|
+
hash,
|
|
3875
|
+
confirmations: 1
|
|
3876
|
+
});
|
|
3877
|
+
if (receipt.status !== "success") {
|
|
3878
|
+
throw new OwneyError(
|
|
3879
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3880
|
+
`Yieldseeker transaction reverted (${hash}).`,
|
|
3881
|
+
{ transactionHash: hash },
|
|
3882
|
+
this.id
|
|
3883
|
+
);
|
|
3884
|
+
}
|
|
3885
|
+
return hash;
|
|
3886
|
+
}
|
|
3887
|
+
async waitForReceipt(state, chainId, transactionHash) {
|
|
3888
|
+
if (this.unwindReceiptWaiter) {
|
|
3889
|
+
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
const publicClient = createPublicClient3({
|
|
3893
|
+
chain: base3,
|
|
3894
|
+
transport: custom2(state.provider)
|
|
3895
|
+
});
|
|
3896
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3897
|
+
hash: transactionHash,
|
|
3898
|
+
confirmations: 1
|
|
3899
|
+
});
|
|
3900
|
+
if (receipt.status !== "success") {
|
|
3901
|
+
throw new OwneyError(
|
|
3902
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3903
|
+
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3904
|
+
{ transactionHash },
|
|
3905
|
+
this.id
|
|
3906
|
+
);
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
assertTransaction(transaction, state, chainId) {
|
|
3910
|
+
if (!transaction || typeof transaction.from !== "string" || !isAddress2(transaction.from) || typeof transaction.to !== "string" || !isAddress2(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || getAddress2(transaction.from) !== getAddress2(state.walletAddress)) {
|
|
3911
|
+
throw this.invalidResponse("transaction");
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
assertAgent(agent) {
|
|
3915
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress2(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3916
|
+
throw this.invalidResponse("agent");
|
|
2758
3917
|
}
|
|
2759
|
-
throw error;
|
|
2760
3918
|
}
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2769
|
-
async function awaitWithdrawalArrival(options) {
|
|
2770
|
-
const {
|
|
2771
|
-
readBalance,
|
|
2772
|
-
baseline,
|
|
2773
|
-
timeoutMs = DEFAULT_TIMEOUT_MS2,
|
|
2774
|
-
pollMs = DEFAULT_POLL_MS2
|
|
2775
|
-
} = options;
|
|
2776
|
-
const deadline = Date.now() + timeoutMs;
|
|
2777
|
-
debugLog("owney-sdk", "withdraw: waiting for funds to land", {
|
|
2778
|
-
baseline: baseline.toString(),
|
|
2779
|
-
timeoutMs
|
|
2780
|
-
});
|
|
2781
|
-
let lastError;
|
|
2782
|
-
for (; ; ) {
|
|
2783
|
-
try {
|
|
2784
|
-
const balance = await readBalance();
|
|
2785
|
-
if (balance > baseline) {
|
|
2786
|
-
const arrived = balance - baseline;
|
|
2787
|
-
debugLog("owney-sdk", "withdraw: funds landed", {
|
|
2788
|
-
arrived: arrived.toString()
|
|
2789
|
-
});
|
|
2790
|
-
return arrived;
|
|
3919
|
+
isOwneyAgent(agent) {
|
|
3920
|
+
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3921
|
+
}
|
|
3922
|
+
assetForAgent(agent) {
|
|
3923
|
+
for (const asset of ["USDC", "WETH"]) {
|
|
3924
|
+
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3925
|
+
return asset;
|
|
2791
3926
|
}
|
|
2792
|
-
} catch (error) {
|
|
2793
|
-
lastError = error;
|
|
2794
|
-
debugLog("owney-sdk", "withdraw: balance read failed, retrying", {
|
|
2795
|
-
message: error instanceof Error ? error.message : String(error)
|
|
2796
|
-
});
|
|
2797
3927
|
}
|
|
2798
|
-
|
|
3928
|
+
return null;
|
|
3929
|
+
}
|
|
3930
|
+
isTransactionHash(value) {
|
|
3931
|
+
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3932
|
+
}
|
|
3933
|
+
assertChain(chainId) {
|
|
3934
|
+
if (chainId !== 8453) {
|
|
3935
|
+
throw new OwneyError(
|
|
3936
|
+
"CHAIN_UNSUPPORTED",
|
|
3937
|
+
`Yieldseeker does not support chain ${chainId}.`,
|
|
3938
|
+
{ chainId, supportedChainIds: [8453] },
|
|
3939
|
+
this.id
|
|
3940
|
+
);
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
assertOptionalChain(chainId) {
|
|
3944
|
+
if (chainId !== void 0) this.assertChain(chainId);
|
|
3945
|
+
}
|
|
3946
|
+
assertAsset(asset) {
|
|
3947
|
+
if (asset !== "USDC" && asset !== "WETH") {
|
|
2799
3948
|
throw new OwneyError(
|
|
2800
|
-
"
|
|
2801
|
-
|
|
3949
|
+
"ASSET_UNSUPPORTED",
|
|
3950
|
+
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
2802
3951
|
{
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
}
|
|
3952
|
+
asset,
|
|
3953
|
+
supportedAssets: ["USDC", "WETH"],
|
|
3954
|
+
providerAlsoAdvertises: ["cbBTC"]
|
|
3955
|
+
},
|
|
3956
|
+
this.id
|
|
2809
3957
|
);
|
|
2810
3958
|
}
|
|
2811
|
-
await sleep(pollMs);
|
|
2812
3959
|
}
|
|
3960
|
+
invalidResponse(operation, details = {}) {
|
|
3961
|
+
return new OwneyError(
|
|
3962
|
+
"AGENT_INVALID_RESPONSE",
|
|
3963
|
+
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3964
|
+
details,
|
|
3965
|
+
this.id
|
|
3966
|
+
);
|
|
3967
|
+
}
|
|
3968
|
+
};
|
|
3969
|
+
|
|
3970
|
+
// src/lib/routing-api.ts
|
|
3971
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3972
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3973
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3974
|
+
try {
|
|
3975
|
+
const res = await fetch(url, {
|
|
3976
|
+
method: "GET",
|
|
3977
|
+
headers: {
|
|
3978
|
+
"Content-Type": "application/json",
|
|
3979
|
+
"x-owney-api-key": `${apiKey}`
|
|
3980
|
+
}
|
|
3981
|
+
});
|
|
3982
|
+
if (!res.ok) {
|
|
3983
|
+
if (res.status !== 404) {
|
|
3984
|
+
console.warn(
|
|
3985
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
3986
|
+
);
|
|
3987
|
+
}
|
|
3988
|
+
return null;
|
|
3989
|
+
}
|
|
3990
|
+
const json = await res.json();
|
|
3991
|
+
const policy = json.success ? json.data ?? null : null;
|
|
3992
|
+
debugLog(
|
|
3993
|
+
"owney-sdk",
|
|
3994
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
3995
|
+
policy ?? void 0
|
|
3996
|
+
);
|
|
3997
|
+
return policy;
|
|
3998
|
+
} catch (error) {
|
|
3999
|
+
console.warn(
|
|
4000
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
4001
|
+
error instanceof Error ? error.message : String(error)
|
|
4002
|
+
);
|
|
4003
|
+
return null;
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
4007
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
4008
|
+
const res = await fetch(url, {
|
|
4009
|
+
method: "GET",
|
|
4010
|
+
headers: {
|
|
4011
|
+
"Content-Type": "application/json",
|
|
4012
|
+
"x-owney-api-key": `${apiKey}`
|
|
4013
|
+
}
|
|
4014
|
+
});
|
|
4015
|
+
if (!res.ok) {
|
|
4016
|
+
const text = await res.text().catch(() => "");
|
|
4017
|
+
throw new OwneyError(
|
|
4018
|
+
"API_ROUTING_ERROR",
|
|
4019
|
+
`Routing API error ${res.status}: ${text}`,
|
|
4020
|
+
{ statusCode: res.status, responseBody: text }
|
|
4021
|
+
);
|
|
4022
|
+
}
|
|
4023
|
+
const json = await res.json();
|
|
4024
|
+
if (!json.success) {
|
|
4025
|
+
throw new OwneyError(
|
|
4026
|
+
"API_ROUTING_FAILED",
|
|
4027
|
+
`Routing API request failed: ${json.message}`,
|
|
4028
|
+
{ message: json.message }
|
|
4029
|
+
);
|
|
4030
|
+
}
|
|
4031
|
+
return json.data;
|
|
2813
4032
|
}
|
|
2814
4033
|
|
|
2815
4034
|
// src/lib/health-report.ts
|
|
2816
|
-
var
|
|
2817
|
-
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl =
|
|
4035
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
4036
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
2818
4037
|
try {
|
|
2819
4038
|
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
2820
4039
|
method: "POST",
|
|
@@ -2854,8 +4073,29 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
2854
4073
|
const tokenBalance = agentBalance?.tokens.find(
|
|
2855
4074
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
2856
4075
|
);
|
|
2857
|
-
|
|
2858
|
-
|
|
4076
|
+
let balance = tokenBalance ? parseUnits(tokenBalance.amount, decimals) : 0n;
|
|
4077
|
+
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
4078
|
+
const chainNameById = {
|
|
4079
|
+
1: "ETHEREUM",
|
|
4080
|
+
8453: "BASE",
|
|
4081
|
+
42161: "ARBITRUM"
|
|
4082
|
+
};
|
|
4083
|
+
const targetChain = chainNameById[chainId];
|
|
4084
|
+
for (const position2 of agentBalance?.positions ?? []) {
|
|
4085
|
+
const positionChain = position2.chain.trim().toUpperCase();
|
|
4086
|
+
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
4087
|
+
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
4088
|
+
if (position2.amountRaw !== void 0) {
|
|
4089
|
+
try {
|
|
4090
|
+
balance += BigInt(position2.amountRaw);
|
|
4091
|
+
continue;
|
|
4092
|
+
} catch {
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4095
|
+
balance += parseUnits(position2.amount, decimals);
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
return { agent, balance };
|
|
2859
4099
|
});
|
|
2860
4100
|
}
|
|
2861
4101
|
function planProportionalShares(balances, requested, totalAvailable) {
|
|
@@ -2881,7 +4121,9 @@ function planProportionalShares(balances, requested, totalAvailable) {
|
|
|
2881
4121
|
return plans;
|
|
2882
4122
|
}
|
|
2883
4123
|
function planDisabledDrain(disabled, requested) {
|
|
2884
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
4124
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
4125
|
+
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
4126
|
+
);
|
|
2885
4127
|
const plans = [];
|
|
2886
4128
|
let remaining = requested;
|
|
2887
4129
|
for (const { agent, balance } of sorted) {
|
|
@@ -2932,6 +4174,13 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
|
2932
4174
|
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
2933
4175
|
}
|
|
2934
4176
|
const normalizedToken = tokenSymbol.toUpperCase();
|
|
4177
|
+
const snapshots = balance.assetBalances?.filter(
|
|
4178
|
+
(token) => Number(token.chainId) === chainId && String(token.asset).toUpperCase() === normalizedToken
|
|
4179
|
+
);
|
|
4180
|
+
if (snapshots?.length) {
|
|
4181
|
+
const amount = snapshots.reduce((sum, token) => sum + Number(token.amount), 0);
|
|
4182
|
+
if (Number.isFinite(amount)) return Math.max(0, amount);
|
|
4183
|
+
}
|
|
2935
4184
|
return balance.tokens.reduce((total, token) => {
|
|
2936
4185
|
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
2937
4186
|
return total;
|
|
@@ -3003,329 +4252,312 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
3003
4252
|
|
|
3004
4253
|
// src/client.ts
|
|
3005
4254
|
import {
|
|
3006
|
-
createPublicClient as
|
|
3007
|
-
createWalletClient,
|
|
3008
|
-
custom
|
|
3009
|
-
erc20Abi as erc20Abi2
|
|
4255
|
+
createPublicClient as createPublicClient4,
|
|
4256
|
+
createWalletClient as createWalletClient3,
|
|
4257
|
+
custom as custom3
|
|
3010
4258
|
} from "viem";
|
|
3011
|
-
import { base as
|
|
4259
|
+
import { base as base4, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
3012
4260
|
|
|
3013
|
-
// src/lib/
|
|
3014
|
-
import {
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
4261
|
+
// src/lib/sponsored-token-batch.ts
|
|
4262
|
+
import {
|
|
4263
|
+
isAddressEqual,
|
|
4264
|
+
keccak256,
|
|
4265
|
+
toBytes
|
|
4266
|
+
} from "viem";
|
|
4267
|
+
|
|
4268
|
+
// src/lib/permit2-batch.ts
|
|
4269
|
+
import { parseAbi as parseAbi2, hashStruct } from "viem";
|
|
4270
|
+
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4271
|
+
var PERMIT_BATCH_TYPES = {
|
|
4272
|
+
PermitBatchWitnessTransferFrom: [
|
|
4273
|
+
{ name: "permitted", type: "TokenPermissions[]" },
|
|
4274
|
+
{ name: "spender", type: "address" },
|
|
4275
|
+
{ name: "nonce", type: "uint256" },
|
|
4276
|
+
{ name: "deadline", type: "uint256" },
|
|
4277
|
+
{ name: "witness", type: "Deposit" }
|
|
4278
|
+
],
|
|
4279
|
+
Deposit: [{ name: "recipients", type: "address[]" }],
|
|
4280
|
+
TokenPermissions: [
|
|
4281
|
+
{ name: "token", type: "address" },
|
|
4282
|
+
{ name: "amount", type: "uint256" }
|
|
4283
|
+
]
|
|
4284
|
+
};
|
|
4285
|
+
var PERMIT2_BATCH_ABI = parseAbi2([
|
|
4286
|
+
"struct TokenPermissions { address token; uint256 amount; }",
|
|
4287
|
+
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
4288
|
+
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
4289
|
+
"function permitWitnessTransferFrom(PermitBatchTransferFrom permit,SignatureTransferDetails[] transferDetails,address owner,bytes32 witness,string witnessTypeString,bytes signature)",
|
|
4290
|
+
"function nonceBitmap(address owner,uint256 wordPos) view returns (uint256)"
|
|
4291
|
+
]);
|
|
4292
|
+
function batchPermit(b) {
|
|
3020
4293
|
return {
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
{ name: "validAfter", type: "uint256" },
|
|
3028
|
-
{ name: "validBefore", type: "uint256" },
|
|
3029
|
-
{ name: "nonce", type: "bytes32" }
|
|
3030
|
-
]
|
|
3031
|
-
},
|
|
3032
|
-
primaryType: "TransferWithAuthorization",
|
|
3033
|
-
message: input.message
|
|
4294
|
+
permitted: b.transfers.map((t) => ({
|
|
4295
|
+
token: b.token,
|
|
4296
|
+
amount: BigInt(t.amount)
|
|
4297
|
+
})),
|
|
4298
|
+
nonce: BigInt(b.nonce),
|
|
4299
|
+
deadline: BigInt(b.deadline)
|
|
3034
4300
|
};
|
|
3035
4301
|
}
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
}
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
4302
|
+
function batchTypedData(b, spender) {
|
|
4303
|
+
return {
|
|
4304
|
+
domain: {
|
|
4305
|
+
name: "Permit2",
|
|
4306
|
+
chainId: b.chainId,
|
|
4307
|
+
verifyingContract: BATCH_PERMIT2_ADDRESS
|
|
4308
|
+
},
|
|
4309
|
+
types: PERMIT_BATCH_TYPES,
|
|
4310
|
+
primaryType: "PermitBatchWitnessTransferFrom",
|
|
4311
|
+
message: {
|
|
4312
|
+
...batchPermit(b),
|
|
4313
|
+
spender,
|
|
4314
|
+
witness: { recipients: b.transfers.map((t) => t.to) }
|
|
4315
|
+
}
|
|
4316
|
+
};
|
|
3047
4317
|
}
|
|
3048
4318
|
|
|
3049
|
-
// src/lib/
|
|
3050
|
-
var
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
},
|
|
3061
|
-
body: JSON.stringify(input.body)
|
|
3062
|
-
});
|
|
3063
|
-
} catch (networkError) {
|
|
3064
|
-
throw new OwneyError(
|
|
3065
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3066
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
3067
|
-
{ cause: String(networkError) }
|
|
3068
|
-
);
|
|
3069
|
-
}
|
|
3070
|
-
const text = await res.text();
|
|
3071
|
-
let parsed = null;
|
|
3072
|
-
try {
|
|
3073
|
-
parsed = JSON.parse(text);
|
|
3074
|
-
} catch {
|
|
3075
|
-
}
|
|
3076
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
3077
|
-
throw new OwneyError(
|
|
3078
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3079
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
3080
|
-
{
|
|
3081
|
-
statusCode: res.status,
|
|
3082
|
-
responseBody: text.slice(0, 500),
|
|
3083
|
-
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
3084
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
3085
|
-
safeToFallback: res.status === 503
|
|
3086
|
-
}
|
|
3087
|
-
);
|
|
3088
|
-
}
|
|
3089
|
-
return parsed.data;
|
|
4319
|
+
// src/lib/sponsored-token-batch.ts
|
|
4320
|
+
var memory = /* @__PURE__ */ new Map();
|
|
4321
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
4322
|
+
var planOf = (transfers) => JSON.stringify(
|
|
4323
|
+
transfers.map((t) => ({
|
|
4324
|
+
to: t.to.toLowerCase(),
|
|
4325
|
+
amount: BigInt(t.amount).toString()
|
|
4326
|
+
}))
|
|
4327
|
+
);
|
|
4328
|
+
function read(key2) {
|
|
4329
|
+
return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
|
|
3090
4330
|
}
|
|
3091
|
-
|
|
3092
|
-
const
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
"content-type": "application/json",
|
|
3099
|
-
"x-owney-api-key": input.apiKey
|
|
3100
|
-
},
|
|
3101
|
-
body: JSON.stringify(input.body)
|
|
3102
|
-
});
|
|
3103
|
-
} catch (networkError) {
|
|
3104
|
-
throw new OwneyError(
|
|
3105
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3106
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
3107
|
-
{ cause: String(networkError), safeToFallback: false }
|
|
3108
|
-
);
|
|
3109
|
-
}
|
|
3110
|
-
const text = await res.text();
|
|
3111
|
-
let parsed = null;
|
|
3112
|
-
try {
|
|
3113
|
-
parsed = JSON.parse(text);
|
|
3114
|
-
} catch {
|
|
3115
|
-
}
|
|
3116
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
3117
|
-
throw new OwneyError(
|
|
3118
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3119
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
3120
|
-
{
|
|
3121
|
-
statusCode: res.status,
|
|
3122
|
-
responseBody: text.slice(0, 500),
|
|
3123
|
-
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
3124
|
-
}
|
|
3125
|
-
);
|
|
3126
|
-
}
|
|
3127
|
-
return parsed.data;
|
|
4331
|
+
function save(key2, body) {
|
|
4332
|
+
const value = JSON.stringify({
|
|
4333
|
+
...body,
|
|
4334
|
+
transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
|
|
4335
|
+
});
|
|
4336
|
+
if (typeof window === "undefined") memory.set(key2, value);
|
|
4337
|
+
else window.localStorage.setItem(key2, value);
|
|
3128
4338
|
}
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
try {
|
|
3133
|
-
res = await fetch(
|
|
3134
|
-
`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
3135
|
-
{
|
|
3136
|
-
headers: { "x-owney-api-key": input.apiKey }
|
|
3137
|
-
}
|
|
3138
|
-
);
|
|
3139
|
-
} catch (networkError) {
|
|
3140
|
-
throw new OwneyError(
|
|
3141
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3142
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
3143
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
3144
|
-
);
|
|
3145
|
-
}
|
|
3146
|
-
const text = await res.text();
|
|
3147
|
-
let parsed = null;
|
|
3148
|
-
try {
|
|
3149
|
-
parsed = JSON.parse(text);
|
|
3150
|
-
} catch {
|
|
3151
|
-
}
|
|
3152
|
-
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
3153
|
-
throw new OwneyError(
|
|
3154
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3155
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
3156
|
-
{
|
|
3157
|
-
statusCode: res.status,
|
|
3158
|
-
responseBody: text.slice(0, 500),
|
|
3159
|
-
safeToFallback: true
|
|
3160
|
-
}
|
|
3161
|
-
);
|
|
3162
|
-
}
|
|
3163
|
-
return parsed.data.relayer;
|
|
4339
|
+
function clear(key2) {
|
|
4340
|
+
if (typeof window === "undefined") memory.delete(key2);
|
|
4341
|
+
else window.localStorage.removeItem(key2);
|
|
3164
4342
|
}
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
"CHAIN_UNSUPPORTED",
|
|
3176
|
-
`No sponsored token configured for chain ${chainId}`
|
|
4343
|
+
function sponsorTokenBatch(i) {
|
|
4344
|
+
const key2 = `owney.token-batch.v1:${keccak256(toBytes(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
|
|
4345
|
+
const plan = planOf(i.transfers);
|
|
4346
|
+
const active = inflight.get(key2);
|
|
4347
|
+
if (active) {
|
|
4348
|
+
if (active.plan !== plan)
|
|
4349
|
+
return Promise.reject(
|
|
4350
|
+
new Error(
|
|
4351
|
+
"A token deposit is already in progress. Wait for its result before depositing again."
|
|
4352
|
+
)
|
|
3177
4353
|
);
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
4354
|
+
return active.promise;
|
|
4355
|
+
}
|
|
4356
|
+
const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
|
|
4357
|
+
inflight.set(key2, { plan, promise });
|
|
4358
|
+
return promise;
|
|
4359
|
+
}
|
|
4360
|
+
async function execute(i, key2, plan) {
|
|
4361
|
+
if (!i.transfers.length || i.transfers.length > 16 || i.transfers.some(
|
|
4362
|
+
(t) => BigInt(t.amount) <= 0n || BigInt(t.amount) >= 1n << 256n
|
|
4363
|
+
) || new Set(i.transfers.map((t) => t.to.toLowerCase())).size !== i.transfers.length)
|
|
4364
|
+
throw new Error("Invalid token deposit shares.");
|
|
4365
|
+
const send = async (initial) => {
|
|
4366
|
+
let body = initial;
|
|
4367
|
+
save(key2, body);
|
|
3182
4368
|
try {
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
)
|
|
4369
|
+
if (!body.serializedTransaction) {
|
|
4370
|
+
const prepared = await postSponsorBatchTransfer({
|
|
4371
|
+
apiKey: i.apiKey,
|
|
4372
|
+
baseUrl: i.baseUrl,
|
|
4373
|
+
body
|
|
4374
|
+
});
|
|
4375
|
+
if (!prepared.serializedTransaction || keccak256(prepared.serializedTransaction) !== prepared.txHash)
|
|
4376
|
+
throw new Error(
|
|
4377
|
+
"Sponsorship API did not return a valid prepared transaction."
|
|
4378
|
+
);
|
|
4379
|
+
body = {
|
|
4380
|
+
...body,
|
|
4381
|
+
serializedTransaction: prepared.serializedTransaction
|
|
4382
|
+
};
|
|
4383
|
+
save(key2, body);
|
|
3190
4384
|
}
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
)
|
|
4385
|
+
const result = await postSponsorBatchTransfer({
|
|
4386
|
+
apiKey: i.apiKey,
|
|
4387
|
+
baseUrl: i.baseUrl,
|
|
4388
|
+
body
|
|
4389
|
+
});
|
|
4390
|
+
if (result.txHash !== keccak256(body.serializedTransaction))
|
|
4391
|
+
throw new Error(
|
|
4392
|
+
"Sponsorship receipt does not match the pending transaction."
|
|
4393
|
+
);
|
|
4394
|
+
clear(key2);
|
|
4395
|
+
return result.txHash;
|
|
4396
|
+
} catch (error) {
|
|
4397
|
+
if (!body.serializedTransaction || error instanceof OwneyError && error.details?.notSubmitted === true)
|
|
4398
|
+
clear(key2);
|
|
4399
|
+
throw error;
|
|
3197
4400
|
}
|
|
3198
|
-
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
3199
|
-
const validAfter = 0n;
|
|
3200
|
-
const validBefore = BigInt(
|
|
3201
|
-
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
3202
|
-
);
|
|
3203
|
-
const nonce = randomAuthNonce();
|
|
3204
|
-
const typedData = buildTransferWithAuthorizationTypedData({
|
|
3205
|
-
token,
|
|
3206
|
-
chainId: cid,
|
|
3207
|
-
tokenName,
|
|
3208
|
-
tokenVersion,
|
|
3209
|
-
message: {
|
|
3210
|
-
from: deps.ownerAddress,
|
|
3211
|
-
to: smartWallet,
|
|
3212
|
-
value: BigInt(amount),
|
|
3213
|
-
validAfter,
|
|
3214
|
-
validBefore,
|
|
3215
|
-
nonce
|
|
3216
|
-
}
|
|
3217
|
-
});
|
|
3218
|
-
const authSignature = await wallet.signTypedData({
|
|
3219
|
-
account: deps.ownerAddress,
|
|
3220
|
-
...typedData
|
|
3221
|
-
});
|
|
3222
|
-
deps.onApproved?.();
|
|
3223
|
-
const result = await post({
|
|
3224
|
-
baseUrl: deps.baseUrl,
|
|
3225
|
-
apiKey: deps.apiKey,
|
|
3226
|
-
body: {
|
|
3227
|
-
chainId: cid,
|
|
3228
|
-
token,
|
|
3229
|
-
from: deps.ownerAddress,
|
|
3230
|
-
to: smartWallet,
|
|
3231
|
-
value: amount,
|
|
3232
|
-
validAfter: validAfter.toString(),
|
|
3233
|
-
validBefore: validBefore.toString(),
|
|
3234
|
-
nonce,
|
|
3235
|
-
authSignature,
|
|
3236
|
-
tokenName,
|
|
3237
|
-
tokenVersion
|
|
3238
|
-
}
|
|
3239
|
-
});
|
|
3240
|
-
return result.txHash;
|
|
3241
4401
|
};
|
|
4402
|
+
const saved = read(key2);
|
|
4403
|
+
if (saved) {
|
|
4404
|
+
const previous = JSON.parse(saved);
|
|
4405
|
+
if (previous.chainId !== i.chainId || !isAddressEqual(previous.from, i.owner) || !isAddressEqual(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
4406
|
+
throw new Error(
|
|
4407
|
+
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
4408
|
+
);
|
|
4409
|
+
i.onApproved?.();
|
|
4410
|
+
return send({ ...previous, transfers: i.transfers });
|
|
4411
|
+
}
|
|
4412
|
+
const total = i.transfers.reduce((sum, t) => sum + BigInt(t.amount), 0n);
|
|
4413
|
+
const [balance, allowance] = await Promise.all([
|
|
4414
|
+
readErc20Balance(i.pub, i.token, i.owner),
|
|
4415
|
+
readPermit2Allowance(i.pub, i.token, i.owner)
|
|
4416
|
+
]);
|
|
4417
|
+
if (balance < total)
|
|
4418
|
+
throw new OwneyError(
|
|
4419
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4420
|
+
"Insufficient token balance for this deposit."
|
|
4421
|
+
);
|
|
4422
|
+
if (allowance < total)
|
|
4423
|
+
throw new OwneyError(
|
|
4424
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
4425
|
+
"token deposits need a one-time Permit2 approval."
|
|
4426
|
+
);
|
|
4427
|
+
const relayer = await getSponsorRelayerAddress({
|
|
4428
|
+
apiKey: i.apiKey,
|
|
4429
|
+
baseUrl: i.baseUrl,
|
|
4430
|
+
chainId: i.chainId
|
|
4431
|
+
});
|
|
4432
|
+
const now = (await i.pub.getBlock()).timestamp;
|
|
4433
|
+
const unsigned = {
|
|
4434
|
+
chainId: i.chainId,
|
|
4435
|
+
token: i.token,
|
|
4436
|
+
from: i.owner,
|
|
4437
|
+
transfers: i.transfers,
|
|
4438
|
+
nonce: randomPermit2Nonce().toString(),
|
|
4439
|
+
deadline: (now + 900n).toString()
|
|
4440
|
+
};
|
|
4441
|
+
const signature = await i.wallet.signTypedData({
|
|
4442
|
+
account: i.owner,
|
|
4443
|
+
...batchTypedData(unsigned, relayer)
|
|
4444
|
+
});
|
|
4445
|
+
i.onApproved?.();
|
|
4446
|
+
return send({ ...unsigned, signature });
|
|
3242
4447
|
}
|
|
3243
4448
|
|
|
3244
|
-
// src/lib/sponsored-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
3249
|
-
return async (smartWallet, chainId, amount) => {
|
|
3250
|
-
const cid = chainId;
|
|
3251
|
-
const token = deps.tokenAddressByChain[cid];
|
|
3252
|
-
if (!token) {
|
|
4449
|
+
// src/lib/sponsored-token-deposit.ts
|
|
4450
|
+
function makeSponsoredTokenCallback(deps) {
|
|
4451
|
+
const batch = async (chainId, transfers) => {
|
|
4452
|
+
if (chainId !== 8453 && chainId !== 42161 && chainId !== 1)
|
|
3253
4453
|
throw new OwneyError(
|
|
3254
4454
|
"CHAIN_UNSUPPORTED",
|
|
3255
|
-
`No sponsored
|
|
3256
|
-
);
|
|
3257
|
-
}
|
|
3258
|
-
const amountWei = BigInt(amount);
|
|
3259
|
-
const pub = deps.getPublicClient(cid);
|
|
3260
|
-
const wallet = deps.getWalletClient(cid);
|
|
3261
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
3262
|
-
try {
|
|
3263
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
3264
|
-
if (balance < amountWei) {
|
|
3265
|
-
throw new OwneyError(
|
|
3266
|
-
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
3267
|
-
"Insufficient WETH balance for this deposit.",
|
|
3268
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3269
|
-
);
|
|
3270
|
-
}
|
|
3271
|
-
} catch (err) {
|
|
3272
|
-
if (err instanceof OwneyError) throw err;
|
|
3273
|
-
console.warn(
|
|
3274
|
-
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
3275
|
-
err instanceof Error ? err.message : String(err)
|
|
4455
|
+
`No sponsored token configured for chain ${chainId}`
|
|
3276
4456
|
);
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
if (allowance < amountWei) {
|
|
4457
|
+
const token = deps.tokenAddressByChain[chainId];
|
|
4458
|
+
if (!token)
|
|
3280
4459
|
throw new OwneyError(
|
|
3281
|
-
"
|
|
3282
|
-
|
|
3283
|
-
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
4460
|
+
"CHAIN_UNSUPPORTED",
|
|
4461
|
+
`No sponsored token configured for chain ${chainId}`
|
|
3284
4462
|
);
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
4463
|
+
const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
|
|
4464
|
+
await ensureWalletOnChain(pub, wallet, chainId);
|
|
4465
|
+
return sponsorTokenBatch({
|
|
3288
4466
|
apiKey: deps.apiKey,
|
|
3289
|
-
chainId: cid
|
|
3290
|
-
});
|
|
3291
|
-
const nonce = randomPermit2Nonce();
|
|
3292
|
-
const deadline = BigInt(
|
|
3293
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
3294
|
-
);
|
|
3295
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
3296
|
-
chainId: cid,
|
|
3297
|
-
message: {
|
|
3298
|
-
permitted: { token, amount: amountWei },
|
|
3299
|
-
spender: relayer,
|
|
3300
|
-
nonce,
|
|
3301
|
-
deadline
|
|
3302
|
-
}
|
|
3303
|
-
});
|
|
3304
|
-
const signature = await wallet.signTypedData({
|
|
3305
|
-
account: deps.ownerAddress,
|
|
3306
|
-
...typedData
|
|
3307
|
-
});
|
|
3308
|
-
deps.onApproved?.();
|
|
3309
|
-
const result = await post({
|
|
3310
4467
|
baseUrl: deps.baseUrl,
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
nonce: nonce.toString(),
|
|
3319
|
-
deadline: deadline.toString(),
|
|
3320
|
-
signature
|
|
3321
|
-
}
|
|
4468
|
+
owner: deps.ownerAddress,
|
|
4469
|
+
token,
|
|
4470
|
+
chainId,
|
|
4471
|
+
transfers,
|
|
4472
|
+
pub,
|
|
4473
|
+
wallet,
|
|
4474
|
+
onApproved: deps.onApproved
|
|
3322
4475
|
});
|
|
3323
|
-
return result.txHash;
|
|
3324
4476
|
};
|
|
4477
|
+
const callback = makeVerificationAwareDepositCallback(
|
|
4478
|
+
(to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
|
|
4479
|
+
);
|
|
4480
|
+
registerDepositBatch(callback, batch);
|
|
4481
|
+
return callback;
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
// src/lib/agent-deposit-batch.ts
|
|
4485
|
+
function deferred() {
|
|
4486
|
+
let resolve, reject;
|
|
4487
|
+
const promise = new Promise((yes, no) => {
|
|
4488
|
+
resolve = yes;
|
|
4489
|
+
reject = no;
|
|
4490
|
+
});
|
|
4491
|
+
void promise.catch(() => {
|
|
4492
|
+
});
|
|
4493
|
+
return { promise, resolve, reject };
|
|
4494
|
+
}
|
|
4495
|
+
async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
4496
|
+
const funding = deferred();
|
|
4497
|
+
const tasks = [];
|
|
4498
|
+
const transfers = [];
|
|
4499
|
+
try {
|
|
4500
|
+
for (const leg of legs) {
|
|
4501
|
+
const ready = deferred();
|
|
4502
|
+
let entered = false;
|
|
4503
|
+
const callback = makeVerificationAwareDepositCallback(
|
|
4504
|
+
(to, cid, amount, verification) => {
|
|
4505
|
+
if (entered || cid !== chainId || BigInt(amount) !== BigInt(leg.amount)) {
|
|
4506
|
+
const error = new Error(
|
|
4507
|
+
"Agent changed its prepared deposit share."
|
|
4508
|
+
);
|
|
4509
|
+
ready.reject(error);
|
|
4510
|
+
throw error;
|
|
4511
|
+
}
|
|
4512
|
+
entered = true;
|
|
4513
|
+
ready.resolve(toBatchTransfer(to, amount, verification));
|
|
4514
|
+
return funding.promise;
|
|
4515
|
+
}
|
|
4516
|
+
);
|
|
4517
|
+
const task = Promise.resolve().then(() => leg.run(callback));
|
|
4518
|
+
tasks.push(task);
|
|
4519
|
+
void task.then(
|
|
4520
|
+
() => {
|
|
4521
|
+
if (!entered)
|
|
4522
|
+
ready.reject(
|
|
4523
|
+
new Error("Agent did not prepare a deposit transfer.")
|
|
4524
|
+
);
|
|
4525
|
+
},
|
|
4526
|
+
(error) => ready.reject(error)
|
|
4527
|
+
);
|
|
4528
|
+
transfers.push(await ready.promise);
|
|
4529
|
+
}
|
|
4530
|
+
const txHash = await transfer(chainId, transfers);
|
|
4531
|
+
funding.resolve(txHash);
|
|
4532
|
+
const settled = await Promise.allSettled(tasks);
|
|
4533
|
+
const agentResults = {};
|
|
4534
|
+
const failures = [];
|
|
4535
|
+
for (const [index, result] of settled.entries()) {
|
|
4536
|
+
if (result.status === "fulfilled")
|
|
4537
|
+
agentResults[legs[index].id] = result.value;
|
|
4538
|
+
else failures.push(legs[index].id);
|
|
4539
|
+
}
|
|
4540
|
+
if (failures.length)
|
|
4541
|
+
throw new OwneyError(
|
|
4542
|
+
"DEPOSIT_PARTIAL_FAILURE",
|
|
4543
|
+
"The deposit was sent to all agents, but some agent updates could not be confirmed. Check activity before depositing again.",
|
|
4544
|
+
{
|
|
4545
|
+
txHash,
|
|
4546
|
+
fundsSubmitted: true,
|
|
4547
|
+
agentResults,
|
|
4548
|
+
failedAgentIds: failures
|
|
4549
|
+
}
|
|
4550
|
+
);
|
|
4551
|
+
return { agentResults };
|
|
4552
|
+
} catch (error) {
|
|
4553
|
+
funding.reject(error);
|
|
4554
|
+
await Promise.allSettled(tasks);
|
|
4555
|
+
throw error;
|
|
4556
|
+
}
|
|
3325
4557
|
}
|
|
3326
4558
|
|
|
3327
4559
|
// src/lib/sponsored-calls-deposit.ts
|
|
3328
|
-
import { encodeFunctionData, erc20Abi, toHex
|
|
4560
|
+
import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi2, toHex } from "viem";
|
|
3329
4561
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
3330
4562
|
var DEFAULT_MAX_POLLS = 30;
|
|
3331
4563
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -3333,7 +4565,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
3333
4565
|
method: "wallet_getCapabilities",
|
|
3334
4566
|
params: [owner]
|
|
3335
4567
|
});
|
|
3336
|
-
const forChain = caps?.[
|
|
4568
|
+
const forChain = caps?.[toHex(chainId)] ?? caps?.[String(chainId)];
|
|
3337
4569
|
return Boolean(forChain?.paymasterService?.supported);
|
|
3338
4570
|
}
|
|
3339
4571
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -3351,7 +4583,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3351
4583
|
}
|
|
3352
4584
|
return new URL(configured, origin).toString();
|
|
3353
4585
|
};
|
|
3354
|
-
|
|
4586
|
+
const batch = async (chainId, transfers) => {
|
|
3355
4587
|
const cid = chainId;
|
|
3356
4588
|
const token = deps.tokenAddressByChain[cid];
|
|
3357
4589
|
if (!token) {
|
|
@@ -3367,22 +4599,53 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3367
4599
|
{ chainId }
|
|
3368
4600
|
);
|
|
3369
4601
|
}
|
|
3370
|
-
const
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
4602
|
+
const calls = transfers.map((transfer) => ({
|
|
4603
|
+
to: token,
|
|
4604
|
+
value: "0x0",
|
|
4605
|
+
data: encodeFunctionData2({
|
|
4606
|
+
abi: erc20Abi2,
|
|
4607
|
+
functionName: "transfer",
|
|
4608
|
+
args: [transfer.to, BigInt(transfer.amount)]
|
|
4609
|
+
})
|
|
4610
|
+
}));
|
|
4611
|
+
let paymasterUrl = absolutePaymasterUrl();
|
|
4612
|
+
for (const transfer of transfers) {
|
|
4613
|
+
const verification = transfer.yieldseeker;
|
|
4614
|
+
if (!verification) continue;
|
|
4615
|
+
if (chainId !== 8453)
|
|
4616
|
+
throw new OwneyError(
|
|
4617
|
+
"CHAIN_UNSUPPORTED",
|
|
4618
|
+
`Yieldseeker sponsorship is not available on chain ${chainId}.`
|
|
4619
|
+
);
|
|
4620
|
+
const { intent } = await postPaymasterIntent({
|
|
4621
|
+
baseUrl: deps.routingApiBaseUrl,
|
|
4622
|
+
apiKey: deps.apiKey,
|
|
4623
|
+
yieldseekerSignature: verification.signature,
|
|
4624
|
+
body: {
|
|
4625
|
+
chainId,
|
|
4626
|
+
token,
|
|
4627
|
+
from: deps.ownerAddress,
|
|
4628
|
+
to: transfer.to,
|
|
4629
|
+
amount: transfer.amount,
|
|
4630
|
+
yieldseekerUserId: verification.userId,
|
|
4631
|
+
yieldseekerAgentId: verification.agentId
|
|
4632
|
+
}
|
|
4633
|
+
});
|
|
4634
|
+
const url = new URL(paymasterUrl);
|
|
4635
|
+
url.searchParams.append("owneyIntent", intent);
|
|
4636
|
+
paymasterUrl = url.toString();
|
|
4637
|
+
}
|
|
3375
4638
|
const sendResult = await deps.provider.request({
|
|
3376
4639
|
method: "wallet_sendCalls",
|
|
3377
4640
|
params: [
|
|
3378
4641
|
{
|
|
3379
4642
|
version: "2.0.0",
|
|
3380
4643
|
from: deps.ownerAddress,
|
|
3381
|
-
chainId:
|
|
3382
|
-
atomicRequired:
|
|
3383
|
-
calls
|
|
4644
|
+
chainId: toHex(chainId),
|
|
4645
|
+
atomicRequired: transfers.length > 1,
|
|
4646
|
+
calls,
|
|
3384
4647
|
capabilities: {
|
|
3385
|
-
paymasterService: { url:
|
|
4648
|
+
paymasterService: { url: paymasterUrl }
|
|
3386
4649
|
}
|
|
3387
4650
|
}
|
|
3388
4651
|
]
|
|
@@ -3402,7 +4665,24 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3402
4665
|
params: [callsId]
|
|
3403
4666
|
});
|
|
3404
4667
|
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
3405
|
-
if (
|
|
4668
|
+
if (status?.receipts?.some((receipt) => receipt.status === "0x0") || typeof status?.status === "number" && status.status >= 400) {
|
|
4669
|
+
throw new OwneyError(
|
|
4670
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4671
|
+
"The sponsored deposit did not complete successfully.",
|
|
4672
|
+
{ chainId, callsId, safeToFallback: false }
|
|
4673
|
+
);
|
|
4674
|
+
}
|
|
4675
|
+
if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
|
|
4676
|
+
if (status?.receipts?.some(
|
|
4677
|
+
(receipt) => receipt.transactionHash !== txHash
|
|
4678
|
+
))
|
|
4679
|
+
throw new OwneyError(
|
|
4680
|
+
"SPONSORED_CALLS_NO_RECEIPT",
|
|
4681
|
+
"The wallet returned multiple transactions for an atomic deposit. Check activity before trying again.",
|
|
4682
|
+
{ chainId, callsId }
|
|
4683
|
+
);
|
|
4684
|
+
return txHash;
|
|
4685
|
+
}
|
|
3406
4686
|
if (pollIntervalMs > 0) {
|
|
3407
4687
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
3408
4688
|
}
|
|
@@ -3413,6 +4693,11 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3413
4693
|
{ chainId, callsId }
|
|
3414
4694
|
);
|
|
3415
4695
|
};
|
|
4696
|
+
const callback = makeVerificationAwareDepositCallback(
|
|
4697
|
+
(to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
|
|
4698
|
+
);
|
|
4699
|
+
registerDepositBatch(callback, batch);
|
|
4700
|
+
return callback;
|
|
3416
4701
|
}
|
|
3417
4702
|
|
|
3418
4703
|
// src/client.ts
|
|
@@ -3442,7 +4727,7 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
3442
4727
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
3443
4728
|
};
|
|
3444
4729
|
var VIEM_CHAIN2 = {
|
|
3445
|
-
8453:
|
|
4730
|
+
8453: base4,
|
|
3446
4731
|
42161: arbitrum2,
|
|
3447
4732
|
1: mainnet2
|
|
3448
4733
|
};
|
|
@@ -3451,6 +4736,11 @@ var SPONSORED_WETH_BY_CHAIN = {
|
|
|
3451
4736
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
3452
4737
|
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
3453
4738
|
};
|
|
4739
|
+
var SPONSORED_TOKENS_BY_ASSET = { USDC: SPONSORED_USDC_BY_CHAIN, WETH: SPONSORED_WETH_BY_CHAIN };
|
|
4740
|
+
function sponsoredTokensFor(asset) {
|
|
4741
|
+
if (!Object.prototype.hasOwnProperty.call(SPONSORED_TOKENS_BY_ASSET, asset)) throw new OwneyError("ASSET_UNSUPPORTED", `No sponsored token configured for ${asset}`);
|
|
4742
|
+
return SPONSORED_TOKENS_BY_ASSET[asset];
|
|
4743
|
+
}
|
|
3454
4744
|
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
3455
4745
|
return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
3456
4746
|
}
|
|
@@ -3472,6 +4762,8 @@ var OwneySDK = class {
|
|
|
3472
4762
|
orgAgentConfig;
|
|
3473
4763
|
orgAgentConfigPromise = null;
|
|
3474
4764
|
zyfaiRpcUrls;
|
|
4765
|
+
yieldseekerApiBaseUrl;
|
|
4766
|
+
yieldseekerSiweOrigin;
|
|
3475
4767
|
routingApiBaseUrl;
|
|
3476
4768
|
referralSource;
|
|
3477
4769
|
cachedSponsoredCallback = null;
|
|
@@ -3494,6 +4786,8 @@ var OwneySDK = class {
|
|
|
3494
4786
|
this.apiKey = config.apiKey;
|
|
3495
4787
|
if (config.debug) setOwneyDebug(true);
|
|
3496
4788
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
4789
|
+
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
4790
|
+
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
3497
4791
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
3498
4792
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
3499
4793
|
this.referralSource = config.referralSource;
|
|
@@ -3527,6 +4821,7 @@ var OwneySDK = class {
|
|
|
3527
4821
|
* After calling this, `connect()` must be called again before using agent methods.
|
|
3528
4822
|
*/
|
|
3529
4823
|
async disconnect() {
|
|
4824
|
+
this.state = null;
|
|
3530
4825
|
for (const agent of this.agents.values()) {
|
|
3531
4826
|
await agent.disconnect();
|
|
3532
4827
|
}
|
|
@@ -3580,18 +4875,13 @@ var OwneySDK = class {
|
|
|
3580
4875
|
}
|
|
3581
4876
|
return this.state.provider;
|
|
3582
4877
|
}
|
|
3583
|
-
/**
|
|
3584
|
-
* Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
|
|
3585
|
-
* used when the caller omits `depositCallback`. Wraps the connected EIP-1193
|
|
3586
|
-
* provider with viem `custom(provider)` to read token meta and sign the
|
|
3587
|
-
* `TransferWithAuthorization`, then POSTs to the sponsor API.
|
|
3588
|
-
*/
|
|
4878
|
+
/** Builds the default USDC batch callback for the connected wallet. */
|
|
3589
4879
|
getDefaultSponsoredCallback(onApproved) {
|
|
3590
4880
|
if (!onApproved && this.cachedSponsoredCallback)
|
|
3591
4881
|
return this.cachedSponsoredCallback;
|
|
3592
4882
|
const provider = this.requireConnectedProvider();
|
|
3593
4883
|
const owner = this.state.walletAddress;
|
|
3594
|
-
const callback =
|
|
4884
|
+
const callback = makeSponsoredTokenCallback({
|
|
3595
4885
|
apiKey: this.apiKey,
|
|
3596
4886
|
baseUrl: this.routingApiBaseUrl,
|
|
3597
4887
|
ownerAddress: owner,
|
|
@@ -3600,35 +4890,32 @@ var OwneySDK = class {
|
|
|
3600
4890
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3601
4891
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3602
4892
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3603
|
-
getPublicClient: (cid) =>
|
|
4893
|
+
getPublicClient: (cid) => createPublicClient4({
|
|
3604
4894
|
chain: VIEM_CHAIN2[cid],
|
|
3605
|
-
transport:
|
|
4895
|
+
transport: custom3(provider)
|
|
3606
4896
|
}),
|
|
3607
|
-
getWalletClient: (cid) =>
|
|
4897
|
+
getWalletClient: (cid) => createWalletClient3({
|
|
3608
4898
|
account: owner,
|
|
3609
4899
|
chain: VIEM_CHAIN2[cid],
|
|
3610
|
-
transport:
|
|
4900
|
+
transport: custom3(provider)
|
|
3611
4901
|
})
|
|
3612
4902
|
});
|
|
3613
4903
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
3614
4904
|
return callback;
|
|
3615
4905
|
}
|
|
3616
|
-
/**
|
|
3617
|
-
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
3618
|
-
* callback used when the caller omits `depositCallback` for a WETH
|
|
3619
|
-
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
3620
|
-
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
3621
|
-
*/
|
|
4906
|
+
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
3622
4907
|
getDefaultSponsoredCallsCallback(asset, onApproved) {
|
|
3623
4908
|
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
3624
4909
|
if (!onApproved && cached) return cached;
|
|
3625
4910
|
const provider = this.requireConnectedProvider();
|
|
3626
4911
|
const callback = makeSponsoredCallsCallback({
|
|
4912
|
+
apiKey: this.apiKey,
|
|
4913
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
3627
4914
|
provider,
|
|
3628
4915
|
ownerAddress: this.state.walletAddress,
|
|
3629
4916
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
3630
4917
|
onApproved,
|
|
3631
|
-
tokenAddressByChain: asset
|
|
4918
|
+
tokenAddressByChain: sponsoredTokensFor(asset)
|
|
3632
4919
|
});
|
|
3633
4920
|
if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
3634
4921
|
return callback;
|
|
@@ -3637,14 +4924,14 @@ var OwneySDK = class {
|
|
|
3637
4924
|
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
3638
4925
|
* callback used when the caller omits `depositCallback` for a WETH deposit.
|
|
3639
4926
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
3640
|
-
*
|
|
4927
|
+
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
3641
4928
|
*/
|
|
3642
4929
|
getDefaultWethSponsoredCallback(onApproved) {
|
|
3643
4930
|
if (!onApproved && this.cachedWethSponsoredCallback)
|
|
3644
4931
|
return this.cachedWethSponsoredCallback;
|
|
3645
4932
|
const provider = this.requireConnectedProvider();
|
|
3646
4933
|
const owner = this.state.walletAddress;
|
|
3647
|
-
const callback =
|
|
4934
|
+
const callback = makeSponsoredTokenCallback({
|
|
3648
4935
|
apiKey: this.apiKey,
|
|
3649
4936
|
baseUrl: this.routingApiBaseUrl,
|
|
3650
4937
|
ownerAddress: owner,
|
|
@@ -3653,14 +4940,14 @@ var OwneySDK = class {
|
|
|
3653
4940
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3654
4941
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3655
4942
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3656
|
-
getPublicClient: (cid) =>
|
|
4943
|
+
getPublicClient: (cid) => createPublicClient4({
|
|
3657
4944
|
chain: VIEM_CHAIN2[cid],
|
|
3658
|
-
transport:
|
|
4945
|
+
transport: custom3(provider)
|
|
3659
4946
|
}),
|
|
3660
|
-
getWalletClient: (cid) =>
|
|
4947
|
+
getWalletClient: (cid) => createWalletClient3({
|
|
3661
4948
|
account: owner,
|
|
3662
4949
|
chain: VIEM_CHAIN2[cid],
|
|
3663
|
-
transport:
|
|
4950
|
+
transport: custom3(provider)
|
|
3664
4951
|
})
|
|
3665
4952
|
});
|
|
3666
4953
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -3734,7 +5021,14 @@ var OwneySDK = class {
|
|
|
3734
5021
|
this.routingApiBaseUrl
|
|
3735
5022
|
);
|
|
3736
5023
|
this.disabledAgents.clear();
|
|
3737
|
-
for (const {
|
|
5024
|
+
for (const {
|
|
5025
|
+
key: key2,
|
|
5026
|
+
agent_type,
|
|
5027
|
+
is_enabled,
|
|
5028
|
+
is_configured
|
|
5029
|
+
} of agentKeys) {
|
|
5030
|
+
const configured = is_configured ?? Boolean(key2);
|
|
5031
|
+
if (!configured) continue;
|
|
3738
5032
|
const agent = this.createAgent(agent_type, key2);
|
|
3739
5033
|
if (!agent) continue;
|
|
3740
5034
|
this.agents.set(agent_type, agent);
|
|
@@ -3758,8 +5052,15 @@ var OwneySDK = class {
|
|
|
3758
5052
|
}
|
|
3759
5053
|
createAgent(agentId, key2) {
|
|
3760
5054
|
if (agentId === "zyfai") {
|
|
5055
|
+
if (!key2) return null;
|
|
3761
5056
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
3762
5057
|
}
|
|
5058
|
+
if (agentId === "yieldseeker") {
|
|
5059
|
+
return new YieldseekerAgent(this.apiKey, {
|
|
5060
|
+
auth: { origin: this.yieldseekerSiweOrigin },
|
|
5061
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
5062
|
+
});
|
|
5063
|
+
}
|
|
3763
5064
|
return null;
|
|
3764
5065
|
}
|
|
3765
5066
|
/**
|
|
@@ -3804,9 +5105,10 @@ var OwneySDK = class {
|
|
|
3804
5105
|
* If provided, ALL specified agents must support the chainId or the call
|
|
3805
5106
|
* throws before activating any agent.
|
|
3806
5107
|
*/
|
|
3807
|
-
async activateAgent(chainId, agentId) {
|
|
5108
|
+
async activateAgent(chainId, agentId, asset) {
|
|
3808
5109
|
const state = this.requireState();
|
|
3809
5110
|
await this.ensureAgentsInitialized();
|
|
5111
|
+
this.assertActivationSession(state);
|
|
3810
5112
|
if (agentId !== void 0) {
|
|
3811
5113
|
if (agentId.length === 0) {
|
|
3812
5114
|
throw new OwneyError(
|
|
@@ -3840,7 +5142,7 @@ var OwneySDK = class {
|
|
|
3840
5142
|
this.activeAgents.add(id);
|
|
3841
5143
|
}
|
|
3842
5144
|
state.chainId = chainId;
|
|
3843
|
-
await this.activateAgentsInTurn(agents, state, chainId);
|
|
5145
|
+
await this.activateAgentsInTurn(agents, state, chainId, asset);
|
|
3844
5146
|
return;
|
|
3845
5147
|
}
|
|
3846
5148
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -3861,7 +5163,12 @@ var OwneySDK = class {
|
|
|
3861
5163
|
const enabledCompatible = compatible.filter(
|
|
3862
5164
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
3863
5165
|
);
|
|
3864
|
-
await this.activateAgentsInTurn(enabledCompatible, state, chainId);
|
|
5166
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
|
|
5167
|
+
}
|
|
5168
|
+
assertActivationSession(state) {
|
|
5169
|
+
if (this.state !== state) {
|
|
5170
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed. Please try again.");
|
|
5171
|
+
}
|
|
3865
5172
|
}
|
|
3866
5173
|
/**
|
|
3867
5174
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -3876,26 +5183,51 @@ var OwneySDK = class {
|
|
|
3876
5183
|
* Serializing costs no real wall-clock: the user can only approve one prompt
|
|
3877
5184
|
* at a time anyway.
|
|
3878
5185
|
*
|
|
3879
|
-
*
|
|
3880
|
-
*
|
|
3881
|
-
*
|
|
3882
|
-
* have had a chance to activate.
|
|
5186
|
+
* Stop at the first failure so a canceled sign-in does not open another
|
|
5187
|
+
* agent's wallet prompt. Report any earlier successes for diagnostics; the
|
|
5188
|
+
* app discards the session when the complete sign-in does not succeed.
|
|
3883
5189
|
*/
|
|
3884
|
-
async activateAgentsInTurn(agents, state, chainId) {
|
|
5190
|
+
async activateAgentsInTurn(agents, state, chainId, asset) {
|
|
3885
5191
|
let firstError = null;
|
|
5192
|
+
const activatedAgentIds = [];
|
|
5193
|
+
const failedAgents = [];
|
|
3886
5194
|
for (const agent of agents) {
|
|
5195
|
+
this.assertActivationSession(state);
|
|
3887
5196
|
try {
|
|
3888
|
-
await agent.activateAgent(state, chainId);
|
|
5197
|
+
await agent.activateAgent(state, chainId, asset);
|
|
5198
|
+
this.assertActivationSession(state);
|
|
3889
5199
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
5200
|
+
this.assertActivationSession(state);
|
|
5201
|
+
activatedAgentIds.push(agent.id);
|
|
3890
5202
|
} catch (error) {
|
|
5203
|
+
this.assertActivationSession(state);
|
|
5204
|
+
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.";
|
|
5205
|
+
failedAgents.push({
|
|
5206
|
+
agentId: agent.id,
|
|
5207
|
+
code: error instanceof OwneyError ? error.code : void 0,
|
|
5208
|
+
message,
|
|
5209
|
+
...error instanceof OwneyError && error.details ? { details: error.details } : {}
|
|
5210
|
+
});
|
|
3891
5211
|
if (firstError === null) {
|
|
3892
5212
|
firstError = error;
|
|
3893
5213
|
} else {
|
|
3894
5214
|
console.error(`activateAgent(${agent.id}) failed:`, error);
|
|
3895
5215
|
}
|
|
5216
|
+
break;
|
|
3896
5217
|
}
|
|
3897
5218
|
}
|
|
3898
|
-
if (firstError
|
|
5219
|
+
if (firstError === null) return;
|
|
5220
|
+
if (activatedAgentIds.length === 0) throw firstError;
|
|
5221
|
+
const failedAgentIds = failedAgents.map(({ agentId }) => agentId);
|
|
5222
|
+
const activeNames = activatedAgentIds.map(this.formatAgentName).join(", ");
|
|
5223
|
+
const failureMessages = failedAgents.map(
|
|
5224
|
+
({ agentId, message }) => `${this.formatAgentName(agentId)} activation failed: ${message}`
|
|
5225
|
+
).join(" ");
|
|
5226
|
+
throw new OwneyError(
|
|
5227
|
+
"AGENT_ACTIVATION_PARTIAL_FAILURE",
|
|
5228
|
+
`${activeNames} activated. ${failureMessages}`,
|
|
5229
|
+
{ activatedAgentIds, failedAgentIds, failures: failedAgents }
|
|
5230
|
+
);
|
|
3899
5231
|
}
|
|
3900
5232
|
/**
|
|
3901
5233
|
* Deposit into a specific agent, or distribute across all eligible agents.
|
|
@@ -3905,7 +5237,8 @@ var OwneySDK = class {
|
|
|
3905
5237
|
* @param options.asset - Asset symbol to deposit (e.g. "USDC")
|
|
3906
5238
|
* @param options.depositCallback - Callback that performs the token transfer and returns a tx hash.
|
|
3907
5239
|
* When agentId is omitted, this callback is invoked once per eligible agent with that agent's
|
|
3908
|
-
* split amount and smart wallet address
|
|
5240
|
+
* split amount and smart wallet address. Default sponsored deposits batch
|
|
5241
|
+
* all shares into one signature; custom callbacks still run once per agent.
|
|
3909
5242
|
* @param options.agentId - Optional explicit target. Otherwise split equally,
|
|
3910
5243
|
* or fund remaining agents when a recovery deposit cannot meet every minimum.
|
|
3911
5244
|
* @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
|
|
@@ -3990,6 +5323,39 @@ var OwneySDK = class {
|
|
|
3990
5323
|
}
|
|
3991
5324
|
);
|
|
3992
5325
|
}
|
|
5326
|
+
const batchTransfer = getDepositBatchTransfer(effectiveCallback);
|
|
5327
|
+
if (!depositCallback && batchTransfer) {
|
|
5328
|
+
return runAgentDepositBatch(
|
|
5329
|
+
chainId,
|
|
5330
|
+
agentAmounts.map(({ agent, amount: amount2 }) => ({
|
|
5331
|
+
id: agent.id,
|
|
5332
|
+
amount: amount2,
|
|
5333
|
+
run: (callback) => withFailureReporting(
|
|
5334
|
+
this.apiKey,
|
|
5335
|
+
agent.id,
|
|
5336
|
+
() => agent.deposit(state, chainId, amount2, asset, callback),
|
|
5337
|
+
this.routingApiBaseUrl
|
|
5338
|
+
)
|
|
5339
|
+
})),
|
|
5340
|
+
async (cid, transfers) => {
|
|
5341
|
+
try {
|
|
5342
|
+
return await batchTransfer(cid, transfers);
|
|
5343
|
+
} catch (error) {
|
|
5344
|
+
if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
|
|
5345
|
+
throw error;
|
|
5346
|
+
const requiredAmount = transfers.reduce(
|
|
5347
|
+
(sum, transfer) => sum + BigInt(transfer.amount),
|
|
5348
|
+
0n
|
|
5349
|
+
);
|
|
5350
|
+
await this.approvePermit2(
|
|
5351
|
+
asset,
|
|
5352
|
+
requiredAmount
|
|
5353
|
+
);
|
|
5354
|
+
return batchTransfer(cid, transfers);
|
|
5355
|
+
}
|
|
5356
|
+
}
|
|
5357
|
+
);
|
|
5358
|
+
}
|
|
3993
5359
|
const agentResults = {};
|
|
3994
5360
|
for (const [
|
|
3995
5361
|
index,
|
|
@@ -4029,7 +5395,7 @@ var OwneySDK = class {
|
|
|
4029
5395
|
*
|
|
4030
5396
|
* 1. Missing Permit2 allowance: when the app did not supply its own
|
|
4031
5397
|
* callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
|
|
4032
|
-
*
|
|
5398
|
+
* token deposit, this is the wallet's first Permit2 deposit for that token. We send
|
|
4033
5399
|
* the one-time (user-paid) Permit2 approval via `approvePermit2()` and
|
|
4034
5400
|
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
4035
5401
|
* per call so a wallet/agent that keeps reporting the allowance as
|
|
@@ -4066,12 +5432,15 @@ var OwneySDK = class {
|
|
|
4066
5432
|
try {
|
|
4067
5433
|
return await attempt(effectiveCallback);
|
|
4068
5434
|
} catch (error) {
|
|
4069
|
-
if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
5435
|
+
if (!approvalAttempted && appCallback === void 0 && (asset === "WETH" || asset === "USDC") && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
4070
5436
|
approvalAttempted = true;
|
|
4071
5437
|
console.warn(
|
|
4072
|
-
"[owney-sdk] First
|
|
5438
|
+
"[owney-sdk] First token deposit: sending one-time Permit2 approval..."
|
|
5439
|
+
);
|
|
5440
|
+
await this.approvePermit2(
|
|
5441
|
+
asset,
|
|
5442
|
+
BigInt(amount)
|
|
4073
5443
|
);
|
|
4074
|
-
await this.approvePermit2();
|
|
4075
5444
|
continue;
|
|
4076
5445
|
}
|
|
4077
5446
|
if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
|
|
@@ -4109,10 +5478,10 @@ var OwneySDK = class {
|
|
|
4109
5478
|
agent,
|
|
4110
5479
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
4111
5480
|
}));
|
|
4112
|
-
const
|
|
5481
|
+
const valid2 = splits.filter(
|
|
4113
5482
|
(s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
|
|
4114
5483
|
);
|
|
4115
|
-
if (
|
|
5484
|
+
if (valid2.length === agents.length) {
|
|
4116
5485
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
4117
5486
|
}
|
|
4118
5487
|
const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
|
|
@@ -4131,6 +5500,11 @@ var OwneySDK = class {
|
|
|
4131
5500
|
)
|
|
4132
5501
|
}));
|
|
4133
5502
|
}
|
|
5503
|
+
formatAgentName(agentId) {
|
|
5504
|
+
if (agentId === "zyfai") return "Zyfai";
|
|
5505
|
+
if (agentId === "yieldseeker") return "Yieldseeker";
|
|
5506
|
+
return agentId;
|
|
5507
|
+
}
|
|
4134
5508
|
async validateMinDepositAmount(agent, state, chainId, asset, amount) {
|
|
4135
5509
|
const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
|
|
4136
5510
|
const parsedAmount = BigInt(amount);
|
|
@@ -4162,12 +5536,12 @@ var OwneySDK = class {
|
|
|
4162
5536
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
4163
5537
|
);
|
|
4164
5538
|
const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
|
|
4165
|
-
const
|
|
5539
|
+
const position2 = (balance.positions ?? []).find((p) => {
|
|
4166
5540
|
const positionChain = p.chain.trim().toUpperCase();
|
|
4167
5541
|
const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
|
|
4168
5542
|
return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
|
|
4169
5543
|
});
|
|
4170
|
-
return !!token && Number(token.amount) > 0 || !!
|
|
5544
|
+
return !!token && Number(token.amount) > 0 || !!position2;
|
|
4171
5545
|
} catch (error) {
|
|
4172
5546
|
if (requireReliableRead) {
|
|
4173
5547
|
throw new OwneyError(
|
|
@@ -4213,330 +5587,6 @@ var OwneySDK = class {
|
|
|
4213
5587
|
return eligible;
|
|
4214
5588
|
}
|
|
4215
5589
|
// --- Fund operations ---
|
|
4216
|
-
// --- Swap to yield (ROUT-242) ---
|
|
4217
|
-
/** Lazily built so an app that never swaps pays nothing for it. */
|
|
4218
|
-
swapApiClient;
|
|
4219
|
-
swapApi() {
|
|
4220
|
-
this.swapApiClient ??= createSwapApi(
|
|
4221
|
-
this.routingApiBaseUrl ?? ROUTING_API_BASE_URL,
|
|
4222
|
-
this.apiKey
|
|
4223
|
-
);
|
|
4224
|
-
return this.swapApiClient;
|
|
4225
|
-
}
|
|
4226
|
-
/**
|
|
4227
|
-
* Put the wallet on `chainId`, or fail with something actionable.
|
|
4228
|
-
*
|
|
4229
|
-
* Reuses the same guard the deposit rail uses, which re-reads the chain after
|
|
4230
|
-
* switching — some wallets resolve wallet_switchEthereumChain before the
|
|
4231
|
-
* network has actually changed.
|
|
4232
|
-
*/
|
|
4233
|
-
async ensureSwapChain(chainId) {
|
|
4234
|
-
const provider = this.requireConnectedProvider();
|
|
4235
|
-
const state = this.requireState();
|
|
4236
|
-
const chain = VIEM_CHAIN2[chainId];
|
|
4237
|
-
if (!chain) {
|
|
4238
|
-
throw new OwneyError(
|
|
4239
|
-
"CHAIN_UNSUPPORTED",
|
|
4240
|
-
`Chain ${chainId} is not supported`,
|
|
4241
|
-
{ chainId }
|
|
4242
|
-
);
|
|
4243
|
-
}
|
|
4244
|
-
await ensureWalletOnChain(
|
|
4245
|
-
createPublicClient2({ chain, transport: custom(provider) }),
|
|
4246
|
-
createWalletClient({
|
|
4247
|
-
account: state.walletAddress,
|
|
4248
|
-
chain,
|
|
4249
|
-
transport: custom(provider)
|
|
4250
|
-
}),
|
|
4251
|
-
chainId
|
|
4252
|
-
);
|
|
4253
|
-
}
|
|
4254
|
-
/**
|
|
4255
|
-
* Binds the executor's abstract deps to this client's wallet.
|
|
4256
|
-
*
|
|
4257
|
-
* Kept as a builder rather than baked into the executor so the whole swap
|
|
4258
|
-
* flow stays testable without a provider — the executor never imports viem.
|
|
4259
|
-
*/
|
|
4260
|
-
buildSwapDeps(quote) {
|
|
4261
|
-
const state = this.requireState();
|
|
4262
|
-
const provider = this.requireConnectedProvider();
|
|
4263
|
-
const srcChain = VIEM_CHAIN2[quote.src.chainId];
|
|
4264
|
-
const dstChain = VIEM_CHAIN2[quote.dst.chainId];
|
|
4265
|
-
const wallet = createWalletClient({
|
|
4266
|
-
account: state.walletAddress,
|
|
4267
|
-
chain: srcChain,
|
|
4268
|
-
transport: custom(provider)
|
|
4269
|
-
});
|
|
4270
|
-
const srcPublic = createPublicClient2({
|
|
4271
|
-
chain: srcChain,
|
|
4272
|
-
transport: swapReadTransport(quote.src.chainId, this.zyfaiRpcUrls)
|
|
4273
|
-
});
|
|
4274
|
-
const dstPublic = createPublicClient2({
|
|
4275
|
-
chain: dstChain,
|
|
4276
|
-
transport: swapReadTransport(quote.dst.chainId, this.zyfaiRpcUrls)
|
|
4277
|
-
});
|
|
4278
|
-
return {
|
|
4279
|
-
api: this.swapApi(),
|
|
4280
|
-
// Native-aware, like readSourceBalance below. Native ETH is never a
|
|
4281
|
-
// DEPOSIT target, so this only ever mattered once withdrawal shipped —
|
|
4282
|
-
// and there it is the headline case. balanceOf() on the 0xEeee sentinel
|
|
4283
|
-
// reverts, which would have read as "the swap landed nothing".
|
|
4284
|
-
readTargetBalance: async () => {
|
|
4285
|
-
const dst = quote.dst.address;
|
|
4286
|
-
if (dst.toLowerCase().startsWith("0xeeee")) {
|
|
4287
|
-
return dstPublic.getBalance({ address: state.walletAddress });
|
|
4288
|
-
}
|
|
4289
|
-
return dstPublic.readContract({
|
|
4290
|
-
address: dst,
|
|
4291
|
-
abi: erc20Abi2,
|
|
4292
|
-
functionName: "balanceOf",
|
|
4293
|
-
args: [state.walletAddress]
|
|
4294
|
-
});
|
|
4295
|
-
},
|
|
4296
|
-
sendTransaction: async (tx) => {
|
|
4297
|
-
const hash = await wallet.sendTransaction({
|
|
4298
|
-
to: tx.to,
|
|
4299
|
-
data: tx.data,
|
|
4300
|
-
value: BigInt(tx.value || "0"),
|
|
4301
|
-
account: state.walletAddress,
|
|
4302
|
-
chain: srcChain
|
|
4303
|
-
});
|
|
4304
|
-
const receipt = await srcPublic.waitForTransactionReceipt({
|
|
4305
|
-
timeout: receiptTimeoutMs(quote.src.chainId),
|
|
4306
|
-
hash,
|
|
4307
|
-
confirmations: 1
|
|
4308
|
-
});
|
|
4309
|
-
if (receipt.status !== "success") {
|
|
4310
|
-
throw new OwneyError(
|
|
4311
|
-
"SWAP_REQUEST_FAILED",
|
|
4312
|
-
`Swap transaction reverted (tx ${hash})`,
|
|
4313
|
-
{ hash }
|
|
4314
|
-
);
|
|
4315
|
-
}
|
|
4316
|
-
return hash;
|
|
4317
|
-
},
|
|
4318
|
-
signTypedData: (typedData) => wallet.signTypedData({
|
|
4319
|
-
account: state.walletAddress,
|
|
4320
|
-
...typedData
|
|
4321
|
-
}),
|
|
4322
|
-
// Chain-bound like every other read here: the wallet provider's chain is
|
|
4323
|
-
// not ours to rely on mid-swap.
|
|
4324
|
-
readSourceBalance: async () => {
|
|
4325
|
-
const src = quote.src.address;
|
|
4326
|
-
if (src.toLowerCase().startsWith("0xeeee")) {
|
|
4327
|
-
return srcPublic.getBalance({ address: state.walletAddress });
|
|
4328
|
-
}
|
|
4329
|
-
return srcPublic.readContract({
|
|
4330
|
-
address: src,
|
|
4331
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
4332
|
-
functionName: "balanceOf",
|
|
4333
|
-
args: [state.walletAddress]
|
|
4334
|
-
});
|
|
4335
|
-
},
|
|
4336
|
-
readAllowance: (spender) => srcPublic.readContract({
|
|
4337
|
-
address: quote.src.address,
|
|
4338
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
4339
|
-
functionName: "allowance",
|
|
4340
|
-
args: [state.walletAddress, spender]
|
|
4341
|
-
}),
|
|
4342
|
-
approve: async (spender, amount) => {
|
|
4343
|
-
const hash = await wallet.writeContract({
|
|
4344
|
-
address: quote.src.address,
|
|
4345
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
4346
|
-
functionName: "approve",
|
|
4347
|
-
args: [spender, amount],
|
|
4348
|
-
account: state.walletAddress,
|
|
4349
|
-
chain: srcChain
|
|
4350
|
-
});
|
|
4351
|
-
await srcPublic.waitForTransactionReceipt({
|
|
4352
|
-
hash,
|
|
4353
|
-
confirmations: 1,
|
|
4354
|
-
timeout: receiptTimeoutMs(quote.src.chainId)
|
|
4355
|
-
});
|
|
4356
|
-
return hash;
|
|
4357
|
-
},
|
|
4358
|
-
ensureChain: (chainId) => this.ensureSwapChain(chainId),
|
|
4359
|
-
runner: {
|
|
4360
|
-
readyForSecrets: (h) => this.swapApi().readyForSecrets(h),
|
|
4361
|
-
submitSecret: (h, secret) => this.swapApi().submitSecret(h, secret),
|
|
4362
|
-
orderStatus: (h) => this.swapApi().orderStatus(h),
|
|
4363
|
-
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
4364
|
-
now: () => Date.now()
|
|
4365
|
-
}
|
|
4366
|
-
};
|
|
4367
|
-
}
|
|
4368
|
-
/**
|
|
4369
|
-
* Assets the user may pay with, and what each chain deposits into.
|
|
4370
|
-
*
|
|
4371
|
-
* The source list is deliberately wider than the deposit list: it includes
|
|
4372
|
-
* native ETH and USDT, which Owney never holds but users often do.
|
|
4373
|
-
*/
|
|
4374
|
-
async getSwapTokens() {
|
|
4375
|
-
return this.swapApi().listTokens();
|
|
4376
|
-
}
|
|
4377
|
-
/**
|
|
4378
|
-
* Price a swap without committing to it.
|
|
4379
|
-
*
|
|
4380
|
-
* `dstAmountMin` is the number to validate against a deposit minimum —
|
|
4381
|
-
* `dst.amount` is an estimate that a decaying auction or slippage can undercut,
|
|
4382
|
-
* and a swap landing below the floor leaves the user swapped but not
|
|
4383
|
-
* deposited.
|
|
4384
|
-
*/
|
|
4385
|
-
async getSwapQuote(params) {
|
|
4386
|
-
const state = this.requireState();
|
|
4387
|
-
return this.swapApi().quote({
|
|
4388
|
-
...params,
|
|
4389
|
-
walletAddress: state.walletAddress
|
|
4390
|
-
});
|
|
4391
|
-
}
|
|
4392
|
-
/**
|
|
4393
|
-
* Swap an asset the user holds into a deposit asset, then deposit it.
|
|
4394
|
-
*
|
|
4395
|
-
* Kept separate from `deposit()` rather than bolted on as an option: the
|
|
4396
|
-
* return shape differs, the staging callback is meaningless on the plain
|
|
4397
|
-
* path, and integrators who never swap should not have to reason about any
|
|
4398
|
-
* of it.
|
|
4399
|
-
*
|
|
4400
|
-
* The deposit runs on the MEASURED arrival, not the quote. A quote is an
|
|
4401
|
-
* estimate, so depositing the quoted figure would either strand dust or try
|
|
4402
|
-
* to move funds that never came.
|
|
4403
|
-
*
|
|
4404
|
-
* Failure modes differ in a way callers must respect. A same-chain swap is
|
|
4405
|
-
* atomic — if it fails, nothing moved. A cross-chain swap escrows the user's
|
|
4406
|
-
* funds first, so SWAP_ORDER_EXPIRED / REFUNDED / CANCELLED all mean the
|
|
4407
|
-
* money left the wallet. Only the former can honestly say "nothing has left
|
|
4408
|
-
* your wallet".
|
|
4409
|
-
*/
|
|
4410
|
-
async swapAndDeposit(options) {
|
|
4411
|
-
const state = this.requireState();
|
|
4412
|
-
const api = this.swapApi();
|
|
4413
|
-
const quote = await api.quote({
|
|
4414
|
-
from: options.from,
|
|
4415
|
-
to: options.to,
|
|
4416
|
-
walletAddress: state.walletAddress
|
|
4417
|
-
});
|
|
4418
|
-
await this.ensureSwapChain(quote.src.chainId);
|
|
4419
|
-
const swap = await executeSwap(this.buildSwapDeps(quote), {
|
|
4420
|
-
quote,
|
|
4421
|
-
walletAddress: state.walletAddress,
|
|
4422
|
-
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
4423
|
-
...options.onSwapProgress ? { onStage: options.onSwapProgress } : {}
|
|
4424
|
-
});
|
|
4425
|
-
options.onSwapProgress?.("depositing");
|
|
4426
|
-
await this.ensureSwapChain(quote.dst.chainId);
|
|
4427
|
-
const deposit = await this.deposit({
|
|
4428
|
-
amount: swap.received,
|
|
4429
|
-
asset: options.to.symbol,
|
|
4430
|
-
...options.agentId ? { agentId: options.agentId } : {}
|
|
4431
|
-
});
|
|
4432
|
-
return { swap, deposit };
|
|
4433
|
-
}
|
|
4434
|
-
/**
|
|
4435
|
-
* Withdraw from an agent and swap the proceeds into whatever the user wants
|
|
4436
|
-
* to hold, delivered to their own wallet.
|
|
4437
|
-
*
|
|
4438
|
-
* The mirror of `swapAndDeposit()`, with one structural difference that
|
|
4439
|
-
* drives the whole implementation: a deposit swap starts from funds already
|
|
4440
|
-
* sitting in the wallet, but a withdrawal has to wait for them. The agent's
|
|
4441
|
-
* provider acknowledges a withdrawal and *then* queues the on-chain transfer
|
|
4442
|
-
* to the EOA, so `withdraw()` resolving means "accepted", not "arrived".
|
|
4443
|
-
* Quoting before the tokens land would size the swap against a balance that
|
|
4444
|
-
* is not there yet.
|
|
4445
|
-
*
|
|
4446
|
-
* The swap is therefore sized from the MEASURED arrival, exactly as the
|
|
4447
|
-
* deposit path sizes its deposit from the measured swap output. On a full
|
|
4448
|
-
* withdrawal there is no other number available — "MAX" has no figure until
|
|
4449
|
-
* the agent picks one.
|
|
4450
|
-
*
|
|
4451
|
-
* **Failure here is not symmetrical with the deposit path.** A failed
|
|
4452
|
-
* deposit-swap leaves the user holding what they started with. A failed
|
|
4453
|
-
* withdrawal-swap leaves them holding the AGENT'S asset in their own wallet:
|
|
4454
|
-
* the money is out, safe, and in the wrong denomination. Both
|
|
4455
|
-
* `WITHDRAW_ARRIVAL_TIMEOUT` and `WITHDRAW_SWAP_FAILED` carry `withdrawn` for
|
|
4456
|
-
* that reason — the UI has to tell the user where their money actually is,
|
|
4457
|
-
* and must never present either as a lost withdrawal.
|
|
4458
|
-
*/
|
|
4459
|
-
async withdrawAndSwap(options) {
|
|
4460
|
-
const state = this.requireState();
|
|
4461
|
-
const activeChainId = this.requireChainId();
|
|
4462
|
-
if (options.from.chainId !== activeChainId) {
|
|
4463
|
-
throw new OwneyError(
|
|
4464
|
-
"CHAIN_MISMATCH",
|
|
4465
|
-
`Cannot withdraw from chain ${options.from.chainId} while the active chain is ${activeChainId}. Activate on that chain first.`,
|
|
4466
|
-
{ requested: options.from.chainId, active: activeChainId }
|
|
4467
|
-
);
|
|
4468
|
-
}
|
|
4469
|
-
const asset = SupportedAssets.find(
|
|
4470
|
-
(a) => a.chainId === options.from.chainId && a.symbol === options.from.symbol.toUpperCase()
|
|
4471
|
-
);
|
|
4472
|
-
if (!asset) {
|
|
4473
|
-
throw new OwneyError(
|
|
4474
|
-
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
4475
|
-
`${options.from.symbol} on chain ${options.from.chainId} is not an asset Owney holds`,
|
|
4476
|
-
{ ...options.from }
|
|
4477
|
-
);
|
|
4478
|
-
}
|
|
4479
|
-
const srcChain = VIEM_CHAIN2[options.from.chainId];
|
|
4480
|
-
const srcPublic = createPublicClient2({
|
|
4481
|
-
chain: srcChain,
|
|
4482
|
-
transport: swapReadTransport(options.from.chainId, this.zyfaiRpcUrls)
|
|
4483
|
-
});
|
|
4484
|
-
const readWalletBalance = () => srcPublic.readContract({
|
|
4485
|
-
address: asset.address,
|
|
4486
|
-
abi: erc20Abi2,
|
|
4487
|
-
functionName: "balanceOf",
|
|
4488
|
-
args: [state.walletAddress]
|
|
4489
|
-
});
|
|
4490
|
-
const baseline = await readWalletBalance();
|
|
4491
|
-
debugLog("owney-sdk", "withdrawAndSwap: baseline", {
|
|
4492
|
-
asset: `${asset.symbol}@${asset.chainId}`,
|
|
4493
|
-
baseline: baseline.toString()
|
|
4494
|
-
});
|
|
4495
|
-
options.onSwapProgress?.("withdrawing");
|
|
4496
|
-
const withdraw = await this.withdraw({
|
|
4497
|
-
asset: options.from.symbol,
|
|
4498
|
-
...options.amount === void 0 ? {} : { amount: options.amount },
|
|
4499
|
-
...options.agentId ? { agentId: options.agentId } : {}
|
|
4500
|
-
});
|
|
4501
|
-
const arrived = await awaitWithdrawalArrival({
|
|
4502
|
-
readBalance: readWalletBalance,
|
|
4503
|
-
baseline,
|
|
4504
|
-
...options.arrivalTimeoutMs === void 0 ? {} : { timeoutMs: options.arrivalTimeoutMs }
|
|
4505
|
-
});
|
|
4506
|
-
const withdrawn = arrived.toString();
|
|
4507
|
-
options.onSwapProgress?.("withdrawn");
|
|
4508
|
-
try {
|
|
4509
|
-
const quote = await this.swapApi().quote({
|
|
4510
|
-
from: { ...options.from, amount: withdrawn },
|
|
4511
|
-
to: options.to,
|
|
4512
|
-
direction: "withdraw",
|
|
4513
|
-
walletAddress: state.walletAddress
|
|
4514
|
-
});
|
|
4515
|
-
await this.ensureSwapChain(quote.src.chainId);
|
|
4516
|
-
const swap = await executeSwap(this.buildSwapDeps(quote), {
|
|
4517
|
-
quote,
|
|
4518
|
-
walletAddress: state.walletAddress,
|
|
4519
|
-
direction: "withdraw",
|
|
4520
|
-
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
4521
|
-
...options.onSwapProgress ? { onStage: options.onSwapProgress } : {}
|
|
4522
|
-
});
|
|
4523
|
-
return { withdraw, withdrawn, swap };
|
|
4524
|
-
} catch (error) {
|
|
4525
|
-
throw new OwneyError(
|
|
4526
|
-
"WITHDRAW_SWAP_FAILED",
|
|
4527
|
-
`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}.`,
|
|
4528
|
-
{
|
|
4529
|
-
withdrawn,
|
|
4530
|
-
asset: asset.symbol,
|
|
4531
|
-
chainId: asset.chainId,
|
|
4532
|
-
intendedSymbol: options.to.symbol,
|
|
4533
|
-
intendedChainId: options.to.chainId,
|
|
4534
|
-
cause: error instanceof Error ? error.message : String(error),
|
|
4535
|
-
...error instanceof OwneyError ? { causeCode: error.code } : {}
|
|
4536
|
-
}
|
|
4537
|
-
);
|
|
4538
|
-
}
|
|
4539
|
-
}
|
|
4540
5590
|
/**
|
|
4541
5591
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
4542
5592
|
* Validates that the asset is supported by the target agent(s) on the active chain.
|
|
@@ -4606,6 +5656,10 @@ var OwneySDK = class {
|
|
|
4606
5656
|
}
|
|
4607
5657
|
const requested = BigInt(amount);
|
|
4608
5658
|
const aggregated = await this.getBalances();
|
|
5659
|
+
const unavailableAgents = eligibleAgents.filter(
|
|
5660
|
+
(agent) => !(agent.id in aggregated.agentBalances)
|
|
5661
|
+
);
|
|
5662
|
+
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
4609
5663
|
const balances = projectAgentBalancesForAsset(
|
|
4610
5664
|
eligibleAgents,
|
|
4611
5665
|
aggregated.agentBalances,
|
|
@@ -4614,7 +5668,18 @@ var OwneySDK = class {
|
|
|
4614
5668
|
assetInfo.decimals
|
|
4615
5669
|
);
|
|
4616
5670
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
4617
|
-
if (totalAvailable
|
|
5671
|
+
if (totalAvailable === 0n && unavailableAgents.length > 0) {
|
|
5672
|
+
throw new OwneyError(
|
|
5673
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5674
|
+
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
5675
|
+
{
|
|
5676
|
+
asset,
|
|
5677
|
+
unavailableAgents: unavailableAgentIds,
|
|
5678
|
+
agentErrors: aggregated.agentErrors
|
|
5679
|
+
}
|
|
5680
|
+
);
|
|
5681
|
+
}
|
|
5682
|
+
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
4618
5683
|
throw new OwneyError(
|
|
4619
5684
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4620
5685
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -4625,6 +5690,7 @@ var OwneySDK = class {
|
|
|
4625
5690
|
}
|
|
4626
5691
|
);
|
|
4627
5692
|
}
|
|
5693
|
+
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
4628
5694
|
const disabledBalances = balances.filter(
|
|
4629
5695
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
4630
5696
|
);
|
|
@@ -4633,7 +5699,7 @@ var OwneySDK = class {
|
|
|
4633
5699
|
);
|
|
4634
5700
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
4635
5701
|
disabledBalances,
|
|
4636
|
-
|
|
5702
|
+
plannedTarget
|
|
4637
5703
|
);
|
|
4638
5704
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
4639
5705
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -4643,7 +5709,9 @@ var OwneySDK = class {
|
|
|
4643
5709
|
}));
|
|
4644
5710
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
4645
5711
|
const results = {};
|
|
4646
|
-
const agentErrors = {
|
|
5712
|
+
const agentErrors = {
|
|
5713
|
+
...aggregated.agentErrors ?? {}
|
|
5714
|
+
};
|
|
4647
5715
|
for (let i = 0; i < plans.length; i++) {
|
|
4648
5716
|
const p = plans[i];
|
|
4649
5717
|
if (p.planned === 0n) continue;
|
|
@@ -4690,7 +5758,8 @@ var OwneySDK = class {
|
|
|
4690
5758
|
requested: amount,
|
|
4691
5759
|
withdrawn: withdrawn.toString(),
|
|
4692
5760
|
partialResults: results,
|
|
4693
|
-
agentErrors
|
|
5761
|
+
agentErrors,
|
|
5762
|
+
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
4694
5763
|
}
|
|
4695
5764
|
);
|
|
4696
5765
|
}
|
|
@@ -4707,24 +5776,25 @@ var OwneySDK = class {
|
|
|
4707
5776
|
const chainId = this.requireChainId();
|
|
4708
5777
|
if (agentId) {
|
|
4709
5778
|
const agent = this.getAgent(agentId);
|
|
4710
|
-
const result = await this.readAgent(
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
return result;
|
|
5779
|
+
const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
5780
|
+
return {
|
|
5781
|
+
...result,
|
|
5782
|
+
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5783
|
+
};
|
|
4716
5784
|
}
|
|
4717
5785
|
let totalBalance = 0;
|
|
4718
5786
|
const results = {};
|
|
4719
5787
|
const entries = [...this.getActiveAgents().entries()];
|
|
4720
5788
|
const balanceResults = await Promise.allSettled(
|
|
4721
5789
|
entries.map(async ([id, agent]) => {
|
|
4722
|
-
const b = await this.readAgent(
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
5790
|
+
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
5791
|
+
return [
|
|
5792
|
+
id,
|
|
5793
|
+
{
|
|
5794
|
+
...b,
|
|
5795
|
+
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5796
|
+
}
|
|
5797
|
+
];
|
|
4728
5798
|
})
|
|
4729
5799
|
);
|
|
4730
5800
|
let successCount = 0;
|
|
@@ -4744,9 +5814,9 @@ var OwneySDK = class {
|
|
|
4744
5814
|
const reason = settledResult.reason;
|
|
4745
5815
|
agentFailures.push(reason);
|
|
4746
5816
|
const retryDelay = rateLimitDelay(reason);
|
|
4747
|
-
if (retryDelay !== void 0)
|
|
4748
|
-
agentRetryAt[agentId2] = Date.now() + retryDelay;
|
|
5817
|
+
if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
|
|
4749
5818
|
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
5819
|
+
console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
|
|
4750
5820
|
}
|
|
4751
5821
|
if (successCount === 0) {
|
|
4752
5822
|
throw new OwneyError(
|
|
@@ -4772,22 +5842,14 @@ var OwneySDK = class {
|
|
|
4772
5842
|
const chainId = this.requireChainId();
|
|
4773
5843
|
if (agentId) {
|
|
4774
5844
|
const agent = this.getAgent(agentId);
|
|
4775
|
-
return this.readAgent(
|
|
4776
|
-
agent,
|
|
4777
|
-
"earnings",
|
|
4778
|
-
() => agent.getEarnings(state, chainId)
|
|
4779
|
-
);
|
|
5845
|
+
return this.readAgent(agent, "earnings", () => agent.getEarnings(state, chainId));
|
|
4780
5846
|
}
|
|
4781
5847
|
let totalEarnings = 0;
|
|
4782
5848
|
const results = {};
|
|
4783
5849
|
const entries = [...this.getActiveAgents().entries()];
|
|
4784
5850
|
const earningsResults = await Promise.all(
|
|
4785
5851
|
entries.map(async ([id, agent]) => {
|
|
4786
|
-
const e = await this.readAgent(
|
|
4787
|
-
agent,
|
|
4788
|
-
"earnings",
|
|
4789
|
-
() => agent.getEarnings(state, chainId)
|
|
4790
|
-
);
|
|
5852
|
+
const e = await this.readAgent(agent, "earnings", () => agent.getEarnings(state, chainId));
|
|
4791
5853
|
return [id, e];
|
|
4792
5854
|
})
|
|
4793
5855
|
);
|
|
@@ -4932,12 +5994,11 @@ var OwneySDK = class {
|
|
|
4932
5994
|
),
|
|
4933
5995
|
Promise.all(
|
|
4934
5996
|
entries.map(async ([id, agent]) => {
|
|
4935
|
-
const b = await this.readAgent(
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
(
|
|
4939
|
-
|
|
4940
|
-
return [id, balanceForApyScope(b, chainId, tokenSymbol)];
|
|
5997
|
+
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
5998
|
+
return [
|
|
5999
|
+
id,
|
|
6000
|
+
balanceForApyScope(b, chainId, tokenSymbol)
|
|
6001
|
+
];
|
|
4941
6002
|
})
|
|
4942
6003
|
)
|
|
4943
6004
|
]);
|
|
@@ -4996,12 +6057,7 @@ var OwneySDK = class {
|
|
|
4996
6057
|
const { agentId, filters } = options ?? {};
|
|
4997
6058
|
if (agentId) {
|
|
4998
6059
|
const agent = this.getAgent(agentId);
|
|
4999
|
-
return this.readAgent(
|
|
5000
|
-
agent,
|
|
5001
|
-
"history",
|
|
5002
|
-
() => agent.getHistory(state, chainId, filters),
|
|
5003
|
-
filters
|
|
5004
|
-
);
|
|
6060
|
+
return this.readAgent(agent, "history", () => agent.getHistory(state, chainId, filters), filters);
|
|
5005
6061
|
}
|
|
5006
6062
|
const activeAgents = [...this.getActiveAgents().values()];
|
|
5007
6063
|
const cursorMap = filters?.cursor ? decodeMultiAgentCursor(filters.cursor) : {};
|
|
@@ -5058,21 +6114,13 @@ var OwneySDK = class {
|
|
|
5058
6114
|
const chainId = this.requireChainId();
|
|
5059
6115
|
if (agentId) {
|
|
5060
6116
|
const agent = this.getAgent(agentId);
|
|
5061
|
-
return this.readAgent(
|
|
5062
|
-
agent,
|
|
5063
|
-
"profile",
|
|
5064
|
-
() => agent.getUserProfile(state, chainId)
|
|
5065
|
-
);
|
|
6117
|
+
return this.readAgent(agent, "profile", () => agent.getUserProfile(state, chainId));
|
|
5066
6118
|
}
|
|
5067
6119
|
const results = {};
|
|
5068
6120
|
const entries = [...this.getActiveAgents().entries()];
|
|
5069
6121
|
const profileResults = await Promise.all(
|
|
5070
6122
|
entries.map(async ([id, agent]) => {
|
|
5071
|
-
const p = await this.readAgent(
|
|
5072
|
-
agent,
|
|
5073
|
-
"profile",
|
|
5074
|
-
() => agent.getUserProfile(state, chainId)
|
|
5075
|
-
);
|
|
6123
|
+
const p = await this.readAgent(agent, "profile", () => agent.getUserProfile(state, chainId));
|
|
5076
6124
|
return [id, p];
|
|
5077
6125
|
})
|
|
5078
6126
|
);
|
|
@@ -5111,43 +6159,44 @@ var OwneySDK = class {
|
|
|
5111
6159
|
return pending;
|
|
5112
6160
|
}
|
|
5113
6161
|
/**
|
|
5114
|
-
*
|
|
5115
|
-
* the
|
|
5116
|
-
*
|
|
5117
|
-
*
|
|
5118
|
-
*
|
|
6162
|
+
* User-paid approval of Permit2 on the selected token for the active chain.
|
|
6163
|
+
* Grants the maximum ERC20 allowance so later deposits do not require another
|
|
6164
|
+
* approval. Resolves after one confirmation so the subsequent deposit attempt
|
|
6165
|
+
* sees the new allowance.
|
|
6166
|
+
*
|
|
6167
|
+
* @param requiredAmount Raw base-unit amount the pending deposit must cover.
|
|
5119
6168
|
* @returns the approval transaction hash.
|
|
5120
6169
|
*/
|
|
5121
|
-
async approvePermit2(asset = "WETH") {
|
|
5122
|
-
void asset;
|
|
6170
|
+
async approvePermit2(asset = "WETH", requiredAmount = 0n) {
|
|
5123
6171
|
const state = this.requireState();
|
|
5124
6172
|
const chainId = this.requireChainId();
|
|
5125
|
-
this.
|
|
5126
|
-
const token =
|
|
6173
|
+
this.getEligibleAgents(chainId, asset, { excludeDisabled: true });
|
|
6174
|
+
const token = sponsoredTokensFor(asset)[chainId];
|
|
5127
6175
|
if (!token) {
|
|
5128
6176
|
throw new OwneyError(
|
|
5129
6177
|
"CHAIN_UNSUPPORTED",
|
|
5130
|
-
`No sponsored
|
|
6178
|
+
`No sponsored token on chain ${chainId}`
|
|
5131
6179
|
);
|
|
5132
6180
|
}
|
|
5133
6181
|
const provider = this.requireConnectedProvider();
|
|
5134
|
-
const
|
|
6182
|
+
const publicClient = createPublicClient4({
|
|
6183
|
+
chain: VIEM_CHAIN2[chainId],
|
|
6184
|
+
transport: custom3(provider)
|
|
6185
|
+
});
|
|
6186
|
+
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
6187
|
+
const wallet = createWalletClient3({
|
|
5135
6188
|
account: state.walletAddress,
|
|
5136
6189
|
chain: VIEM_CHAIN2[chainId],
|
|
5137
|
-
transport:
|
|
6190
|
+
transport: custom3(provider)
|
|
5138
6191
|
});
|
|
5139
6192
|
const hash = await wallet.writeContract({
|
|
5140
6193
|
address: token,
|
|
5141
6194
|
abi: ERC20_ALLOWANCE_ABI,
|
|
5142
6195
|
functionName: "approve",
|
|
5143
|
-
args: [PERMIT2_ADDRESS,
|
|
6196
|
+
args: [PERMIT2_ADDRESS, approvalAmount],
|
|
5144
6197
|
account: state.walletAddress,
|
|
5145
6198
|
chain: VIEM_CHAIN2[chainId]
|
|
5146
6199
|
});
|
|
5147
|
-
const publicClient = createPublicClient2({
|
|
5148
|
-
chain: VIEM_CHAIN2[chainId],
|
|
5149
|
-
transport: custom(provider)
|
|
5150
|
-
});
|
|
5151
6200
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
5152
6201
|
hash,
|
|
5153
6202
|
confirmations: 1
|
|
@@ -5180,23 +6229,15 @@ var OwneySDK = class {
|
|
|
5180
6229
|
const agentOptions = { tokenSymbol, chainId };
|
|
5181
6230
|
if (agentId) {
|
|
5182
6231
|
const agent = this.getAgent(agentId);
|
|
5183
|
-
return this.readAgent(
|
|
5184
|
-
agent,
|
|
5185
|
-
"agentApy",
|
|
5186
|
-
() => agent.getAgentApy(days, agentOptions),
|
|
5187
|
-
{ days, ...agentOptions }
|
|
5188
|
-
);
|
|
6232
|
+
return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
5189
6233
|
}
|
|
5190
6234
|
const results = {};
|
|
5191
|
-
const agentEntries = [...this.agents.entries()]
|
|
6235
|
+
const agentEntries = [...this.agents.entries()].filter(
|
|
6236
|
+
([id]) => !this.isAgentDisabled(id)
|
|
6237
|
+
);
|
|
5192
6238
|
const apyResults = await Promise.all(
|
|
5193
6239
|
agentEntries.map(async ([id, agent]) => {
|
|
5194
|
-
const apy = await this.readAgent(
|
|
5195
|
-
agent,
|
|
5196
|
-
"agentApy",
|
|
5197
|
-
() => agent.getAgentApy(days, agentOptions),
|
|
5198
|
-
{ days, ...agentOptions }
|
|
5199
|
-
);
|
|
6240
|
+
const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
5200
6241
|
return [id, apy];
|
|
5201
6242
|
})
|
|
5202
6243
|
);
|
|
@@ -5223,11 +6264,7 @@ var OwneySDK = class {
|
|
|
5223
6264
|
const entries = [...activeAgents.entries()];
|
|
5224
6265
|
const balanceResults = await Promise.allSettled(
|
|
5225
6266
|
entries.map(async ([id, agent]) => {
|
|
5226
|
-
const b = await this.readAgent(
|
|
5227
|
-
agent,
|
|
5228
|
-
"balances",
|
|
5229
|
-
() => agent.getBalances(state, chainId)
|
|
5230
|
-
);
|
|
6267
|
+
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
5231
6268
|
return [id, b.positions ?? []];
|
|
5232
6269
|
})
|
|
5233
6270
|
);
|
|
@@ -5272,13 +6309,13 @@ var OwneySDK = class {
|
|
|
5272
6309
|
};
|
|
5273
6310
|
|
|
5274
6311
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
5275
|
-
import { getAddress } from "viem";
|
|
5276
|
-
import { SiweMessage } from "siwe";
|
|
6312
|
+
import { getAddress as getAddress3 } from "viem";
|
|
6313
|
+
import { SiweMessage as SiweMessage2 } from "siwe";
|
|
5277
6314
|
import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
|
|
5278
6315
|
|
|
5279
6316
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
5280
|
-
var
|
|
5281
|
-
var
|
|
6317
|
+
var KEY_PREFIX4 = "owney.siwx.session";
|
|
6318
|
+
var storage4 = () => {
|
|
5282
6319
|
if (typeof window === "undefined") return null;
|
|
5283
6320
|
try {
|
|
5284
6321
|
return window.localStorage;
|
|
@@ -5286,8 +6323,8 @@ var storage3 = () => {
|
|
|
5286
6323
|
return null;
|
|
5287
6324
|
}
|
|
5288
6325
|
};
|
|
5289
|
-
var
|
|
5290
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
6326
|
+
var buildKey3 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}`;
|
|
6327
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX4}:${address.toLowerCase()}:`;
|
|
5291
6328
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
5292
6329
|
var readLegacySiwxSession = (store, address) => {
|
|
5293
6330
|
if (!store) return null;
|
|
@@ -5318,17 +6355,17 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
5318
6355
|
};
|
|
5319
6356
|
var readSiwxSession = (address, chainId) => {
|
|
5320
6357
|
if (typeof window === "undefined") return null;
|
|
5321
|
-
const key2 =
|
|
5322
|
-
const store =
|
|
5323
|
-
let
|
|
6358
|
+
const key2 = buildKey3(address);
|
|
6359
|
+
const store = storage4();
|
|
6360
|
+
let raw2 = null;
|
|
5324
6361
|
try {
|
|
5325
|
-
|
|
6362
|
+
raw2 = store?.getItem(key2) ?? null;
|
|
5326
6363
|
} catch {
|
|
5327
|
-
|
|
6364
|
+
raw2 = null;
|
|
5328
6365
|
}
|
|
5329
|
-
if (
|
|
6366
|
+
if (raw2) {
|
|
5330
6367
|
try {
|
|
5331
|
-
return JSON.parse(
|
|
6368
|
+
return JSON.parse(raw2);
|
|
5332
6369
|
} catch {
|
|
5333
6370
|
memorySiwxSessions.delete(key2);
|
|
5334
6371
|
try {
|
|
@@ -5347,18 +6384,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
5347
6384
|
};
|
|
5348
6385
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
5349
6386
|
if (typeof window === "undefined") return;
|
|
5350
|
-
const key2 =
|
|
6387
|
+
const key2 = buildKey3(address);
|
|
5351
6388
|
memorySiwxSessions.set(key2, session);
|
|
5352
|
-
const store =
|
|
6389
|
+
const store = storage4();
|
|
5353
6390
|
try {
|
|
5354
6391
|
store?.setItem(key2, JSON.stringify(session));
|
|
5355
6392
|
} catch {
|
|
5356
6393
|
}
|
|
5357
6394
|
};
|
|
5358
6395
|
var clearSiwxSession = (address, _chainId) => {
|
|
5359
|
-
const key2 =
|
|
6396
|
+
const key2 = buildKey3(address);
|
|
5360
6397
|
memorySiwxSessions.delete(key2);
|
|
5361
|
-
const store =
|
|
6398
|
+
const store = storage4();
|
|
5362
6399
|
try {
|
|
5363
6400
|
store?.removeItem(key2);
|
|
5364
6401
|
} catch {
|
|
@@ -5398,8 +6435,8 @@ function buildSIWXConfig(deps) {
|
|
|
5398
6435
|
statement: STATEMENT,
|
|
5399
6436
|
issuedAt,
|
|
5400
6437
|
toString() {
|
|
5401
|
-
return new
|
|
5402
|
-
address:
|
|
6438
|
+
return new SiweMessage2({
|
|
6439
|
+
address: getAddress3(accountAddress),
|
|
5403
6440
|
chainId: numericChainId(chainId),
|
|
5404
6441
|
domain,
|
|
5405
6442
|
uri,
|
|
@@ -5441,7 +6478,7 @@ function buildSIWXConfig(deps) {
|
|
|
5441
6478
|
const persistSession = async (session) => {
|
|
5442
6479
|
const address = session.data.accountAddress;
|
|
5443
6480
|
const id = numericChainId(session.data.chainId);
|
|
5444
|
-
const message = new
|
|
6481
|
+
const message = new SiweMessage2(session.message);
|
|
5445
6482
|
const login = await post("/auth/login", {
|
|
5446
6483
|
message,
|
|
5447
6484
|
signature: session.signature,
|
|
@@ -5477,9 +6514,9 @@ function buildSIWXConfig(deps) {
|
|
|
5477
6514
|
}
|
|
5478
6515
|
function createOwneySIWX(config) {
|
|
5479
6516
|
const zyfai = new ZyfaiSDK2({ apiKey: config.apiKey });
|
|
5480
|
-
const
|
|
6517
|
+
const http2 = zyfai.httpClient;
|
|
5481
6518
|
return buildSIWXConfig({
|
|
5482
|
-
post: (url, data) =>
|
|
6519
|
+
post: (url, data) => http2.post(url, data),
|
|
5483
6520
|
referralSource: config.referralSource
|
|
5484
6521
|
});
|
|
5485
6522
|
}
|
|
@@ -5490,7 +6527,7 @@ export {
|
|
|
5490
6527
|
NotConnectedError,
|
|
5491
6528
|
OwneyError,
|
|
5492
6529
|
OwneySDK,
|
|
6530
|
+
YieldseekerAgent,
|
|
5493
6531
|
createOwneySIWX,
|
|
5494
|
-
listOrders as listPendingSwaps,
|
|
5495
6532
|
setOwneyDebug
|
|
5496
6533
|
};
|