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