@owney/sdk 0.7.21-beta.3 → 0.7.22-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 +0 -2
- package/dist/chunk-3HL52KM7.js +283 -0
- package/dist/chunk-AURO3C3R.js +58 -0
- package/dist/chunk-XBDJWZXY.js +141 -0
- package/dist/index.cjs +3702 -4259
- package/dist/index.d.cts +28 -128
- package/dist/index.d.ts +28 -128
- package/dist/index.js +694 -2574
- package/dist/surfliquid.agent-AE7RZ66F.js +638 -0
- package/dist/surfliquid.smart-account-C535356J.js +135 -0
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -1,51 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
constructor(agentId, available) {
|
|
17
|
-
super(
|
|
18
|
-
"AGENT_NOT_FOUND",
|
|
19
|
-
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
20
|
-
{ agentId, available },
|
|
21
|
-
agentId
|
|
22
|
-
);
|
|
23
|
-
this.name = "AgentNotFoundError";
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
var NotConnectedError = class extends OwneyError {
|
|
27
|
-
constructor() {
|
|
28
|
-
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
29
|
-
this.name = "NotConnectedError";
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
var AgentChainIncompatibleError = class extends OwneyError {
|
|
33
|
-
incompatibleAgents;
|
|
34
|
-
connectedChainId;
|
|
35
|
-
constructor(incompatibleAgents, connectedChainId) {
|
|
36
|
-
const details = incompatibleAgents.map(
|
|
37
|
-
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
38
|
-
).join("; ");
|
|
39
|
-
super(
|
|
40
|
-
"AGENT_CHAIN_INCOMPATIBLE",
|
|
41
|
-
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
42
|
-
{ incompatibleAgents, connectedChainId }
|
|
43
|
-
);
|
|
44
|
-
this.name = "AgentChainIncompatibleError";
|
|
45
|
-
this.incompatibleAgents = incompatibleAgents;
|
|
46
|
-
this.connectedChainId = connectedChainId;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
AgentChainIncompatibleError,
|
|
3
|
+
AgentNotFoundError,
|
|
4
|
+
NotConnectedError,
|
|
5
|
+
OwneyError,
|
|
6
|
+
debugLog,
|
|
7
|
+
fetchAgentKeys,
|
|
8
|
+
fetchOrgAgentConfig,
|
|
9
|
+
setOwneyDebug
|
|
10
|
+
} from "./chunk-XBDJWZXY.js";
|
|
11
|
+
import {
|
|
12
|
+
buildTransferWithAuthorizationTypedData,
|
|
13
|
+
randomAuthNonce,
|
|
14
|
+
readTokenMeta
|
|
15
|
+
} from "./chunk-AURO3C3R.js";
|
|
49
16
|
|
|
50
17
|
// src/lib/rate-limit.ts
|
|
51
18
|
function rateLimitDelay(error, now = Date.now()) {
|
|
@@ -212,23 +179,6 @@ var SupportedAssets = [
|
|
|
212
179
|
}
|
|
213
180
|
];
|
|
214
181
|
|
|
215
|
-
// src/lib/debug.ts
|
|
216
|
-
var configuredDebug = false;
|
|
217
|
-
function setOwneyDebug(enabled) {
|
|
218
|
-
configuredDebug = enabled;
|
|
219
|
-
}
|
|
220
|
-
function isOwneyDebug() {
|
|
221
|
-
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
222
|
-
}
|
|
223
|
-
function debugLog(scope, message, data) {
|
|
224
|
-
if (!isOwneyDebug()) return;
|
|
225
|
-
if (data === void 0) {
|
|
226
|
-
console.log(`[${scope}] ${message}`);
|
|
227
|
-
} else {
|
|
228
|
-
console.log(`[${scope}] ${message}`, data);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
182
|
// src/lib/utils.ts
|
|
233
183
|
var isValidChainId = (chainId) => {
|
|
234
184
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
|
|
@@ -240,6 +190,12 @@ var isValidChainId = (chainId) => {
|
|
|
240
190
|
}
|
|
241
191
|
return chainId;
|
|
242
192
|
};
|
|
193
|
+
function isSameAsset(tokenSymbol, asset) {
|
|
194
|
+
const token = tokenSymbol.toLowerCase();
|
|
195
|
+
const target = asset.toLowerCase();
|
|
196
|
+
if (token === target) return true;
|
|
197
|
+
return target === "weth" && token === "eth" || target === "eth" && token === "weth";
|
|
198
|
+
}
|
|
243
199
|
function hexToDecimal(hex, decimals = 6) {
|
|
244
200
|
const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
|
|
245
201
|
const parsed = BigInt(normalized);
|
|
@@ -277,18 +233,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
277
233
|
return explicit;
|
|
278
234
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
279
235
|
}
|
|
280
|
-
function mapDeposit(
|
|
236
|
+
function mapDeposit(raw) {
|
|
281
237
|
return {
|
|
282
|
-
txHash:
|
|
283
|
-
smartWallet:
|
|
284
|
-
amount:
|
|
238
|
+
txHash: raw.txHash,
|
|
239
|
+
smartWallet: raw.smartWallet,
|
|
240
|
+
amount: raw.amount
|
|
285
241
|
};
|
|
286
242
|
}
|
|
287
|
-
function mapWithdraw(
|
|
243
|
+
function mapWithdraw(raw) {
|
|
288
244
|
return {
|
|
289
|
-
txHash:
|
|
290
|
-
type:
|
|
291
|
-
amount:
|
|
245
|
+
txHash: raw.txHash,
|
|
246
|
+
type: raw.type,
|
|
247
|
+
amount: raw.amount
|
|
292
248
|
};
|
|
293
249
|
}
|
|
294
250
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -307,10 +263,10 @@ function resolveChainId(chain) {
|
|
|
307
263
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
308
264
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
309
265
|
}
|
|
310
|
-
function mapPendingAllocations(
|
|
311
|
-
if (!Array.isArray(
|
|
266
|
+
function mapPendingAllocations(raw) {
|
|
267
|
+
if (!Array.isArray(raw)) return void 0;
|
|
312
268
|
const pending = [];
|
|
313
|
-
for (const entry of
|
|
269
|
+
for (const entry of raw) {
|
|
314
270
|
if (typeof entry !== "object" || entry === null) continue;
|
|
315
271
|
const e = entry;
|
|
316
272
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -333,8 +289,8 @@ function mapPendingAllocations(raw2) {
|
|
|
333
289
|
}
|
|
334
290
|
return pending.length > 0 ? pending : void 0;
|
|
335
291
|
}
|
|
336
|
-
function mapBalances(
|
|
337
|
-
const portfolio =
|
|
292
|
+
function mapBalances(raw, _chainId, smartWallet) {
|
|
293
|
+
const portfolio = raw.portfolio;
|
|
338
294
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
339
295
|
let totalBalance = 0;
|
|
340
296
|
const tokens = [];
|
|
@@ -403,8 +359,8 @@ function sumTokenValues(tokens) {
|
|
|
403
359
|
function sumTokenEarnings(tokens) {
|
|
404
360
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
405
361
|
}
|
|
406
|
-
function mapEarnings(
|
|
407
|
-
const totalEarningsByChain =
|
|
362
|
+
function mapEarnings(raw, smartWallet) {
|
|
363
|
+
const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
|
|
408
364
|
const tokens = [];
|
|
409
365
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
410
366
|
totalEarningsByChain
|
|
@@ -423,15 +379,15 @@ function mapEarnings(raw2, smartWallet) {
|
|
|
423
379
|
return {
|
|
424
380
|
smartWallet,
|
|
425
381
|
lifetimeEarnings: sumTokenEarnings(
|
|
426
|
-
|
|
382
|
+
raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
|
|
427
383
|
),
|
|
428
384
|
tokens
|
|
429
385
|
};
|
|
430
386
|
}
|
|
431
|
-
function mapWeightedApyByChain(
|
|
432
|
-
if (!
|
|
387
|
+
function mapWeightedApyByChain(raw) {
|
|
388
|
+
if (!raw) return void 0;
|
|
433
389
|
const out = {};
|
|
434
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
390
|
+
for (const [chainKey, tokenApy] of Object.entries(raw)) {
|
|
435
391
|
const chainId = Number(chainKey);
|
|
436
392
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
437
393
|
const perAsset = {};
|
|
@@ -471,8 +427,8 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
471
427
|
}
|
|
472
428
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
473
429
|
}
|
|
474
|
-
function mapApyHistory(
|
|
475
|
-
const history = Object.entries(
|
|
430
|
+
function mapApyHistory(raw, chainId, tokenSymbol) {
|
|
431
|
+
const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
|
|
476
432
|
date,
|
|
477
433
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol),
|
|
478
434
|
// Provider position balances are treated as decimal amounts of the
|
|
@@ -488,9 +444,9 @@ function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
|
488
444
|
} : {}
|
|
489
445
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
490
446
|
return {
|
|
491
|
-
walletAddress:
|
|
492
|
-
weightedApyAfterFee:
|
|
493
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
447
|
+
walletAddress: raw.walletAddress,
|
|
448
|
+
weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
|
|
449
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
|
|
494
450
|
history
|
|
495
451
|
};
|
|
496
452
|
}
|
|
@@ -586,23 +542,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
586
542
|
};
|
|
587
543
|
});
|
|
588
544
|
}
|
|
589
|
-
function mapUserProfile(
|
|
545
|
+
function mapUserProfile(raw, userAddress) {
|
|
590
546
|
return {
|
|
591
547
|
address: userAddress,
|
|
592
|
-
smartWallet:
|
|
593
|
-
chains:
|
|
594
|
-
strategy:
|
|
595
|
-
hasActiveSessionKey:
|
|
596
|
-
protocols:
|
|
597
|
-
splitting:
|
|
598
|
-
minSplits:
|
|
548
|
+
smartWallet: raw.smartWallet || "",
|
|
549
|
+
chains: raw.chains || [],
|
|
550
|
+
strategy: raw.strategy,
|
|
551
|
+
hasActiveSessionKey: raw.hasActiveSessionKey || false,
|
|
552
|
+
protocols: raw.protocols || [],
|
|
553
|
+
splitting: raw.splitting,
|
|
554
|
+
minSplits: raw.minSplits
|
|
599
555
|
};
|
|
600
556
|
}
|
|
601
|
-
function mapApyByStrategy(
|
|
557
|
+
function mapApyByStrategy(raw) {
|
|
602
558
|
const apyPerAsset = {};
|
|
603
559
|
let apySum = 0;
|
|
604
560
|
let apyCount = 0;
|
|
605
|
-
for (const entry of
|
|
561
|
+
for (const entry of raw.data) {
|
|
606
562
|
const supported = SupportedAssets.find(
|
|
607
563
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
608
564
|
);
|
|
@@ -773,15 +729,15 @@ var readSession = (address, _chainId) => {
|
|
|
773
729
|
if (typeof window === "undefined") return null;
|
|
774
730
|
const key2 = buildKey(address);
|
|
775
731
|
const store = storage();
|
|
776
|
-
let
|
|
732
|
+
let raw = null;
|
|
777
733
|
try {
|
|
778
|
-
|
|
734
|
+
raw = store?.getItem(key2) ?? null;
|
|
779
735
|
} catch {
|
|
780
|
-
|
|
736
|
+
raw = null;
|
|
781
737
|
}
|
|
782
|
-
if (
|
|
738
|
+
if (raw) {
|
|
783
739
|
try {
|
|
784
|
-
const parsed = JSON.parse(
|
|
740
|
+
const parsed = JSON.parse(raw);
|
|
785
741
|
if (isFreshSession(parsed)) return parsed;
|
|
786
742
|
} catch {
|
|
787
743
|
}
|
|
@@ -949,8 +905,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
949
905
|
}
|
|
950
906
|
return result;
|
|
951
907
|
}
|
|
952
|
-
function flattenAvailablePools(
|
|
953
|
-
const byChain =
|
|
908
|
+
function flattenAvailablePools(raw) {
|
|
909
|
+
const byChain = raw ?? {};
|
|
954
910
|
const names = [];
|
|
955
911
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
956
912
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -1453,8 +1409,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1453
1409
|
const poolResults = await Promise.all(
|
|
1454
1410
|
universe.map(async (protocol) => {
|
|
1455
1411
|
try {
|
|
1456
|
-
const
|
|
1457
|
-
return [protocol.id, flattenAvailablePools(
|
|
1412
|
+
const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1413
|
+
return [protocol.id, flattenAvailablePools(raw)];
|
|
1458
1414
|
} catch (error) {
|
|
1459
1415
|
console.warn(
|
|
1460
1416
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1493,6 +1449,37 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1493
1449
|
);
|
|
1494
1450
|
}
|
|
1495
1451
|
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Records the deposit with Zyfai, retrying transient failures.
|
|
1454
|
+
*
|
|
1455
|
+
* This runs AFTER the transfer has already landed on-chain, so it must never
|
|
1456
|
+
* fail the deposit — the funds moved. But it is also the ONLY source of the
|
|
1457
|
+
* "Top up wallet" entry the history is built from: Zyfai auto-deploys the
|
|
1458
|
+
* balance it detects either way, so when this call is lost the user's deposit
|
|
1459
|
+
* never appears in Activity (an earlier withdrawal stays the newest row) and
|
|
1460
|
+
* nothing ever backfills it. Retry, then log loudly enough to be recoverable.
|
|
1461
|
+
*/
|
|
1462
|
+
async logDepositWithRetry(chainId, txHash, amount, tokenAddress) {
|
|
1463
|
+
const ATTEMPTS = 3;
|
|
1464
|
+
const RETRY_DELAY_MS = 1e3;
|
|
1465
|
+
for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
|
|
1466
|
+
try {
|
|
1467
|
+
await (tokenAddress ? this.sdk.logDeposit(chainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(chainId, txHash, amount));
|
|
1468
|
+
return;
|
|
1469
|
+
} catch (logError) {
|
|
1470
|
+
if (attempt === ATTEMPTS) {
|
|
1471
|
+
console.error(
|
|
1472
|
+
"[owney-sdk] Deposit landed on-chain but logDeposit failed \u2014 it will be missing from Zyfai history:",
|
|
1473
|
+
{ txHash, chainId, amount, tokenAddress, error: logError }
|
|
1474
|
+
);
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
await new Promise(
|
|
1478
|
+
(resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt)
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1496
1483
|
/**
|
|
1497
1484
|
* True when `smartWallet` is the backend-managed pool wallet Zyfai assigned
|
|
1498
1485
|
* to this EOA — the "new wallet" kind that is predeployed on the chains it
|
|
@@ -1520,14 +1507,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1520
1507
|
async readWalletState(ownerAddress) {
|
|
1521
1508
|
try {
|
|
1522
1509
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1523
|
-
const
|
|
1510
|
+
const raw = portfolio;
|
|
1524
1511
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1525
|
-
predeployed:
|
|
1526
|
-
hasActiveSessionKey:
|
|
1512
|
+
predeployed: raw?.predeployed,
|
|
1513
|
+
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1527
1514
|
});
|
|
1528
1515
|
return {
|
|
1529
|
-
predeployed:
|
|
1530
|
-
hasActiveSessionKey:
|
|
1516
|
+
predeployed: raw?.predeployed,
|
|
1517
|
+
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1531
1518
|
};
|
|
1532
1519
|
} catch (error) {
|
|
1533
1520
|
console.warn(
|
|
@@ -1802,25 +1789,23 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1802
1789
|
);
|
|
1803
1790
|
const txHash = await depositCallback(smartWallet, validChainId, amount);
|
|
1804
1791
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
);
|
|
1812
|
-
}
|
|
1792
|
+
await this.logDepositWithRetry(
|
|
1793
|
+
validChainId,
|
|
1794
|
+
txHash,
|
|
1795
|
+
amount,
|
|
1796
|
+
tokenAddress
|
|
1797
|
+
);
|
|
1813
1798
|
return { txHash, smartWallet, amount };
|
|
1814
1799
|
}
|
|
1815
1800
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1816
|
-
const
|
|
1801
|
+
const raw = await this.sdk.depositFunds(
|
|
1817
1802
|
this.getAddress(),
|
|
1818
1803
|
validChainId,
|
|
1819
1804
|
amount,
|
|
1820
1805
|
asset,
|
|
1821
1806
|
"aggressive"
|
|
1822
1807
|
);
|
|
1823
|
-
return mapDeposit(
|
|
1808
|
+
return mapDeposit(raw);
|
|
1824
1809
|
} catch (error) {
|
|
1825
1810
|
throw error;
|
|
1826
1811
|
}
|
|
@@ -1829,27 +1814,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1829
1814
|
async withdraw(state, chainId, token, amount) {
|
|
1830
1815
|
const validChainId = isValidChainId(chainId);
|
|
1831
1816
|
await this.ensureConnected(state, validChainId);
|
|
1832
|
-
const
|
|
1817
|
+
const raw = await this.sdk.withdrawFunds(
|
|
1833
1818
|
this.getAddress(),
|
|
1834
1819
|
validChainId,
|
|
1835
1820
|
amount,
|
|
1836
1821
|
token
|
|
1837
1822
|
);
|
|
1838
|
-
if (!
|
|
1823
|
+
if (!raw.success) {
|
|
1839
1824
|
throw new OwneyError(
|
|
1840
1825
|
"WITHDRAW_FAILED",
|
|
1841
|
-
|
|
1842
|
-
{ chainId: validChainId, token, amount, response:
|
|
1826
|
+
raw.message || "Zyfai withdraw failed.",
|
|
1827
|
+
{ chainId: validChainId, token, amount, response: raw },
|
|
1843
1828
|
this.id
|
|
1844
1829
|
);
|
|
1845
1830
|
}
|
|
1846
|
-
return mapWithdraw(
|
|
1831
|
+
return mapWithdraw(raw);
|
|
1847
1832
|
}
|
|
1848
1833
|
// --- IAgent: Portfolio reads ---
|
|
1849
1834
|
async getBalances(state, chainId) {
|
|
1850
1835
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1851
|
-
const
|
|
1852
|
-
return mapBalances(
|
|
1836
|
+
const raw = await this.sdk.getPortfolio(this.getAddress());
|
|
1837
|
+
return mapBalances(raw, validChainId, smartWallet);
|
|
1853
1838
|
}
|
|
1854
1839
|
earningsKey(state, chainId, smartWallet) {
|
|
1855
1840
|
return JSON.stringify([
|
|
@@ -1862,11 +1847,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1862
1847
|
const existing = this.earningsReads.get(key2);
|
|
1863
1848
|
if (existing) return existing;
|
|
1864
1849
|
const generation = this.earningsGeneration;
|
|
1865
|
-
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((
|
|
1850
|
+
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw) => {
|
|
1866
1851
|
if (generation === this.earningsGeneration) {
|
|
1867
|
-
this.earningsSnapshot = { key: key2, raw
|
|
1852
|
+
this.earningsSnapshot = { key: key2, raw, at: Date.now() };
|
|
1868
1853
|
}
|
|
1869
|
-
return
|
|
1854
|
+
return raw;
|
|
1870
1855
|
}).finally(() => {
|
|
1871
1856
|
if (this.earningsReads.get(key2) === pending)
|
|
1872
1857
|
this.earningsReads.delete(key2);
|
|
@@ -1876,11 +1861,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1876
1861
|
}
|
|
1877
1862
|
async getEarnings(state, chainId) {
|
|
1878
1863
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1879
|
-
const
|
|
1864
|
+
const raw = await this.readEarnings(
|
|
1880
1865
|
this.earningsKey(state, chainId, smartWallet),
|
|
1881
1866
|
smartWallet
|
|
1882
1867
|
);
|
|
1883
|
-
return mapEarnings(
|
|
1868
|
+
return mapEarnings(raw, smartWallet);
|
|
1884
1869
|
}
|
|
1885
1870
|
async refreshEarnings(state, chainId) {
|
|
1886
1871
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -1907,8 +1892,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1907
1892
|
}
|
|
1908
1893
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1909
1894
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1910
|
-
const
|
|
1911
|
-
return mapApyHistory(
|
|
1895
|
+
const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
1896
|
+
return mapApyHistory(raw, chainId, tokenSymbol);
|
|
1912
1897
|
}
|
|
1913
1898
|
/**
|
|
1914
1899
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -1943,7 +1928,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1943
1928
|
const matched = [];
|
|
1944
1929
|
let backendExhausted = false;
|
|
1945
1930
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
1946
|
-
const
|
|
1931
|
+
const raw = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
1947
1932
|
limit: backendPageSize,
|
|
1948
1933
|
offset,
|
|
1949
1934
|
fromDate: options?.fromDate,
|
|
@@ -1954,13 +1939,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1954
1939
|
// asset's rows and handing back a page that filters to nothing.
|
|
1955
1940
|
assetType
|
|
1956
1941
|
});
|
|
1957
|
-
|
|
1942
|
+
raw.data.forEach((entry, idx) => {
|
|
1958
1943
|
if (entry.chainId === validChainId) {
|
|
1959
1944
|
matched.push({ entry, rawIdx: offset + idx });
|
|
1960
1945
|
}
|
|
1961
1946
|
});
|
|
1962
|
-
offset +=
|
|
1963
|
-
if (
|
|
1947
|
+
offset += raw.data.length;
|
|
1948
|
+
if (raw.data.length < backendPageSize) {
|
|
1964
1949
|
backendExhausted = true;
|
|
1965
1950
|
break;
|
|
1966
1951
|
}
|
|
@@ -1982,18 +1967,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1982
1967
|
}
|
|
1983
1968
|
async getUserProfile(state, chainId) {
|
|
1984
1969
|
await this.connectAuth(state, chainId);
|
|
1985
|
-
const
|
|
1970
|
+
const raw = await this.sdk.getUserDetails();
|
|
1986
1971
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
1987
1972
|
asset: "USDC (default \u2014 no asset passed)",
|
|
1988
|
-
splitting:
|
|
1989
|
-
minSplits:
|
|
1990
|
-
strategy:
|
|
1991
|
-
chains:
|
|
1992
|
-
protocolCount:
|
|
1993
|
-
hasActiveSessionKey:
|
|
1994
|
-
smartWallet:
|
|
1973
|
+
splitting: raw.splitting,
|
|
1974
|
+
minSplits: raw.minSplits,
|
|
1975
|
+
strategy: raw.strategy,
|
|
1976
|
+
chains: raw.chains,
|
|
1977
|
+
protocolCount: raw.protocols?.length,
|
|
1978
|
+
hasActiveSessionKey: raw.hasActiveSessionKey,
|
|
1979
|
+
smartWallet: raw.smartWallet
|
|
1995
1980
|
});
|
|
1996
|
-
return mapUserProfile(
|
|
1981
|
+
return mapUserProfile(raw, this.connectedAddress);
|
|
1997
1982
|
}
|
|
1998
1983
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
1999
1984
|
await this.connectAuth(state, chainId);
|
|
@@ -2012,210 +1997,282 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2012
1997
|
}
|
|
2013
1998
|
// --- IAgent: Discovery (no wallet required) ---
|
|
2014
1999
|
async getAgentApy(days, options) {
|
|
2015
|
-
const
|
|
2000
|
+
const raw = await this.sdk.getAPYPerStrategy(
|
|
2016
2001
|
false,
|
|
2017
2002
|
DayFilterMapping[days],
|
|
2018
2003
|
"aggressive",
|
|
2019
2004
|
options?.chainId,
|
|
2020
2005
|
options?.tokenSymbol
|
|
2021
2006
|
);
|
|
2022
|
-
return mapApyByStrategy(
|
|
2007
|
+
return mapApyByStrategy(raw);
|
|
2023
2008
|
}
|
|
2024
2009
|
};
|
|
2025
2010
|
|
|
2026
|
-
// src/
|
|
2027
|
-
import {
|
|
2028
|
-
createPublicClient as createPublicClient3,
|
|
2029
|
-
createWalletClient as createWalletClient2,
|
|
2030
|
-
custom as custom2,
|
|
2031
|
-
encodeFunctionData,
|
|
2032
|
-
erc20Abi,
|
|
2033
|
-
getAddress as getAddress2,
|
|
2034
|
-
isAddress as isAddress2
|
|
2035
|
-
} from "viem";
|
|
2036
|
-
import { base as base3 } from "viem/chains";
|
|
2037
|
-
|
|
2038
|
-
// src/lib/chain-guard.ts
|
|
2039
|
-
var CHAIN_NAMES = {
|
|
2040
|
-
1: "Ethereum",
|
|
2041
|
-
8453: "Base",
|
|
2042
|
-
42161: "Arbitrum"
|
|
2043
|
-
};
|
|
2044
|
-
function chainName(chainId) {
|
|
2045
|
-
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2046
|
-
}
|
|
2047
|
-
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2048
|
-
const actual = await pub.getChainId();
|
|
2049
|
-
if (actual === expected) return;
|
|
2050
|
-
try {
|
|
2051
|
-
await wallet.switchChain({ id: expected });
|
|
2052
|
-
} catch (error) {
|
|
2053
|
-
throw new OwneyError(
|
|
2054
|
-
"CHAIN_MISMATCH",
|
|
2055
|
-
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2056
|
-
{
|
|
2057
|
-
expectedChainId: expected,
|
|
2058
|
-
actualChainId: actual,
|
|
2059
|
-
cause: error instanceof Error ? error.message : String(error)
|
|
2060
|
-
}
|
|
2061
|
-
);
|
|
2062
|
-
}
|
|
2063
|
-
const after = await pub.getChainId();
|
|
2064
|
-
if (after !== expected) {
|
|
2065
|
-
throw new OwneyError(
|
|
2066
|
-
"CHAIN_MISMATCH",
|
|
2067
|
-
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2068
|
-
{ expectedChainId: expected, actualChainId: after }
|
|
2069
|
-
);
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2072
|
-
|
|
2073
|
-
// src/lib/transfer-auth.ts
|
|
2074
|
-
import { bytesToHex } from "viem";
|
|
2075
|
-
var ERC20_META_ABI = [
|
|
2076
|
-
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2077
|
-
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
2078
|
-
];
|
|
2079
|
-
function buildTransferWithAuthorizationTypedData(input) {
|
|
2080
|
-
return {
|
|
2081
|
-
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
2082
|
-
types: {
|
|
2083
|
-
TransferWithAuthorization: [
|
|
2084
|
-
{ name: "from", type: "address" },
|
|
2085
|
-
{ name: "to", type: "address" },
|
|
2086
|
-
{ name: "value", type: "uint256" },
|
|
2087
|
-
{ name: "validAfter", type: "uint256" },
|
|
2088
|
-
{ name: "validBefore", type: "uint256" },
|
|
2089
|
-
{ name: "nonce", type: "bytes32" }
|
|
2090
|
-
]
|
|
2091
|
-
},
|
|
2092
|
-
primaryType: "TransferWithAuthorization",
|
|
2093
|
-
message: input.message
|
|
2094
|
-
};
|
|
2095
|
-
}
|
|
2096
|
-
async function readTokenMeta(publicClient, token) {
|
|
2097
|
-
const [tokenName, tokenVersion] = await Promise.all([
|
|
2098
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
2099
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
2100
|
-
]);
|
|
2101
|
-
return { tokenName, tokenVersion };
|
|
2102
|
-
}
|
|
2103
|
-
function randomAuthNonce() {
|
|
2104
|
-
const bytes = new Uint8Array(32);
|
|
2105
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2106
|
-
return bytesToHex(bytes);
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2109
|
-
// src/lib/sponsor-client.ts
|
|
2011
|
+
// src/lib/health-report.ts
|
|
2110
2012
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2111
|
-
async function
|
|
2112
|
-
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2113
|
-
let res;
|
|
2013
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL) {
|
|
2114
2014
|
try {
|
|
2115
|
-
|
|
2015
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
2116
2016
|
method: "POST",
|
|
2117
2017
|
headers: {
|
|
2118
|
-
"
|
|
2119
|
-
"x-owney-api-key":
|
|
2120
|
-
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
2018
|
+
"Content-Type": "application/json",
|
|
2019
|
+
"x-owney-api-key": apiKey
|
|
2121
2020
|
},
|
|
2122
|
-
body: JSON.stringify(
|
|
2021
|
+
body: JSON.stringify({
|
|
2022
|
+
agent_type: agentType,
|
|
2023
|
+
error_code: errorCode,
|
|
2024
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2025
|
+
})
|
|
2123
2026
|
});
|
|
2124
|
-
} catch (
|
|
2125
|
-
|
|
2126
|
-
"
|
|
2127
|
-
|
|
2128
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
2027
|
+
} catch (err) {
|
|
2028
|
+
console.warn(
|
|
2029
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
2030
|
+
err instanceof Error ? err.message : err
|
|
2129
2031
|
);
|
|
2130
2032
|
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2033
|
+
}
|
|
2034
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
2133
2035
|
try {
|
|
2134
|
-
|
|
2135
|
-
} catch {
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
throw
|
|
2139
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2140
|
-
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2141
|
-
{
|
|
2142
|
-
statusCode: res.status,
|
|
2143
|
-
responseBody: text.slice(0, 500),
|
|
2144
|
-
safeToFallback: true
|
|
2145
|
-
}
|
|
2146
|
-
);
|
|
2036
|
+
return await fn();
|
|
2037
|
+
} catch (err) {
|
|
2038
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
2039
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
2040
|
+
throw err;
|
|
2147
2041
|
}
|
|
2148
|
-
return parsed.data;
|
|
2149
2042
|
}
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
"x-owney-api-key": input.apiKey,
|
|
2159
|
-
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2160
|
-
},
|
|
2161
|
-
body: JSON.stringify(input.body)
|
|
2162
|
-
});
|
|
2163
|
-
} catch (networkError) {
|
|
2164
|
-
throw new OwneyError(
|
|
2165
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2166
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2167
|
-
{ cause: String(networkError) }
|
|
2043
|
+
|
|
2044
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
2045
|
+
import { parseUnits } from "viem";
|
|
2046
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
2047
|
+
return agents.map((agent) => {
|
|
2048
|
+
const agentBalance = aggregated[agent.id];
|
|
2049
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
2050
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
2168
2051
|
);
|
|
2052
|
+
if (!tokenBalance) return { agent, balance: 0n };
|
|
2053
|
+
return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
2057
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
2058
|
+
agent,
|
|
2059
|
+
balance,
|
|
2060
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
2061
|
+
}));
|
|
2062
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
2063
|
+
let remainder = requested - assigned;
|
|
2064
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
2065
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
2066
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2067
|
+
});
|
|
2068
|
+
for (const p of byHeadroom) {
|
|
2069
|
+
if (remainder === 0n) break;
|
|
2070
|
+
const headroom = p.balance - p.planned;
|
|
2071
|
+
if (headroom <= 0n) continue;
|
|
2072
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
2073
|
+
p.planned += take;
|
|
2074
|
+
remainder -= take;
|
|
2169
2075
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2185
|
-
safeToFallback: res.status === 503
|
|
2186
|
-
}
|
|
2187
|
-
);
|
|
2076
|
+
return plans;
|
|
2077
|
+
}
|
|
2078
|
+
function planDisabledDrain(disabled, requested) {
|
|
2079
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
2080
|
+
const plans = [];
|
|
2081
|
+
let remaining = requested;
|
|
2082
|
+
for (const { agent, balance } of sorted) {
|
|
2083
|
+
if (remaining === 0n) {
|
|
2084
|
+
plans.push({ agent, balance, planned: 0n });
|
|
2085
|
+
continue;
|
|
2086
|
+
}
|
|
2087
|
+
const take = balance < remaining ? balance : remaining;
|
|
2088
|
+
plans.push({ agent, balance, planned: take });
|
|
2089
|
+
remaining -= take;
|
|
2188
2090
|
}
|
|
2189
|
-
return
|
|
2091
|
+
return { plans, remaining };
|
|
2190
2092
|
}
|
|
2191
|
-
|
|
2192
|
-
const
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2207
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2208
|
-
{ cause: String(networkError), safeToFallback: false }
|
|
2209
|
-
);
|
|
2093
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
2094
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
2095
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
2096
|
+
const totalHeadroom = candidates.reduce(
|
|
2097
|
+
(s, c) => s + (c.balance - c.planned),
|
|
2098
|
+
0n
|
|
2099
|
+
);
|
|
2100
|
+
if (totalHeadroom === 0n) return;
|
|
2101
|
+
let distributed = 0n;
|
|
2102
|
+
for (const c of candidates) {
|
|
2103
|
+
const headroom = c.balance - c.planned;
|
|
2104
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
2105
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
2106
|
+
c.planned += give;
|
|
2107
|
+
distributed += give;
|
|
2210
2108
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2109
|
+
let leftover = amount - distributed;
|
|
2110
|
+
for (const c of candidates) {
|
|
2111
|
+
if (leftover === 0n) break;
|
|
2112
|
+
const headroom = c.balance - c.planned;
|
|
2113
|
+
if (headroom <= 0n) continue;
|
|
2114
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
2115
|
+
c.planned += take;
|
|
2116
|
+
leftover -= take;
|
|
2216
2117
|
}
|
|
2217
|
-
|
|
2218
|
-
|
|
2118
|
+
}
|
|
2119
|
+
function sumWithdrawnAmount(results) {
|
|
2120
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
2124
|
+
function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
2125
|
+
if (!tokenSymbol) {
|
|
2126
|
+
const total = Number(balance.totalBalance);
|
|
2127
|
+
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
2128
|
+
}
|
|
2129
|
+
const normalizedToken = tokenSymbol.toUpperCase();
|
|
2130
|
+
return balance.tokens.reduce((total, token) => {
|
|
2131
|
+
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
2132
|
+
return total;
|
|
2133
|
+
}
|
|
2134
|
+
const amount = Number(token.amount);
|
|
2135
|
+
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
2136
|
+
}, 0);
|
|
2137
|
+
}
|
|
2138
|
+
function aggregateApyHistory(agentApys) {
|
|
2139
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
2140
|
+
for (const accountApy of Object.values(agentApys)) {
|
|
2141
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2142
|
+
for (const point of accountApy.history ?? []) {
|
|
2143
|
+
if (!point.date || seen.has(point.date) || !Number.isFinite(point.apy)) continue;
|
|
2144
|
+
seen.add(point.date);
|
|
2145
|
+
const points = byDate.get(point.date) ?? [];
|
|
2146
|
+
points.push(point);
|
|
2147
|
+
byDate.set(point.date, points);
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).flatMap(([date, points]) => {
|
|
2151
|
+
if (points.length === 1 && !points[0].historicalBalance) {
|
|
2152
|
+
return [{ date, apy: points[0].apy }];
|
|
2153
|
+
}
|
|
2154
|
+
const unit = points[0].historicalBalance?.unit;
|
|
2155
|
+
if (!unit || points.some(
|
|
2156
|
+
({ historicalBalance: balance }) => !balance || balance.unit !== unit || !Number.isFinite(balance.amount) || balance.amount < 0
|
|
2157
|
+
)) return [];
|
|
2158
|
+
const total = points.reduce((sum, p) => sum + p.historicalBalance.amount, 0);
|
|
2159
|
+
if (total <= 0 || !Number.isFinite(total)) return [];
|
|
2160
|
+
const apy = points.reduce((sum, p) => sum + p.apy * (p.historicalBalance.amount / total), 0);
|
|
2161
|
+
return Number.isFinite(apy) ? [{ date, apy }] : [];
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
2165
|
+
const sums = {};
|
|
2166
|
+
const weights = {};
|
|
2167
|
+
for (const id of Object.keys(agentApys)) {
|
|
2168
|
+
const cells = agentApys[id].apyByChainAndAsset;
|
|
2169
|
+
const balance = agentBalances[id] ?? 0;
|
|
2170
|
+
if (!cells || balance <= 0) continue;
|
|
2171
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
2172
|
+
if (!perAsset) continue;
|
|
2173
|
+
const chainId = Number(chainKey);
|
|
2174
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
2175
|
+
const apy = Number(apyValue ?? 0);
|
|
2176
|
+
if (apy === 0) continue;
|
|
2177
|
+
sums[chainId] ??= {};
|
|
2178
|
+
weights[chainId] ??= {};
|
|
2179
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
2180
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
const out = {};
|
|
2185
|
+
for (const chainKey of Object.keys(sums)) {
|
|
2186
|
+
const chainId = Number(chainKey);
|
|
2187
|
+
const perAssetOut = {};
|
|
2188
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
2189
|
+
const w = weights[chainId][asset];
|
|
2190
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
2191
|
+
}
|
|
2192
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
2193
|
+
out[chainId] = perAssetOut;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
return out;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
// src/client.ts
|
|
2200
|
+
import {
|
|
2201
|
+
createPublicClient as createPublicClient2,
|
|
2202
|
+
createWalletClient,
|
|
2203
|
+
custom
|
|
2204
|
+
} from "viem";
|
|
2205
|
+
import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
2206
|
+
|
|
2207
|
+
// src/lib/sponsor-client.ts
|
|
2208
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2209
|
+
async function postSponsorTransferAuth(input) {
|
|
2210
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL2;
|
|
2211
|
+
let res;
|
|
2212
|
+
try {
|
|
2213
|
+
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2214
|
+
method: "POST",
|
|
2215
|
+
headers: {
|
|
2216
|
+
"content-type": "application/json",
|
|
2217
|
+
"x-owney-api-key": input.apiKey
|
|
2218
|
+
},
|
|
2219
|
+
body: JSON.stringify(input.body)
|
|
2220
|
+
});
|
|
2221
|
+
} catch (networkError) {
|
|
2222
|
+
throw new OwneyError(
|
|
2223
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2224
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2225
|
+
{ cause: String(networkError) }
|
|
2226
|
+
);
|
|
2227
|
+
}
|
|
2228
|
+
const text = await res.text();
|
|
2229
|
+
let parsed = null;
|
|
2230
|
+
try {
|
|
2231
|
+
parsed = JSON.parse(text);
|
|
2232
|
+
} catch {
|
|
2233
|
+
}
|
|
2234
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2235
|
+
throw new OwneyError(
|
|
2236
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2237
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2238
|
+
{
|
|
2239
|
+
statusCode: res.status,
|
|
2240
|
+
responseBody: text.slice(0, 500),
|
|
2241
|
+
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2242
|
+
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2243
|
+
safeToFallback: res.status === 503
|
|
2244
|
+
}
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
return parsed.data;
|
|
2248
|
+
}
|
|
2249
|
+
async function postSponsorPermit2Transfer(input) {
|
|
2250
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL2;
|
|
2251
|
+
let res;
|
|
2252
|
+
try {
|
|
2253
|
+
res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
|
|
2254
|
+
method: "POST",
|
|
2255
|
+
headers: {
|
|
2256
|
+
"content-type": "application/json",
|
|
2257
|
+
"x-owney-api-key": input.apiKey
|
|
2258
|
+
},
|
|
2259
|
+
body: JSON.stringify(input.body)
|
|
2260
|
+
});
|
|
2261
|
+
} catch (networkError) {
|
|
2262
|
+
throw new OwneyError(
|
|
2263
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2264
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2265
|
+
{ cause: String(networkError), safeToFallback: false }
|
|
2266
|
+
);
|
|
2267
|
+
}
|
|
2268
|
+
const text = await res.text();
|
|
2269
|
+
let parsed = null;
|
|
2270
|
+
try {
|
|
2271
|
+
parsed = JSON.parse(text);
|
|
2272
|
+
} catch {
|
|
2273
|
+
}
|
|
2274
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2275
|
+
throw new OwneyError(
|
|
2219
2276
|
"SPONSOR_REQUEST_FAILED",
|
|
2220
2277
|
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2221
2278
|
{
|
|
@@ -2228,11 +2285,11 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2228
2285
|
return parsed.data;
|
|
2229
2286
|
}
|
|
2230
2287
|
async function getSponsorRelayerAddress(input) {
|
|
2231
|
-
const
|
|
2288
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL2;
|
|
2232
2289
|
let res;
|
|
2233
2290
|
try {
|
|
2234
2291
|
res = await fetch(
|
|
2235
|
-
`${
|
|
2292
|
+
`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2236
2293
|
{
|
|
2237
2294
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2238
2295
|
}
|
|
@@ -2265,7 +2322,7 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2265
2322
|
}
|
|
2266
2323
|
|
|
2267
2324
|
// src/lib/permit2.ts
|
|
2268
|
-
import { bytesToHex
|
|
2325
|
+
import { bytesToHex } from "viem";
|
|
2269
2326
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2270
2327
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2271
2328
|
var ERC20_ALLOWANCE_ABI = [
|
|
@@ -2323,2090 +2380,223 @@ function buildPermitTransferFromTypedData(input) {
|
|
|
2323
2380
|
function randomPermit2Nonce() {
|
|
2324
2381
|
const bytes = new Uint8Array(32);
|
|
2325
2382
|
globalThis.crypto.getRandomValues(bytes);
|
|
2326
|
-
return BigInt(
|
|
2383
|
+
return BigInt(bytesToHex(bytes));
|
|
2327
2384
|
}
|
|
2328
2385
|
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2329
|
-
return publicClient.readContract({
|
|
2330
|
-
address: token,
|
|
2331
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2332
|
-
functionName: "allowance",
|
|
2333
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
2334
|
-
});
|
|
2335
|
-
}
|
|
2336
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
2337
|
-
return publicClient.readContract({
|
|
2338
|
-
address: token,
|
|
2339
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2340
|
-
functionName: "balanceOf",
|
|
2341
|
-
args: [owner]
|
|
2342
|
-
});
|
|
2343
|
-
}
|
|
2344
|
-
|
|
2345
|
-
// src/lib/sponsored-deposit.ts
|
|
2346
|
-
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2347
|
-
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2348
|
-
function provideDepositVerificationContext(callback, context) {
|
|
2349
|
-
callback[verificationSetter]?.(context);
|
|
2350
|
-
}
|
|
2351
|
-
function makeVerificationAwareDepositCallback(implementation) {
|
|
2352
|
-
let nextVerification;
|
|
2353
|
-
const callback = async (smartWallet, chainId, amount) => {
|
|
2354
|
-
const verification = nextVerification;
|
|
2355
|
-
nextVerification = void 0;
|
|
2356
|
-
return implementation(smartWallet, chainId, amount, verification);
|
|
2357
|
-
};
|
|
2358
|
-
Object.defineProperty(callback, verificationSetter, {
|
|
2359
|
-
value: (context) => {
|
|
2360
|
-
nextVerification = context;
|
|
2361
|
-
}
|
|
2362
|
-
});
|
|
2363
|
-
return callback;
|
|
2364
|
-
}
|
|
2365
|
-
function makeSponsoredDepositCallback(deps) {
|
|
2366
|
-
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2367
|
-
return makeVerificationAwareDepositCallback(
|
|
2368
|
-
async (smartWallet, chainId, amount, verification) => {
|
|
2369
|
-
const cid = chainId;
|
|
2370
|
-
const token = deps.tokenAddressByChain[cid];
|
|
2371
|
-
if (!token) {
|
|
2372
|
-
throw new OwneyError(
|
|
2373
|
-
"CHAIN_UNSUPPORTED",
|
|
2374
|
-
`No sponsored token configured for chain ${chainId}`
|
|
2375
|
-
);
|
|
2376
|
-
}
|
|
2377
|
-
const pub = deps.getPublicClient(cid);
|
|
2378
|
-
const wallet = deps.getWalletClient(cid);
|
|
2379
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2380
|
-
try {
|
|
2381
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2382
|
-
if (balance < BigInt(amount)) {
|
|
2383
|
-
throw new OwneyError(
|
|
2384
|
-
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2385
|
-
"Insufficient balance for this deposit.",
|
|
2386
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2387
|
-
);
|
|
2388
|
-
}
|
|
2389
|
-
} catch (err) {
|
|
2390
|
-
if (err instanceof OwneyError) throw err;
|
|
2391
|
-
console.warn(
|
|
2392
|
-
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2393
|
-
err instanceof Error ? err.message : String(err)
|
|
2394
|
-
);
|
|
2395
|
-
}
|
|
2396
|
-
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2397
|
-
const validAfter = 0n;
|
|
2398
|
-
const validBefore = BigInt(
|
|
2399
|
-
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2400
|
-
);
|
|
2401
|
-
const nonce = randomAuthNonce();
|
|
2402
|
-
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2403
|
-
token,
|
|
2404
|
-
chainId: cid,
|
|
2405
|
-
tokenName,
|
|
2406
|
-
tokenVersion,
|
|
2407
|
-
message: {
|
|
2408
|
-
from: deps.ownerAddress,
|
|
2409
|
-
to: smartWallet,
|
|
2410
|
-
value: BigInt(amount),
|
|
2411
|
-
validAfter,
|
|
2412
|
-
validBefore,
|
|
2413
|
-
nonce
|
|
2414
|
-
}
|
|
2415
|
-
});
|
|
2416
|
-
const authSignature = await wallet.signTypedData({
|
|
2417
|
-
account: deps.ownerAddress,
|
|
2418
|
-
...typedData
|
|
2419
|
-
});
|
|
2420
|
-
deps.onApproved?.();
|
|
2421
|
-
const result = await post({
|
|
2422
|
-
baseUrl: deps.baseUrl,
|
|
2423
|
-
apiKey: deps.apiKey,
|
|
2424
|
-
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2425
|
-
body: {
|
|
2426
|
-
chainId: cid,
|
|
2427
|
-
token,
|
|
2428
|
-
from: deps.ownerAddress,
|
|
2429
|
-
to: smartWallet,
|
|
2430
|
-
value: amount,
|
|
2431
|
-
validAfter: validAfter.toString(),
|
|
2432
|
-
validBefore: validBefore.toString(),
|
|
2433
|
-
nonce,
|
|
2434
|
-
authSignature,
|
|
2435
|
-
tokenName,
|
|
2436
|
-
tokenVersion,
|
|
2437
|
-
...verification?.agentId === "yieldseeker" ? {
|
|
2438
|
-
yieldseekerUserId: verification.userId,
|
|
2439
|
-
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
2440
|
-
} : {}
|
|
2441
|
-
}
|
|
2442
|
-
});
|
|
2443
|
-
return result.txHash;
|
|
2444
|
-
}
|
|
2445
|
-
);
|
|
2446
|
-
}
|
|
2447
|
-
|
|
2448
|
-
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2449
|
-
import { SiweMessage, generateNonce } from "siwe";
|
|
2450
|
-
import {
|
|
2451
|
-
createPublicClient as createPublicClient2,
|
|
2452
|
-
createWalletClient,
|
|
2453
|
-
custom,
|
|
2454
|
-
getAddress
|
|
2455
|
-
} from "viem";
|
|
2456
|
-
import { base as base2 } from "viem/chains";
|
|
2457
|
-
|
|
2458
|
-
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2459
|
-
var KEY_PREFIX2 = "owney.yieldseeker.session.v5";
|
|
2460
|
-
var INVALIDATED_KEY_PREFIXES = [
|
|
2461
|
-
"owney.yieldseeker.session",
|
|
2462
|
-
"owney.yieldseeker.session.v3",
|
|
2463
|
-
"owney.yieldseeker.session.v4"
|
|
2464
|
-
];
|
|
2465
|
-
var storage2 = () => {
|
|
2466
|
-
if (typeof window === "undefined") return null;
|
|
2467
|
-
try {
|
|
2468
|
-
return window.localStorage;
|
|
2469
|
-
} catch {
|
|
2470
|
-
return null;
|
|
2471
|
-
}
|
|
2472
|
-
};
|
|
2473
|
-
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2474
|
-
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2475
|
-
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2476
|
-
);
|
|
2477
|
-
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2478
|
-
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2479
|
-
memorySessions2.delete(key2);
|
|
2480
|
-
try {
|
|
2481
|
-
store?.removeItem(key2);
|
|
2482
|
-
} catch {
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
|
-
};
|
|
2486
|
-
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2487
|
-
var isValidSession = (session) => {
|
|
2488
|
-
if (!session?.token) return false;
|
|
2489
|
-
try {
|
|
2490
|
-
const parsed = JSON.parse(atob(session.token));
|
|
2491
|
-
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2492
|
-
} catch {
|
|
2493
|
-
return false;
|
|
2494
|
-
}
|
|
2495
|
-
};
|
|
2496
|
-
var readYieldseekerSession = (address, chainId) => {
|
|
2497
|
-
if (typeof window === "undefined") return null;
|
|
2498
|
-
const key2 = buildKey2(address, chainId);
|
|
2499
|
-
const store = storage2();
|
|
2500
|
-
clearInvalidatedSessions(store, address, chainId);
|
|
2501
|
-
let raw2 = null;
|
|
2502
|
-
try {
|
|
2503
|
-
raw2 = store?.getItem(key2) ?? null;
|
|
2504
|
-
} catch {
|
|
2505
|
-
raw2 = null;
|
|
2506
|
-
}
|
|
2507
|
-
if (raw2) {
|
|
2508
|
-
try {
|
|
2509
|
-
const parsed = JSON.parse(raw2);
|
|
2510
|
-
if (isValidSession(parsed)) return parsed.token;
|
|
2511
|
-
} catch {
|
|
2512
|
-
}
|
|
2513
|
-
memorySessions2.delete(key2);
|
|
2514
|
-
try {
|
|
2515
|
-
store?.removeItem(key2);
|
|
2516
|
-
} catch {
|
|
2517
|
-
}
|
|
2518
|
-
return null;
|
|
2519
|
-
}
|
|
2520
|
-
const cached = memorySessions2.get(key2);
|
|
2521
|
-
if (isValidSession(cached)) return cached.token;
|
|
2522
|
-
if (cached) memorySessions2.delete(key2);
|
|
2523
|
-
return null;
|
|
2524
|
-
};
|
|
2525
|
-
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2526
|
-
if (typeof window === "undefined") return;
|
|
2527
|
-
const session = { token };
|
|
2528
|
-
if (!isValidSession(session)) return;
|
|
2529
|
-
const key2 = buildKey2(address, chainId);
|
|
2530
|
-
memorySessions2.set(key2, session);
|
|
2531
|
-
const store = storage2();
|
|
2532
|
-
try {
|
|
2533
|
-
store?.setItem(key2, JSON.stringify(session));
|
|
2534
|
-
} catch {
|
|
2535
|
-
}
|
|
2536
|
-
};
|
|
2537
|
-
var clearYieldseekerSession = (address, chainId) => {
|
|
2538
|
-
const key2 = buildKey2(address, chainId);
|
|
2539
|
-
memorySessions2.delete(key2);
|
|
2540
|
-
const store = storage2();
|
|
2541
|
-
clearInvalidatedSessions(store, address, chainId);
|
|
2542
|
-
try {
|
|
2543
|
-
store?.removeItem(key2);
|
|
2544
|
-
} catch {
|
|
2545
|
-
}
|
|
2546
|
-
};
|
|
2547
|
-
|
|
2548
|
-
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2549
|
-
function resolveSiweOrigin(override) {
|
|
2550
|
-
const origin = override?.trim() || (typeof window !== "undefined" ? window.location?.origin : void 0);
|
|
2551
|
-
if (!origin || origin === "null") {
|
|
2552
|
-
throw new Error("Yieldseeker sign-in requires the requesting app's browser origin.");
|
|
2553
|
-
}
|
|
2554
|
-
const url = new URL(origin);
|
|
2555
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
2556
|
-
throw new Error("Yieldseeker sign-in requires an HTTP or HTTPS app origin.");
|
|
2557
|
-
}
|
|
2558
|
-
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
2559
|
-
throw new Error("Yieldseeker SIWE override must be an origin without credentials, path, query, or fragment.");
|
|
2560
|
-
}
|
|
2561
|
-
return url;
|
|
2562
|
-
}
|
|
2563
|
-
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2564
|
-
const url = resolveSiweOrigin(dependencies.origin);
|
|
2565
|
-
return new SiweMessage({
|
|
2566
|
-
scheme: url.protocol.slice(0, -1),
|
|
2567
|
-
domain: url.host,
|
|
2568
|
-
address: getAddress(address),
|
|
2569
|
-
uri: url.origin,
|
|
2570
|
-
version: "1",
|
|
2571
|
-
chainId,
|
|
2572
|
-
nonce: (dependencies.nonce ?? generateNonce)(),
|
|
2573
|
-
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2574
|
-
}).prepareMessage();
|
|
2575
|
-
}
|
|
2576
|
-
function encodeYieldseekerAuthToken(token) {
|
|
2577
|
-
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2578
|
-
let binary = "";
|
|
2579
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2580
|
-
return btoa(binary);
|
|
2581
|
-
}
|
|
2582
|
-
var YieldseekerAuth = class {
|
|
2583
|
-
constructor(dependencies = {}) {
|
|
2584
|
-
this.dependencies = dependencies;
|
|
2585
|
-
}
|
|
2586
|
-
dependencies;
|
|
2587
|
-
tokens = /* @__PURE__ */ new Map();
|
|
2588
|
-
pending = /* @__PURE__ */ new Map();
|
|
2589
|
-
scopes = /* @__PURE__ */ new Map();
|
|
2590
|
-
key(state, chainId) {
|
|
2591
|
-
return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
|
|
2592
|
-
}
|
|
2593
|
-
async getToken(state, chainId) {
|
|
2594
|
-
const key2 = this.key(state, chainId);
|
|
2595
|
-
const scope = { address: state.walletAddress, chainId };
|
|
2596
|
-
this.scopes.set(key2, scope);
|
|
2597
|
-
const cached = this.tokens.get(key2);
|
|
2598
|
-
if (cached) return cached;
|
|
2599
|
-
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2600
|
-
if (persisted && this.matchesOrigin(persisted)) {
|
|
2601
|
-
this.tokens.set(key2, persisted);
|
|
2602
|
-
return persisted;
|
|
2603
|
-
}
|
|
2604
|
-
if (persisted) clearYieldseekerSession(scope.address, scope.chainId);
|
|
2605
|
-
const inFlight = this.pending.get(key2);
|
|
2606
|
-
if (inFlight) return inFlight;
|
|
2607
|
-
const request = this.sign(state, chainId).then((token) => {
|
|
2608
|
-
if (this.pending.get(key2) !== request) throw new Error("Wallet sign-in session changed. Please try again.");
|
|
2609
|
-
this.tokens.set(key2, token);
|
|
2610
|
-
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2611
|
-
return token;
|
|
2612
|
-
});
|
|
2613
|
-
this.pending.set(key2, request);
|
|
2614
|
-
try {
|
|
2615
|
-
return await request;
|
|
2616
|
-
} finally {
|
|
2617
|
-
if (this.pending.get(key2) === request) this.pending.delete(key2);
|
|
2618
|
-
}
|
|
2619
|
-
}
|
|
2620
|
-
async refreshToken(state, chainId, rejectedToken) {
|
|
2621
|
-
const key2 = this.key(state, chainId);
|
|
2622
|
-
if (this.tokens.get(key2) === rejectedToken) {
|
|
2623
|
-
this.tokens.delete(key2);
|
|
2624
|
-
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2625
|
-
}
|
|
2626
|
-
return this.getToken(state, chainId);
|
|
2627
|
-
}
|
|
2628
|
-
matchesOrigin(token) {
|
|
2629
|
-
try {
|
|
2630
|
-
const message = new SiweMessage(JSON.parse(atob(token)).message);
|
|
2631
|
-
const url = resolveSiweOrigin(this.dependencies.origin);
|
|
2632
|
-
return message.domain === url.host && message.uri === url.origin && message.scheme === url.protocol.slice(0, -1);
|
|
2633
|
-
} catch {
|
|
2634
|
-
return false;
|
|
2635
|
-
}
|
|
2636
|
-
}
|
|
2637
|
-
clear(state, chainId) {
|
|
2638
|
-
if (!state || chainId === void 0) {
|
|
2639
|
-
for (const scope of this.scopes.values()) {
|
|
2640
|
-
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2641
|
-
}
|
|
2642
|
-
this.tokens.clear();
|
|
2643
|
-
this.pending.clear();
|
|
2644
|
-
this.scopes.clear();
|
|
2645
|
-
return;
|
|
2646
|
-
}
|
|
2647
|
-
const key2 = this.key(state, chainId);
|
|
2648
|
-
this.tokens.delete(key2);
|
|
2649
|
-
this.pending.delete(key2);
|
|
2650
|
-
this.scopes.delete(key2);
|
|
2651
|
-
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2652
|
-
}
|
|
2653
|
-
async sign(state, chainId) {
|
|
2654
|
-
const account = getAddress(state.walletAddress);
|
|
2655
|
-
const publicClient = createPublicClient2({
|
|
2656
|
-
chain: base2,
|
|
2657
|
-
transport: custom(state.provider)
|
|
2658
|
-
});
|
|
2659
|
-
const walletClient = createWalletClient({
|
|
2660
|
-
account,
|
|
2661
|
-
chain: base2,
|
|
2662
|
-
transport: custom(state.provider)
|
|
2663
|
-
});
|
|
2664
|
-
await ensureWalletOnChain(
|
|
2665
|
-
publicClient,
|
|
2666
|
-
walletClient,
|
|
2667
|
-
8453
|
|
2668
|
-
);
|
|
2669
|
-
const message = createYieldseekerSiweMessage(
|
|
2670
|
-
account,
|
|
2671
|
-
chainId,
|
|
2672
|
-
this.dependencies
|
|
2673
|
-
);
|
|
2674
|
-
const signature = await walletClient.signMessage({ account, message });
|
|
2675
|
-
return encodeYieldseekerAuthToken({ message, signature });
|
|
2676
|
-
}
|
|
2677
|
-
};
|
|
2678
|
-
|
|
2679
|
-
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2680
|
-
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2681
|
-
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2682
|
-
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2683
|
-
var storage3 = () => {
|
|
2684
|
-
if (typeof window === "undefined") return null;
|
|
2685
|
-
try {
|
|
2686
|
-
return window.localStorage;
|
|
2687
|
-
} catch {
|
|
2688
|
-
return null;
|
|
2689
|
-
}
|
|
2690
|
-
};
|
|
2691
|
-
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2692
|
-
function valid(value, walletAddress, chainId, now) {
|
|
2693
|
-
return Boolean(
|
|
2694
|
-
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
|
|
2695
|
-
);
|
|
2696
|
-
}
|
|
2697
|
-
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2698
|
-
if (typeof window === "undefined") return null;
|
|
2699
|
-
const key2 = keyFor(walletAddress, chainId);
|
|
2700
|
-
const store = storage3();
|
|
2701
|
-
let parsed = null;
|
|
2702
|
-
try {
|
|
2703
|
-
const raw2 = store?.getItem(key2);
|
|
2704
|
-
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2705
|
-
} catch {
|
|
2706
|
-
parsed = null;
|
|
2707
|
-
}
|
|
2708
|
-
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2709
|
-
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2710
|
-
memoryIdentities.set(key2, candidate);
|
|
2711
|
-
return { userId: candidate.userId };
|
|
2712
|
-
}
|
|
2713
|
-
memoryIdentities.delete(key2);
|
|
2714
|
-
try {
|
|
2715
|
-
store?.removeItem(key2);
|
|
2716
|
-
} catch {
|
|
2717
|
-
}
|
|
2718
|
-
return null;
|
|
2719
|
-
}
|
|
2720
|
-
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2721
|
-
if (typeof window === "undefined") return;
|
|
2722
|
-
const identity = {
|
|
2723
|
-
userId,
|
|
2724
|
-
walletAddress,
|
|
2725
|
-
chainId,
|
|
2726
|
-
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2727
|
-
};
|
|
2728
|
-
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2729
|
-
const key2 = keyFor(walletAddress, chainId);
|
|
2730
|
-
memoryIdentities.set(key2, identity);
|
|
2731
|
-
try {
|
|
2732
|
-
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2733
|
-
} catch {
|
|
2734
|
-
}
|
|
2735
|
-
}
|
|
2736
|
-
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2737
|
-
const key2 = keyFor(walletAddress, chainId);
|
|
2738
|
-
memoryIdentities.delete(key2);
|
|
2739
|
-
try {
|
|
2740
|
-
storage3()?.removeItem(key2);
|
|
2741
|
-
} catch {
|
|
2742
|
-
}
|
|
2743
|
-
}
|
|
2744
|
-
|
|
2745
|
-
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2746
|
-
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2747
|
-
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2748
|
-
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2749
|
-
}
|
|
2750
|
-
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2751
|
-
var YieldseekerApiError = class extends Error {
|
|
2752
|
-
constructor(status, providerCode, responseFields) {
|
|
2753
|
-
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2754
|
-
this.status = status;
|
|
2755
|
-
this.providerCode = providerCode;
|
|
2756
|
-
this.responseFields = responseFields;
|
|
2757
|
-
this.name = "YieldseekerApiError";
|
|
2758
|
-
}
|
|
2759
|
-
status;
|
|
2760
|
-
providerCode;
|
|
2761
|
-
responseFields;
|
|
2762
|
-
get isAuthenticationError() {
|
|
2763
|
-
return this.status === 401 || this.status === 403;
|
|
2764
|
-
}
|
|
2765
|
-
};
|
|
2766
|
-
function providerError(body, fallback) {
|
|
2767
|
-
if (!body || typeof body !== "object") return { code: fallback };
|
|
2768
|
-
const record = body;
|
|
2769
|
-
return {
|
|
2770
|
-
code: typeof record.message === "string" ? record.message : fallback,
|
|
2771
|
-
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2772
|
-
};
|
|
2773
|
-
}
|
|
2774
|
-
var YieldseekerApiClient = class {
|
|
2775
|
-
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch.bind(globalThis)) {
|
|
2776
|
-
this.owneyApiKey = owneyApiKey;
|
|
2777
|
-
this.baseUrl = baseUrl;
|
|
2778
|
-
this.fetchFn = fetchFn;
|
|
2779
|
-
}
|
|
2780
|
-
owneyApiKey;
|
|
2781
|
-
baseUrl;
|
|
2782
|
-
fetchFn;
|
|
2783
|
-
async request(path, options = {}) {
|
|
2784
|
-
const controller = new AbortController();
|
|
2785
|
-
const timer = setTimeout(
|
|
2786
|
-
() => controller.abort(),
|
|
2787
|
-
options.timeoutMs ?? 15e3
|
|
2788
|
-
);
|
|
2789
|
-
try {
|
|
2790
|
-
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2791
|
-
method: options.method ?? "GET",
|
|
2792
|
-
headers: {
|
|
2793
|
-
"Content-Type": "application/json",
|
|
2794
|
-
"x-owney-api-key": this.owneyApiKey,
|
|
2795
|
-
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2796
|
-
},
|
|
2797
|
-
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2798
|
-
signal: controller.signal
|
|
2799
|
-
});
|
|
2800
|
-
const payload = await response.json().catch(() => null);
|
|
2801
|
-
if (!response.ok) {
|
|
2802
|
-
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2803
|
-
throw new YieldseekerApiError(
|
|
2804
|
-
response.status,
|
|
2805
|
-
error.code,
|
|
2806
|
-
error.fields
|
|
2807
|
-
);
|
|
2808
|
-
}
|
|
2809
|
-
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2810
|
-
return payload.data;
|
|
2811
|
-
}
|
|
2812
|
-
return payload;
|
|
2813
|
-
} catch (error) {
|
|
2814
|
-
if (error instanceof YieldseekerApiError) throw error;
|
|
2815
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2816
|
-
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2817
|
-
}
|
|
2818
|
-
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2819
|
-
cause: error instanceof Error ? error.message : String(error)
|
|
2820
|
-
});
|
|
2821
|
-
} finally {
|
|
2822
|
-
clearTimeout(timer);
|
|
2823
|
-
}
|
|
2824
|
-
}
|
|
2825
|
-
};
|
|
2826
|
-
|
|
2827
|
-
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2828
|
-
import { formatUnits, isAddress } from "viem";
|
|
2829
|
-
|
|
2830
|
-
// src/lib/helpers/snapshot-apy.ts
|
|
2831
|
-
var DAY_MS = 864e5;
|
|
2832
|
-
function snapshotTime(date) {
|
|
2833
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return void 0;
|
|
2834
|
-
const time = Date.parse(date);
|
|
2835
|
-
return Number.isFinite(time) && new Date(time).toISOString().slice(0, 10) === date ? time : void 0;
|
|
2836
|
-
}
|
|
2837
|
-
function returnFactor(value) {
|
|
2838
|
-
if (typeof value !== "number" && typeof value !== "string") return void 0;
|
|
2839
|
-
if (typeof value === "string" && value.trim() === "") return void 0;
|
|
2840
|
-
const factor = Number(value);
|
|
2841
|
-
return Number.isFinite(factor) && factor >= 0 ? factor : void 0;
|
|
2842
|
-
}
|
|
2843
|
-
function calculateSnapshotApy(snapshots, lookbackDays, now = Date.now()) {
|
|
2844
|
-
if (!Number.isFinite(lookbackDays) || lookbackDays <= 0 || !Number.isFinite(now)) {
|
|
2845
|
-
return void 0;
|
|
2846
|
-
}
|
|
2847
|
-
const points = snapshots.flatMap((snapshot) => {
|
|
2848
|
-
const time = snapshotTime(snapshot.date);
|
|
2849
|
-
return time !== void 0 && time <= now ? [{ snapshot, time }] : [];
|
|
2850
|
-
}).sort((a, b) => a.time - b.time);
|
|
2851
|
-
const end = points.at(-1);
|
|
2852
|
-
if (!end) return void 0;
|
|
2853
|
-
const cutoff = end.time - lookbackDays * DAY_MS;
|
|
2854
|
-
const start = points.find((point) => point.time >= cutoff);
|
|
2855
|
-
const actualDays = (end.time - start.time) / DAY_MS;
|
|
2856
|
-
if (actualDays <= 0) return void 0;
|
|
2857
|
-
const startFactor = returnFactor(start.snapshot.cumulativeReturnFactor);
|
|
2858
|
-
const endFactor = returnFactor(end.snapshot.cumulativeReturnFactor);
|
|
2859
|
-
if (startFactor === void 0 || startFactor === 0 || endFactor === void 0) {
|
|
2860
|
-
return void 0;
|
|
2861
|
-
}
|
|
2862
|
-
const periodReturn = endFactor / startFactor - 1;
|
|
2863
|
-
const apy = Math.pow(1 + periodReturn, 365 / actualDays) - 1;
|
|
2864
|
-
return Number.isFinite(apy) ? apy : void 0;
|
|
2865
|
-
}
|
|
2866
|
-
|
|
2867
|
-
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2868
|
-
var YIELDSEEKER_ASSET_METADATA = {
|
|
2869
|
-
USDC: {
|
|
2870
|
-
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2871
|
-
decimals: 6
|
|
2872
|
-
},
|
|
2873
|
-
WETH: {
|
|
2874
|
-
address: "0x4200000000000000000000000000000000000006",
|
|
2875
|
-
decimals: 18
|
|
2876
|
-
}
|
|
2877
|
-
};
|
|
2878
|
-
|
|
2879
|
-
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2880
|
-
function invalid(endpoint, detail) {
|
|
2881
|
-
throw new OwneyError(
|
|
2882
|
-
"AGENT_INVALID_RESPONSE",
|
|
2883
|
-
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2884
|
-
{ endpoint, detail },
|
|
2885
|
-
"yieldseeker"
|
|
2886
|
-
);
|
|
2887
|
-
}
|
|
2888
|
-
function raw(value, endpoint) {
|
|
2889
|
-
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2890
|
-
return invalid(endpoint, "expected a base-10 integer string");
|
|
2891
|
-
}
|
|
2892
|
-
return BigInt(value);
|
|
2893
|
-
}
|
|
2894
|
-
function decimal(value, decimals, endpoint) {
|
|
2895
|
-
return formatUnits(raw(value, endpoint), decimals);
|
|
2896
|
-
}
|
|
2897
|
-
function usd(rawAmount, decimals, price) {
|
|
2898
|
-
return Number(formatUnits(rawAmount, decimals)) * price;
|
|
2899
|
-
}
|
|
2900
|
-
function percent(value) {
|
|
2901
|
-
const result = Number(value);
|
|
2902
|
-
return Number.isFinite(result) ? result * 100 : 0;
|
|
2903
|
-
}
|
|
2904
|
-
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2905
|
-
function publicApyAfterYieldseekerFee(value) {
|
|
2906
|
-
const grossPercent = percent(value);
|
|
2907
|
-
if (grossPercent <= 0) return grossPercent;
|
|
2908
|
-
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2909
|
-
return Math.round(netPercent * 1e12) / 1e12;
|
|
2910
|
-
}
|
|
2911
|
-
function riskAdjustedApyForDays(option, days) {
|
|
2912
|
-
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2913
|
-
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2914
|
-
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2915
|
-
}
|
|
2916
|
-
function assetAddressValue(record, address) {
|
|
2917
|
-
const entry = Object.entries(record).find(
|
|
2918
|
-
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2919
|
-
);
|
|
2920
|
-
return entry?.[1] ?? "0";
|
|
2921
|
-
}
|
|
2922
|
-
function position(value, asset, baseAssetDecimals) {
|
|
2923
|
-
const option = value?.yieldOption;
|
|
2924
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress(option.address)) {
|
|
2925
|
-
return invalid("yield positions", "missing vault metadata");
|
|
2926
|
-
}
|
|
2927
|
-
return {
|
|
2928
|
-
chain: "BASE",
|
|
2929
|
-
protocol: option.provider,
|
|
2930
|
-
protocolId: option.address,
|
|
2931
|
-
pool: option.name,
|
|
2932
|
-
asset,
|
|
2933
|
-
// `assetsRaw` is the ERC-4626 vault-share quantity, whose decimals can
|
|
2934
|
-
// differ from the underlying asset. Yieldseeker already converts it to
|
|
2935
|
-
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
2936
|
-
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
2937
|
-
// share quantity separately because withdraw-from-position expects it.
|
|
2938
|
-
amount: decimal(
|
|
2939
|
-
value.assetsBase,
|
|
2940
|
-
baseAssetDecimals,
|
|
2941
|
-
"yield positions"
|
|
2942
|
-
),
|
|
2943
|
-
amountRaw: String(value.assetsRaw),
|
|
2944
|
-
apy: percent(option.riskAdjustedApy),
|
|
2945
|
-
tvl: Number(option.totalDepositsUsd),
|
|
2946
|
-
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2947
|
-
};
|
|
2948
|
-
}
|
|
2949
|
-
function mapYieldseekerBalances(contexts) {
|
|
2950
|
-
const tokens = [];
|
|
2951
|
-
const assetBalances = [];
|
|
2952
|
-
const positions = [];
|
|
2953
|
-
let totalUsd = 0;
|
|
2954
|
-
for (const context of contexts) {
|
|
2955
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2956
|
-
assetBalances.push({
|
|
2957
|
-
chain: "BASE",
|
|
2958
|
-
chainId: 8453,
|
|
2959
|
-
asset: context.asset,
|
|
2960
|
-
amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
|
|
2961
|
-
});
|
|
2962
|
-
const idle = assetAddressValue(
|
|
2963
|
-
context.snapshot.tokenBalances,
|
|
2964
|
-
metadata.address
|
|
2965
|
-
);
|
|
2966
|
-
tokens.push({
|
|
2967
|
-
chain: "BASE",
|
|
2968
|
-
chainId: 8453,
|
|
2969
|
-
asset: context.asset,
|
|
2970
|
-
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2971
|
-
});
|
|
2972
|
-
positions.push(
|
|
2973
|
-
...context.positions.map(
|
|
2974
|
-
(entry) => position(
|
|
2975
|
-
entry,
|
|
2976
|
-
context.asset,
|
|
2977
|
-
context.snapshot.baseAssetDecimals
|
|
2978
|
-
)
|
|
2979
|
-
)
|
|
2980
|
-
);
|
|
2981
|
-
totalUsd += usd(
|
|
2982
|
-
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2983
|
-
context.snapshot.baseAssetDecimals,
|
|
2984
|
-
context.snapshot.baseAssetPriceUsd
|
|
2985
|
-
);
|
|
2986
|
-
}
|
|
2987
|
-
return {
|
|
2988
|
-
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2989
|
-
totalBalance: String(totalUsd),
|
|
2990
|
-
totalBalanceAsset: "usdc",
|
|
2991
|
-
assetBalances,
|
|
2992
|
-
tokens,
|
|
2993
|
-
positions
|
|
2994
|
-
};
|
|
2995
|
-
}
|
|
2996
|
-
function mapYieldseekerEarnings(contexts) {
|
|
2997
|
-
const tokens = [];
|
|
2998
|
-
let lifetimeEarnings = 0;
|
|
2999
|
-
for (const context of contexts) {
|
|
3000
|
-
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
3001
|
-
tokens.push({
|
|
3002
|
-
chain: "BASE",
|
|
3003
|
-
chainId: 8453,
|
|
3004
|
-
asset: context.asset,
|
|
3005
|
-
amount: formatUnits(amount, context.snapshot.baseAssetDecimals)
|
|
3006
|
-
});
|
|
3007
|
-
lifetimeEarnings += usd(
|
|
3008
|
-
amount,
|
|
3009
|
-
context.snapshot.baseAssetDecimals,
|
|
3010
|
-
context.snapshot.baseAssetPriceUsd
|
|
3011
|
-
);
|
|
3012
|
-
}
|
|
3013
|
-
return {
|
|
3014
|
-
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
3015
|
-
lifetimeEarnings,
|
|
3016
|
-
tokens
|
|
3017
|
-
};
|
|
3018
|
-
}
|
|
3019
|
-
function apyForDays(context, days, now) {
|
|
3020
|
-
if (days === "7D") return percent(context.snapshot.apy7d);
|
|
3021
|
-
if (days === "30D") return percent(context.snapshot.apy30d);
|
|
3022
|
-
const apy = calculateSnapshotApy(context.historic?.dailyYieldSnapshots ?? [], 14, now);
|
|
3023
|
-
const apyPercent = apy === void 0 ? void 0 : apy * 100;
|
|
3024
|
-
return apyPercent !== void 0 && Number.isFinite(apyPercent) ? apyPercent : void 0;
|
|
3025
|
-
}
|
|
3026
|
-
function dailyApy(point) {
|
|
3027
|
-
const total = raw(point.totalValueBase, "historic position");
|
|
3028
|
-
const earned = raw(point.dailyYieldBase, "historic position");
|
|
3029
|
-
const principal = total - earned;
|
|
3030
|
-
if (principal <= 0n || earned === 0n) return 0;
|
|
3031
|
-
return Number(earned) / Number(principal) * 365 * 100;
|
|
3032
|
-
}
|
|
3033
|
-
function aggregateHistory(contexts, dayCount, now) {
|
|
3034
|
-
const today = new Date(now).toISOString().slice(0, 10);
|
|
3035
|
-
const cutoff = new Date(Date.parse(today) - (dayCount - 1) * 864e5).toISOString().slice(0, 10);
|
|
3036
|
-
const assets = new Set(contexts.map((context) => context.asset.toUpperCase()));
|
|
3037
|
-
const unit = assets.size === 1 ? [...assets][0] : "USD";
|
|
3038
|
-
const byDate = /* @__PURE__ */ new Map();
|
|
3039
|
-
for (const context of contexts) {
|
|
3040
|
-
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
3041
|
-
for (const point of points) {
|
|
3042
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(point.date) || !Number.isFinite(Date.parse(point.date)) || point.date < cutoff || point.date > today) continue;
|
|
3043
|
-
const tokens = Number(decimal(point.totalValueBase, point.baseAssetDecimals, "historic position"));
|
|
3044
|
-
const amount = assets.size === 1 ? tokens : tokens * point.baseAssetPriceUsd;
|
|
3045
|
-
if (!Number.isFinite(amount) || amount < 0) {
|
|
3046
|
-
invalid("historic position", "expected a finite non-negative balance");
|
|
3047
|
-
}
|
|
3048
|
-
const current = byDate.get(point.date) ?? { weighted: 0, amount: 0 };
|
|
3049
|
-
current.weighted += dailyApy(point) * amount;
|
|
3050
|
-
current.amount += amount;
|
|
3051
|
-
byDate.set(point.date, current);
|
|
3052
|
-
}
|
|
3053
|
-
}
|
|
3054
|
-
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
3055
|
-
date,
|
|
3056
|
-
apy: value.amount > 0 ? value.weighted / value.amount : 0,
|
|
3057
|
-
historicalBalance: { amount: value.amount, unit }
|
|
3058
|
-
}));
|
|
3059
|
-
}
|
|
3060
|
-
function mapYieldseekerApy(walletAddress, contexts, days, now = Date.now()) {
|
|
3061
|
-
let weighted = 0;
|
|
3062
|
-
let totalUsd = 0;
|
|
3063
|
-
const byAsset = {};
|
|
3064
|
-
for (const context of contexts) {
|
|
3065
|
-
const valueUsd = usd(
|
|
3066
|
-
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
3067
|
-
context.snapshot.baseAssetDecimals,
|
|
3068
|
-
context.snapshot.baseAssetPriceUsd
|
|
3069
|
-
);
|
|
3070
|
-
const apy = apyForDays(context, days, now);
|
|
3071
|
-
if (apy === void 0) continue;
|
|
3072
|
-
weighted += apy * valueUsd;
|
|
3073
|
-
totalUsd += valueUsd;
|
|
3074
|
-
byAsset[context.asset] = apy;
|
|
3075
|
-
}
|
|
3076
|
-
const dayCount = Number(days.slice(0, -1));
|
|
3077
|
-
return {
|
|
3078
|
-
walletAddress,
|
|
3079
|
-
...days !== "14D" || Object.keys(byAsset).length > 0 ? { weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0 } : {},
|
|
3080
|
-
apyByChainAndAsset: { 8453: byAsset },
|
|
3081
|
-
history: aggregateHistory(contexts, dayCount, now)
|
|
3082
|
-
};
|
|
3083
|
-
}
|
|
3084
|
-
function actionType(value) {
|
|
3085
|
-
const normalized = value.toLowerCase();
|
|
3086
|
-
if (normalized.includes("deposit")) return "Deposit";
|
|
3087
|
-
if (normalized.includes("withdraw")) return "Withdraw";
|
|
3088
|
-
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
3089
|
-
return "Earned";
|
|
3090
|
-
return "Rebalance";
|
|
3091
|
-
}
|
|
3092
|
-
function transactionHashes(details) {
|
|
3093
|
-
if (!details) return [];
|
|
3094
|
-
const values = [
|
|
3095
|
-
details.transactionHash,
|
|
3096
|
-
details.txHash,
|
|
3097
|
-
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
3098
|
-
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
3099
|
-
];
|
|
3100
|
-
return values.filter(
|
|
3101
|
-
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
3102
|
-
).filter((value, index, all) => all.indexOf(value) === index);
|
|
3103
|
-
}
|
|
3104
|
-
function actionEntry(action) {
|
|
3105
|
-
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
3106
|
-
return {
|
|
3107
|
-
agent: "yieldseeker",
|
|
3108
|
-
action: actionType(action.actionType),
|
|
3109
|
-
date: action.createdDate,
|
|
3110
|
-
oldApy: null,
|
|
3111
|
-
newApy: null,
|
|
3112
|
-
transactions: [
|
|
3113
|
-
{
|
|
3114
|
-
txHashes: transactionHashes(action.details),
|
|
3115
|
-
chainId: 8453
|
|
3116
|
-
}
|
|
3117
|
-
],
|
|
3118
|
-
rebalanceLog: []
|
|
3119
|
-
};
|
|
3120
|
-
}
|
|
3121
|
-
function depositDestination(context, movement) {
|
|
3122
|
-
const to = movement.toAddress.toLowerCase();
|
|
3123
|
-
const option = context.positions.find(({ yieldOption }) => yieldOption.chainId === movement.chainId && yieldOption.assetAddress.toLowerCase() === context.agent.assetAddress.toLowerCase() && yieldOption.address.toLowerCase() === to)?.yieldOption;
|
|
3124
|
-
if (option) return { protocol: option.provider || "Vault", pool: option.name, tokenSymbol: context.asset };
|
|
3125
|
-
const receipt = context.historic?.assets.find((asset) => asset.chainId === movement.chainId && !asset.isSpam && asset.address.toLowerCase() === to && asset.address.toLowerCase() !== context.agent.assetAddress.toLowerCase());
|
|
3126
|
-
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);
|
|
3127
|
-
if (receipt && minted) return { protocol: "Vault", pool: receipt.name || receipt.symbol, tokenSymbol: context.asset };
|
|
3128
|
-
return void 0;
|
|
3129
|
-
}
|
|
3130
|
-
function movementEntry(movement, wallet, agent, asset, ownerAddress, destination) {
|
|
3131
|
-
const from = movement.fromAddress.toLowerCase();
|
|
3132
|
-
const to = movement.toAddress.toLowerCase();
|
|
3133
|
-
const owner = ownerAddress.toLowerCase();
|
|
3134
|
-
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
3135
|
-
const baseAsset = agent.assetAddress.toLowerCase();
|
|
3136
|
-
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
3137
|
-
return void 0;
|
|
3138
|
-
}
|
|
3139
|
-
let action;
|
|
3140
|
-
if (from === owner && to === agentWallet) {
|
|
3141
|
-
action = "Top up";
|
|
3142
|
-
} else if (from === agentWallet && to === owner) {
|
|
3143
|
-
action = "Withdraw";
|
|
3144
|
-
} else if (from === agentWallet && destination) {
|
|
3145
|
-
action = "Deposit";
|
|
3146
|
-
}
|
|
3147
|
-
if (!action) return void 0;
|
|
3148
|
-
return {
|
|
3149
|
-
agent: "yieldseeker",
|
|
3150
|
-
action,
|
|
3151
|
-
...action === "Deposit" && destination ? { positions: [{
|
|
3152
|
-
...destination,
|
|
3153
|
-
amount: decimal(movement.assetAmount, YIELDSEEKER_ASSET_METADATA[asset].decimals, "historic position")
|
|
3154
|
-
}] } : {},
|
|
3155
|
-
date: movement.blockDate,
|
|
3156
|
-
oldApy: null,
|
|
3157
|
-
newApy: null,
|
|
3158
|
-
transactions: [
|
|
3159
|
-
{
|
|
3160
|
-
txHashes: [movement.transactionHash],
|
|
3161
|
-
chainId: agent.chainId,
|
|
3162
|
-
tokenSymbol: asset,
|
|
3163
|
-
amount: decimal(
|
|
3164
|
-
movement.assetAmount,
|
|
3165
|
-
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
3166
|
-
"historic position"
|
|
3167
|
-
)
|
|
3168
|
-
}
|
|
3169
|
-
],
|
|
3170
|
-
rebalanceLog: []
|
|
3171
|
-
};
|
|
3172
|
-
}
|
|
3173
|
-
function mapYieldseekerHistory(contexts, options) {
|
|
3174
|
-
const entries = contexts.flatMap((context) => {
|
|
3175
|
-
const seenMovements = /* @__PURE__ */ new Set();
|
|
3176
|
-
const movements = (context.historic?.movements ?? []).filter((movement) => {
|
|
3177
|
-
const key2 = `${movement.chainId}:${movement.transactionHash.toLowerCase()}:${movement.logIndex}`;
|
|
3178
|
-
if (seenMovements.has(key2)) return false;
|
|
3179
|
-
seenMovements.add(key2);
|
|
3180
|
-
return true;
|
|
3181
|
-
});
|
|
3182
|
-
return [
|
|
3183
|
-
...movements.map(
|
|
3184
|
-
(movement) => movementEntry(
|
|
3185
|
-
movement,
|
|
3186
|
-
context.wallet,
|
|
3187
|
-
context.agent,
|
|
3188
|
-
context.asset,
|
|
3189
|
-
options.ownerAddress,
|
|
3190
|
-
depositDestination(context, movement)
|
|
3191
|
-
)
|
|
3192
|
-
),
|
|
3193
|
-
...(context.actions ?? []).map(actionEntry)
|
|
3194
|
-
].filter((entry) => entry !== void 0);
|
|
3195
|
-
});
|
|
3196
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
3197
|
-
const ungrouped = [];
|
|
3198
|
-
for (const entry of entries) {
|
|
3199
|
-
const tx = entry.transactions[0];
|
|
3200
|
-
const hash = tx?.txHashes[0];
|
|
3201
|
-
if (!hash) {
|
|
3202
|
-
ungrouped.push(entry);
|
|
3203
|
-
continue;
|
|
3204
|
-
}
|
|
3205
|
-
const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}`;
|
|
3206
|
-
const previous = grouped.get(key2);
|
|
3207
|
-
if (!previous) {
|
|
3208
|
-
grouped.set(key2, entry);
|
|
3209
|
-
continue;
|
|
3210
|
-
}
|
|
3211
|
-
if (entry.action === "Deposit" && entry.positions?.length) {
|
|
3212
|
-
if (!previous.positions?.length) {
|
|
3213
|
-
grouped.set(key2, entry);
|
|
3214
|
-
continue;
|
|
3215
|
-
}
|
|
3216
|
-
previous.positions.push(...entry.positions);
|
|
3217
|
-
previous.transactions.push(...entry.transactions);
|
|
3218
|
-
}
|
|
3219
|
-
}
|
|
3220
|
-
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));
|
|
3221
|
-
return {
|
|
3222
|
-
data: filtered.slice(0, options.limit),
|
|
3223
|
-
// v1 returns the whole action/movement collection and defines no cursor.
|
|
3224
|
-
// Report a terminal page so callers never loop over the same prefix.
|
|
3225
|
-
hasMore: false
|
|
3226
|
-
};
|
|
3227
|
-
}
|
|
3228
|
-
function mapYieldseekerProfile(address, contexts) {
|
|
3229
|
-
const protocols = /* @__PURE__ */ new Set();
|
|
3230
|
-
for (const context of contexts) {
|
|
3231
|
-
for (const current of context.positions) {
|
|
3232
|
-
if (current.yieldOption?.provider) {
|
|
3233
|
-
protocols.add(String(current.yieldOption.provider));
|
|
3234
|
-
}
|
|
3235
|
-
}
|
|
3236
|
-
}
|
|
3237
|
-
return {
|
|
3238
|
-
address,
|
|
3239
|
-
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
3240
|
-
chains: contexts.length > 0 ? [8453] : [],
|
|
3241
|
-
hasActiveSessionKey: contexts.some(
|
|
3242
|
-
(context) => context.wallet.initializedDate != null
|
|
3243
|
-
),
|
|
3244
|
-
protocols: [...protocols]
|
|
3245
|
-
};
|
|
3246
|
-
}
|
|
3247
|
-
function mapYieldseekerAgentApy(options, days) {
|
|
3248
|
-
const perAsset = {};
|
|
3249
|
-
const all = [];
|
|
3250
|
-
for (const entry of options) {
|
|
3251
|
-
const apys = entry.yieldOptions.map(
|
|
3252
|
-
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
3253
|
-
).filter(Number.isFinite);
|
|
3254
|
-
if (apys.length === 0) continue;
|
|
3255
|
-
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
3256
|
-
perAsset[entry.asset] = average;
|
|
3257
|
-
all.push(average);
|
|
3258
|
-
}
|
|
3259
|
-
return {
|
|
3260
|
-
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
3261
|
-
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
3262
|
-
};
|
|
3263
|
-
}
|
|
3264
|
-
|
|
3265
|
-
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3266
|
-
var OWNEY_AGENT_NAME = "owney";
|
|
3267
|
-
var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
3268
|
-
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3269
|
-
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3270
|
-
function generateYieldseekerUsername() {
|
|
3271
|
-
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3272
|
-
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
3273
|
-
}
|
|
3274
|
-
function isUsernameConflict(error) {
|
|
3275
|
-
if (!(error instanceof YieldseekerApiError)) return false;
|
|
3276
|
-
const code = error.providerCode.toUpperCase();
|
|
3277
|
-
return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
|
|
3278
|
-
}
|
|
3279
|
-
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
3280
|
-
{
|
|
3281
|
-
type: "function",
|
|
3282
|
-
name: "withdrawAssetToUser",
|
|
3283
|
-
stateMutability: "nonpayable",
|
|
3284
|
-
inputs: [
|
|
3285
|
-
{ name: "recipient", type: "address" },
|
|
3286
|
-
{ name: "asset", type: "address" },
|
|
3287
|
-
{ name: "amount", type: "uint256" }
|
|
3288
|
-
],
|
|
3289
|
-
outputs: []
|
|
3290
|
-
},
|
|
3291
|
-
{
|
|
3292
|
-
type: "function",
|
|
3293
|
-
name: "withdrawAllAssetToUser",
|
|
3294
|
-
stateMutability: "nonpayable",
|
|
3295
|
-
inputs: [
|
|
3296
|
-
{ name: "recipient", type: "address" },
|
|
3297
|
-
{ name: "asset", type: "address" }
|
|
3298
|
-
],
|
|
3299
|
-
outputs: []
|
|
3300
|
-
}
|
|
3301
|
-
];
|
|
3302
|
-
function query(params) {
|
|
3303
|
-
const search = new URLSearchParams();
|
|
3304
|
-
for (const [key2, value] of Object.entries(params)) {
|
|
3305
|
-
if (value !== void 0) search.set(key2, String(value));
|
|
3306
|
-
}
|
|
3307
|
-
const encoded = search.toString();
|
|
3308
|
-
return encoded ? `?${encoded}` : "";
|
|
3309
|
-
}
|
|
3310
|
-
var YieldseekerAgent = class {
|
|
3311
|
-
id = "yieldseeker";
|
|
3312
|
-
balanceComposition = "tokens-plus-positions";
|
|
3313
|
-
supportedChainIds = [8453];
|
|
3314
|
-
supportedAssets = [
|
|
3315
|
-
{
|
|
3316
|
-
chainId: 8453,
|
|
3317
|
-
chain: "BASE",
|
|
3318
|
-
assets: [
|
|
3319
|
-
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
3320
|
-
{ symbol: "WETH", minDepositAmount: "1" }
|
|
3321
|
-
]
|
|
3322
|
-
}
|
|
3323
|
-
];
|
|
3324
|
-
api;
|
|
3325
|
-
auth;
|
|
3326
|
-
transactionExecutor;
|
|
3327
|
-
unwindReceiptWaiter;
|
|
3328
|
-
agentContexts = /* @__PURE__ */ new Map();
|
|
3329
|
-
users = /* @__PURE__ */ new Map();
|
|
3330
|
-
pendingAgents = /* @__PURE__ */ new Map();
|
|
3331
|
-
constructor(owneyApiKey, options = {}) {
|
|
3332
|
-
this.api = new YieldseekerApiClient(
|
|
3333
|
-
owneyApiKey,
|
|
3334
|
-
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
3335
|
-
options.fetchFn
|
|
3336
|
-
);
|
|
3337
|
-
this.auth = new YieldseekerAuth(options.auth);
|
|
3338
|
-
this.transactionExecutor = options.transactionExecutor;
|
|
3339
|
-
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3340
|
-
}
|
|
3341
|
-
async disconnect() {
|
|
3342
|
-
this.auth.clear();
|
|
3343
|
-
for (const key2 of this.users.keys()) {
|
|
3344
|
-
const [walletAddress, chainId] = key2.split(":");
|
|
3345
|
-
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
3346
|
-
}
|
|
3347
|
-
this.users.clear();
|
|
3348
|
-
this.agentContexts.clear();
|
|
3349
|
-
this.pendingAgents.clear();
|
|
3350
|
-
}
|
|
3351
|
-
async activateAgent(state, chainId, asset) {
|
|
3352
|
-
this.assertChain(chainId);
|
|
3353
|
-
const targetAsset = asset ?? "USDC";
|
|
3354
|
-
this.assertAsset(targetAsset);
|
|
3355
|
-
await this.ensureAgent(state, chainId, targetAsset);
|
|
3356
|
-
}
|
|
3357
|
-
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3358
|
-
this.assertChain(chainId);
|
|
3359
|
-
this.assertAsset(asset);
|
|
3360
|
-
if (BigInt(amount) <= 0n) {
|
|
3361
|
-
throw new OwneyError(
|
|
3362
|
-
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
3363
|
-
"Yieldseeker deposits must be greater than zero.",
|
|
3364
|
-
{ amount, minDepositAmount: "1" },
|
|
3365
|
-
this.id
|
|
3366
|
-
);
|
|
3367
|
-
}
|
|
3368
|
-
const context = await this.ensureAgent(state, chainId, asset);
|
|
3369
|
-
let txHash;
|
|
3370
|
-
try {
|
|
3371
|
-
if (depositCallback) {
|
|
3372
|
-
provideDepositVerificationContext(depositCallback, {
|
|
3373
|
-
agentId: "yieldseeker",
|
|
3374
|
-
signature: await this.auth.getToken(state, chainId),
|
|
3375
|
-
userId: context.user.userId,
|
|
3376
|
-
yieldseekerAgentId: context.agent.agentId
|
|
3377
|
-
});
|
|
3378
|
-
txHash = await depositCallback(
|
|
3379
|
-
context.wallet.walletAddress,
|
|
3380
|
-
chainId,
|
|
3381
|
-
amount
|
|
3382
|
-
);
|
|
3383
|
-
await this.waitForReceipt(state, chainId, txHash);
|
|
3384
|
-
} else {
|
|
3385
|
-
txHash = await this.submitTransaction(state, chainId, {
|
|
3386
|
-
from: getAddress2(state.walletAddress),
|
|
3387
|
-
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3388
|
-
data: encodeFunctionData({
|
|
3389
|
-
abi: erc20Abi,
|
|
3390
|
-
functionName: "transfer",
|
|
3391
|
-
args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
|
|
3392
|
-
}),
|
|
3393
|
-
value: "0",
|
|
3394
|
-
chainId
|
|
3395
|
-
});
|
|
3396
|
-
}
|
|
3397
|
-
await this.deployAfterFunding(state, chainId, context);
|
|
3398
|
-
} finally {
|
|
3399
|
-
await this.refreshSnapshotAfterMovement(
|
|
3400
|
-
state,
|
|
3401
|
-
chainId,
|
|
3402
|
-
context,
|
|
3403
|
-
"deposit"
|
|
3404
|
-
);
|
|
3405
|
-
}
|
|
3406
|
-
return {
|
|
3407
|
-
txHash,
|
|
3408
|
-
smartWallet: context.wallet.walletAddress,
|
|
3409
|
-
amount
|
|
3410
|
-
};
|
|
3411
|
-
}
|
|
3412
|
-
async withdraw(state, chainId, asset, amount) {
|
|
3413
|
-
this.assertChain(chainId);
|
|
3414
|
-
this.assertAsset(asset);
|
|
3415
|
-
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
3416
|
-
throw new OwneyError(
|
|
3417
|
-
"WITHDRAW_FAILED",
|
|
3418
|
-
"Yieldseeker withdrawals must be greater than zero.",
|
|
3419
|
-
{ amount },
|
|
3420
|
-
this.id
|
|
3421
|
-
);
|
|
3422
|
-
}
|
|
3423
|
-
const context = await this.findAgent(state, chainId, asset);
|
|
3424
|
-
if (!context) {
|
|
3425
|
-
throw new OwneyError(
|
|
3426
|
-
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3427
|
-
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3428
|
-
{ asset, available: "0" },
|
|
3429
|
-
this.id
|
|
3430
|
-
);
|
|
3431
|
-
}
|
|
3432
|
-
try {
|
|
3433
|
-
const portfolio = await this.loadPortfolioContext(
|
|
3434
|
-
state,
|
|
3435
|
-
chainId,
|
|
3436
|
-
context
|
|
3437
|
-
);
|
|
3438
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3439
|
-
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3440
|
-
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3441
|
-
);
|
|
3442
|
-
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3443
|
-
const deployed = portfolio.positions.reduce(
|
|
3444
|
-
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3445
|
-
0n
|
|
3446
|
-
);
|
|
3447
|
-
const totalAvailable = idle + deployed;
|
|
3448
|
-
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3449
|
-
if (requested > totalAvailable) {
|
|
3450
|
-
throw new OwneyError(
|
|
3451
|
-
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3452
|
-
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3453
|
-
{
|
|
3454
|
-
asset,
|
|
3455
|
-
requested: requested.toString(),
|
|
3456
|
-
available: totalAvailable.toString()
|
|
3457
|
-
},
|
|
3458
|
-
this.id
|
|
3459
|
-
);
|
|
3460
|
-
}
|
|
3461
|
-
let remaining = requested > idle ? requested - idle : 0n;
|
|
3462
|
-
for (const position2 of portfolio.positions) {
|
|
3463
|
-
if (remaining === 0n) break;
|
|
3464
|
-
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3465
|
-
if (available <= 0n) continue;
|
|
3466
|
-
const assetsRaw = available < remaining ? available : remaining;
|
|
3467
|
-
const response = await this.walletRequest(
|
|
3468
|
-
state,
|
|
3469
|
-
chainId,
|
|
3470
|
-
this.agentPath(context, "withdraw-from-position"),
|
|
3471
|
-
{
|
|
3472
|
-
method: "POST",
|
|
3473
|
-
body: {
|
|
3474
|
-
chainId,
|
|
3475
|
-
vaultAddress: position2.yieldOption.address,
|
|
3476
|
-
assetsRaw: assetsRaw.toString()
|
|
3477
|
-
}
|
|
3478
|
-
}
|
|
3479
|
-
);
|
|
3480
|
-
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3481
|
-
throw this.invalidResponse("position withdrawal");
|
|
3482
|
-
}
|
|
3483
|
-
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3484
|
-
remaining -= assetsRaw;
|
|
3485
|
-
}
|
|
3486
|
-
if (remaining > 0n) {
|
|
3487
|
-
throw this.invalidResponse("yield positions", {
|
|
3488
|
-
reason: "Withdrawable positions could not cover the request.",
|
|
3489
|
-
remaining: remaining.toString()
|
|
3490
|
-
});
|
|
3491
|
-
}
|
|
3492
|
-
const account = getAddress2(state.walletAddress);
|
|
3493
|
-
const txHash = await this.submitTransaction(state, chainId, {
|
|
3494
|
-
from: account,
|
|
3495
|
-
to: getAddress2(context.wallet.walletAddress),
|
|
3496
|
-
data: amount === void 0 ? encodeFunctionData({
|
|
3497
|
-
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3498
|
-
functionName: "withdrawAllAssetToUser",
|
|
3499
|
-
args: [account, metadata.address]
|
|
3500
|
-
}) : encodeFunctionData({
|
|
3501
|
-
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3502
|
-
functionName: "withdrawAssetToUser",
|
|
3503
|
-
args: [account, metadata.address, requested]
|
|
3504
|
-
}),
|
|
3505
|
-
value: "0",
|
|
3506
|
-
chainId
|
|
3507
|
-
});
|
|
3508
|
-
return {
|
|
3509
|
-
txHash,
|
|
3510
|
-
type: amount === void 0 ? "full" : "partial",
|
|
3511
|
-
amount: requested.toString()
|
|
3512
|
-
};
|
|
3513
|
-
} finally {
|
|
3514
|
-
await this.refreshSnapshotAfterMovement(
|
|
3515
|
-
state,
|
|
3516
|
-
chainId,
|
|
3517
|
-
context,
|
|
3518
|
-
"withdrawal"
|
|
3519
|
-
);
|
|
3520
|
-
}
|
|
3521
|
-
}
|
|
3522
|
-
async getBalances(state, chainId) {
|
|
3523
|
-
this.assertChain(chainId);
|
|
3524
|
-
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3525
|
-
}
|
|
3526
|
-
async getEarnings(state, chainId) {
|
|
3527
|
-
this.assertChain(chainId);
|
|
3528
|
-
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3529
|
-
}
|
|
3530
|
-
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3531
|
-
this.assertChain(chainId);
|
|
3532
|
-
const asset = tokenSymbol?.toUpperCase();
|
|
3533
|
-
if (asset !== void 0) this.assertAsset(asset);
|
|
3534
|
-
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3535
|
-
...asset ? { asset } : {},
|
|
3536
|
-
historic: true
|
|
3537
|
-
});
|
|
3538
|
-
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3539
|
-
}
|
|
3540
|
-
async getHistory(state, chainId, options) {
|
|
3541
|
-
this.assertChain(chainId);
|
|
3542
|
-
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3543
|
-
if (asset !== void 0) this.assertAsset(asset);
|
|
3544
|
-
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3545
|
-
...asset ? { asset } : {},
|
|
3546
|
-
historic: true,
|
|
3547
|
-
actions: true
|
|
3548
|
-
});
|
|
3549
|
-
return mapYieldseekerHistory(contexts, {
|
|
3550
|
-
limit: options?.limit ?? 10,
|
|
3551
|
-
ownerAddress: state.walletAddress,
|
|
3552
|
-
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3553
|
-
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3554
|
-
});
|
|
3555
|
-
}
|
|
3556
|
-
async getUserProfile(state, chainId) {
|
|
3557
|
-
this.assertChain(chainId);
|
|
3558
|
-
return mapYieldseekerProfile(
|
|
3559
|
-
state.walletAddress,
|
|
3560
|
-
await this.loadPortfolio(state, chainId, {})
|
|
3561
|
-
);
|
|
3562
|
-
}
|
|
3563
|
-
async getAgentApy(days, options) {
|
|
3564
|
-
this.assertOptionalChain(options?.chainId);
|
|
3565
|
-
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3566
|
-
if (requested !== void 0) this.assertAsset(requested);
|
|
3567
|
-
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3568
|
-
const values = await Promise.all(
|
|
3569
|
-
assets.map(async (asset) => {
|
|
3570
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3571
|
-
const response = await this.api.request(
|
|
3572
|
-
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3573
|
-
);
|
|
3574
|
-
if (!Array.isArray(response?.yieldOptions)) {
|
|
3575
|
-
throw this.invalidResponse("yield options");
|
|
3576
|
-
}
|
|
3577
|
-
return { asset, yieldOptions: response.yieldOptions };
|
|
3578
|
-
})
|
|
3579
|
-
);
|
|
3580
|
-
return mapYieldseekerAgentApy(values, days);
|
|
3581
|
-
}
|
|
3582
|
-
userKey(state, chainId) {
|
|
3583
|
-
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3584
|
-
}
|
|
3585
|
-
contextKey(state, chainId, asset) {
|
|
3586
|
-
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3587
|
-
}
|
|
3588
|
-
async resolveUser(state, chainId) {
|
|
3589
|
-
const key2 = this.userKey(state, chainId);
|
|
3590
|
-
const inMemory = this.users.get(key2);
|
|
3591
|
-
if (inMemory) return inMemory;
|
|
3592
|
-
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3593
|
-
if (persisted) {
|
|
3594
|
-
this.users.set(key2, persisted);
|
|
3595
|
-
return persisted;
|
|
3596
|
-
}
|
|
3597
|
-
const walletAddress = getAddress2(state.walletAddress);
|
|
3598
|
-
let user = null;
|
|
3599
|
-
try {
|
|
3600
|
-
const login = await this.providerRequest(
|
|
3601
|
-
state,
|
|
3602
|
-
chainId,
|
|
3603
|
-
"/users/login-with-wallet",
|
|
3604
|
-
{ method: "POST", body: { walletAddress } }
|
|
3605
|
-
);
|
|
3606
|
-
user = login?.user ?? null;
|
|
3607
|
-
if (!user) {
|
|
3608
|
-
throw this.invalidResponse("wallet login", {
|
|
3609
|
-
reason: "A successful login returned no user."
|
|
3610
|
-
});
|
|
3611
|
-
}
|
|
3612
|
-
} catch (error) {
|
|
3613
|
-
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3614
|
-
if (error instanceof OwneyError) throw error;
|
|
3615
|
-
throw this.mapApiError(error);
|
|
3616
|
-
}
|
|
3617
|
-
let created;
|
|
3618
|
-
for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
|
|
3619
|
-
try {
|
|
3620
|
-
created = await this.providerRequest(
|
|
3621
|
-
state,
|
|
3622
|
-
chainId,
|
|
3623
|
-
"/users",
|
|
3624
|
-
{
|
|
3625
|
-
method: "POST",
|
|
3626
|
-
body: {
|
|
3627
|
-
walletAddress,
|
|
3628
|
-
username: generateYieldseekerUsername()
|
|
3629
|
-
}
|
|
3630
|
-
}
|
|
3631
|
-
);
|
|
3632
|
-
break;
|
|
3633
|
-
} catch (createError) {
|
|
3634
|
-
const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
|
|
3635
|
-
if (canRetry) continue;
|
|
3636
|
-
throw this.mapApiError(createError);
|
|
3637
|
-
}
|
|
3638
|
-
}
|
|
3639
|
-
user = created?.user ?? null;
|
|
3640
|
-
}
|
|
3641
|
-
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3642
|
-
throw this.invalidResponse("wallet identity");
|
|
3643
|
-
}
|
|
3644
|
-
const resolved = { userId: user.userId };
|
|
3645
|
-
this.users.set(key2, resolved);
|
|
3646
|
-
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3647
|
-
return resolved;
|
|
3648
|
-
}
|
|
3649
|
-
forgetUser(state, chainId) {
|
|
3650
|
-
this.users.delete(this.userKey(state, chainId));
|
|
3651
|
-
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3652
|
-
}
|
|
3653
|
-
async ensureAgent(state, chainId, asset) {
|
|
3654
|
-
const key2 = this.contextKey(state, chainId, asset);
|
|
3655
|
-
const cached = this.agentContexts.get(key2);
|
|
3656
|
-
if (cached) return cached;
|
|
3657
|
-
const pending = this.pendingAgents.get(key2);
|
|
3658
|
-
if (pending) return pending;
|
|
3659
|
-
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3660
|
-
(context) => {
|
|
3661
|
-
if (!context) throw this.invalidResponse("agent creation");
|
|
3662
|
-
this.agentContexts.set(key2, context);
|
|
3663
|
-
return context;
|
|
3664
|
-
}
|
|
3665
|
-
);
|
|
3666
|
-
this.pendingAgents.set(key2, request);
|
|
3667
|
-
try {
|
|
3668
|
-
return await request;
|
|
3669
|
-
} finally {
|
|
3670
|
-
this.pendingAgents.delete(key2);
|
|
3671
|
-
}
|
|
3672
|
-
}
|
|
3673
|
-
async findAgent(state, chainId, asset) {
|
|
3674
|
-
const key2 = this.contextKey(state, chainId, asset);
|
|
3675
|
-
const cached = this.agentContexts.get(key2);
|
|
3676
|
-
if (cached) return cached;
|
|
3677
|
-
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3678
|
-
if (context) this.agentContexts.set(key2, context);
|
|
3679
|
-
return context;
|
|
3680
|
-
}
|
|
3681
|
-
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3682
|
-
const user = await this.resolveUser(state, chainId);
|
|
3683
|
-
const response = await this.walletRequest(
|
|
3684
|
-
state,
|
|
3685
|
-
chainId,
|
|
3686
|
-
`/users/${user.userId}/agents`
|
|
3687
|
-
);
|
|
3688
|
-
if (!Array.isArray(response?.agents)) {
|
|
3689
|
-
throw this.invalidResponse("agent list");
|
|
3690
|
-
}
|
|
3691
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3692
|
-
let agent = response.agents.find(
|
|
3693
|
-
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3694
|
-
);
|
|
3695
|
-
if (!agent && createIfMissing) {
|
|
3696
|
-
const created = await this.walletRequest(
|
|
3697
|
-
state,
|
|
3698
|
-
chainId,
|
|
3699
|
-
`/users/${user.userId}/agents`,
|
|
3700
|
-
{
|
|
3701
|
-
method: "POST",
|
|
3702
|
-
body: {
|
|
3703
|
-
name: OWNEY_AGENT_NAME,
|
|
3704
|
-
emoji: "\u{1F989}",
|
|
3705
|
-
chainId,
|
|
3706
|
-
assetAddress: metadata.address,
|
|
3707
|
-
type: "vault",
|
|
3708
|
-
rulePreset: null
|
|
3709
|
-
}
|
|
3710
|
-
}
|
|
3711
|
-
);
|
|
3712
|
-
agent = created?.agent;
|
|
3713
|
-
}
|
|
3714
|
-
if (!agent) return null;
|
|
3715
|
-
this.assertAgent(agent);
|
|
3716
|
-
const walletResponse = await this.walletRequest(
|
|
3717
|
-
state,
|
|
3718
|
-
chainId,
|
|
3719
|
-
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3720
|
-
);
|
|
3721
|
-
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3722
|
-
throw this.invalidResponse("agent wallet");
|
|
3723
|
-
}
|
|
3724
|
-
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3725
|
-
}
|
|
3726
|
-
async loadPortfolio(state, chainId, options) {
|
|
3727
|
-
const user = await this.resolveUser(state, chainId);
|
|
3728
|
-
const response = await this.walletRequest(
|
|
3729
|
-
state,
|
|
3730
|
-
chainId,
|
|
3731
|
-
`/users/${user.userId}/agents`
|
|
3732
|
-
);
|
|
3733
|
-
if (!Array.isArray(response?.agents)) {
|
|
3734
|
-
throw this.invalidResponse("agent list");
|
|
3735
|
-
}
|
|
3736
|
-
const contexts = [];
|
|
3737
|
-
for (const agent of response.agents) {
|
|
3738
|
-
const asset = this.assetForAgent(agent);
|
|
3739
|
-
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3740
|
-
continue;
|
|
3741
|
-
}
|
|
3742
|
-
this.assertAgent(agent);
|
|
3743
|
-
const walletResponse = await this.walletRequest(
|
|
3744
|
-
state,
|
|
3745
|
-
chainId,
|
|
3746
|
-
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3747
|
-
);
|
|
3748
|
-
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3749
|
-
throw this.invalidResponse("agent wallet");
|
|
3750
|
-
}
|
|
3751
|
-
const context = {
|
|
3752
|
-
user,
|
|
3753
|
-
agent,
|
|
3754
|
-
wallet: walletResponse.agentWallet,
|
|
3755
|
-
asset
|
|
3756
|
-
};
|
|
3757
|
-
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3758
|
-
contexts.push(context);
|
|
3759
|
-
}
|
|
3760
|
-
return Promise.all(
|
|
3761
|
-
contexts.map(
|
|
3762
|
-
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3763
|
-
)
|
|
3764
|
-
);
|
|
3765
|
-
}
|
|
3766
|
-
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3767
|
-
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3768
|
-
this.walletRequest(
|
|
3769
|
-
state,
|
|
3770
|
-
chainId,
|
|
3771
|
-
`${this.agentPath(context, "snapshot")}${query({
|
|
3772
|
-
shouldOnlyUseRecentValue: true,
|
|
3773
|
-
shouldAllowStaleOnError: true
|
|
3774
|
-
})}`
|
|
3775
|
-
),
|
|
3776
|
-
this.walletRequest(
|
|
3777
|
-
state,
|
|
3778
|
-
chainId,
|
|
3779
|
-
this.agentPath(context, "yield-positions")
|
|
3780
|
-
),
|
|
3781
|
-
options.historic ? this.walletRequest(
|
|
3782
|
-
state,
|
|
3783
|
-
chainId,
|
|
3784
|
-
this.agentPath(context, "wallet/historic-position")
|
|
3785
|
-
) : Promise.resolve(void 0),
|
|
3786
|
-
options.actions ? this.walletRequest(
|
|
3787
|
-
state,
|
|
3788
|
-
chainId,
|
|
3789
|
-
this.agentPath(context, "actions")
|
|
3790
|
-
) : Promise.resolve(void 0)
|
|
3791
|
-
]);
|
|
3792
|
-
if (!snapshot?.agentSnapshot) {
|
|
3793
|
-
throw this.invalidResponse("agent snapshot");
|
|
3794
|
-
}
|
|
3795
|
-
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3796
|
-
throw this.invalidResponse("yield positions");
|
|
3797
|
-
}
|
|
3798
|
-
return {
|
|
3799
|
-
...context,
|
|
3800
|
-
snapshot: snapshot.agentSnapshot,
|
|
3801
|
-
positions: positions.yieldPositions,
|
|
3802
|
-
...historic?.position ? { historic: historic.position } : {},
|
|
3803
|
-
...actions?.actions ? { actions: actions.actions } : {}
|
|
3804
|
-
};
|
|
3805
|
-
}
|
|
3806
|
-
async deployAfterFunding(state, chainId, context) {
|
|
3807
|
-
if (context.wallet.initializedDate != null) return;
|
|
3808
|
-
try {
|
|
3809
|
-
const deployed = await this.walletRequest(
|
|
3810
|
-
state,
|
|
3811
|
-
chainId,
|
|
3812
|
-
this.agentPath(context, "deploy"),
|
|
3813
|
-
{ method: "POST", body: {} }
|
|
3814
|
-
);
|
|
3815
|
-
if (deployed?.agentWallet) {
|
|
3816
|
-
context.wallet = deployed.agentWallet;
|
|
3817
|
-
}
|
|
3818
|
-
} catch (error) {
|
|
3819
|
-
console.warn(
|
|
3820
|
-
"[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
|
|
3821
|
-
error
|
|
3822
|
-
);
|
|
3823
|
-
}
|
|
3824
|
-
}
|
|
3825
|
-
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3826
|
-
try {
|
|
3827
|
-
const response = await this.walletRequest(
|
|
3828
|
-
state,
|
|
3829
|
-
chainId,
|
|
3830
|
-
`${this.agentPath(context, "snapshot")}${query({
|
|
3831
|
-
shouldForceRefresh: true
|
|
3832
|
-
})}`
|
|
3833
|
-
);
|
|
3834
|
-
if (!response?.agentSnapshot) {
|
|
3835
|
-
throw this.invalidResponse("agent snapshot refresh");
|
|
3836
|
-
}
|
|
3837
|
-
} catch (error) {
|
|
3838
|
-
console.warn(
|
|
3839
|
-
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3840
|
-
error
|
|
3841
|
-
);
|
|
3842
|
-
}
|
|
3843
|
-
}
|
|
3844
|
-
agentPath(context, suffix) {
|
|
3845
|
-
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
3846
|
-
}
|
|
3847
|
-
async walletRequest(state, chainId, path, options = {}) {
|
|
3848
|
-
try {
|
|
3849
|
-
return await this.providerRequest(state, chainId, path, options);
|
|
3850
|
-
} catch (error) {
|
|
3851
|
-
throw this.mapApiError(error);
|
|
3852
|
-
}
|
|
3853
|
-
}
|
|
3854
|
-
async providerRequest(state, chainId, path, options = {}) {
|
|
3855
|
-
this.assertChain(chainId);
|
|
3856
|
-
const request = (signature2) => this.api.request(path, {
|
|
3857
|
-
...options,
|
|
3858
|
-
signature: signature2
|
|
3859
|
-
});
|
|
3860
|
-
let signature = await this.auth.getToken(state, chainId);
|
|
3861
|
-
try {
|
|
3862
|
-
return await request(signature);
|
|
3863
|
-
} catch (error) {
|
|
3864
|
-
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
3865
|
-
if (error.providerCode === "NO_USER") throw error;
|
|
3866
|
-
if (!error.isAuthenticationError) throw error;
|
|
3867
|
-
signature = await this.auth.refreshToken(state, chainId, signature);
|
|
3868
|
-
try {
|
|
3869
|
-
return await request(signature);
|
|
3870
|
-
} catch (retryError) {
|
|
3871
|
-
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3872
|
-
this.forgetUser(state, chainId);
|
|
3873
|
-
}
|
|
3874
|
-
throw retryError;
|
|
3875
|
-
}
|
|
3876
|
-
}
|
|
3877
|
-
}
|
|
3878
|
-
mapApiError(error) {
|
|
3879
|
-
if (!(error instanceof YieldseekerApiError)) {
|
|
3880
|
-
return new OwneyError(
|
|
3881
|
-
"AGENT_API_ERROR",
|
|
3882
|
-
"Yieldseeker request failed.",
|
|
3883
|
-
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3884
|
-
this.id
|
|
3885
|
-
);
|
|
3886
|
-
}
|
|
3887
|
-
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3888
|
-
return new OwneyError(
|
|
3889
|
-
code,
|
|
3890
|
-
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3891
|
-
{
|
|
3892
|
-
statusCode: error.status,
|
|
3893
|
-
providerCode: error.providerCode,
|
|
3894
|
-
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3895
|
-
},
|
|
3896
|
-
this.id
|
|
3897
|
-
);
|
|
3898
|
-
}
|
|
3899
|
-
async submitTransaction(state, chainId, transaction) {
|
|
3900
|
-
if (this.transactionExecutor) {
|
|
3901
|
-
return this.transactionExecutor(state, chainId, transaction);
|
|
3902
|
-
}
|
|
3903
|
-
this.assertTransaction(transaction, state, chainId);
|
|
3904
|
-
const account = getAddress2(state.walletAddress);
|
|
3905
|
-
const walletClient = createWalletClient2({
|
|
3906
|
-
account,
|
|
3907
|
-
chain: base3,
|
|
3908
|
-
transport: custom2(state.provider)
|
|
3909
|
-
});
|
|
3910
|
-
const publicClient = createPublicClient3({
|
|
3911
|
-
chain: base3,
|
|
3912
|
-
transport: custom2(state.provider)
|
|
3913
|
-
});
|
|
3914
|
-
await ensureWalletOnChain(
|
|
3915
|
-
publicClient,
|
|
3916
|
-
walletClient,
|
|
3917
|
-
8453
|
|
3918
|
-
);
|
|
3919
|
-
const hash = await walletClient.sendTransaction({
|
|
3920
|
-
account,
|
|
3921
|
-
chain: base3,
|
|
3922
|
-
to: getAddress2(transaction.to),
|
|
3923
|
-
data: transaction.data,
|
|
3924
|
-
value: BigInt(transaction.value)
|
|
3925
|
-
});
|
|
3926
|
-
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3927
|
-
hash,
|
|
3928
|
-
confirmations: 1
|
|
3929
|
-
});
|
|
3930
|
-
if (receipt.status !== "success") {
|
|
3931
|
-
throw new OwneyError(
|
|
3932
|
-
"AGENT_TRANSACTION_REVERTED",
|
|
3933
|
-
`Yieldseeker transaction reverted (${hash}).`,
|
|
3934
|
-
{ transactionHash: hash },
|
|
3935
|
-
this.id
|
|
3936
|
-
);
|
|
3937
|
-
}
|
|
3938
|
-
return hash;
|
|
3939
|
-
}
|
|
3940
|
-
async waitForReceipt(state, chainId, transactionHash) {
|
|
3941
|
-
if (this.unwindReceiptWaiter) {
|
|
3942
|
-
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3943
|
-
return;
|
|
3944
|
-
}
|
|
3945
|
-
const publicClient = createPublicClient3({
|
|
3946
|
-
chain: base3,
|
|
3947
|
-
transport: custom2(state.provider)
|
|
3948
|
-
});
|
|
3949
|
-
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3950
|
-
hash: transactionHash,
|
|
3951
|
-
confirmations: 1
|
|
3952
|
-
});
|
|
3953
|
-
if (receipt.status !== "success") {
|
|
3954
|
-
throw new OwneyError(
|
|
3955
|
-
"AGENT_TRANSACTION_REVERTED",
|
|
3956
|
-
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3957
|
-
{ transactionHash },
|
|
3958
|
-
this.id
|
|
3959
|
-
);
|
|
3960
|
-
}
|
|
3961
|
-
}
|
|
3962
|
-
assertTransaction(transaction, state, chainId) {
|
|
3963
|
-
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)) {
|
|
3964
|
-
throw this.invalidResponse("transaction");
|
|
3965
|
-
}
|
|
3966
|
-
}
|
|
3967
|
-
assertAgent(agent) {
|
|
3968
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress2(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3969
|
-
throw this.invalidResponse("agent");
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
isOwneyAgent(agent) {
|
|
3973
|
-
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3974
|
-
}
|
|
3975
|
-
assetForAgent(agent) {
|
|
3976
|
-
for (const asset of ["USDC", "WETH"]) {
|
|
3977
|
-
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3978
|
-
return asset;
|
|
3979
|
-
}
|
|
3980
|
-
}
|
|
3981
|
-
return null;
|
|
3982
|
-
}
|
|
3983
|
-
isTransactionHash(value) {
|
|
3984
|
-
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3985
|
-
}
|
|
3986
|
-
assertChain(chainId) {
|
|
3987
|
-
if (chainId !== 8453) {
|
|
3988
|
-
throw new OwneyError(
|
|
3989
|
-
"CHAIN_UNSUPPORTED",
|
|
3990
|
-
`Yieldseeker does not support chain ${chainId}.`,
|
|
3991
|
-
{ chainId, supportedChainIds: [8453] },
|
|
3992
|
-
this.id
|
|
3993
|
-
);
|
|
3994
|
-
}
|
|
3995
|
-
}
|
|
3996
|
-
assertOptionalChain(chainId) {
|
|
3997
|
-
if (chainId !== void 0) this.assertChain(chainId);
|
|
3998
|
-
}
|
|
3999
|
-
assertAsset(asset) {
|
|
4000
|
-
if (asset !== "USDC" && asset !== "WETH") {
|
|
4001
|
-
throw new OwneyError(
|
|
4002
|
-
"ASSET_UNSUPPORTED",
|
|
4003
|
-
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
4004
|
-
{
|
|
4005
|
-
asset,
|
|
4006
|
-
supportedAssets: ["USDC", "WETH"],
|
|
4007
|
-
providerAlsoAdvertises: ["cbBTC"]
|
|
4008
|
-
},
|
|
4009
|
-
this.id
|
|
4010
|
-
);
|
|
4011
|
-
}
|
|
4012
|
-
}
|
|
4013
|
-
invalidResponse(operation, details = {}) {
|
|
4014
|
-
return new OwneyError(
|
|
4015
|
-
"AGENT_INVALID_RESPONSE",
|
|
4016
|
-
`Yieldseeker returned an invalid ${operation} response.`,
|
|
4017
|
-
details,
|
|
4018
|
-
this.id
|
|
4019
|
-
);
|
|
4020
|
-
}
|
|
4021
|
-
};
|
|
4022
|
-
|
|
4023
|
-
// src/lib/routing-api.ts
|
|
4024
|
-
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
4025
|
-
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
4026
|
-
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
4027
|
-
try {
|
|
4028
|
-
const res = await fetch(url, {
|
|
4029
|
-
method: "GET",
|
|
4030
|
-
headers: {
|
|
4031
|
-
"Content-Type": "application/json",
|
|
4032
|
-
"x-owney-api-key": `${apiKey}`
|
|
4033
|
-
}
|
|
4034
|
-
});
|
|
4035
|
-
if (!res.ok) {
|
|
4036
|
-
if (res.status !== 404) {
|
|
4037
|
-
console.warn(
|
|
4038
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
4039
|
-
);
|
|
4040
|
-
}
|
|
4041
|
-
return null;
|
|
4042
|
-
}
|
|
4043
|
-
const json = await res.json();
|
|
4044
|
-
const policy = json.success ? json.data ?? null : null;
|
|
4045
|
-
debugLog(
|
|
4046
|
-
"owney-sdk",
|
|
4047
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
4048
|
-
policy ?? void 0
|
|
4049
|
-
);
|
|
4050
|
-
return policy;
|
|
4051
|
-
} catch (error) {
|
|
4052
|
-
console.warn(
|
|
4053
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
4054
|
-
error instanceof Error ? error.message : String(error)
|
|
4055
|
-
);
|
|
4056
|
-
return null;
|
|
4057
|
-
}
|
|
4058
|
-
}
|
|
4059
|
-
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
4060
|
-
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
4061
|
-
const res = await fetch(url, {
|
|
4062
|
-
method: "GET",
|
|
4063
|
-
headers: {
|
|
4064
|
-
"Content-Type": "application/json",
|
|
4065
|
-
"x-owney-api-key": `${apiKey}`
|
|
4066
|
-
}
|
|
4067
|
-
});
|
|
4068
|
-
if (!res.ok) {
|
|
4069
|
-
const text = await res.text().catch(() => "");
|
|
4070
|
-
throw new OwneyError(
|
|
4071
|
-
"API_ROUTING_ERROR",
|
|
4072
|
-
`Routing API error ${res.status}: ${text}`,
|
|
4073
|
-
{ statusCode: res.status, responseBody: text }
|
|
4074
|
-
);
|
|
4075
|
-
}
|
|
4076
|
-
const json = await res.json();
|
|
4077
|
-
if (!json.success) {
|
|
4078
|
-
throw new OwneyError(
|
|
4079
|
-
"API_ROUTING_FAILED",
|
|
4080
|
-
`Routing API request failed: ${json.message}`,
|
|
4081
|
-
{ message: json.message }
|
|
4082
|
-
);
|
|
4083
|
-
}
|
|
4084
|
-
return json.data;
|
|
4085
|
-
}
|
|
4086
|
-
|
|
4087
|
-
// src/lib/health-report.ts
|
|
4088
|
-
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
4089
|
-
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
4090
|
-
try {
|
|
4091
|
-
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
4092
|
-
method: "POST",
|
|
4093
|
-
headers: {
|
|
4094
|
-
"Content-Type": "application/json",
|
|
4095
|
-
"x-owney-api-key": apiKey
|
|
4096
|
-
},
|
|
4097
|
-
body: JSON.stringify({
|
|
4098
|
-
agent_type: agentType,
|
|
4099
|
-
error_code: errorCode,
|
|
4100
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4101
|
-
})
|
|
4102
|
-
});
|
|
4103
|
-
} catch (err) {
|
|
4104
|
-
console.warn(
|
|
4105
|
-
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
4106
|
-
err instanceof Error ? err.message : err
|
|
4107
|
-
);
|
|
4108
|
-
}
|
|
4109
|
-
}
|
|
4110
|
-
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
4111
|
-
try {
|
|
4112
|
-
return await fn();
|
|
4113
|
-
} catch (err) {
|
|
4114
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
4115
|
-
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
4116
|
-
throw err;
|
|
4117
|
-
}
|
|
4118
|
-
}
|
|
4119
|
-
|
|
4120
|
-
// src/lib/helpers/withdraw-helper.ts
|
|
4121
|
-
import { parseUnits } from "viem";
|
|
4122
|
-
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
4123
|
-
const target = asset.toUpperCase();
|
|
4124
|
-
return agents.map((agent) => {
|
|
4125
|
-
const agentBalance = aggregated[agent.id];
|
|
4126
|
-
const tokenBalance = agentBalance?.tokens.find(
|
|
4127
|
-
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4128
|
-
);
|
|
4129
|
-
let balance = tokenBalance ? parseUnits(tokenBalance.amount, decimals) : 0n;
|
|
4130
|
-
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
4131
|
-
const chainNameById = {
|
|
4132
|
-
1: "ETHEREUM",
|
|
4133
|
-
8453: "BASE",
|
|
4134
|
-
42161: "ARBITRUM"
|
|
4135
|
-
};
|
|
4136
|
-
const targetChain = chainNameById[chainId];
|
|
4137
|
-
for (const position2 of agentBalance?.positions ?? []) {
|
|
4138
|
-
const positionChain = position2.chain.trim().toUpperCase();
|
|
4139
|
-
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
4140
|
-
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
4141
|
-
if (position2.amountRaw !== void 0) {
|
|
4142
|
-
try {
|
|
4143
|
-
balance += BigInt(position2.amountRaw);
|
|
4144
|
-
continue;
|
|
4145
|
-
} catch {
|
|
4146
|
-
}
|
|
4147
|
-
}
|
|
4148
|
-
balance += parseUnits(position2.amount, decimals);
|
|
4149
|
-
}
|
|
4150
|
-
}
|
|
4151
|
-
return { agent, balance };
|
|
4152
|
-
});
|
|
4153
|
-
}
|
|
4154
|
-
function planProportionalShares(balances, requested, totalAvailable) {
|
|
4155
|
-
const plans = balances.map(({ agent, balance }) => ({
|
|
4156
|
-
agent,
|
|
4157
|
-
balance,
|
|
4158
|
-
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
4159
|
-
}));
|
|
4160
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
4161
|
-
let remainder = requested - assigned;
|
|
4162
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
4163
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
4164
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
4165
|
-
});
|
|
4166
|
-
for (const p of byHeadroom) {
|
|
4167
|
-
if (remainder === 0n) break;
|
|
4168
|
-
const headroom = p.balance - p.planned;
|
|
4169
|
-
if (headroom <= 0n) continue;
|
|
4170
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
4171
|
-
p.planned += take;
|
|
4172
|
-
remainder -= take;
|
|
4173
|
-
}
|
|
4174
|
-
return plans;
|
|
4175
|
-
}
|
|
4176
|
-
function planDisabledDrain(disabled, requested) {
|
|
4177
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
4178
|
-
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
4179
|
-
);
|
|
4180
|
-
const plans = [];
|
|
4181
|
-
let remaining = requested;
|
|
4182
|
-
for (const { agent, balance } of sorted) {
|
|
4183
|
-
if (remaining === 0n) {
|
|
4184
|
-
plans.push({ agent, balance, planned: 0n });
|
|
4185
|
-
continue;
|
|
4186
|
-
}
|
|
4187
|
-
const take = balance < remaining ? balance : remaining;
|
|
4188
|
-
plans.push({ agent, balance, planned: take });
|
|
4189
|
-
remaining -= take;
|
|
4190
|
-
}
|
|
4191
|
-
return { plans, remaining };
|
|
4192
|
-
}
|
|
4193
|
-
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
4194
|
-
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
4195
|
-
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
4196
|
-
const totalHeadroom = candidates.reduce(
|
|
4197
|
-
(s, c) => s + (c.balance - c.planned),
|
|
4198
|
-
0n
|
|
4199
|
-
);
|
|
4200
|
-
if (totalHeadroom === 0n) return;
|
|
4201
|
-
let distributed = 0n;
|
|
4202
|
-
for (const c of candidates) {
|
|
4203
|
-
const headroom = c.balance - c.planned;
|
|
4204
|
-
const proportional = headroom * amount / totalHeadroom;
|
|
4205
|
-
const give = proportional > headroom ? headroom : proportional;
|
|
4206
|
-
c.planned += give;
|
|
4207
|
-
distributed += give;
|
|
4208
|
-
}
|
|
4209
|
-
let leftover = amount - distributed;
|
|
4210
|
-
for (const c of candidates) {
|
|
4211
|
-
if (leftover === 0n) break;
|
|
4212
|
-
const headroom = c.balance - c.planned;
|
|
4213
|
-
if (headroom <= 0n) continue;
|
|
4214
|
-
const take = headroom < leftover ? headroom : leftover;
|
|
4215
|
-
c.planned += take;
|
|
4216
|
-
leftover -= take;
|
|
4217
|
-
}
|
|
2386
|
+
return publicClient.readContract({
|
|
2387
|
+
address: token,
|
|
2388
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2389
|
+
functionName: "allowance",
|
|
2390
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
2391
|
+
});
|
|
4218
2392
|
}
|
|
4219
|
-
function
|
|
4220
|
-
return
|
|
2393
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
2394
|
+
return publicClient.readContract({
|
|
2395
|
+
address: token,
|
|
2396
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2397
|
+
functionName: "balanceOf",
|
|
2398
|
+
args: [owner]
|
|
2399
|
+
});
|
|
4221
2400
|
}
|
|
4222
2401
|
|
|
4223
|
-
// src/lib/
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
2402
|
+
// src/lib/chain-guard.ts
|
|
2403
|
+
var CHAIN_NAMES = {
|
|
2404
|
+
1: "Ethereum",
|
|
2405
|
+
8453: "Base",
|
|
2406
|
+
42161: "Arbitrum"
|
|
2407
|
+
};
|
|
2408
|
+
function chainName(chainId) {
|
|
2409
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2410
|
+
}
|
|
2411
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2412
|
+
const actual = await pub.getChainId();
|
|
2413
|
+
if (actual === expected) return;
|
|
2414
|
+
try {
|
|
2415
|
+
await wallet.switchChain({ id: expected });
|
|
2416
|
+
} catch (error) {
|
|
2417
|
+
throw new OwneyError(
|
|
2418
|
+
"CHAIN_MISMATCH",
|
|
2419
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2420
|
+
{
|
|
2421
|
+
expectedChainId: expected,
|
|
2422
|
+
actualChainId: actual,
|
|
2423
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2424
|
+
}
|
|
2425
|
+
);
|
|
4228
2426
|
}
|
|
4229
|
-
const
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
2427
|
+
const after = await pub.getChainId();
|
|
2428
|
+
if (after !== expected) {
|
|
2429
|
+
throw new OwneyError(
|
|
2430
|
+
"CHAIN_MISMATCH",
|
|
2431
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2432
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
2433
|
+
);
|
|
4236
2434
|
}
|
|
4237
|
-
return balance.tokens.reduce((total, token) => {
|
|
4238
|
-
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
4239
|
-
return total;
|
|
4240
|
-
}
|
|
4241
|
-
const amount = Number(token.amount);
|
|
4242
|
-
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
4243
|
-
}, 0);
|
|
4244
2435
|
}
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
if (points.length === 1 && !points[0].historicalBalance) {
|
|
4259
|
-
return [{ date, apy: points[0].apy }];
|
|
2436
|
+
|
|
2437
|
+
// src/lib/sponsored-deposit.ts
|
|
2438
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2439
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2440
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2441
|
+
return async (smartWallet, chainId, amount) => {
|
|
2442
|
+
const cid = chainId;
|
|
2443
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2444
|
+
if (!token) {
|
|
2445
|
+
throw new OwneyError(
|
|
2446
|
+
"CHAIN_UNSUPPORTED",
|
|
2447
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2448
|
+
);
|
|
4260
2449
|
}
|
|
4261
|
-
const
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
}
|
|
4271
|
-
|
|
4272
|
-
const sums = {};
|
|
4273
|
-
const weights = {};
|
|
4274
|
-
for (const id of Object.keys(agentApys)) {
|
|
4275
|
-
const cells = agentApys[id].apyByChainAndAsset;
|
|
4276
|
-
const balance = agentBalances[id] ?? 0;
|
|
4277
|
-
if (!cells || balance <= 0) continue;
|
|
4278
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
4279
|
-
if (!perAsset) continue;
|
|
4280
|
-
const chainId = Number(chainKey);
|
|
4281
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
4282
|
-
const apy = Number(apyValue ?? 0);
|
|
4283
|
-
if (apy === 0) continue;
|
|
4284
|
-
sums[chainId] ??= {};
|
|
4285
|
-
weights[chainId] ??= {};
|
|
4286
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
4287
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2450
|
+
const pub = deps.getPublicClient(cid);
|
|
2451
|
+
const wallet = deps.getWalletClient(cid);
|
|
2452
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2453
|
+
try {
|
|
2454
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2455
|
+
if (balance < BigInt(amount)) {
|
|
2456
|
+
throw new OwneyError(
|
|
2457
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2458
|
+
"Insufficient balance for this deposit.",
|
|
2459
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2460
|
+
);
|
|
4288
2461
|
}
|
|
2462
|
+
} catch (err) {
|
|
2463
|
+
if (err instanceof OwneyError) throw err;
|
|
2464
|
+
console.warn(
|
|
2465
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2466
|
+
err instanceof Error ? err.message : String(err)
|
|
2467
|
+
);
|
|
4289
2468
|
}
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
2469
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2470
|
+
const validAfter = 0n;
|
|
2471
|
+
const validBefore = BigInt(
|
|
2472
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2473
|
+
);
|
|
2474
|
+
const nonce = randomAuthNonce();
|
|
2475
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2476
|
+
token,
|
|
2477
|
+
chainId: cid,
|
|
2478
|
+
tokenName,
|
|
2479
|
+
tokenVersion,
|
|
2480
|
+
message: {
|
|
2481
|
+
from: deps.ownerAddress,
|
|
2482
|
+
to: smartWallet,
|
|
2483
|
+
value: BigInt(amount),
|
|
2484
|
+
validAfter,
|
|
2485
|
+
validBefore,
|
|
2486
|
+
nonce
|
|
2487
|
+
}
|
|
2488
|
+
});
|
|
2489
|
+
const authSignature = await wallet.signTypedData({
|
|
2490
|
+
account: deps.ownerAddress,
|
|
2491
|
+
...typedData
|
|
2492
|
+
});
|
|
2493
|
+
deps.onApproved?.();
|
|
2494
|
+
const result = await post({
|
|
2495
|
+
baseUrl: deps.baseUrl,
|
|
2496
|
+
apiKey: deps.apiKey,
|
|
2497
|
+
body: {
|
|
2498
|
+
chainId: cid,
|
|
2499
|
+
token,
|
|
2500
|
+
from: deps.ownerAddress,
|
|
2501
|
+
to: smartWallet,
|
|
2502
|
+
value: amount,
|
|
2503
|
+
validAfter: validAfter.toString(),
|
|
2504
|
+
validBefore: validBefore.toString(),
|
|
2505
|
+
nonce,
|
|
2506
|
+
authSignature,
|
|
2507
|
+
tokenName,
|
|
2508
|
+
tokenVersion
|
|
2509
|
+
}
|
|
2510
|
+
});
|
|
2511
|
+
return result.txHash;
|
|
2512
|
+
};
|
|
4304
2513
|
}
|
|
4305
2514
|
|
|
4306
|
-
// src/client.ts
|
|
4307
|
-
import {
|
|
4308
|
-
createPublicClient as createPublicClient4,
|
|
4309
|
-
createWalletClient as createWalletClient3,
|
|
4310
|
-
custom as custom3
|
|
4311
|
-
} from "viem";
|
|
4312
|
-
import { base as base4, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
4313
|
-
|
|
4314
2515
|
// src/lib/sponsored-weth-deposit.ts
|
|
4315
2516
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
4316
2517
|
function makeSponsoredWethCallback(deps) {
|
|
4317
2518
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
4318
2519
|
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
4319
|
-
return
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
`No sponsored WETH configured for chain ${chainId}`
|
|
4327
|
-
);
|
|
4328
|
-
}
|
|
4329
|
-
const amountWei = BigInt(amount);
|
|
4330
|
-
const pub = deps.getPublicClient(cid);
|
|
4331
|
-
const wallet = deps.getWalletClient(cid);
|
|
4332
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
4333
|
-
try {
|
|
4334
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
4335
|
-
if (balance < amountWei) {
|
|
4336
|
-
throw new OwneyError(
|
|
4337
|
-
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4338
|
-
"Insufficient WETH balance for this deposit.",
|
|
4339
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
4340
|
-
);
|
|
4341
|
-
}
|
|
4342
|
-
} catch (err) {
|
|
4343
|
-
if (err instanceof OwneyError) throw err;
|
|
4344
|
-
console.warn(
|
|
4345
|
-
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
4346
|
-
err instanceof Error ? err.message : String(err)
|
|
4347
|
-
);
|
|
4348
|
-
}
|
|
4349
|
-
const allowance = await readPermit2Allowance(
|
|
4350
|
-
pub,
|
|
4351
|
-
token,
|
|
4352
|
-
deps.ownerAddress
|
|
2520
|
+
return async (smartWallet, chainId, amount) => {
|
|
2521
|
+
const cid = chainId;
|
|
2522
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2523
|
+
if (!token) {
|
|
2524
|
+
throw new OwneyError(
|
|
2525
|
+
"CHAIN_UNSUPPORTED",
|
|
2526
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
4353
2527
|
);
|
|
4354
|
-
|
|
2528
|
+
}
|
|
2529
|
+
const amountWei = BigInt(amount);
|
|
2530
|
+
const pub = deps.getPublicClient(cid);
|
|
2531
|
+
const wallet = deps.getWalletClient(cid);
|
|
2532
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2533
|
+
try {
|
|
2534
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2535
|
+
if (balance < amountWei) {
|
|
4355
2536
|
throw new OwneyError(
|
|
4356
|
-
"
|
|
4357
|
-
"WETH
|
|
4358
|
-
{ token, chainId: cid,
|
|
2537
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2538
|
+
"Insufficient WETH balance for this deposit.",
|
|
2539
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
4359
2540
|
);
|
|
4360
2541
|
}
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
const nonce = randomPermit2Nonce();
|
|
4367
|
-
const deadline = BigInt(
|
|
4368
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2542
|
+
} catch (err) {
|
|
2543
|
+
if (err instanceof OwneyError) throw err;
|
|
2544
|
+
console.warn(
|
|
2545
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
2546
|
+
err instanceof Error ? err.message : String(err)
|
|
4369
2547
|
);
|
|
4370
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
4371
|
-
chainId: cid,
|
|
4372
|
-
message: {
|
|
4373
|
-
permitted: { token, amount: amountWei },
|
|
4374
|
-
spender: relayer,
|
|
4375
|
-
nonce,
|
|
4376
|
-
deadline
|
|
4377
|
-
}
|
|
4378
|
-
});
|
|
4379
|
-
const signature = await wallet.signTypedData({
|
|
4380
|
-
account: deps.ownerAddress,
|
|
4381
|
-
...typedData
|
|
4382
|
-
});
|
|
4383
|
-
deps.onApproved?.();
|
|
4384
|
-
const result = await post({
|
|
4385
|
-
baseUrl: deps.baseUrl,
|
|
4386
|
-
apiKey: deps.apiKey,
|
|
4387
|
-
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
4388
|
-
body: {
|
|
4389
|
-
chainId: cid,
|
|
4390
|
-
token,
|
|
4391
|
-
from: deps.ownerAddress,
|
|
4392
|
-
to: smartWallet,
|
|
4393
|
-
amount,
|
|
4394
|
-
nonce: nonce.toString(),
|
|
4395
|
-
deadline: deadline.toString(),
|
|
4396
|
-
signature,
|
|
4397
|
-
...verification?.agentId === "yieldseeker" ? {
|
|
4398
|
-
yieldseekerUserId: verification.userId,
|
|
4399
|
-
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4400
|
-
} : {}
|
|
4401
|
-
}
|
|
4402
|
-
});
|
|
4403
|
-
return result.txHash;
|
|
4404
2548
|
}
|
|
4405
|
-
|
|
2549
|
+
const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
|
|
2550
|
+
if (allowance < amountWei) {
|
|
2551
|
+
throw new OwneyError(
|
|
2552
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
2553
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
2554
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2557
|
+
const relayer = await get({
|
|
2558
|
+
baseUrl: deps.baseUrl,
|
|
2559
|
+
apiKey: deps.apiKey,
|
|
2560
|
+
chainId: cid
|
|
2561
|
+
});
|
|
2562
|
+
const nonce = randomPermit2Nonce();
|
|
2563
|
+
const deadline = BigInt(
|
|
2564
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2565
|
+
);
|
|
2566
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
2567
|
+
chainId: cid,
|
|
2568
|
+
message: {
|
|
2569
|
+
permitted: { token, amount: amountWei },
|
|
2570
|
+
spender: relayer,
|
|
2571
|
+
nonce,
|
|
2572
|
+
deadline
|
|
2573
|
+
}
|
|
2574
|
+
});
|
|
2575
|
+
const signature = await wallet.signTypedData({
|
|
2576
|
+
account: deps.ownerAddress,
|
|
2577
|
+
...typedData
|
|
2578
|
+
});
|
|
2579
|
+
deps.onApproved?.();
|
|
2580
|
+
const result = await post({
|
|
2581
|
+
baseUrl: deps.baseUrl,
|
|
2582
|
+
apiKey: deps.apiKey,
|
|
2583
|
+
body: {
|
|
2584
|
+
chainId: cid,
|
|
2585
|
+
token,
|
|
2586
|
+
from: deps.ownerAddress,
|
|
2587
|
+
to: smartWallet,
|
|
2588
|
+
amount,
|
|
2589
|
+
nonce: nonce.toString(),
|
|
2590
|
+
deadline: deadline.toString(),
|
|
2591
|
+
signature
|
|
2592
|
+
}
|
|
2593
|
+
});
|
|
2594
|
+
return result.txHash;
|
|
2595
|
+
};
|
|
4406
2596
|
}
|
|
4407
2597
|
|
|
4408
2598
|
// src/lib/sponsored-calls-deposit.ts
|
|
4409
|
-
import { encodeFunctionData
|
|
2599
|
+
import { encodeFunctionData, erc20Abi, toHex } from "viem";
|
|
4410
2600
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
4411
2601
|
var DEFAULT_MAX_POLLS = 30;
|
|
4412
2602
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -4432,7 +2622,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4432
2622
|
}
|
|
4433
2623
|
return new URL(configured, origin).toString();
|
|
4434
2624
|
};
|
|
4435
|
-
return
|
|
2625
|
+
return async (smartWallet, chainId, amount) => {
|
|
4436
2626
|
const cid = chainId;
|
|
4437
2627
|
const token = deps.tokenAddressByChain[cid];
|
|
4438
2628
|
if (!token) {
|
|
@@ -4448,37 +2638,11 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4448
2638
|
{ chainId }
|
|
4449
2639
|
);
|
|
4450
2640
|
}
|
|
4451
|
-
const data =
|
|
4452
|
-
abi:
|
|
2641
|
+
const data = encodeFunctionData({
|
|
2642
|
+
abi: erc20Abi,
|
|
4453
2643
|
functionName: "transfer",
|
|
4454
2644
|
args: [smartWallet, BigInt(amount)]
|
|
4455
2645
|
});
|
|
4456
|
-
let paymasterUrl = absolutePaymasterUrl();
|
|
4457
|
-
if (verification?.agentId === "yieldseeker") {
|
|
4458
|
-
if (chainId !== 8453) {
|
|
4459
|
-
throw new OwneyError(
|
|
4460
|
-
"CHAIN_UNSUPPORTED",
|
|
4461
|
-
`Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
|
|
4462
|
-
);
|
|
4463
|
-
}
|
|
4464
|
-
const { intent } = await postPaymasterIntent({
|
|
4465
|
-
baseUrl: deps.routingApiBaseUrl,
|
|
4466
|
-
apiKey: deps.apiKey,
|
|
4467
|
-
yieldseekerSignature: verification.signature,
|
|
4468
|
-
body: {
|
|
4469
|
-
chainId,
|
|
4470
|
-
token,
|
|
4471
|
-
from: deps.ownerAddress,
|
|
4472
|
-
to: smartWallet,
|
|
4473
|
-
amount,
|
|
4474
|
-
yieldseekerUserId: verification.userId,
|
|
4475
|
-
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4476
|
-
}
|
|
4477
|
-
});
|
|
4478
|
-
const url = new URL(paymasterUrl);
|
|
4479
|
-
url.searchParams.set("owneyIntent", intent);
|
|
4480
|
-
paymasterUrl = url.toString();
|
|
4481
|
-
}
|
|
4482
2646
|
const sendResult = await deps.provider.request({
|
|
4483
2647
|
method: "wallet_sendCalls",
|
|
4484
2648
|
params: [
|
|
@@ -4489,7 +2653,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4489
2653
|
atomicRequired: false,
|
|
4490
2654
|
calls: [{ to: token, value: "0x0", data }],
|
|
4491
2655
|
capabilities: {
|
|
4492
|
-
paymasterService: { url:
|
|
2656
|
+
paymasterService: { url: absolutePaymasterUrl() }
|
|
4493
2657
|
}
|
|
4494
2658
|
}
|
|
4495
2659
|
]
|
|
@@ -4519,7 +2683,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4519
2683
|
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
4520
2684
|
{ chainId, callsId }
|
|
4521
2685
|
);
|
|
4522
|
-
}
|
|
2686
|
+
};
|
|
4523
2687
|
}
|
|
4524
2688
|
|
|
4525
2689
|
// src/client.ts
|
|
@@ -4549,7 +2713,7 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
4549
2713
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
4550
2714
|
};
|
|
4551
2715
|
var VIEM_CHAIN2 = {
|
|
4552
|
-
8453:
|
|
2716
|
+
8453: base2,
|
|
4553
2717
|
42161: arbitrum2,
|
|
4554
2718
|
1: mainnet2
|
|
4555
2719
|
};
|
|
@@ -4561,6 +2725,7 @@ var SPONSORED_WETH_BY_CHAIN = {
|
|
|
4561
2725
|
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
4562
2726
|
return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
4563
2727
|
}
|
|
2728
|
+
var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai"];
|
|
4564
2729
|
var OwneySDK = class {
|
|
4565
2730
|
agents = /* @__PURE__ */ new Map();
|
|
4566
2731
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -4579,8 +2744,6 @@ var OwneySDK = class {
|
|
|
4579
2744
|
orgAgentConfig;
|
|
4580
2745
|
orgAgentConfigPromise = null;
|
|
4581
2746
|
zyfaiRpcUrls;
|
|
4582
|
-
yieldseekerApiBaseUrl;
|
|
4583
|
-
yieldseekerSiweOrigin;
|
|
4584
2747
|
routingApiBaseUrl;
|
|
4585
2748
|
referralSource;
|
|
4586
2749
|
cachedSponsoredCallback = null;
|
|
@@ -4603,8 +2766,6 @@ var OwneySDK = class {
|
|
|
4603
2766
|
this.apiKey = config.apiKey;
|
|
4604
2767
|
if (config.debug) setOwneyDebug(true);
|
|
4605
2768
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
4606
|
-
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
4607
|
-
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
4608
2769
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
4609
2770
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
4610
2771
|
this.referralSource = config.referralSource;
|
|
@@ -4638,7 +2799,6 @@ var OwneySDK = class {
|
|
|
4638
2799
|
* After calling this, `connect()` must be called again before using agent methods.
|
|
4639
2800
|
*/
|
|
4640
2801
|
async disconnect() {
|
|
4641
|
-
this.state = null;
|
|
4642
2802
|
for (const agent of this.agents.values()) {
|
|
4643
2803
|
await agent.disconnect();
|
|
4644
2804
|
}
|
|
@@ -4712,14 +2872,14 @@ var OwneySDK = class {
|
|
|
4712
2872
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4713
2873
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4714
2874
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4715
|
-
getPublicClient: (cid) =>
|
|
2875
|
+
getPublicClient: (cid) => createPublicClient2({
|
|
4716
2876
|
chain: VIEM_CHAIN2[cid],
|
|
4717
|
-
transport:
|
|
2877
|
+
transport: custom(provider)
|
|
4718
2878
|
}),
|
|
4719
|
-
getWalletClient: (cid) =>
|
|
2879
|
+
getWalletClient: (cid) => createWalletClient({
|
|
4720
2880
|
account: owner,
|
|
4721
2881
|
chain: VIEM_CHAIN2[cid],
|
|
4722
|
-
transport:
|
|
2882
|
+
transport: custom(provider)
|
|
4723
2883
|
})
|
|
4724
2884
|
});
|
|
4725
2885
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -4736,8 +2896,6 @@ var OwneySDK = class {
|
|
|
4736
2896
|
if (!onApproved && cached) return cached;
|
|
4737
2897
|
const provider = this.requireConnectedProvider();
|
|
4738
2898
|
const callback = makeSponsoredCallsCallback({
|
|
4739
|
-
apiKey: this.apiKey,
|
|
4740
|
-
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
4741
2899
|
provider,
|
|
4742
2900
|
ownerAddress: this.state.walletAddress,
|
|
4743
2901
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
@@ -4767,14 +2925,14 @@ var OwneySDK = class {
|
|
|
4767
2925
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4768
2926
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4769
2927
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4770
|
-
getPublicClient: (cid) =>
|
|
2928
|
+
getPublicClient: (cid) => createPublicClient2({
|
|
4771
2929
|
chain: VIEM_CHAIN2[cid],
|
|
4772
|
-
transport:
|
|
2930
|
+
transport: custom(provider)
|
|
4773
2931
|
}),
|
|
4774
|
-
getWalletClient: (cid) =>
|
|
2932
|
+
getWalletClient: (cid) => createWalletClient({
|
|
4775
2933
|
account: owner,
|
|
4776
2934
|
chain: VIEM_CHAIN2[cid],
|
|
4777
|
-
transport:
|
|
2935
|
+
transport: custom(provider)
|
|
4778
2936
|
})
|
|
4779
2937
|
});
|
|
4780
2938
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -4805,10 +2963,12 @@ var OwneySDK = class {
|
|
|
4805
2963
|
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
4806
2964
|
this.apiKey,
|
|
4807
2965
|
this.routingApiBaseUrl
|
|
4808
|
-
).then(
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
2966
|
+
).then(
|
|
2967
|
+
(config) => {
|
|
2968
|
+
this.orgAgentConfig = config;
|
|
2969
|
+
return config;
|
|
2970
|
+
}
|
|
2971
|
+
);
|
|
4812
2972
|
}
|
|
4813
2973
|
return this.orgAgentConfigPromise;
|
|
4814
2974
|
}
|
|
@@ -4848,15 +3008,8 @@ var OwneySDK = class {
|
|
|
4848
3008
|
this.routingApiBaseUrl
|
|
4849
3009
|
);
|
|
4850
3010
|
this.disabledAgents.clear();
|
|
4851
|
-
for (const {
|
|
4852
|
-
|
|
4853
|
-
agent_type,
|
|
4854
|
-
is_enabled,
|
|
4855
|
-
is_configured
|
|
4856
|
-
} of agentKeys) {
|
|
4857
|
-
const configured = is_configured ?? Boolean(key2);
|
|
4858
|
-
if (!configured) continue;
|
|
4859
|
-
const agent = this.createAgent(agent_type, key2);
|
|
3011
|
+
for (const { key: key2, agent_type, is_enabled } of agentKeys) {
|
|
3012
|
+
const agent = await this.createAgent(agent_type, key2);
|
|
4860
3013
|
if (!agent) continue;
|
|
4861
3014
|
this.agents.set(agent_type, agent);
|
|
4862
3015
|
if (is_enabled === false) {
|
|
@@ -4877,15 +3030,16 @@ var OwneySDK = class {
|
|
|
4877
3030
|
this.initializingAgentsPromise = null;
|
|
4878
3031
|
}
|
|
4879
3032
|
}
|
|
4880
|
-
createAgent(agentId, key2) {
|
|
3033
|
+
async createAgent(agentId, key2) {
|
|
4881
3034
|
if (agentId === "zyfai") {
|
|
4882
|
-
if (!key2) return null;
|
|
4883
3035
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
4884
3036
|
}
|
|
4885
|
-
if (agentId === "
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
3037
|
+
if (agentId === "surfliquid") {
|
|
3038
|
+
if (!key2) return null;
|
|
3039
|
+
const { SurfLiquidAgent } = await import("./surfliquid.agent-AE7RZ66F.js");
|
|
3040
|
+
return new SurfLiquidAgent({
|
|
3041
|
+
apiKey: this.apiKey,
|
|
3042
|
+
routingApiBaseUrl: this.routingApiBaseUrl
|
|
4889
3043
|
});
|
|
4890
3044
|
}
|
|
4891
3045
|
return null;
|
|
@@ -4932,10 +3086,9 @@ var OwneySDK = class {
|
|
|
4932
3086
|
* If provided, ALL specified agents must support the chainId or the call
|
|
4933
3087
|
* throws before activating any agent.
|
|
4934
3088
|
*/
|
|
4935
|
-
async activateAgent(chainId, agentId
|
|
3089
|
+
async activateAgent(chainId, agentId) {
|
|
4936
3090
|
const state = this.requireState();
|
|
4937
3091
|
await this.ensureAgentsInitialized();
|
|
4938
|
-
this.assertActivationSession(state);
|
|
4939
3092
|
if (agentId !== void 0) {
|
|
4940
3093
|
if (agentId.length === 0) {
|
|
4941
3094
|
throw new OwneyError(
|
|
@@ -4969,7 +3122,7 @@ var OwneySDK = class {
|
|
|
4969
3122
|
this.activeAgents.add(id);
|
|
4970
3123
|
}
|
|
4971
3124
|
state.chainId = chainId;
|
|
4972
|
-
await this.activateAgentsInTurn(agents, state, chainId
|
|
3125
|
+
await this.activateAgentsInTurn(agents, state, chainId);
|
|
4973
3126
|
return;
|
|
4974
3127
|
}
|
|
4975
3128
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -4990,12 +3143,7 @@ var OwneySDK = class {
|
|
|
4990
3143
|
const enabledCompatible = compatible.filter(
|
|
4991
3144
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
4992
3145
|
);
|
|
4993
|
-
await this.activateAgentsInTurn(enabledCompatible, state, chainId
|
|
4994
|
-
}
|
|
4995
|
-
assertActivationSession(state) {
|
|
4996
|
-
if (this.state !== state) {
|
|
4997
|
-
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed. Please try again.");
|
|
4998
|
-
}
|
|
3146
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId);
|
|
4999
3147
|
}
|
|
5000
3148
|
/**
|
|
5001
3149
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -5011,31 +3159,17 @@ var OwneySDK = class {
|
|
|
5011
3159
|
* at a time anyway.
|
|
5012
3160
|
*
|
|
5013
3161
|
* Every agent is attempted even if an earlier one fails, so one declined
|
|
5014
|
-
* signature can't deny the remaining agents their turn.
|
|
5015
|
-
*
|
|
5016
|
-
*
|
|
3162
|
+
* signature can't deny the remaining agents their turn. The first failure is
|
|
3163
|
+
* rethrown (matching the previous `Promise.all` rejection) once all agents
|
|
3164
|
+
* have had a chance to activate.
|
|
5017
3165
|
*/
|
|
5018
|
-
async activateAgentsInTurn(agents, state, chainId
|
|
3166
|
+
async activateAgentsInTurn(agents, state, chainId) {
|
|
5019
3167
|
let firstError = null;
|
|
5020
|
-
const activatedAgentIds = [];
|
|
5021
|
-
const failedAgents = [];
|
|
5022
3168
|
for (const agent of agents) {
|
|
5023
|
-
this.assertActivationSession(state);
|
|
5024
3169
|
try {
|
|
5025
|
-
await agent.activateAgent(state, chainId
|
|
5026
|
-
this.assertActivationSession(state);
|
|
3170
|
+
await agent.activateAgent(state, chainId);
|
|
5027
3171
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
5028
|
-
this.assertActivationSession(state);
|
|
5029
|
-
activatedAgentIds.push(agent.id);
|
|
5030
3172
|
} catch (error) {
|
|
5031
|
-
this.assertActivationSession(state);
|
|
5032
|
-
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.";
|
|
5033
|
-
failedAgents.push({
|
|
5034
|
-
agentId: agent.id,
|
|
5035
|
-
code: error instanceof OwneyError ? error.code : void 0,
|
|
5036
|
-
message,
|
|
5037
|
-
...error instanceof OwneyError && error.details ? { details: error.details } : {}
|
|
5038
|
-
});
|
|
5039
3173
|
if (firstError === null) {
|
|
5040
3174
|
firstError = error;
|
|
5041
3175
|
} else {
|
|
@@ -5043,18 +3177,7 @@ var OwneySDK = class {
|
|
|
5043
3177
|
}
|
|
5044
3178
|
}
|
|
5045
3179
|
}
|
|
5046
|
-
if (firstError
|
|
5047
|
-
if (activatedAgentIds.length === 0) throw firstError;
|
|
5048
|
-
const failedAgentIds = failedAgents.map(({ agentId }) => agentId);
|
|
5049
|
-
const activeNames = activatedAgentIds.map(this.formatAgentName).join(", ");
|
|
5050
|
-
const failureMessages = failedAgents.map(
|
|
5051
|
-
({ agentId, message }) => `${this.formatAgentName(agentId)} activation failed: ${message}`
|
|
5052
|
-
).join(" ");
|
|
5053
|
-
throw new OwneyError(
|
|
5054
|
-
"AGENT_ACTIVATION_PARTIAL_FAILURE",
|
|
5055
|
-
`${activeNames} activated. ${failureMessages}`,
|
|
5056
|
-
{ activatedAgentIds, failedAgentIds, failures: failedAgents }
|
|
5057
|
-
);
|
|
3180
|
+
if (firstError !== null) throw firstError;
|
|
5058
3181
|
}
|
|
5059
3182
|
/**
|
|
5060
3183
|
* Deposit into a specific agent, or distribute across all eligible agents.
|
|
@@ -5268,10 +3391,10 @@ var OwneySDK = class {
|
|
|
5268
3391
|
agent,
|
|
5269
3392
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
5270
3393
|
}));
|
|
5271
|
-
const
|
|
3394
|
+
const valid = splits.filter(
|
|
5272
3395
|
(s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
|
|
5273
3396
|
);
|
|
5274
|
-
if (
|
|
3397
|
+
if (valid.length === agents.length) {
|
|
5275
3398
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
5276
3399
|
}
|
|
5277
3400
|
const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
|
|
@@ -5290,11 +3413,6 @@ var OwneySDK = class {
|
|
|
5290
3413
|
)
|
|
5291
3414
|
}));
|
|
5292
3415
|
}
|
|
5293
|
-
formatAgentName(agentId) {
|
|
5294
|
-
if (agentId === "zyfai") return "Zyfai";
|
|
5295
|
-
if (agentId === "yieldseeker") return "Yieldseeker";
|
|
5296
|
-
return agentId;
|
|
5297
|
-
}
|
|
5298
3416
|
async validateMinDepositAmount(agent, state, chainId, asset, amount) {
|
|
5299
3417
|
const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
|
|
5300
3418
|
const parsedAmount = BigInt(amount);
|
|
@@ -5321,17 +3439,16 @@ var OwneySDK = class {
|
|
|
5321
3439
|
async hasExistingBalance(agent, state, chainId, asset, requireReliableRead = false) {
|
|
5322
3440
|
try {
|
|
5323
3441
|
const balance = await agent.getBalances(state, chainId);
|
|
5324
|
-
const target = asset.toLowerCase();
|
|
5325
3442
|
const token = balance.tokens.find(
|
|
5326
|
-
(t) => t.chainId === chainId && t.asset
|
|
3443
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
5327
3444
|
);
|
|
5328
3445
|
const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
|
|
5329
|
-
const
|
|
3446
|
+
const position = (balance.positions ?? []).find((p) => {
|
|
5330
3447
|
const positionChain = p.chain.trim().toUpperCase();
|
|
5331
3448
|
const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
|
|
5332
|
-
return matchesChain && p.asset
|
|
3449
|
+
return matchesChain && isSameAsset(p.asset, asset) && Number(p.amount) > 0;
|
|
5333
3450
|
});
|
|
5334
|
-
return !!token && Number(token.amount) > 0 || !!
|
|
3451
|
+
return !!token && Number(token.amount) > 0 || !!position;
|
|
5335
3452
|
} catch (error) {
|
|
5336
3453
|
if (requireReliableRead) {
|
|
5337
3454
|
throw new OwneyError(
|
|
@@ -5376,6 +3493,34 @@ var OwneySDK = class {
|
|
|
5376
3493
|
}
|
|
5377
3494
|
return eligible;
|
|
5378
3495
|
}
|
|
3496
|
+
/**
|
|
3497
|
+
* Agent ids the routing API provisioned for this org that support the given
|
|
3498
|
+
* chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
|
|
3499
|
+
* surfliquid first). Returns `[]` when the org has no compatible agent — never
|
|
3500
|
+
* throws on an empty org. Loads agent keys on first call (apiKey only, no
|
|
3501
|
+
* wallet), so the UI can resolve which agent to use before the user connects.
|
|
3502
|
+
*
|
|
3503
|
+
* This is the source of truth for agent availability: an agent appears here
|
|
3504
|
+
* iff the routing API returned its key. No per-app feature flags.
|
|
3505
|
+
*/
|
|
3506
|
+
async getEligibleAgentIds(chainId, asset) {
|
|
3507
|
+
try {
|
|
3508
|
+
await this.ensureAgentsInitialized();
|
|
3509
|
+
} catch (error) {
|
|
3510
|
+
if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
|
|
3511
|
+
return [];
|
|
3512
|
+
}
|
|
3513
|
+
throw error;
|
|
3514
|
+
}
|
|
3515
|
+
return [...this.agents.values()].filter((agent) => {
|
|
3516
|
+
const chainAssets = agent.supportedAssets.find(
|
|
3517
|
+
(sa) => sa.chainId === chainId
|
|
3518
|
+
);
|
|
3519
|
+
return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
|
|
3520
|
+
}).map((agent) => agent.id).sort(
|
|
3521
|
+
(a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
|
|
3522
|
+
);
|
|
3523
|
+
}
|
|
5379
3524
|
// --- Fund operations ---
|
|
5380
3525
|
/**
|
|
5381
3526
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -5412,9 +3557,19 @@ var OwneySDK = class {
|
|
|
5412
3557
|
}
|
|
5413
3558
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
5414
3559
|
if (!amount) {
|
|
3560
|
+
const aggregated2 = await this.getBalances();
|
|
3561
|
+
const funded = projectAgentBalancesForAsset(
|
|
3562
|
+
eligibleAgents,
|
|
3563
|
+
aggregated2.agentBalances,
|
|
3564
|
+
chainId,
|
|
3565
|
+
asset,
|
|
3566
|
+
assetInfo.decimals
|
|
3567
|
+
).filter(
|
|
3568
|
+
({ agent, balance }) => balance > 0n || aggregated2.agentErrors?.[agent.id] !== void 0
|
|
3569
|
+
).map(({ agent }) => agent);
|
|
5415
3570
|
const results2 = {};
|
|
5416
3571
|
const agentErrors2 = {};
|
|
5417
|
-
for (const agent of
|
|
3572
|
+
for (const agent of funded) {
|
|
5418
3573
|
try {
|
|
5419
3574
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
5420
3575
|
} catch (err) {
|
|
@@ -5446,10 +3601,6 @@ var OwneySDK = class {
|
|
|
5446
3601
|
}
|
|
5447
3602
|
const requested = BigInt(amount);
|
|
5448
3603
|
const aggregated = await this.getBalances();
|
|
5449
|
-
const unavailableAgents = eligibleAgents.filter(
|
|
5450
|
-
(agent) => !(agent.id in aggregated.agentBalances)
|
|
5451
|
-
);
|
|
5452
|
-
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
5453
3604
|
const balances = projectAgentBalancesForAsset(
|
|
5454
3605
|
eligibleAgents,
|
|
5455
3606
|
aggregated.agentBalances,
|
|
@@ -5458,18 +3609,7 @@ var OwneySDK = class {
|
|
|
5458
3609
|
assetInfo.decimals
|
|
5459
3610
|
);
|
|
5460
3611
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
5461
|
-
if (totalAvailable
|
|
5462
|
-
throw new OwneyError(
|
|
5463
|
-
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5464
|
-
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
5465
|
-
{
|
|
5466
|
-
asset,
|
|
5467
|
-
unavailableAgents: unavailableAgentIds,
|
|
5468
|
-
agentErrors: aggregated.agentErrors
|
|
5469
|
-
}
|
|
5470
|
-
);
|
|
5471
|
-
}
|
|
5472
|
-
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3612
|
+
if (totalAvailable < requested) {
|
|
5473
3613
|
throw new OwneyError(
|
|
5474
3614
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
5475
3615
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -5480,7 +3620,6 @@ var OwneySDK = class {
|
|
|
5480
3620
|
}
|
|
5481
3621
|
);
|
|
5482
3622
|
}
|
|
5483
|
-
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
5484
3623
|
const disabledBalances = balances.filter(
|
|
5485
3624
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
5486
3625
|
);
|
|
@@ -5489,7 +3628,7 @@ var OwneySDK = class {
|
|
|
5489
3628
|
);
|
|
5490
3629
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
5491
3630
|
disabledBalances,
|
|
5492
|
-
|
|
3631
|
+
requested
|
|
5493
3632
|
);
|
|
5494
3633
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
5495
3634
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -5499,9 +3638,7 @@ var OwneySDK = class {
|
|
|
5499
3638
|
}));
|
|
5500
3639
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
5501
3640
|
const results = {};
|
|
5502
|
-
const agentErrors = {
|
|
5503
|
-
...aggregated.agentErrors ?? {}
|
|
5504
|
-
};
|
|
3641
|
+
const agentErrors = {};
|
|
5505
3642
|
for (let i = 0; i < plans.length; i++) {
|
|
5506
3643
|
const p = plans[i];
|
|
5507
3644
|
if (p.planned === 0n) continue;
|
|
@@ -5548,8 +3685,7 @@ var OwneySDK = class {
|
|
|
5548
3685
|
requested: amount,
|
|
5549
3686
|
withdrawn: withdrawn.toString(),
|
|
5550
3687
|
partialResults: results,
|
|
5551
|
-
agentErrors
|
|
5552
|
-
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3688
|
+
agentErrors
|
|
5553
3689
|
}
|
|
5554
3690
|
);
|
|
5555
3691
|
}
|
|
@@ -5567,10 +3703,7 @@ var OwneySDK = class {
|
|
|
5567
3703
|
if (agentId) {
|
|
5568
3704
|
const agent = this.getAgent(agentId);
|
|
5569
3705
|
const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
5570
|
-
return
|
|
5571
|
-
...result,
|
|
5572
|
-
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5573
|
-
};
|
|
3706
|
+
return result;
|
|
5574
3707
|
}
|
|
5575
3708
|
let totalBalance = 0;
|
|
5576
3709
|
const results = {};
|
|
@@ -5578,13 +3711,7 @@ var OwneySDK = class {
|
|
|
5578
3711
|
const balanceResults = await Promise.allSettled(
|
|
5579
3712
|
entries.map(async ([id, agent]) => {
|
|
5580
3713
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
5581
|
-
return [
|
|
5582
|
-
id,
|
|
5583
|
-
{
|
|
5584
|
-
...b,
|
|
5585
|
-
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
5586
|
-
}
|
|
5587
|
-
];
|
|
3714
|
+
return [id, b];
|
|
5588
3715
|
})
|
|
5589
3716
|
);
|
|
5590
3717
|
let successCount = 0;
|
|
@@ -5606,7 +3733,6 @@ var OwneySDK = class {
|
|
|
5606
3733
|
const retryDelay = rateLimitDelay(reason);
|
|
5607
3734
|
if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
|
|
5608
3735
|
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
5609
|
-
console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
|
|
5610
3736
|
}
|
|
5611
3737
|
if (successCount === 0) {
|
|
5612
3738
|
throw new OwneyError(
|
|
@@ -5719,10 +3845,7 @@ var OwneySDK = class {
|
|
|
5719
3845
|
Promise.all(
|
|
5720
3846
|
entries.map(async ([id, agent]) => {
|
|
5721
3847
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
5722
|
-
return [
|
|
5723
|
-
id,
|
|
5724
|
-
balanceForApyScope(b, chainId, tokenSymbol)
|
|
5725
|
-
];
|
|
3848
|
+
return [id, balanceForApyScope(b, chainId, tokenSymbol)];
|
|
5726
3849
|
})
|
|
5727
3850
|
)
|
|
5728
3851
|
]);
|
|
@@ -5903,10 +4026,10 @@ var OwneySDK = class {
|
|
|
5903
4026
|
);
|
|
5904
4027
|
}
|
|
5905
4028
|
const provider = this.requireConnectedProvider();
|
|
5906
|
-
const wallet =
|
|
4029
|
+
const wallet = createWalletClient({
|
|
5907
4030
|
account: state.walletAddress,
|
|
5908
4031
|
chain: VIEM_CHAIN2[chainId],
|
|
5909
|
-
transport:
|
|
4032
|
+
transport: custom(provider)
|
|
5910
4033
|
});
|
|
5911
4034
|
const hash = await wallet.writeContract({
|
|
5912
4035
|
address: token,
|
|
@@ -5916,9 +4039,9 @@ var OwneySDK = class {
|
|
|
5916
4039
|
account: state.walletAddress,
|
|
5917
4040
|
chain: VIEM_CHAIN2[chainId]
|
|
5918
4041
|
});
|
|
5919
|
-
const publicClient =
|
|
4042
|
+
const publicClient = createPublicClient2({
|
|
5920
4043
|
chain: VIEM_CHAIN2[chainId],
|
|
5921
|
-
transport:
|
|
4044
|
+
transport: custom(provider)
|
|
5922
4045
|
});
|
|
5923
4046
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
5924
4047
|
hash,
|
|
@@ -5955,9 +4078,7 @@ var OwneySDK = class {
|
|
|
5955
4078
|
return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
5956
4079
|
}
|
|
5957
4080
|
const results = {};
|
|
5958
|
-
const agentEntries = [...this.agents.entries()]
|
|
5959
|
-
([id]) => !this.isAgentDisabled(id)
|
|
5960
|
-
);
|
|
4081
|
+
const agentEntries = [...this.agents.entries()];
|
|
5961
4082
|
const apyResults = await Promise.all(
|
|
5962
4083
|
agentEntries.map(async ([id, agent]) => {
|
|
5963
4084
|
const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
@@ -6032,13 +4153,13 @@ var OwneySDK = class {
|
|
|
6032
4153
|
};
|
|
6033
4154
|
|
|
6034
4155
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
6035
|
-
import { getAddress
|
|
6036
|
-
import { SiweMessage
|
|
4156
|
+
import { getAddress } from "viem";
|
|
4157
|
+
import { SiweMessage } from "siwe";
|
|
6037
4158
|
import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
|
|
6038
4159
|
|
|
6039
4160
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
6040
|
-
var
|
|
6041
|
-
var
|
|
4161
|
+
var KEY_PREFIX2 = "owney.siwx.session";
|
|
4162
|
+
var storage2 = () => {
|
|
6042
4163
|
if (typeof window === "undefined") return null;
|
|
6043
4164
|
try {
|
|
6044
4165
|
return window.localStorage;
|
|
@@ -6046,8 +4167,8 @@ var storage4 = () => {
|
|
|
6046
4167
|
return null;
|
|
6047
4168
|
}
|
|
6048
4169
|
};
|
|
6049
|
-
var
|
|
6050
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
4170
|
+
var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
|
|
4171
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
|
|
6051
4172
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
6052
4173
|
var readLegacySiwxSession = (store, address) => {
|
|
6053
4174
|
if (!store) return null;
|
|
@@ -6078,17 +4199,17 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
6078
4199
|
};
|
|
6079
4200
|
var readSiwxSession = (address, chainId) => {
|
|
6080
4201
|
if (typeof window === "undefined") return null;
|
|
6081
|
-
const key2 =
|
|
6082
|
-
const store =
|
|
6083
|
-
let
|
|
4202
|
+
const key2 = buildKey2(address);
|
|
4203
|
+
const store = storage2();
|
|
4204
|
+
let raw = null;
|
|
6084
4205
|
try {
|
|
6085
|
-
|
|
4206
|
+
raw = store?.getItem(key2) ?? null;
|
|
6086
4207
|
} catch {
|
|
6087
|
-
|
|
4208
|
+
raw = null;
|
|
6088
4209
|
}
|
|
6089
|
-
if (
|
|
4210
|
+
if (raw) {
|
|
6090
4211
|
try {
|
|
6091
|
-
return JSON.parse(
|
|
4212
|
+
return JSON.parse(raw);
|
|
6092
4213
|
} catch {
|
|
6093
4214
|
memorySiwxSessions.delete(key2);
|
|
6094
4215
|
try {
|
|
@@ -6107,18 +4228,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
6107
4228
|
};
|
|
6108
4229
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
6109
4230
|
if (typeof window === "undefined") return;
|
|
6110
|
-
const key2 =
|
|
4231
|
+
const key2 = buildKey2(address);
|
|
6111
4232
|
memorySiwxSessions.set(key2, session);
|
|
6112
|
-
const store =
|
|
4233
|
+
const store = storage2();
|
|
6113
4234
|
try {
|
|
6114
4235
|
store?.setItem(key2, JSON.stringify(session));
|
|
6115
4236
|
} catch {
|
|
6116
4237
|
}
|
|
6117
4238
|
};
|
|
6118
4239
|
var clearSiwxSession = (address, _chainId) => {
|
|
6119
|
-
const key2 =
|
|
4240
|
+
const key2 = buildKey2(address);
|
|
6120
4241
|
memorySiwxSessions.delete(key2);
|
|
6121
|
-
const store =
|
|
4242
|
+
const store = storage2();
|
|
6122
4243
|
try {
|
|
6123
4244
|
store?.removeItem(key2);
|
|
6124
4245
|
} catch {
|
|
@@ -6158,8 +4279,8 @@ function buildSIWXConfig(deps) {
|
|
|
6158
4279
|
statement: STATEMENT,
|
|
6159
4280
|
issuedAt,
|
|
6160
4281
|
toString() {
|
|
6161
|
-
return new
|
|
6162
|
-
address:
|
|
4282
|
+
return new SiweMessage({
|
|
4283
|
+
address: getAddress(accountAddress),
|
|
6163
4284
|
chainId: numericChainId(chainId),
|
|
6164
4285
|
domain,
|
|
6165
4286
|
uri,
|
|
@@ -6201,7 +4322,7 @@ function buildSIWXConfig(deps) {
|
|
|
6201
4322
|
const persistSession = async (session) => {
|
|
6202
4323
|
const address = session.data.accountAddress;
|
|
6203
4324
|
const id = numericChainId(session.data.chainId);
|
|
6204
|
-
const message = new
|
|
4325
|
+
const message = new SiweMessage(session.message);
|
|
6205
4326
|
const login = await post("/auth/login", {
|
|
6206
4327
|
message,
|
|
6207
4328
|
signature: session.signature,
|
|
@@ -6250,7 +4371,6 @@ export {
|
|
|
6250
4371
|
NotConnectedError,
|
|
6251
4372
|
OwneyError,
|
|
6252
4373
|
OwneySDK,
|
|
6253
|
-
YieldseekerAgent,
|
|
6254
4374
|
createOwneySIWX,
|
|
6255
4375
|
setOwneyDebug
|
|
6256
4376
|
};
|