@owney/sdk 0.7.25-beta.9 → 0.7.25
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 +26 -74
- package/dist/index.cjs +698 -3256
- package/dist/index.d.cts +181 -354
- package/dist/index.d.ts +181 -354
- package/dist/index.js +694 -3271
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -26,32 +26,11 @@ __export(index_exports, {
|
|
|
26
26
|
NotConnectedError: () => NotConnectedError,
|
|
27
27
|
OwneyError: () => OwneyError,
|
|
28
28
|
OwneySDK: () => OwneySDK,
|
|
29
|
-
YieldseekerAgent: () => YieldseekerAgent,
|
|
30
29
|
createOwneySIWX: () => createOwneySIWX,
|
|
31
30
|
setOwneyDebug: () => setOwneyDebug
|
|
32
31
|
});
|
|
33
32
|
module.exports = __toCommonJS(index_exports);
|
|
34
33
|
|
|
35
|
-
// src/lib/deposit-batch-callback.ts
|
|
36
|
-
var batchCallbacks = /* @__PURE__ */ new WeakMap();
|
|
37
|
-
var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
|
|
38
|
-
function toBatchTransfer(to, amount, verification) {
|
|
39
|
-
return {
|
|
40
|
-
to,
|
|
41
|
-
amount,
|
|
42
|
-
...verification ? {
|
|
43
|
-
yieldseeker: {
|
|
44
|
-
signature: verification.signature,
|
|
45
|
-
userId: verification.userId,
|
|
46
|
-
agentId: verification.yieldseekerAgentId
|
|
47
|
-
}
|
|
48
|
-
} : {}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
function registerDepositBatch(callback, transfer) {
|
|
52
|
-
batchCallbacks.set(callback, transfer);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
34
|
// src/errors.ts
|
|
56
35
|
var OwneyError = class extends Error {
|
|
57
36
|
code;
|
|
@@ -159,34 +138,6 @@ function rateLimitDelay(error, now = Date.now()) {
|
|
|
159
138
|
}
|
|
160
139
|
|
|
161
140
|
// src/lib/agent-reads.ts
|
|
162
|
-
function rateLimitDiagnostics(error) {
|
|
163
|
-
const seen = /* @__PURE__ */ new Set();
|
|
164
|
-
let details = {};
|
|
165
|
-
function visit(value, depth = 0) {
|
|
166
|
-
if (depth > 6 || !value || typeof value !== "object" || seen.has(value))
|
|
167
|
-
return;
|
|
168
|
-
seen.add(value);
|
|
169
|
-
const record = value;
|
|
170
|
-
for (const key2 of [
|
|
171
|
-
"rpcSource",
|
|
172
|
-
"chainId",
|
|
173
|
-
"rpcMethod",
|
|
174
|
-
"providerRequestId",
|
|
175
|
-
"statusCode"
|
|
176
|
-
]) {
|
|
177
|
-
const candidate = record[key2];
|
|
178
|
-
if (candidate !== void 0 && details[key2] === void 0)
|
|
179
|
-
details = { ...details, [key2]: candidate };
|
|
180
|
-
}
|
|
181
|
-
for (const key2 of ["details", "cause", "response", "fields", "error"])
|
|
182
|
-
visit(record[key2], depth + 1);
|
|
183
|
-
}
|
|
184
|
-
visit(error);
|
|
185
|
-
return {
|
|
186
|
-
rpcSource: details.rpcSource ?? "agent-api",
|
|
187
|
-
...details
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
141
|
var AgentReads = class {
|
|
191
142
|
inFlight = /* @__PURE__ */ new Map();
|
|
192
143
|
cooldowns = /* @__PURE__ */ new Map();
|
|
@@ -194,15 +145,14 @@ var AgentReads = class {
|
|
|
194
145
|
clearInFlight() {
|
|
195
146
|
this.inFlight.clear();
|
|
196
147
|
}
|
|
197
|
-
limited(agentId, until
|
|
148
|
+
limited(agentId, until) {
|
|
198
149
|
return new OwneyError(
|
|
199
150
|
"AGENT_RATE_LIMITED",
|
|
200
151
|
"Too many requests. Please wait before trying again.",
|
|
201
152
|
{
|
|
202
153
|
statusCode: 429,
|
|
203
154
|
retryAt: until,
|
|
204
|
-
retryAfterSeconds: Math.max(0, Math.ceil((until - Date.now()) / 1e3))
|
|
205
|
-
...diagnostics
|
|
155
|
+
retryAfterSeconds: Math.max(0, Math.ceil((until - Date.now()) / 1e3))
|
|
206
156
|
},
|
|
207
157
|
agentId
|
|
208
158
|
);
|
|
@@ -210,9 +160,7 @@ var AgentReads = class {
|
|
|
210
160
|
run(agentId, key2, fetch2) {
|
|
211
161
|
const cooldown = this.cooldowns.get(agentId);
|
|
212
162
|
if (cooldown && cooldown.until > Date.now()) {
|
|
213
|
-
return Promise.reject(
|
|
214
|
-
this.limited(agentId, cooldown.until, cooldown.diagnostics)
|
|
215
|
-
);
|
|
163
|
+
return Promise.reject(this.limited(agentId, cooldown.until));
|
|
216
164
|
}
|
|
217
165
|
const requestKey = JSON.stringify([agentId, key2]);
|
|
218
166
|
const existing = this.inFlight.get(requestKey);
|
|
@@ -233,9 +181,8 @@ var AgentReads = class {
|
|
|
233
181
|
Math.min(3e4 * 2 ** (failures - 1), 3e5)
|
|
234
182
|
);
|
|
235
183
|
const until = Math.max(previous?.until ?? 0, Date.now() + delay);
|
|
236
|
-
|
|
237
|
-
this.
|
|
238
|
-
throw this.limited(agentId, until, diagnostics);
|
|
184
|
+
this.cooldowns.set(agentId, { until, failures });
|
|
185
|
+
throw this.limited(agentId, until);
|
|
239
186
|
}
|
|
240
187
|
).finally(() => {
|
|
241
188
|
if (this.inFlight.get(requestKey) === promise)
|
|
@@ -248,7 +195,7 @@ var AgentReads = class {
|
|
|
248
195
|
|
|
249
196
|
// src/agents/zyfai/zyfai.agent.ts
|
|
250
197
|
var import_sdk = require("@zyfai/sdk");
|
|
251
|
-
var
|
|
198
|
+
var import_viem = require("viem");
|
|
252
199
|
var import_chains = require("viem/chains");
|
|
253
200
|
|
|
254
201
|
// src/types/config.ts
|
|
@@ -363,18 +310,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
363
310
|
return explicit;
|
|
364
311
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
365
312
|
}
|
|
366
|
-
function mapDeposit(
|
|
313
|
+
function mapDeposit(raw) {
|
|
367
314
|
return {
|
|
368
|
-
txHash:
|
|
369
|
-
smartWallet:
|
|
370
|
-
amount:
|
|
315
|
+
txHash: raw.txHash,
|
|
316
|
+
smartWallet: raw.smartWallet,
|
|
317
|
+
amount: raw.amount
|
|
371
318
|
};
|
|
372
319
|
}
|
|
373
|
-
function mapWithdraw(
|
|
320
|
+
function mapWithdraw(raw) {
|
|
374
321
|
return {
|
|
375
|
-
txHash:
|
|
376
|
-
type:
|
|
377
|
-
amount:
|
|
322
|
+
txHash: raw.txHash,
|
|
323
|
+
type: raw.type,
|
|
324
|
+
amount: raw.amount
|
|
378
325
|
};
|
|
379
326
|
}
|
|
380
327
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -393,10 +340,10 @@ function resolveChainId(chain) {
|
|
|
393
340
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
394
341
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
395
342
|
}
|
|
396
|
-
function mapPendingAllocations(
|
|
397
|
-
if (!Array.isArray(
|
|
343
|
+
function mapPendingAllocations(raw) {
|
|
344
|
+
if (!Array.isArray(raw)) return void 0;
|
|
398
345
|
const pending = [];
|
|
399
|
-
for (const entry of
|
|
346
|
+
for (const entry of raw) {
|
|
400
347
|
if (typeof entry !== "object" || entry === null) continue;
|
|
401
348
|
const e = entry;
|
|
402
349
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -419,8 +366,8 @@ function mapPendingAllocations(raw2) {
|
|
|
419
366
|
}
|
|
420
367
|
return pending.length > 0 ? pending : void 0;
|
|
421
368
|
}
|
|
422
|
-
function mapBalances(
|
|
423
|
-
const portfolio =
|
|
369
|
+
function mapBalances(raw, _chainId, smartWallet) {
|
|
370
|
+
const portfolio = raw.portfolio;
|
|
424
371
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
425
372
|
let totalBalance = 0;
|
|
426
373
|
const tokens = [];
|
|
@@ -489,8 +436,8 @@ function sumTokenValues(tokens) {
|
|
|
489
436
|
function sumTokenEarnings(tokens) {
|
|
490
437
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
491
438
|
}
|
|
492
|
-
function mapEarnings(
|
|
493
|
-
const totalEarningsByChain =
|
|
439
|
+
function mapEarnings(raw, smartWallet) {
|
|
440
|
+
const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
|
|
494
441
|
const tokens = [];
|
|
495
442
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
496
443
|
totalEarningsByChain
|
|
@@ -509,15 +456,15 @@ function mapEarnings(raw2, smartWallet) {
|
|
|
509
456
|
return {
|
|
510
457
|
smartWallet,
|
|
511
458
|
lifetimeEarnings: sumTokenEarnings(
|
|
512
|
-
|
|
459
|
+
raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
|
|
513
460
|
),
|
|
514
461
|
tokens
|
|
515
462
|
};
|
|
516
463
|
}
|
|
517
|
-
function mapWeightedApyByChain(
|
|
518
|
-
if (!
|
|
464
|
+
function mapWeightedApyByChain(raw) {
|
|
465
|
+
if (!raw) return void 0;
|
|
519
466
|
const out = {};
|
|
520
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
467
|
+
for (const [chainKey, tokenApy] of Object.entries(raw)) {
|
|
521
468
|
const chainId = Number(chainKey);
|
|
522
469
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
523
470
|
const perAsset = {};
|
|
@@ -557,8 +504,8 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
557
504
|
}
|
|
558
505
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
559
506
|
}
|
|
560
|
-
function mapApyHistory(
|
|
561
|
-
const history = Object.entries(
|
|
507
|
+
function mapApyHistory(raw, chainId, tokenSymbol) {
|
|
508
|
+
const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
|
|
562
509
|
date,
|
|
563
510
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol),
|
|
564
511
|
// Provider position balances are treated as decimal amounts of the
|
|
@@ -574,9 +521,9 @@ function mapApyHistory(raw2, chainId, tokenSymbol) {
|
|
|
574
521
|
} : {}
|
|
575
522
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
576
523
|
return {
|
|
577
|
-
walletAddress:
|
|
578
|
-
weightedApyAfterFee:
|
|
579
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
524
|
+
walletAddress: raw.walletAddress,
|
|
525
|
+
weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
|
|
526
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
|
|
580
527
|
history
|
|
581
528
|
};
|
|
582
529
|
}
|
|
@@ -672,23 +619,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
672
619
|
};
|
|
673
620
|
});
|
|
674
621
|
}
|
|
675
|
-
function mapUserProfile(
|
|
622
|
+
function mapUserProfile(raw, userAddress) {
|
|
676
623
|
return {
|
|
677
624
|
address: userAddress,
|
|
678
|
-
smartWallet:
|
|
679
|
-
chains:
|
|
680
|
-
strategy:
|
|
681
|
-
hasActiveSessionKey:
|
|
682
|
-
protocols:
|
|
683
|
-
splitting:
|
|
684
|
-
minSplits:
|
|
625
|
+
smartWallet: raw.smartWallet || "",
|
|
626
|
+
chains: raw.chains || [],
|
|
627
|
+
strategy: raw.strategy,
|
|
628
|
+
hasActiveSessionKey: raw.hasActiveSessionKey || false,
|
|
629
|
+
protocols: raw.protocols || [],
|
|
630
|
+
splitting: raw.splitting,
|
|
631
|
+
minSplits: raw.minSplits
|
|
685
632
|
};
|
|
686
633
|
}
|
|
687
|
-
function mapApyByStrategy(
|
|
634
|
+
function mapApyByStrategy(raw) {
|
|
688
635
|
const apyPerAsset = {};
|
|
689
636
|
let apySum = 0;
|
|
690
637
|
let apyCount = 0;
|
|
691
|
-
for (const entry of
|
|
638
|
+
for (const entry of raw.data) {
|
|
692
639
|
const supported = SupportedAssets.find(
|
|
693
640
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
694
641
|
);
|
|
@@ -800,9 +747,9 @@ function netDeltaForSnapshot(entry, chainId, asset) {
|
|
|
800
747
|
debugLog("zyfai:earnings", "gross fallback for daily earnings", { gross });
|
|
801
748
|
return gross;
|
|
802
749
|
}
|
|
803
|
-
function mapDailyEarnings(
|
|
750
|
+
function mapDailyEarnings(raw, chainId, tokenSymbol) {
|
|
804
751
|
const wanted = tokenSymbol?.toUpperCase();
|
|
805
|
-
const snapshots = [...
|
|
752
|
+
const snapshots = [...raw.data ?? []].sort(
|
|
806
753
|
(a, b) => a.snapshot_date.localeCompare(b.snapshot_date)
|
|
807
754
|
);
|
|
808
755
|
const byAsset = /* @__PURE__ */ new Map();
|
|
@@ -817,7 +764,7 @@ function mapDailyEarnings(raw2, chainId, tokenSymbol) {
|
|
|
817
764
|
}
|
|
818
765
|
}
|
|
819
766
|
const assets = [...byAsset.entries()].map(([asset, series]) => ({ asset, points: series.points })).sort((a, b) => a.asset.localeCompare(b.asset));
|
|
820
|
-
return { walletAddress:
|
|
767
|
+
return { walletAddress: raw.walletAddress, chainId, assets };
|
|
821
768
|
}
|
|
822
769
|
|
|
823
770
|
// src/agents/zyfai/zyfai.withdraw-amount.ts
|
|
@@ -928,15 +875,15 @@ var readSession = (address, _chainId) => {
|
|
|
928
875
|
if (typeof window === "undefined") return null;
|
|
929
876
|
const key2 = buildKey(address);
|
|
930
877
|
const store = storage();
|
|
931
|
-
let
|
|
878
|
+
let raw = null;
|
|
932
879
|
try {
|
|
933
|
-
|
|
880
|
+
raw = store?.getItem(key2) ?? null;
|
|
934
881
|
} catch {
|
|
935
|
-
|
|
882
|
+
raw = null;
|
|
936
883
|
}
|
|
937
|
-
if (
|
|
884
|
+
if (raw) {
|
|
938
885
|
try {
|
|
939
|
-
const parsed = JSON.parse(
|
|
886
|
+
const parsed = JSON.parse(raw);
|
|
940
887
|
if (isFreshSession(parsed)) return parsed;
|
|
941
888
|
} catch {
|
|
942
889
|
}
|
|
@@ -1104,8 +1051,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
1104
1051
|
}
|
|
1105
1052
|
return result;
|
|
1106
1053
|
}
|
|
1107
|
-
function flattenAvailablePools(
|
|
1108
|
-
const byChain =
|
|
1054
|
+
function flattenAvailablePools(raw) {
|
|
1055
|
+
const byChain = raw ?? {};
|
|
1109
1056
|
const names = [];
|
|
1110
1057
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
1111
1058
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -1129,145 +1076,15 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
1129
1076
|
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
1130
1077
|
}
|
|
1131
1078
|
|
|
1132
|
-
// src/lib/paid-rpc.ts
|
|
1133
|
-
var import_viem = require("viem");
|
|
1134
|
-
var PAID_RPC_RETRY_COUNT = 3;
|
|
1135
|
-
var PAID_RPC_RETRY_DELAY_MS = 1e3;
|
|
1136
|
-
function configuredRpcProxyBaseUrl(baseUrl) {
|
|
1137
|
-
const configured = (baseUrl ?? "")?.trim();
|
|
1138
|
-
if (!configured) {
|
|
1139
|
-
throw new Error(
|
|
1140
|
-
"OWNEY_ROUTING_API_BASE_URL is required for RPC proxy requests unless routingApiBaseUrl or all rpcUrls are configured."
|
|
1141
|
-
);
|
|
1142
|
-
}
|
|
1143
|
-
return configured;
|
|
1144
|
-
}
|
|
1145
|
-
function rpcProxyUrl(chainId, apiKey, baseUrl) {
|
|
1146
|
-
const url = new URL(
|
|
1147
|
-
`${configuredRpcProxyBaseUrl(baseUrl).replace(/\/$/, "")}/api/v1/rpc/${chainId}`
|
|
1148
|
-
);
|
|
1149
|
-
if (apiKey) url.searchParams.set("apiKey", apiKey);
|
|
1150
|
-
return url.toString();
|
|
1151
|
-
}
|
|
1152
|
-
function resolveRpcUrl(rpcUrls, chainId, apiKey = "", baseUrl) {
|
|
1153
|
-
const url = rpcUrls?.[chainId]?.trim();
|
|
1154
|
-
if (url) return url;
|
|
1155
|
-
return rpcProxyUrl(chainId, apiKey, baseUrl);
|
|
1156
|
-
}
|
|
1157
|
-
function resolveRpcUrls(rpcUrls, apiKey = "", baseUrl) {
|
|
1158
|
-
return {
|
|
1159
|
-
1: resolveRpcUrl(rpcUrls, 1, apiKey, baseUrl),
|
|
1160
|
-
8453: resolveRpcUrl(rpcUrls, 8453, apiKey, baseUrl),
|
|
1161
|
-
42161: resolveRpcUrl(rpcUrls, 42161, apiKey, baseUrl)
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
function createPaidRpcClient(chain, rpcUrls) {
|
|
1165
|
-
const url = resolveRpcUrl(
|
|
1166
|
-
rpcUrls,
|
|
1167
|
-
chain.id
|
|
1168
|
-
);
|
|
1169
|
-
return (0, import_viem.createPublicClient)({
|
|
1170
|
-
chain,
|
|
1171
|
-
transport: (0, import_viem.http)(url, {
|
|
1172
|
-
retryCount: PAID_RPC_RETRY_COUNT,
|
|
1173
|
-
retryDelay: PAID_RPC_RETRY_DELAY_MS
|
|
1174
|
-
})
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
function headerValue(error, name) {
|
|
1178
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1179
|
-
let value;
|
|
1180
|
-
function visit(candidate, depth = 0) {
|
|
1181
|
-
if (value || depth > 6 || !candidate || typeof candidate !== "object")
|
|
1182
|
-
return;
|
|
1183
|
-
if (seen.has(candidate)) return;
|
|
1184
|
-
seen.add(candidate);
|
|
1185
|
-
const record = candidate;
|
|
1186
|
-
const headers = record.headers;
|
|
1187
|
-
let found;
|
|
1188
|
-
if (typeof headers?.get === "function") {
|
|
1189
|
-
found = headers.get(name);
|
|
1190
|
-
} else {
|
|
1191
|
-
const headerRecord = headers;
|
|
1192
|
-
found = headerRecord?.[name] ?? headerRecord?.[name.toLowerCase()];
|
|
1193
|
-
}
|
|
1194
|
-
if (typeof found === "string" && found) {
|
|
1195
|
-
value = found;
|
|
1196
|
-
return;
|
|
1197
|
-
}
|
|
1198
|
-
for (const key2 of ["cause", "details", "response", "error"])
|
|
1199
|
-
visit(record[key2], depth + 1);
|
|
1200
|
-
}
|
|
1201
|
-
visit(error);
|
|
1202
|
-
return value;
|
|
1203
|
-
}
|
|
1204
|
-
function statusCode(error) {
|
|
1205
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1206
|
-
let status;
|
|
1207
|
-
function visit(candidate, depth = 0) {
|
|
1208
|
-
if (status || depth > 6 || !candidate || typeof candidate !== "object")
|
|
1209
|
-
return;
|
|
1210
|
-
if (seen.has(candidate)) return;
|
|
1211
|
-
seen.add(candidate);
|
|
1212
|
-
const record = candidate;
|
|
1213
|
-
for (const key2 of ["status", "statusCode"]) {
|
|
1214
|
-
const parsed = Number(record[key2]);
|
|
1215
|
-
if (Number.isInteger(parsed) && parsed >= 100 && parsed <= 599) {
|
|
1216
|
-
status = parsed;
|
|
1217
|
-
return;
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
for (const key2 of ["cause", "details", "response", "error"])
|
|
1221
|
-
visit(record[key2], depth + 1);
|
|
1222
|
-
}
|
|
1223
|
-
visit(error);
|
|
1224
|
-
return status;
|
|
1225
|
-
}
|
|
1226
|
-
function paidRpcError(error, chainId, rpcMethod, agentId) {
|
|
1227
|
-
const delay = rateLimitDelay(error);
|
|
1228
|
-
const providerRequestId = headerValue(error, "x-alchemy-request-id") ?? headerValue(error, "x-request-id");
|
|
1229
|
-
if (delay === void 0) {
|
|
1230
|
-
return new OwneyError(
|
|
1231
|
-
"AGENT_API_ERROR",
|
|
1232
|
-
"The blockchain RPC request failed.",
|
|
1233
|
-
{
|
|
1234
|
-
rpcSource: "paid-rpc",
|
|
1235
|
-
chainId,
|
|
1236
|
-
rpcMethod,
|
|
1237
|
-
...statusCode(error) ? { statusCode: statusCode(error) } : {},
|
|
1238
|
-
...providerRequestId ? { providerRequestId } : {}
|
|
1239
|
-
},
|
|
1240
|
-
agentId
|
|
1241
|
-
);
|
|
1242
|
-
}
|
|
1243
|
-
const retryAt = Date.now() + delay;
|
|
1244
|
-
return new OwneyError(
|
|
1245
|
-
"AGENT_RATE_LIMITED",
|
|
1246
|
-
"The blockchain RPC is rate limited. Please wait before trying again.",
|
|
1247
|
-
{
|
|
1248
|
-
rpcSource: "paid-rpc",
|
|
1249
|
-
chainId,
|
|
1250
|
-
rpcMethod,
|
|
1251
|
-
statusCode: 429,
|
|
1252
|
-
retryAt,
|
|
1253
|
-
retryAfterSeconds: Math.max(0, Math.ceil(delay / 1e3)),
|
|
1254
|
-
...providerRequestId ? { providerRequestId } : {}
|
|
1255
|
-
},
|
|
1256
|
-
agentId
|
|
1257
|
-
);
|
|
1258
|
-
}
|
|
1259
|
-
async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
1260
|
-
try {
|
|
1261
|
-
return await operation();
|
|
1262
|
-
} catch (error) {
|
|
1263
|
-
throw paidRpcError(error, chainId, rpcMethod, agentId);
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
1079
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1268
|
-
var ERC7579_IS_MODULE_INSTALLED_ABI = (0,
|
|
1080
|
+
var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem.parseAbi)([
|
|
1269
1081
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
1270
1082
|
]);
|
|
1083
|
+
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
1084
|
+
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
1085
|
+
42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
1086
|
+
1: "https://eth-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
1087
|
+
};
|
|
1271
1088
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
1272
1089
|
8453: "0x4200000000000000000000000000000000000006",
|
|
1273
1090
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -1333,7 +1150,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1333
1150
|
earningsSnapshot = null;
|
|
1334
1151
|
earningsGeneration = 0;
|
|
1335
1152
|
constructor(apiKey, rpcUrls, referralSource) {
|
|
1336
|
-
this.rpcUrls =
|
|
1153
|
+
this.rpcUrls = rpcUrls ?? DEFAULT_ZYFAI_RPC_URLS;
|
|
1337
1154
|
this.sdk = new import_sdk.ZyfaiSDK({
|
|
1338
1155
|
apiKey,
|
|
1339
1156
|
rpcUrls: this.rpcUrls,
|
|
@@ -1347,10 +1164,10 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1347
1164
|
getPublicClient(chainId) {
|
|
1348
1165
|
const cached = this.publicClients.get(chainId);
|
|
1349
1166
|
if (cached) return cached;
|
|
1350
|
-
const client =
|
|
1351
|
-
VIEM_CHAIN[chainId],
|
|
1352
|
-
this.rpcUrls
|
|
1353
|
-
);
|
|
1167
|
+
const client = (0, import_viem.createPublicClient)({
|
|
1168
|
+
chain: VIEM_CHAIN[chainId],
|
|
1169
|
+
transport: (0, import_viem.http)(this.rpcUrls[chainId])
|
|
1170
|
+
});
|
|
1354
1171
|
this.publicClients.set(chainId, client);
|
|
1355
1172
|
return client;
|
|
1356
1173
|
}
|
|
@@ -1520,7 +1337,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1520
1337
|
* chain/account switch fires the provider's `accountsChanged` →
|
|
1521
1338
|
* `disconnectAccount` (nulls walletClient + authenticatedUserId) while our
|
|
1522
1339
|
* own `connectAuth` guard still believes this chain is connected and
|
|
1523
|
-
* early-returns. Without this, authenticated calls (
|
|
1340
|
+
* early-returns. Without this, authenticated calls (sendDeposit,
|
|
1524
1341
|
* withdrawFunds) fail with "No account connected".
|
|
1525
1342
|
*/
|
|
1526
1343
|
async ensureConnected(state, chainId) {
|
|
@@ -1738,8 +1555,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1738
1555
|
const poolResults = await Promise.all(
|
|
1739
1556
|
universe.map(async (protocol) => {
|
|
1740
1557
|
try {
|
|
1741
|
-
const
|
|
1742
|
-
return [protocol.id, flattenAvailablePools(
|
|
1558
|
+
const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1559
|
+
return [protocol.id, flattenAvailablePools(raw)];
|
|
1743
1560
|
} catch (error) {
|
|
1744
1561
|
console.warn(
|
|
1745
1562
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1805,14 +1622,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1805
1622
|
async readWalletState(ownerAddress) {
|
|
1806
1623
|
try {
|
|
1807
1624
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1808
|
-
const
|
|
1625
|
+
const raw = portfolio;
|
|
1809
1626
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1810
|
-
predeployed:
|
|
1811
|
-
hasActiveSessionKey:
|
|
1627
|
+
predeployed: raw?.predeployed,
|
|
1628
|
+
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1812
1629
|
});
|
|
1813
1630
|
return {
|
|
1814
|
-
predeployed:
|
|
1815
|
-
hasActiveSessionKey:
|
|
1631
|
+
predeployed: raw?.predeployed,
|
|
1632
|
+
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1816
1633
|
};
|
|
1817
1634
|
} catch (error) {
|
|
1818
1635
|
console.warn(
|
|
@@ -2098,14 +1915,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2098
1915
|
return { txHash, smartWallet, amount };
|
|
2099
1916
|
}
|
|
2100
1917
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
2101
|
-
const
|
|
1918
|
+
const raw = await this.sdk.sendDeposit(
|
|
2102
1919
|
this.getAddress(),
|
|
2103
1920
|
validChainId,
|
|
2104
1921
|
amount,
|
|
2105
1922
|
asset,
|
|
2106
1923
|
"aggressive"
|
|
2107
1924
|
);
|
|
2108
|
-
return mapDeposit(
|
|
1925
|
+
return mapDeposit(raw);
|
|
2109
1926
|
} catch (error) {
|
|
2110
1927
|
throw error;
|
|
2111
1928
|
}
|
|
@@ -2114,27 +1931,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2114
1931
|
async withdraw(state, chainId, token, amount) {
|
|
2115
1932
|
const validChainId = isValidChainId(chainId);
|
|
2116
1933
|
await this.ensureConnected(state, validChainId);
|
|
2117
|
-
const
|
|
1934
|
+
const raw = await this.sdk.withdrawFunds(
|
|
2118
1935
|
this.getAddress(),
|
|
2119
1936
|
validChainId,
|
|
2120
1937
|
amount,
|
|
2121
1938
|
token
|
|
2122
1939
|
);
|
|
2123
|
-
if (!
|
|
1940
|
+
if (!raw.success) {
|
|
2124
1941
|
throw new OwneyError(
|
|
2125
1942
|
"WITHDRAW_FAILED",
|
|
2126
|
-
|
|
2127
|
-
{ chainId: validChainId, token, amount, response:
|
|
1943
|
+
raw.message || "Zyfai withdraw failed.",
|
|
1944
|
+
{ chainId: validChainId, token, amount, response: raw },
|
|
2128
1945
|
this.id
|
|
2129
1946
|
);
|
|
2130
1947
|
}
|
|
2131
|
-
return mapWithdraw(
|
|
1948
|
+
return mapWithdraw(raw);
|
|
2132
1949
|
}
|
|
2133
1950
|
// --- IAgent: Portfolio reads ---
|
|
2134
1951
|
async getBalances(state, chainId) {
|
|
2135
1952
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
2136
|
-
const
|
|
2137
|
-
return mapBalances(
|
|
1953
|
+
const raw = await this.sdk.getPortfolio(this.getAddress());
|
|
1954
|
+
return mapBalances(raw, validChainId, smartWallet);
|
|
2138
1955
|
}
|
|
2139
1956
|
earningsKey(state, chainId, smartWallet) {
|
|
2140
1957
|
return JSON.stringify([
|
|
@@ -2147,11 +1964,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2147
1964
|
const existing = this.earningsReads.get(key2);
|
|
2148
1965
|
if (existing) return existing;
|
|
2149
1966
|
const generation = this.earningsGeneration;
|
|
2150
|
-
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((
|
|
1967
|
+
const pending = Promise.resolve().then(() => this.sdk.getOnchainEarnings(smartWallet)).then((raw) => {
|
|
2151
1968
|
if (generation === this.earningsGeneration) {
|
|
2152
|
-
this.earningsSnapshot = { key: key2, raw
|
|
1969
|
+
this.earningsSnapshot = { key: key2, raw, at: Date.now() };
|
|
2153
1970
|
}
|
|
2154
|
-
return
|
|
1971
|
+
return raw;
|
|
2155
1972
|
}).finally(() => {
|
|
2156
1973
|
if (this.earningsReads.get(key2) === pending)
|
|
2157
1974
|
this.earningsReads.delete(key2);
|
|
@@ -2161,11 +1978,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2161
1978
|
}
|
|
2162
1979
|
async getEarnings(state, chainId) {
|
|
2163
1980
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
2164
|
-
const
|
|
1981
|
+
const raw = await this.readEarnings(
|
|
2165
1982
|
this.earningsKey(state, chainId, smartWallet),
|
|
2166
1983
|
smartWallet
|
|
2167
1984
|
);
|
|
2168
|
-
return mapEarnings(
|
|
1985
|
+
return mapEarnings(raw, smartWallet);
|
|
2169
1986
|
}
|
|
2170
1987
|
async refreshEarnings(state, chainId) {
|
|
2171
1988
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
@@ -2192,17 +2009,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2192
2009
|
}
|
|
2193
2010
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
2194
2011
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
2195
|
-
const
|
|
2196
|
-
return mapApyHistory(
|
|
2012
|
+
const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
2013
|
+
return mapApyHistory(raw, chainId, tokenSymbol);
|
|
2197
2014
|
}
|
|
2198
2015
|
async getDailyEarnings(state, chainId, days, tokenSymbol) {
|
|
2199
2016
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
2200
2017
|
const start = new Date(Date.now() - (DayFilterMapping[days] + 1) * 864e5);
|
|
2201
|
-
const
|
|
2018
|
+
const raw = await this.sdk.getDailyEarnings(
|
|
2202
2019
|
smartWallet,
|
|
2203
2020
|
start.toISOString().slice(0, 10)
|
|
2204
2021
|
);
|
|
2205
|
-
return mapDailyEarnings(
|
|
2022
|
+
return mapDailyEarnings(raw, chainId, tokenSymbol);
|
|
2206
2023
|
}
|
|
2207
2024
|
/**
|
|
2208
2025
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -2237,7 +2054,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2237
2054
|
const matched = [];
|
|
2238
2055
|
let backendExhausted = false;
|
|
2239
2056
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
2240
|
-
const
|
|
2057
|
+
const raw = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
2241
2058
|
limit: backendPageSize,
|
|
2242
2059
|
offset,
|
|
2243
2060
|
fromDate: options?.fromDate,
|
|
@@ -2248,13 +2065,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2248
2065
|
// asset's rows and handing back a page that filters to nothing.
|
|
2249
2066
|
assetType
|
|
2250
2067
|
});
|
|
2251
|
-
|
|
2068
|
+
raw.data.forEach((entry, idx) => {
|
|
2252
2069
|
if (entry.chainId === validChainId) {
|
|
2253
2070
|
matched.push({ entry, rawIdx: offset + idx });
|
|
2254
2071
|
}
|
|
2255
2072
|
});
|
|
2256
|
-
offset +=
|
|
2257
|
-
if (
|
|
2073
|
+
offset += raw.data.length;
|
|
2074
|
+
if (raw.data.length < backendPageSize) {
|
|
2258
2075
|
backendExhausted = true;
|
|
2259
2076
|
break;
|
|
2260
2077
|
}
|
|
@@ -2276,18 +2093,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2276
2093
|
}
|
|
2277
2094
|
async getUserProfile(state, chainId) {
|
|
2278
2095
|
await this.connectAuth(state, chainId);
|
|
2279
|
-
const
|
|
2096
|
+
const raw = await this.sdk.getUserDetails();
|
|
2280
2097
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
2281
2098
|
asset: "USDC (default \u2014 no asset passed)",
|
|
2282
|
-
splitting:
|
|
2283
|
-
minSplits:
|
|
2284
|
-
strategy:
|
|
2285
|
-
chains:
|
|
2286
|
-
protocolCount:
|
|
2287
|
-
hasActiveSessionKey:
|
|
2288
|
-
smartWallet:
|
|
2099
|
+
splitting: raw.splitting,
|
|
2100
|
+
minSplits: raw.minSplits,
|
|
2101
|
+
strategy: raw.strategy,
|
|
2102
|
+
chains: raw.chains,
|
|
2103
|
+
protocolCount: raw.protocols?.length,
|
|
2104
|
+
hasActiveSessionKey: raw.hasActiveSessionKey,
|
|
2105
|
+
smartWallet: raw.smartWallet
|
|
2289
2106
|
});
|
|
2290
|
-
return mapUserProfile(
|
|
2107
|
+
return mapUserProfile(raw, this.connectedAddress);
|
|
2291
2108
|
}
|
|
2292
2109
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
2293
2110
|
await this.connectAuth(state, chainId);
|
|
@@ -2306,2234 +2123,116 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2306
2123
|
}
|
|
2307
2124
|
// --- IAgent: Discovery (no wallet required) ---
|
|
2308
2125
|
async getAgentApy(days, options) {
|
|
2309
|
-
const
|
|
2126
|
+
const raw = await this.sdk.getAPYPerStrategy(
|
|
2310
2127
|
false,
|
|
2311
2128
|
DayFilterMapping[days],
|
|
2312
2129
|
"aggressive",
|
|
2313
2130
|
options?.chainId,
|
|
2314
2131
|
options?.tokenSymbol
|
|
2315
2132
|
);
|
|
2316
|
-
return mapApyByStrategy(
|
|
2133
|
+
return mapApyByStrategy(raw);
|
|
2317
2134
|
}
|
|
2318
2135
|
};
|
|
2319
2136
|
|
|
2320
|
-
// src/
|
|
2321
|
-
var
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
// src/lib/chain-guard.ts
|
|
2325
|
-
var CHAIN_NAMES = {
|
|
2326
|
-
1: "Ethereum",
|
|
2327
|
-
8453: "Base",
|
|
2328
|
-
42161: "Arbitrum"
|
|
2329
|
-
};
|
|
2330
|
-
function chainName(chainId) {
|
|
2331
|
-
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2332
|
-
}
|
|
2333
|
-
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2334
|
-
const actual = await pub.getChainId();
|
|
2335
|
-
if (actual === expected) return;
|
|
2137
|
+
// src/lib/routing-api.ts
|
|
2138
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2139
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
2140
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
2336
2141
|
try {
|
|
2337
|
-
await
|
|
2142
|
+
const res = await fetch(url, {
|
|
2143
|
+
method: "GET",
|
|
2144
|
+
headers: {
|
|
2145
|
+
"Content-Type": "application/json",
|
|
2146
|
+
"x-owney-api-key": `${apiKey}`
|
|
2147
|
+
}
|
|
2148
|
+
});
|
|
2149
|
+
if (!res.ok) {
|
|
2150
|
+
if (res.status !== 404) {
|
|
2151
|
+
console.warn(
|
|
2152
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
2155
|
+
return null;
|
|
2156
|
+
}
|
|
2157
|
+
const json = await res.json();
|
|
2158
|
+
const policy = json.success ? json.data ?? null : null;
|
|
2159
|
+
debugLog(
|
|
2160
|
+
"owney-sdk",
|
|
2161
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
2162
|
+
policy ?? void 0
|
|
2163
|
+
);
|
|
2164
|
+
return policy;
|
|
2338
2165
|
} catch (error) {
|
|
2166
|
+
console.warn(
|
|
2167
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
2168
|
+
error instanceof Error ? error.message : String(error)
|
|
2169
|
+
);
|
|
2170
|
+
return null;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
2174
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
2175
|
+
const res = await fetch(url, {
|
|
2176
|
+
method: "GET",
|
|
2177
|
+
headers: {
|
|
2178
|
+
"Content-Type": "application/json",
|
|
2179
|
+
"x-owney-api-key": `${apiKey}`
|
|
2180
|
+
}
|
|
2181
|
+
});
|
|
2182
|
+
if (!res.ok) {
|
|
2183
|
+
const text = await res.text().catch(() => "");
|
|
2339
2184
|
throw new OwneyError(
|
|
2340
|
-
"
|
|
2341
|
-
`
|
|
2342
|
-
{
|
|
2343
|
-
expectedChainId: expected,
|
|
2344
|
-
actualChainId: actual,
|
|
2345
|
-
cause: error instanceof Error ? error.message : String(error)
|
|
2346
|
-
}
|
|
2185
|
+
"API_ROUTING_ERROR",
|
|
2186
|
+
`Routing API error ${res.status}: ${text}`,
|
|
2187
|
+
{ statusCode: res.status, responseBody: text }
|
|
2347
2188
|
);
|
|
2348
2189
|
}
|
|
2349
|
-
const
|
|
2350
|
-
if (
|
|
2190
|
+
const json = await res.json();
|
|
2191
|
+
if (!json.success) {
|
|
2351
2192
|
throw new OwneyError(
|
|
2352
|
-
"
|
|
2353
|
-
`
|
|
2354
|
-
{
|
|
2193
|
+
"API_ROUTING_FAILED",
|
|
2194
|
+
`Routing API request failed: ${json.message}`,
|
|
2195
|
+
{ message: json.message }
|
|
2355
2196
|
);
|
|
2356
2197
|
}
|
|
2198
|
+
return json.data;
|
|
2357
2199
|
}
|
|
2358
2200
|
|
|
2359
|
-
// src/lib/
|
|
2360
|
-
var
|
|
2361
|
-
|
|
2362
|
-
// src/lib/sponsor-client.ts
|
|
2363
|
-
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2364
|
-
async function postPaymasterIntent(input) {
|
|
2365
|
-
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2366
|
-
let res;
|
|
2201
|
+
// src/lib/health-report.ts
|
|
2202
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2203
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
|
|
2367
2204
|
try {
|
|
2368
|
-
|
|
2205
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
2369
2206
|
method: "POST",
|
|
2370
2207
|
headers: {
|
|
2371
|
-
"
|
|
2372
|
-
"x-owney-api-key":
|
|
2373
|
-
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
2208
|
+
"Content-Type": "application/json",
|
|
2209
|
+
"x-owney-api-key": apiKey
|
|
2374
2210
|
},
|
|
2375
|
-
body: JSON.stringify(
|
|
2211
|
+
body: JSON.stringify({
|
|
2212
|
+
agent_type: agentType,
|
|
2213
|
+
error_code: errorCode,
|
|
2214
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2215
|
+
})
|
|
2376
2216
|
});
|
|
2377
|
-
} catch (
|
|
2378
|
-
|
|
2379
|
-
"
|
|
2380
|
-
|
|
2381
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
2382
|
-
);
|
|
2383
|
-
}
|
|
2384
|
-
const text = await res.text();
|
|
2385
|
-
let parsed = null;
|
|
2386
|
-
try {
|
|
2387
|
-
parsed = JSON.parse(text);
|
|
2388
|
-
} catch {
|
|
2389
|
-
}
|
|
2390
|
-
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
2391
|
-
throw new OwneyError(
|
|
2392
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2393
|
-
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2394
|
-
{
|
|
2395
|
-
statusCode: res.status,
|
|
2396
|
-
responseBody: text.slice(0, 500),
|
|
2397
|
-
safeToFallback: true
|
|
2398
|
-
}
|
|
2399
|
-
);
|
|
2400
|
-
}
|
|
2401
|
-
return parsed.data;
|
|
2402
|
-
}
|
|
2403
|
-
async function getSponsorRelayerAddress(input) {
|
|
2404
|
-
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2405
|
-
let res;
|
|
2406
|
-
try {
|
|
2407
|
-
res = await fetch(
|
|
2408
|
-
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2409
|
-
{
|
|
2410
|
-
headers: { "x-owney-api-key": input.apiKey }
|
|
2411
|
-
}
|
|
2412
|
-
);
|
|
2413
|
-
} catch (networkError) {
|
|
2414
|
-
throw new OwneyError(
|
|
2415
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2416
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2417
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
2418
|
-
);
|
|
2419
|
-
}
|
|
2420
|
-
const text = await res.text();
|
|
2421
|
-
let parsed = null;
|
|
2422
|
-
try {
|
|
2423
|
-
parsed = JSON.parse(text);
|
|
2424
|
-
} catch {
|
|
2425
|
-
}
|
|
2426
|
-
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
2427
|
-
throw new OwneyError(
|
|
2428
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2429
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2430
|
-
{
|
|
2431
|
-
statusCode: res.status,
|
|
2432
|
-
responseBody: text.slice(0, 500),
|
|
2433
|
-
safeToFallback: true
|
|
2434
|
-
}
|
|
2217
|
+
} catch (err) {
|
|
2218
|
+
console.warn(
|
|
2219
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
2220
|
+
err instanceof Error ? err.message : err
|
|
2435
2221
|
);
|
|
2436
2222
|
}
|
|
2437
|
-
return parsed.data.relayer;
|
|
2438
2223
|
}
|
|
2439
|
-
async function
|
|
2440
|
-
let res;
|
|
2224
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
2441
2225
|
try {
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
"content-type": "application/json",
|
|
2448
|
-
"x-owney-api-key": input.apiKey
|
|
2449
|
-
},
|
|
2450
|
-
body: JSON.stringify(input.body)
|
|
2451
|
-
}
|
|
2452
|
-
);
|
|
2453
|
-
} catch {
|
|
2454
|
-
throw new OwneyError(
|
|
2455
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2456
|
-
"Deposit status is unknown. Retry the same amount to check it.",
|
|
2457
|
-
{ safeToFallback: false }
|
|
2458
|
-
);
|
|
2459
|
-
}
|
|
2460
|
-
const parsed = await res.json().catch(() => null);
|
|
2461
|
-
if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
|
|
2462
|
-
throw new OwneyError(
|
|
2463
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2464
|
-
"Deposit could not be confirmed. Retry the same amount to check its status.",
|
|
2465
|
-
{
|
|
2466
|
-
statusCode: res.status,
|
|
2467
|
-
safeToFallback: false,
|
|
2468
|
-
notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
|
|
2469
|
-
}
|
|
2470
|
-
);
|
|
2471
|
-
}
|
|
2472
|
-
return parsed.data;
|
|
2473
|
-
}
|
|
2474
|
-
|
|
2475
|
-
// src/lib/permit2.ts
|
|
2476
|
-
var import_viem4 = require("viem");
|
|
2477
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2478
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2479
|
-
function permit2ApprovalAmount(requiredAmount) {
|
|
2480
|
-
if (requiredAmount <= 0n) {
|
|
2481
|
-
throw new Error("Permit2 approval requires a positive deposit amount");
|
|
2482
|
-
}
|
|
2483
|
-
return MAX_UINT256;
|
|
2484
|
-
}
|
|
2485
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
2486
|
-
{
|
|
2487
|
-
type: "function",
|
|
2488
|
-
name: "allowance",
|
|
2489
|
-
stateMutability: "view",
|
|
2490
|
-
inputs: [
|
|
2491
|
-
{ name: "owner", type: "address" },
|
|
2492
|
-
{ name: "spender", type: "address" }
|
|
2493
|
-
],
|
|
2494
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2495
|
-
},
|
|
2496
|
-
{
|
|
2497
|
-
type: "function",
|
|
2498
|
-
name: "approve",
|
|
2499
|
-
stateMutability: "nonpayable",
|
|
2500
|
-
inputs: [
|
|
2501
|
-
{ name: "spender", type: "address" },
|
|
2502
|
-
{ name: "amount", type: "uint256" }
|
|
2503
|
-
],
|
|
2504
|
-
outputs: [{ name: "", type: "bool" }]
|
|
2505
|
-
},
|
|
2506
|
-
{
|
|
2507
|
-
type: "function",
|
|
2508
|
-
name: "balanceOf",
|
|
2509
|
-
stateMutability: "view",
|
|
2510
|
-
inputs: [{ name: "account", type: "address" }],
|
|
2511
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2512
|
-
}
|
|
2513
|
-
];
|
|
2514
|
-
function randomPermit2Nonce() {
|
|
2515
|
-
const bytes = new Uint8Array(32);
|
|
2516
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2517
|
-
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
2518
|
-
}
|
|
2519
|
-
async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
|
|
2520
|
-
return publicClient.readContract({
|
|
2521
|
-
address: token,
|
|
2522
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2523
|
-
functionName: "allowance",
|
|
2524
|
-
args: [owner, PERMIT2_ADDRESS],
|
|
2525
|
-
...blockNumber === void 0 ? {} : { blockNumber }
|
|
2526
|
-
});
|
|
2527
|
-
}
|
|
2528
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
2529
|
-
return publicClient.readContract({
|
|
2530
|
-
address: token,
|
|
2531
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2532
|
-
functionName: "balanceOf",
|
|
2533
|
-
args: [owner]
|
|
2534
|
-
});
|
|
2535
|
-
}
|
|
2536
|
-
|
|
2537
|
-
// src/lib/sponsored-deposit.ts
|
|
2538
|
-
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2539
|
-
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2540
|
-
function provideDepositVerificationContext(callback, context) {
|
|
2541
|
-
callback[verificationSetter]?.(context);
|
|
2542
|
-
}
|
|
2543
|
-
function makeVerificationAwareDepositCallback(implementation) {
|
|
2544
|
-
let nextVerification;
|
|
2545
|
-
const callback = async (smartWallet, chainId, amount) => {
|
|
2546
|
-
const verification = nextVerification;
|
|
2547
|
-
nextVerification = void 0;
|
|
2548
|
-
return implementation(smartWallet, chainId, amount, verification);
|
|
2549
|
-
};
|
|
2550
|
-
Object.defineProperty(callback, verificationSetter, {
|
|
2551
|
-
value: (context) => {
|
|
2552
|
-
nextVerification = context;
|
|
2553
|
-
}
|
|
2554
|
-
});
|
|
2555
|
-
return callback;
|
|
2556
|
-
}
|
|
2557
|
-
|
|
2558
|
-
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2559
|
-
var import_siwe = require("siwe");
|
|
2560
|
-
var import_viem5 = require("viem");
|
|
2561
|
-
var import_chains2 = require("viem/chains");
|
|
2562
|
-
|
|
2563
|
-
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2564
|
-
var KEY_PREFIX2 = "owney.yieldseeker.session.v5";
|
|
2565
|
-
var INVALIDATED_KEY_PREFIXES = [
|
|
2566
|
-
"owney.yieldseeker.session",
|
|
2567
|
-
"owney.yieldseeker.session.v3",
|
|
2568
|
-
"owney.yieldseeker.session.v4"
|
|
2569
|
-
];
|
|
2570
|
-
var storage2 = () => {
|
|
2571
|
-
if (typeof window === "undefined") return null;
|
|
2572
|
-
try {
|
|
2573
|
-
return window.localStorage;
|
|
2574
|
-
} catch {
|
|
2575
|
-
return null;
|
|
2576
|
-
}
|
|
2577
|
-
};
|
|
2578
|
-
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2579
|
-
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2580
|
-
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2581
|
-
);
|
|
2582
|
-
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2583
|
-
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2584
|
-
memorySessions2.delete(key2);
|
|
2585
|
-
try {
|
|
2586
|
-
store?.removeItem(key2);
|
|
2587
|
-
} catch {
|
|
2588
|
-
}
|
|
2589
|
-
}
|
|
2590
|
-
};
|
|
2591
|
-
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2592
|
-
var isValidSession = (session) => {
|
|
2593
|
-
if (!session?.token) return false;
|
|
2594
|
-
try {
|
|
2595
|
-
const parsed = JSON.parse(atob(session.token));
|
|
2596
|
-
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2597
|
-
} catch {
|
|
2598
|
-
return false;
|
|
2599
|
-
}
|
|
2600
|
-
};
|
|
2601
|
-
var readYieldseekerSession = (address, chainId) => {
|
|
2602
|
-
if (typeof window === "undefined") return null;
|
|
2603
|
-
const key2 = buildKey2(address, chainId);
|
|
2604
|
-
const store = storage2();
|
|
2605
|
-
clearInvalidatedSessions(store, address, chainId);
|
|
2606
|
-
let raw2 = null;
|
|
2607
|
-
try {
|
|
2608
|
-
raw2 = store?.getItem(key2) ?? null;
|
|
2609
|
-
} catch {
|
|
2610
|
-
raw2 = null;
|
|
2611
|
-
}
|
|
2612
|
-
if (raw2) {
|
|
2613
|
-
try {
|
|
2614
|
-
const parsed = JSON.parse(raw2);
|
|
2615
|
-
if (isValidSession(parsed)) return parsed.token;
|
|
2616
|
-
} catch {
|
|
2617
|
-
}
|
|
2618
|
-
memorySessions2.delete(key2);
|
|
2619
|
-
try {
|
|
2620
|
-
store?.removeItem(key2);
|
|
2621
|
-
} catch {
|
|
2622
|
-
}
|
|
2623
|
-
return null;
|
|
2624
|
-
}
|
|
2625
|
-
const cached = memorySessions2.get(key2);
|
|
2626
|
-
if (isValidSession(cached)) return cached.token;
|
|
2627
|
-
if (cached) memorySessions2.delete(key2);
|
|
2628
|
-
return null;
|
|
2629
|
-
};
|
|
2630
|
-
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2631
|
-
if (typeof window === "undefined") return;
|
|
2632
|
-
const session = { token };
|
|
2633
|
-
if (!isValidSession(session)) return;
|
|
2634
|
-
const key2 = buildKey2(address, chainId);
|
|
2635
|
-
memorySessions2.set(key2, session);
|
|
2636
|
-
const store = storage2();
|
|
2637
|
-
try {
|
|
2638
|
-
store?.setItem(key2, JSON.stringify(session));
|
|
2639
|
-
} catch {
|
|
2640
|
-
}
|
|
2641
|
-
};
|
|
2642
|
-
var clearYieldseekerSession = (address, chainId) => {
|
|
2643
|
-
const key2 = buildKey2(address, chainId);
|
|
2644
|
-
memorySessions2.delete(key2);
|
|
2645
|
-
const store = storage2();
|
|
2646
|
-
clearInvalidatedSessions(store, address, chainId);
|
|
2647
|
-
try {
|
|
2648
|
-
store?.removeItem(key2);
|
|
2649
|
-
} catch {
|
|
2650
|
-
}
|
|
2651
|
-
};
|
|
2652
|
-
|
|
2653
|
-
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2654
|
-
function resolveSiweOrigin(override) {
|
|
2655
|
-
const origin = override?.trim() || (typeof window !== "undefined" ? window.location?.origin : void 0);
|
|
2656
|
-
if (!origin || origin === "null") {
|
|
2657
|
-
throw new Error("Yieldseeker sign-in requires the requesting app's browser origin.");
|
|
2658
|
-
}
|
|
2659
|
-
const url = new URL(origin);
|
|
2660
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
2661
|
-
throw new Error("Yieldseeker sign-in requires an HTTP or HTTPS app origin.");
|
|
2662
|
-
}
|
|
2663
|
-
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
2664
|
-
throw new Error("Yieldseeker SIWE override must be an origin without credentials, path, query, or fragment.");
|
|
2665
|
-
}
|
|
2666
|
-
return url;
|
|
2667
|
-
}
|
|
2668
|
-
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2669
|
-
const url = resolveSiweOrigin(dependencies.origin);
|
|
2670
|
-
return new import_siwe.SiweMessage({
|
|
2671
|
-
scheme: url.protocol.slice(0, -1),
|
|
2672
|
-
domain: url.host,
|
|
2673
|
-
address: (0, import_viem5.getAddress)(address),
|
|
2674
|
-
uri: url.origin,
|
|
2675
|
-
version: "1",
|
|
2676
|
-
chainId,
|
|
2677
|
-
nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
|
|
2678
|
-
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2679
|
-
}).prepareMessage();
|
|
2680
|
-
}
|
|
2681
|
-
function encodeYieldseekerAuthToken(token) {
|
|
2682
|
-
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2683
|
-
let binary = "";
|
|
2684
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2685
|
-
return btoa(binary);
|
|
2686
|
-
}
|
|
2687
|
-
var YieldseekerAuth = class {
|
|
2688
|
-
constructor(dependencies = {}) {
|
|
2689
|
-
this.dependencies = dependencies;
|
|
2690
|
-
}
|
|
2691
|
-
dependencies;
|
|
2692
|
-
tokens = /* @__PURE__ */ new Map();
|
|
2693
|
-
pending = /* @__PURE__ */ new Map();
|
|
2694
|
-
scopes = /* @__PURE__ */ new Map();
|
|
2695
|
-
key(state, chainId) {
|
|
2696
|
-
return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
|
|
2697
|
-
}
|
|
2698
|
-
async getToken(state, chainId) {
|
|
2699
|
-
const key2 = this.key(state, chainId);
|
|
2700
|
-
const scope = { address: state.walletAddress, chainId };
|
|
2701
|
-
this.scopes.set(key2, scope);
|
|
2702
|
-
const cached = this.tokens.get(key2);
|
|
2703
|
-
if (cached) return cached;
|
|
2704
|
-
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2705
|
-
if (persisted && this.matchesOrigin(persisted)) {
|
|
2706
|
-
this.tokens.set(key2, persisted);
|
|
2707
|
-
return persisted;
|
|
2708
|
-
}
|
|
2709
|
-
if (persisted) clearYieldseekerSession(scope.address, scope.chainId);
|
|
2710
|
-
const inFlight = this.pending.get(key2);
|
|
2711
|
-
if (inFlight) return inFlight;
|
|
2712
|
-
const request = this.sign(state, chainId).then((token) => {
|
|
2713
|
-
if (this.pending.get(key2) !== request) throw new Error("Wallet sign-in session changed. Please try again.");
|
|
2714
|
-
this.tokens.set(key2, token);
|
|
2715
|
-
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2716
|
-
return token;
|
|
2717
|
-
});
|
|
2718
|
-
this.pending.set(key2, request);
|
|
2719
|
-
try {
|
|
2720
|
-
return await request;
|
|
2721
|
-
} finally {
|
|
2722
|
-
if (this.pending.get(key2) === request) this.pending.delete(key2);
|
|
2723
|
-
}
|
|
2724
|
-
}
|
|
2725
|
-
async refreshToken(state, chainId, rejectedToken) {
|
|
2726
|
-
const key2 = this.key(state, chainId);
|
|
2727
|
-
if (this.tokens.get(key2) === rejectedToken) {
|
|
2728
|
-
this.tokens.delete(key2);
|
|
2729
|
-
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2730
|
-
}
|
|
2731
|
-
return this.getToken(state, chainId);
|
|
2732
|
-
}
|
|
2733
|
-
matchesOrigin(token) {
|
|
2734
|
-
try {
|
|
2735
|
-
const message = new import_siwe.SiweMessage(JSON.parse(atob(token)).message);
|
|
2736
|
-
const url = resolveSiweOrigin(this.dependencies.origin);
|
|
2737
|
-
return message.domain === url.host && message.uri === url.origin && message.scheme === url.protocol.slice(0, -1);
|
|
2738
|
-
} catch {
|
|
2739
|
-
return false;
|
|
2740
|
-
}
|
|
2741
|
-
}
|
|
2742
|
-
clear(state, chainId) {
|
|
2743
|
-
if (!state || chainId === void 0) {
|
|
2744
|
-
for (const scope of this.scopes.values()) {
|
|
2745
|
-
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2746
|
-
}
|
|
2747
|
-
this.tokens.clear();
|
|
2748
|
-
this.pending.clear();
|
|
2749
|
-
this.scopes.clear();
|
|
2750
|
-
return;
|
|
2751
|
-
}
|
|
2752
|
-
const key2 = this.key(state, chainId);
|
|
2753
|
-
this.tokens.delete(key2);
|
|
2754
|
-
this.pending.delete(key2);
|
|
2755
|
-
this.scopes.delete(key2);
|
|
2756
|
-
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2757
|
-
}
|
|
2758
|
-
async sign(state, chainId) {
|
|
2759
|
-
const account = (0, import_viem5.getAddress)(state.walletAddress);
|
|
2760
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
2761
|
-
chain: import_chains2.base,
|
|
2762
|
-
transport: (0, import_viem5.custom)(state.provider)
|
|
2763
|
-
});
|
|
2764
|
-
const walletClient = (0, import_viem5.createWalletClient)({
|
|
2765
|
-
account,
|
|
2766
|
-
chain: import_chains2.base,
|
|
2767
|
-
transport: (0, import_viem5.custom)(state.provider)
|
|
2768
|
-
});
|
|
2769
|
-
await ensureWalletOnChain(
|
|
2770
|
-
publicClient,
|
|
2771
|
-
walletClient,
|
|
2772
|
-
8453
|
|
2773
|
-
);
|
|
2774
|
-
const message = createYieldseekerSiweMessage(
|
|
2775
|
-
account,
|
|
2776
|
-
chainId,
|
|
2777
|
-
this.dependencies
|
|
2778
|
-
);
|
|
2779
|
-
const signature = await walletClient.signMessage({ account, message });
|
|
2780
|
-
return encodeYieldseekerAuthToken({ message, signature });
|
|
2781
|
-
}
|
|
2782
|
-
};
|
|
2783
|
-
|
|
2784
|
-
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2785
|
-
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2786
|
-
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2787
|
-
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2788
|
-
var storage3 = () => {
|
|
2789
|
-
if (typeof window === "undefined") return null;
|
|
2790
|
-
try {
|
|
2791
|
-
return window.localStorage;
|
|
2792
|
-
} catch {
|
|
2793
|
-
return null;
|
|
2794
|
-
}
|
|
2795
|
-
};
|
|
2796
|
-
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2797
|
-
function valid(value, walletAddress, chainId, now) {
|
|
2798
|
-
return Boolean(
|
|
2799
|
-
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
|
|
2800
|
-
);
|
|
2801
|
-
}
|
|
2802
|
-
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2803
|
-
if (typeof window === "undefined") return null;
|
|
2804
|
-
const key2 = keyFor(walletAddress, chainId);
|
|
2805
|
-
const store = storage3();
|
|
2806
|
-
let parsed = null;
|
|
2807
|
-
try {
|
|
2808
|
-
const raw2 = store?.getItem(key2);
|
|
2809
|
-
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2810
|
-
} catch {
|
|
2811
|
-
parsed = null;
|
|
2812
|
-
}
|
|
2813
|
-
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2814
|
-
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2815
|
-
memoryIdentities.set(key2, candidate);
|
|
2816
|
-
return { userId: candidate.userId };
|
|
2817
|
-
}
|
|
2818
|
-
memoryIdentities.delete(key2);
|
|
2819
|
-
try {
|
|
2820
|
-
store?.removeItem(key2);
|
|
2821
|
-
} catch {
|
|
2822
|
-
}
|
|
2823
|
-
return null;
|
|
2824
|
-
}
|
|
2825
|
-
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2826
|
-
if (typeof window === "undefined") return;
|
|
2827
|
-
const identity = {
|
|
2828
|
-
userId,
|
|
2829
|
-
walletAddress,
|
|
2830
|
-
chainId,
|
|
2831
|
-
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2832
|
-
};
|
|
2833
|
-
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2834
|
-
const key2 = keyFor(walletAddress, chainId);
|
|
2835
|
-
memoryIdentities.set(key2, identity);
|
|
2836
|
-
try {
|
|
2837
|
-
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2838
|
-
} catch {
|
|
2839
|
-
}
|
|
2840
|
-
}
|
|
2841
|
-
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2842
|
-
const key2 = keyFor(walletAddress, chainId);
|
|
2843
|
-
memoryIdentities.delete(key2);
|
|
2844
|
-
try {
|
|
2845
|
-
storage3()?.removeItem(key2);
|
|
2846
|
-
} catch {
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2849
|
-
|
|
2850
|
-
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2851
|
-
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2852
|
-
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2853
|
-
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2854
|
-
}
|
|
2855
|
-
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2856
|
-
var YieldseekerApiError = class extends Error {
|
|
2857
|
-
constructor(status, providerCode, responseFields) {
|
|
2858
|
-
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2859
|
-
this.status = status;
|
|
2860
|
-
this.providerCode = providerCode;
|
|
2861
|
-
this.responseFields = responseFields;
|
|
2862
|
-
this.name = "YieldseekerApiError";
|
|
2863
|
-
}
|
|
2864
|
-
status;
|
|
2865
|
-
providerCode;
|
|
2866
|
-
responseFields;
|
|
2867
|
-
get isAuthenticationError() {
|
|
2868
|
-
return this.status === 401 || this.status === 403;
|
|
2869
|
-
}
|
|
2870
|
-
};
|
|
2871
|
-
function providerError(body, fallback) {
|
|
2872
|
-
if (!body || typeof body !== "object") return { code: fallback };
|
|
2873
|
-
const record = body;
|
|
2874
|
-
return {
|
|
2875
|
-
code: typeof record.message === "string" ? record.message : fallback,
|
|
2876
|
-
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2877
|
-
};
|
|
2878
|
-
}
|
|
2879
|
-
var YieldseekerApiClient = class {
|
|
2880
|
-
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch.bind(globalThis)) {
|
|
2881
|
-
this.owneyApiKey = owneyApiKey;
|
|
2882
|
-
this.baseUrl = baseUrl;
|
|
2883
|
-
this.fetchFn = fetchFn;
|
|
2884
|
-
}
|
|
2885
|
-
owneyApiKey;
|
|
2886
|
-
baseUrl;
|
|
2887
|
-
fetchFn;
|
|
2888
|
-
async request(path, options = {}) {
|
|
2889
|
-
const controller = new AbortController();
|
|
2890
|
-
const timer = setTimeout(
|
|
2891
|
-
() => controller.abort(),
|
|
2892
|
-
options.timeoutMs ?? 15e3
|
|
2893
|
-
);
|
|
2894
|
-
try {
|
|
2895
|
-
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2896
|
-
method: options.method ?? "GET",
|
|
2897
|
-
headers: {
|
|
2898
|
-
"Content-Type": "application/json",
|
|
2899
|
-
"x-owney-api-key": this.owneyApiKey,
|
|
2900
|
-
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2901
|
-
},
|
|
2902
|
-
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2903
|
-
signal: controller.signal
|
|
2904
|
-
});
|
|
2905
|
-
const payload = await response.json().catch(() => null);
|
|
2906
|
-
if (!response.ok) {
|
|
2907
|
-
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2908
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
2909
|
-
throw new YieldseekerApiError(
|
|
2910
|
-
response.status,
|
|
2911
|
-
error.code,
|
|
2912
|
-
retryAfter ? { ...error.fields, headers: { "Retry-After": retryAfter } } : error.fields
|
|
2913
|
-
);
|
|
2914
|
-
}
|
|
2915
|
-
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2916
|
-
return payload.data;
|
|
2917
|
-
}
|
|
2918
|
-
return payload;
|
|
2919
|
-
} catch (error) {
|
|
2920
|
-
if (error instanceof YieldseekerApiError) throw error;
|
|
2921
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2922
|
-
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2923
|
-
}
|
|
2924
|
-
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2925
|
-
cause: error instanceof Error ? error.message : String(error)
|
|
2926
|
-
});
|
|
2927
|
-
} finally {
|
|
2928
|
-
clearTimeout(timer);
|
|
2929
|
-
}
|
|
2930
|
-
}
|
|
2931
|
-
};
|
|
2932
|
-
|
|
2933
|
-
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2934
|
-
var import_viem6 = require("viem");
|
|
2935
|
-
|
|
2936
|
-
// src/lib/helpers/snapshot-apy.ts
|
|
2937
|
-
var DAY_MS = 864e5;
|
|
2938
|
-
function snapshotTime(date) {
|
|
2939
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return void 0;
|
|
2940
|
-
const time = Date.parse(date);
|
|
2941
|
-
return Number.isFinite(time) && new Date(time).toISOString().slice(0, 10) === date ? time : void 0;
|
|
2942
|
-
}
|
|
2943
|
-
function returnFactor(value) {
|
|
2944
|
-
if (typeof value !== "number" && typeof value !== "string") return void 0;
|
|
2945
|
-
if (typeof value === "string" && value.trim() === "") return void 0;
|
|
2946
|
-
const factor = Number(value);
|
|
2947
|
-
return Number.isFinite(factor) && factor >= 0 ? factor : void 0;
|
|
2948
|
-
}
|
|
2949
|
-
function calculateSnapshotApy(snapshots, lookbackDays, now = Date.now()) {
|
|
2950
|
-
if (!Number.isFinite(lookbackDays) || lookbackDays <= 0 || !Number.isFinite(now)) {
|
|
2951
|
-
return void 0;
|
|
2952
|
-
}
|
|
2953
|
-
const points = snapshots.flatMap((snapshot) => {
|
|
2954
|
-
const time = snapshotTime(snapshot.date);
|
|
2955
|
-
return time !== void 0 && time <= now ? [{ snapshot, time }] : [];
|
|
2956
|
-
}).sort((a, b) => a.time - b.time);
|
|
2957
|
-
const end = points.at(-1);
|
|
2958
|
-
if (!end) return void 0;
|
|
2959
|
-
const cutoff = end.time - lookbackDays * DAY_MS;
|
|
2960
|
-
const start = points.find((point) => point.time >= cutoff);
|
|
2961
|
-
const actualDays = (end.time - start.time) / DAY_MS;
|
|
2962
|
-
if (actualDays <= 0) return void 0;
|
|
2963
|
-
const startFactor = returnFactor(start.snapshot.cumulativeReturnFactor);
|
|
2964
|
-
const endFactor = returnFactor(end.snapshot.cumulativeReturnFactor);
|
|
2965
|
-
if (startFactor === void 0 || startFactor === 0 || endFactor === void 0) {
|
|
2966
|
-
return void 0;
|
|
2967
|
-
}
|
|
2968
|
-
const periodReturn = endFactor / startFactor - 1;
|
|
2969
|
-
const apy = Math.pow(1 + periodReturn, 365 / actualDays) - 1;
|
|
2970
|
-
return Number.isFinite(apy) ? apy : void 0;
|
|
2971
|
-
}
|
|
2972
|
-
|
|
2973
|
-
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2974
|
-
var YIELDSEEKER_ASSET_METADATA = {
|
|
2975
|
-
USDC: {
|
|
2976
|
-
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2977
|
-
decimals: 6
|
|
2978
|
-
},
|
|
2979
|
-
WETH: {
|
|
2980
|
-
address: "0x4200000000000000000000000000000000000006",
|
|
2981
|
-
decimals: 18
|
|
2982
|
-
}
|
|
2983
|
-
};
|
|
2984
|
-
|
|
2985
|
-
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2986
|
-
function invalid(endpoint, detail) {
|
|
2987
|
-
throw new OwneyError(
|
|
2988
|
-
"AGENT_INVALID_RESPONSE",
|
|
2989
|
-
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2990
|
-
{ endpoint, detail },
|
|
2991
|
-
"yieldseeker"
|
|
2992
|
-
);
|
|
2993
|
-
}
|
|
2994
|
-
function raw(value, endpoint) {
|
|
2995
|
-
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2996
|
-
return invalid(endpoint, "expected a base-10 integer string");
|
|
2997
|
-
}
|
|
2998
|
-
return BigInt(value);
|
|
2999
|
-
}
|
|
3000
|
-
function decimal(value, decimals, endpoint) {
|
|
3001
|
-
return (0, import_viem6.formatUnits)(raw(value, endpoint), decimals);
|
|
3002
|
-
}
|
|
3003
|
-
function usd(rawAmount, decimals, price) {
|
|
3004
|
-
return Number((0, import_viem6.formatUnits)(rawAmount, decimals)) * price;
|
|
3005
|
-
}
|
|
3006
|
-
function percent(value) {
|
|
3007
|
-
const result = Number(value);
|
|
3008
|
-
return Number.isFinite(result) ? result * 100 : 0;
|
|
3009
|
-
}
|
|
3010
|
-
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
3011
|
-
function publicApyAfterYieldseekerFee(value) {
|
|
3012
|
-
const grossPercent = percent(value);
|
|
3013
|
-
if (grossPercent <= 0) return grossPercent;
|
|
3014
|
-
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
3015
|
-
return Math.round(netPercent * 1e12) / 1e12;
|
|
3016
|
-
}
|
|
3017
|
-
function riskAdjustedApyForDays(option, days) {
|
|
3018
|
-
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
3019
|
-
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
3020
|
-
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
3021
|
-
}
|
|
3022
|
-
function assetAddressValue(record, address) {
|
|
3023
|
-
const entry = Object.entries(record).find(
|
|
3024
|
-
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
3025
|
-
);
|
|
3026
|
-
return entry?.[1] ?? "0";
|
|
3027
|
-
}
|
|
3028
|
-
function position(value, asset, baseAssetDecimals) {
|
|
3029
|
-
const option = value?.yieldOption;
|
|
3030
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem6.isAddress)(option.address)) {
|
|
3031
|
-
return invalid("yield positions", "missing vault metadata");
|
|
3032
|
-
}
|
|
3033
|
-
return {
|
|
3034
|
-
chain: "BASE",
|
|
3035
|
-
protocol: option.provider,
|
|
3036
|
-
protocolId: option.address,
|
|
3037
|
-
pool: option.name,
|
|
3038
|
-
asset,
|
|
3039
|
-
// `assetsRaw` is the ERC-4626 vault-share quantity, whose decimals can
|
|
3040
|
-
// differ from the underlying asset. Yieldseeker already converts it to
|
|
3041
|
-
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
3042
|
-
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
3043
|
-
// share quantity separate from the withdrawable underlying asset amount.
|
|
3044
|
-
amount: decimal(
|
|
3045
|
-
value.assetsBase,
|
|
3046
|
-
baseAssetDecimals,
|
|
3047
|
-
"yield positions"
|
|
3048
|
-
),
|
|
3049
|
-
amountRaw: String(value.assetsRaw),
|
|
3050
|
-
withdrawableAmountRaw: String(value.withdrawableAssetsRaw),
|
|
3051
|
-
apy: percent(option.riskAdjustedApy),
|
|
3052
|
-
tvl: Number(option.totalDepositsUsd),
|
|
3053
|
-
liquidity: Number(option.withdrawableDepositsUsd)
|
|
3054
|
-
};
|
|
3055
|
-
}
|
|
3056
|
-
function mapYieldseekerBalances(contexts) {
|
|
3057
|
-
const tokens = [];
|
|
3058
|
-
const assetBalances = [];
|
|
3059
|
-
const positions = [];
|
|
3060
|
-
let totalUsd = 0;
|
|
3061
|
-
for (const context of contexts) {
|
|
3062
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
3063
|
-
assetBalances.push({
|
|
3064
|
-
chain: "BASE",
|
|
3065
|
-
chainId: 8453,
|
|
3066
|
-
asset: context.asset,
|
|
3067
|
-
amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
|
|
3068
|
-
});
|
|
3069
|
-
const idle = assetAddressValue(
|
|
3070
|
-
context.snapshot.tokenBalances,
|
|
3071
|
-
metadata.address
|
|
3072
|
-
);
|
|
3073
|
-
tokens.push({
|
|
3074
|
-
chain: "BASE",
|
|
3075
|
-
chainId: 8453,
|
|
3076
|
-
asset: context.asset,
|
|
3077
|
-
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
3078
|
-
});
|
|
3079
|
-
positions.push(
|
|
3080
|
-
...context.positions.map(
|
|
3081
|
-
(entry) => position(
|
|
3082
|
-
entry,
|
|
3083
|
-
context.asset,
|
|
3084
|
-
context.snapshot.baseAssetDecimals
|
|
3085
|
-
)
|
|
3086
|
-
)
|
|
3087
|
-
);
|
|
3088
|
-
totalUsd += usd(
|
|
3089
|
-
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
3090
|
-
context.snapshot.baseAssetDecimals,
|
|
3091
|
-
context.snapshot.baseAssetPriceUsd
|
|
3092
|
-
);
|
|
3093
|
-
}
|
|
3094
|
-
return {
|
|
3095
|
-
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
3096
|
-
totalBalance: String(totalUsd),
|
|
3097
|
-
totalBalanceAsset: "usdc",
|
|
3098
|
-
assetBalances,
|
|
3099
|
-
tokens,
|
|
3100
|
-
positions
|
|
3101
|
-
};
|
|
3102
|
-
}
|
|
3103
|
-
function mapYieldseekerEarnings(contexts) {
|
|
3104
|
-
const tokens = [];
|
|
3105
|
-
let lifetimeEarnings = 0;
|
|
3106
|
-
for (const context of contexts) {
|
|
3107
|
-
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
3108
|
-
tokens.push({
|
|
3109
|
-
chain: "BASE",
|
|
3110
|
-
chainId: 8453,
|
|
3111
|
-
asset: context.asset,
|
|
3112
|
-
amount: (0, import_viem6.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
3113
|
-
});
|
|
3114
|
-
lifetimeEarnings += usd(
|
|
3115
|
-
amount,
|
|
3116
|
-
context.snapshot.baseAssetDecimals,
|
|
3117
|
-
context.snapshot.baseAssetPriceUsd
|
|
3118
|
-
);
|
|
3119
|
-
}
|
|
3120
|
-
return {
|
|
3121
|
-
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
3122
|
-
lifetimeEarnings,
|
|
3123
|
-
tokens
|
|
3124
|
-
};
|
|
3125
|
-
}
|
|
3126
|
-
function apyForDays(context, days, now) {
|
|
3127
|
-
if (days === "7D") return percent(context.snapshot.apy7d);
|
|
3128
|
-
if (days === "30D") return percent(context.snapshot.apy30d);
|
|
3129
|
-
const apy = calculateSnapshotApy(context.historic?.dailyYieldSnapshots ?? [], 14, now);
|
|
3130
|
-
const apyPercent = apy === void 0 ? void 0 : apy * 100;
|
|
3131
|
-
return apyPercent !== void 0 && Number.isFinite(apyPercent) ? apyPercent : void 0;
|
|
3132
|
-
}
|
|
3133
|
-
function dailyApy(point) {
|
|
3134
|
-
const total = raw(point.totalValueBase, "historic position");
|
|
3135
|
-
const earned = raw(point.dailyYieldBase, "historic position");
|
|
3136
|
-
const principal = total - earned;
|
|
3137
|
-
if (principal <= 0n || earned === 0n) return 0;
|
|
3138
|
-
return Number(earned) / Number(principal) * 365 * 100;
|
|
3139
|
-
}
|
|
3140
|
-
function aggregateHistory(contexts, dayCount, now) {
|
|
3141
|
-
const today = new Date(now).toISOString().slice(0, 10);
|
|
3142
|
-
const cutoff = new Date(Date.parse(today) - (dayCount - 1) * 864e5).toISOString().slice(0, 10);
|
|
3143
|
-
const assets = new Set(contexts.map((context) => context.asset.toUpperCase()));
|
|
3144
|
-
const unit = assets.size === 1 ? [...assets][0] : "USD";
|
|
3145
|
-
const byDate = /* @__PURE__ */ new Map();
|
|
3146
|
-
for (const context of contexts) {
|
|
3147
|
-
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
3148
|
-
for (const point of points) {
|
|
3149
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(point.date) || !Number.isFinite(Date.parse(point.date)) || point.date < cutoff || point.date > today) continue;
|
|
3150
|
-
const tokens = Number(decimal(point.totalValueBase, point.baseAssetDecimals, "historic position"));
|
|
3151
|
-
const amount = assets.size === 1 ? tokens : tokens * point.baseAssetPriceUsd;
|
|
3152
|
-
if (!Number.isFinite(amount) || amount < 0) {
|
|
3153
|
-
invalid("historic position", "expected a finite non-negative balance");
|
|
3154
|
-
}
|
|
3155
|
-
const current = byDate.get(point.date) ?? { weighted: 0, amount: 0 };
|
|
3156
|
-
current.weighted += dailyApy(point) * amount;
|
|
3157
|
-
current.amount += amount;
|
|
3158
|
-
byDate.set(point.date, current);
|
|
3159
|
-
}
|
|
3160
|
-
}
|
|
3161
|
-
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
3162
|
-
date,
|
|
3163
|
-
apy: value.amount > 0 ? value.weighted / value.amount : 0,
|
|
3164
|
-
historicalBalance: { amount: value.amount, unit }
|
|
3165
|
-
}));
|
|
3166
|
-
}
|
|
3167
|
-
function mapYieldseekerApy(walletAddress, contexts, days, now = Date.now()) {
|
|
3168
|
-
let weighted = 0;
|
|
3169
|
-
let totalUsd = 0;
|
|
3170
|
-
const byAsset = {};
|
|
3171
|
-
for (const context of contexts) {
|
|
3172
|
-
const valueUsd = usd(
|
|
3173
|
-
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
3174
|
-
context.snapshot.baseAssetDecimals,
|
|
3175
|
-
context.snapshot.baseAssetPriceUsd
|
|
3176
|
-
);
|
|
3177
|
-
const apy = apyForDays(context, days, now);
|
|
3178
|
-
if (apy === void 0) continue;
|
|
3179
|
-
weighted += apy * valueUsd;
|
|
3180
|
-
totalUsd += valueUsd;
|
|
3181
|
-
byAsset[context.asset] = apy;
|
|
3182
|
-
}
|
|
3183
|
-
const dayCount = Number(days.slice(0, -1));
|
|
3184
|
-
return {
|
|
3185
|
-
walletAddress,
|
|
3186
|
-
...days !== "14D" || Object.keys(byAsset).length > 0 ? { weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0 } : {},
|
|
3187
|
-
apyByChainAndAsset: { 8453: byAsset },
|
|
3188
|
-
history: aggregateHistory(contexts, dayCount, now)
|
|
3189
|
-
};
|
|
3190
|
-
}
|
|
3191
|
-
function actionType(value) {
|
|
3192
|
-
const normalized = value.toLowerCase();
|
|
3193
|
-
if (normalized.includes("deposit")) return "Deposit";
|
|
3194
|
-
if (normalized.includes("withdraw")) return "Withdraw";
|
|
3195
|
-
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
3196
|
-
return "Earned";
|
|
3197
|
-
return "Rebalance";
|
|
3198
|
-
}
|
|
3199
|
-
function transactionHashes(details) {
|
|
3200
|
-
if (!details) return [];
|
|
3201
|
-
const values = [
|
|
3202
|
-
details.transactionHash,
|
|
3203
|
-
details.txHash,
|
|
3204
|
-
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
3205
|
-
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
3206
|
-
];
|
|
3207
|
-
return values.filter(
|
|
3208
|
-
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
3209
|
-
).filter((value, index, all) => all.indexOf(value) === index);
|
|
3210
|
-
}
|
|
3211
|
-
function actionEntry(action) {
|
|
3212
|
-
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
3213
|
-
return {
|
|
3214
|
-
agent: "yieldseeker",
|
|
3215
|
-
action: actionType(action.actionType),
|
|
3216
|
-
date: action.createdDate,
|
|
3217
|
-
oldApy: null,
|
|
3218
|
-
newApy: null,
|
|
3219
|
-
transactions: [
|
|
3220
|
-
{
|
|
3221
|
-
txHashes: transactionHashes(action.details),
|
|
3222
|
-
chainId: 8453
|
|
3223
|
-
}
|
|
3224
|
-
],
|
|
3225
|
-
rebalanceLog: []
|
|
3226
|
-
};
|
|
3227
|
-
}
|
|
3228
|
-
function depositDestination(context, movement) {
|
|
3229
|
-
const to = movement.toAddress.toLowerCase();
|
|
3230
|
-
const option = context.positions.find(({ yieldOption }) => yieldOption.chainId === movement.chainId && yieldOption.assetAddress.toLowerCase() === context.agent.assetAddress.toLowerCase() && yieldOption.address.toLowerCase() === to)?.yieldOption;
|
|
3231
|
-
if (option) return { protocol: option.provider || "Vault", pool: option.name, tokenSymbol: context.asset };
|
|
3232
|
-
const receipt = context.historic?.assets.find((asset) => asset.chainId === movement.chainId && !asset.isSpam && asset.address.toLowerCase() === to && asset.address.toLowerCase() !== context.agent.assetAddress.toLowerCase());
|
|
3233
|
-
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);
|
|
3234
|
-
if (receipt && minted) return { protocol: "Vault", pool: receipt.name || receipt.symbol, tokenSymbol: context.asset };
|
|
3235
|
-
return void 0;
|
|
3236
|
-
}
|
|
3237
|
-
function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses, destination) {
|
|
3238
|
-
const from = movement.fromAddress.toLowerCase();
|
|
3239
|
-
const to = movement.toAddress.toLowerCase();
|
|
3240
|
-
const owner = ownerAddress.toLowerCase();
|
|
3241
|
-
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
3242
|
-
const baseAsset = agent.assetAddress.toLowerCase();
|
|
3243
|
-
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
3244
|
-
return void 0;
|
|
3245
|
-
}
|
|
3246
|
-
let action;
|
|
3247
|
-
if (to === agentWallet && !vaultAddresses.has(from)) {
|
|
3248
|
-
action = "Top up";
|
|
3249
|
-
} else if (from === agentWallet && to === owner) {
|
|
3250
|
-
action = "Withdraw";
|
|
3251
|
-
} else if (from === agentWallet && destination) {
|
|
3252
|
-
action = "Deposit";
|
|
3253
|
-
}
|
|
3254
|
-
if (!action) return void 0;
|
|
3255
|
-
return {
|
|
3256
|
-
agent: "yieldseeker",
|
|
3257
|
-
action,
|
|
3258
|
-
...action === "Deposit" && destination ? { positions: [{
|
|
3259
|
-
...destination,
|
|
3260
|
-
amount: decimal(movement.assetAmount, YIELDSEEKER_ASSET_METADATA[asset].decimals, "historic position")
|
|
3261
|
-
}] } : {},
|
|
3262
|
-
date: movement.blockDate,
|
|
3263
|
-
oldApy: null,
|
|
3264
|
-
newApy: null,
|
|
3265
|
-
transactions: [
|
|
3266
|
-
{
|
|
3267
|
-
txHashes: [movement.transactionHash],
|
|
3268
|
-
chainId: agent.chainId,
|
|
3269
|
-
tokenSymbol: asset,
|
|
3270
|
-
amount: decimal(
|
|
3271
|
-
movement.assetAmount,
|
|
3272
|
-
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
3273
|
-
"historic position"
|
|
3274
|
-
)
|
|
3275
|
-
}
|
|
3276
|
-
],
|
|
3277
|
-
rebalanceLog: []
|
|
3278
|
-
};
|
|
3279
|
-
}
|
|
3280
|
-
function mapYieldseekerHistory(contexts, options) {
|
|
3281
|
-
const entries = contexts.flatMap((context) => {
|
|
3282
|
-
const seenMovements = /* @__PURE__ */ new Set();
|
|
3283
|
-
const movements = (context.historic?.movements ?? []).filter((movement) => {
|
|
3284
|
-
const key2 = `${movement.chainId}:${movement.transactionHash.toLowerCase()}:${movement.logIndex}`;
|
|
3285
|
-
if (seenMovements.has(key2)) return false;
|
|
3286
|
-
seenMovements.add(key2);
|
|
3287
|
-
return true;
|
|
3288
|
-
});
|
|
3289
|
-
return [
|
|
3290
|
-
...movements.map(
|
|
3291
|
-
(movement) => movementEntry(
|
|
3292
|
-
movement,
|
|
3293
|
-
context.wallet,
|
|
3294
|
-
context.agent,
|
|
3295
|
-
context.asset,
|
|
3296
|
-
options.ownerAddress,
|
|
3297
|
-
options.vaultAddresses,
|
|
3298
|
-
depositDestination(context, movement)
|
|
3299
|
-
)
|
|
3300
|
-
),
|
|
3301
|
-
...(context.actions ?? []).map(actionEntry)
|
|
3302
|
-
].filter((entry) => entry !== void 0);
|
|
3303
|
-
});
|
|
3304
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
3305
|
-
const ungrouped = [];
|
|
3306
|
-
for (const entry of entries) {
|
|
3307
|
-
const tx = entry.transactions[0];
|
|
3308
|
-
const hash = tx?.txHashes[0];
|
|
3309
|
-
if (!hash) {
|
|
3310
|
-
ungrouped.push(entry);
|
|
3311
|
-
continue;
|
|
3312
|
-
}
|
|
3313
|
-
const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}`;
|
|
3314
|
-
const previous = grouped.get(key2);
|
|
3315
|
-
if (!previous) {
|
|
3316
|
-
grouped.set(key2, entry);
|
|
3317
|
-
continue;
|
|
3318
|
-
}
|
|
3319
|
-
if (entry.action === "Deposit" && entry.positions?.length) {
|
|
3320
|
-
if (!previous.positions?.length) {
|
|
3321
|
-
grouped.set(key2, entry);
|
|
3322
|
-
continue;
|
|
3323
|
-
}
|
|
3324
|
-
previous.positions.push(...entry.positions);
|
|
3325
|
-
previous.transactions.push(...entry.transactions);
|
|
3326
|
-
}
|
|
3327
|
-
}
|
|
3328
|
-
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));
|
|
3329
|
-
return {
|
|
3330
|
-
data: filtered.slice(0, options.limit),
|
|
3331
|
-
// v1 returns the whole action/movement collection and defines no cursor.
|
|
3332
|
-
// Report a terminal page so callers never loop over the same prefix.
|
|
3333
|
-
hasMore: false
|
|
3334
|
-
};
|
|
3335
|
-
}
|
|
3336
|
-
function mapYieldseekerProfile(address, contexts) {
|
|
3337
|
-
const protocols = /* @__PURE__ */ new Set();
|
|
3338
|
-
for (const context of contexts) {
|
|
3339
|
-
for (const current of context.positions) {
|
|
3340
|
-
if (current.yieldOption?.provider) {
|
|
3341
|
-
protocols.add(String(current.yieldOption.provider));
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
}
|
|
3345
|
-
return {
|
|
3346
|
-
address,
|
|
3347
|
-
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
3348
|
-
chains: contexts.length > 0 ? [8453] : [],
|
|
3349
|
-
hasActiveSessionKey: contexts.some(
|
|
3350
|
-
(context) => context.wallet.initializedDate != null
|
|
3351
|
-
),
|
|
3352
|
-
protocols: [...protocols]
|
|
3353
|
-
};
|
|
3354
|
-
}
|
|
3355
|
-
function mapYieldseekerAgentApy(options, days) {
|
|
3356
|
-
const perAsset = {};
|
|
3357
|
-
const all = [];
|
|
3358
|
-
for (const entry of options) {
|
|
3359
|
-
const apys = entry.yieldOptions.map(
|
|
3360
|
-
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
3361
|
-
).filter(Number.isFinite);
|
|
3362
|
-
if (apys.length === 0) continue;
|
|
3363
|
-
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
3364
|
-
perAsset[entry.asset] = average;
|
|
3365
|
-
all.push(average);
|
|
3366
|
-
}
|
|
3367
|
-
return {
|
|
3368
|
-
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
3369
|
-
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
3370
|
-
};
|
|
3371
|
-
}
|
|
3372
|
-
|
|
3373
|
-
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3374
|
-
var OWNEY_AGENT_NAME = "owney";
|
|
3375
|
-
var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
3376
|
-
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3377
|
-
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3378
|
-
var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
|
|
3379
|
-
var YIELDSEEKER_AGENT_LIST_CACHE_MS = 12e4;
|
|
3380
|
-
var YIELDSEEKER_PORTFOLIO_CACHE_MS = 12e4;
|
|
3381
|
-
var YIELDSEEKER_SETTLEMENT_CACHE_MS = 5e3;
|
|
3382
|
-
var YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS = 15e3;
|
|
3383
|
-
var YIELDSEEKER_READ_FAILURE_COOLDOWN_MS = 6e4;
|
|
3384
|
-
var YIELDSEEKER_SETTLEMENT_WINDOW_MS = 5 * 6e4;
|
|
3385
|
-
var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
|
|
3386
|
-
function generateYieldseekerUsername() {
|
|
3387
|
-
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3388
|
-
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
3389
|
-
}
|
|
3390
|
-
function isUsernameConflict(error) {
|
|
3391
|
-
if (!(error instanceof YieldseekerApiError)) return false;
|
|
3392
|
-
const code = error.providerCode.toUpperCase();
|
|
3393
|
-
return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
|
|
3394
|
-
}
|
|
3395
|
-
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
3396
|
-
{
|
|
3397
|
-
type: "function",
|
|
3398
|
-
name: "withdrawAssetToUser",
|
|
3399
|
-
stateMutability: "nonpayable",
|
|
3400
|
-
inputs: [
|
|
3401
|
-
{ name: "recipient", type: "address" },
|
|
3402
|
-
{ name: "asset", type: "address" },
|
|
3403
|
-
{ name: "amount", type: "uint256" }
|
|
3404
|
-
],
|
|
3405
|
-
outputs: []
|
|
3406
|
-
},
|
|
3407
|
-
{
|
|
3408
|
-
type: "function",
|
|
3409
|
-
name: "withdrawAllAssetToUser",
|
|
3410
|
-
stateMutability: "nonpayable",
|
|
3411
|
-
inputs: [
|
|
3412
|
-
{ name: "recipient", type: "address" },
|
|
3413
|
-
{ name: "asset", type: "address" }
|
|
3414
|
-
],
|
|
3415
|
-
outputs: []
|
|
3416
|
-
}
|
|
3417
|
-
];
|
|
3418
|
-
function query(params) {
|
|
3419
|
-
const search = new URLSearchParams();
|
|
3420
|
-
for (const [key2, value] of Object.entries(params)) {
|
|
3421
|
-
if (value !== void 0) search.set(key2, String(value));
|
|
3422
|
-
}
|
|
3423
|
-
const encoded = search.toString();
|
|
3424
|
-
return encoded ? `?${encoded}` : "";
|
|
3425
|
-
}
|
|
3426
|
-
var YieldseekerAgent = class {
|
|
3427
|
-
id = "yieldseeker";
|
|
3428
|
-
balanceComposition = "tokens-plus-positions";
|
|
3429
|
-
withdrawalRequiresWalletApproval = true;
|
|
3430
|
-
supportedChainIds = [8453];
|
|
3431
|
-
supportedAssets = [
|
|
3432
|
-
{
|
|
3433
|
-
chainId: 8453,
|
|
3434
|
-
chain: "BASE",
|
|
3435
|
-
assets: [
|
|
3436
|
-
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
3437
|
-
{ symbol: "WETH", minDepositAmount: "1" }
|
|
3438
|
-
]
|
|
3439
|
-
}
|
|
3440
|
-
];
|
|
3441
|
-
api;
|
|
3442
|
-
auth;
|
|
3443
|
-
rpcUrls;
|
|
3444
|
-
receiptClient;
|
|
3445
|
-
transactionExecutor;
|
|
3446
|
-
unwindReceiptWaiter;
|
|
3447
|
-
agentContexts = /* @__PURE__ */ new Map();
|
|
3448
|
-
users = /* @__PURE__ */ new Map();
|
|
3449
|
-
pendingAgents = /* @__PURE__ */ new Map();
|
|
3450
|
-
pendingWalletContexts = /* @__PURE__ */ new Map();
|
|
3451
|
-
readCache = /* @__PURE__ */ new Map();
|
|
3452
|
-
pendingReads = /* @__PURE__ */ new Map();
|
|
3453
|
-
readGeneration = 0;
|
|
3454
|
-
portfolioVersions = /* @__PURE__ */ new Map();
|
|
3455
|
-
snapshotFailures = /* @__PURE__ */ new Map();
|
|
3456
|
-
standardReadFailures = /* @__PURE__ */ new Map();
|
|
3457
|
-
reconcileUntil = /* @__PURE__ */ new Map();
|
|
3458
|
-
activityRefreshUntil = /* @__PURE__ */ new Map();
|
|
3459
|
-
// null means a confirmed movement has no reliable baseline. Do not guess
|
|
3460
|
-
// settlement; keep slow reconciliation after the fast window expires.
|
|
3461
|
-
snapshotMovements = /* @__PURE__ */ new Map();
|
|
3462
|
-
movementEpochs = /* @__PURE__ */ new Map();
|
|
3463
|
-
yieldOptions = /* @__PURE__ */ new Map();
|
|
3464
|
-
pendingYieldOptions = /* @__PURE__ */ new Map();
|
|
3465
|
-
constructor(owneyApiKey, options = {}) {
|
|
3466
|
-
this.api = new YieldseekerApiClient(
|
|
3467
|
-
owneyApiKey,
|
|
3468
|
-
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
3469
|
-
options.fetchFn
|
|
3470
|
-
);
|
|
3471
|
-
this.auth = new YieldseekerAuth(options.auth);
|
|
3472
|
-
this.rpcUrls = options.rpcUrls;
|
|
3473
|
-
this.transactionExecutor = options.transactionExecutor;
|
|
3474
|
-
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3475
|
-
}
|
|
3476
|
-
getReceiptClient() {
|
|
3477
|
-
this.receiptClient ??= createPaidRpcClient(import_chains3.base, this.rpcUrls);
|
|
3478
|
-
return this.receiptClient;
|
|
3479
|
-
}
|
|
3480
|
-
async disconnect() {
|
|
3481
|
-
this.readGeneration += 1;
|
|
3482
|
-
this.auth.clear();
|
|
3483
|
-
for (const key2 of this.users.keys()) {
|
|
3484
|
-
const [walletAddress, chainId] = key2.split(":");
|
|
3485
|
-
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
3486
|
-
}
|
|
3487
|
-
this.users.clear();
|
|
3488
|
-
this.agentContexts.clear();
|
|
3489
|
-
this.pendingAgents.clear();
|
|
3490
|
-
this.pendingWalletContexts.clear();
|
|
3491
|
-
this.readCache.clear();
|
|
3492
|
-
this.pendingReads.clear();
|
|
3493
|
-
this.portfolioVersions.clear();
|
|
3494
|
-
this.snapshotFailures.clear();
|
|
3495
|
-
this.standardReadFailures.clear();
|
|
3496
|
-
this.reconcileUntil.clear();
|
|
3497
|
-
this.activityRefreshUntil.clear();
|
|
3498
|
-
this.snapshotMovements.clear();
|
|
3499
|
-
this.movementEpochs.clear();
|
|
3500
|
-
}
|
|
3501
|
-
async activateAgent(state, chainId, asset) {
|
|
3502
|
-
this.assertChain(chainId);
|
|
3503
|
-
const targetAsset = asset ?? "USDC";
|
|
3504
|
-
this.assertAsset(targetAsset);
|
|
3505
|
-
await this.ensureAgent(state, chainId, targetAsset);
|
|
3506
|
-
}
|
|
3507
|
-
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3508
|
-
this.assertChain(chainId);
|
|
3509
|
-
this.assertAsset(asset);
|
|
3510
|
-
if (BigInt(amount) <= 0n) {
|
|
3511
|
-
throw new OwneyError(
|
|
3512
|
-
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
3513
|
-
"Yieldseeker deposits must be greater than zero.",
|
|
3514
|
-
{ amount, minDepositAmount: "1" },
|
|
3515
|
-
this.id
|
|
3516
|
-
);
|
|
3517
|
-
}
|
|
3518
|
-
const context = await this.ensureAgent(state, chainId, asset);
|
|
3519
|
-
const generation = this.readGeneration;
|
|
3520
|
-
const movement = this.snapshotMovement(state, chainId, context, BigInt(amount));
|
|
3521
|
-
let txHash;
|
|
3522
|
-
if (depositCallback) {
|
|
3523
|
-
provideDepositVerificationContext(depositCallback, {
|
|
3524
|
-
agentId: "yieldseeker",
|
|
3525
|
-
signature: await this.auth.getToken(state, chainId),
|
|
3526
|
-
userId: context.user.userId,
|
|
3527
|
-
yieldseekerAgentId: context.agent.agentId
|
|
3528
|
-
});
|
|
3529
|
-
txHash = await depositCallback(
|
|
3530
|
-
context.wallet.walletAddress,
|
|
3531
|
-
chainId,
|
|
3532
|
-
amount
|
|
3533
|
-
);
|
|
3534
|
-
await this.waitForReceipt(state, chainId, txHash);
|
|
3535
|
-
} else {
|
|
3536
|
-
txHash = await this.submitTransaction(state, chainId, {
|
|
3537
|
-
from: (0, import_viem7.getAddress)(state.walletAddress),
|
|
3538
|
-
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3539
|
-
data: (0, import_viem7.encodeFunctionData)({
|
|
3540
|
-
abi: import_viem7.erc20Abi,
|
|
3541
|
-
functionName: "transfer",
|
|
3542
|
-
args: [(0, import_viem7.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
3543
|
-
}),
|
|
3544
|
-
value: "0",
|
|
3545
|
-
chainId
|
|
3546
|
-
});
|
|
3547
|
-
}
|
|
3548
|
-
if (this.readGeneration === generation) {
|
|
3549
|
-
await this.refreshSnapshotAfterMovement(state, chainId, context, "deposit", movement);
|
|
3550
|
-
}
|
|
3551
|
-
return {
|
|
3552
|
-
txHash,
|
|
3553
|
-
smartWallet: context.wallet.walletAddress,
|
|
3554
|
-
amount
|
|
3555
|
-
};
|
|
3556
|
-
}
|
|
3557
|
-
async withdraw(state, chainId, asset, amount) {
|
|
3558
|
-
this.assertChain(chainId);
|
|
3559
|
-
this.assertAsset(asset);
|
|
3560
|
-
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
3561
|
-
throw new OwneyError(
|
|
3562
|
-
"WITHDRAW_FAILED",
|
|
3563
|
-
"Yieldseeker withdrawals must be greater than zero.",
|
|
3564
|
-
{ amount },
|
|
3565
|
-
this.id
|
|
3566
|
-
);
|
|
3567
|
-
}
|
|
3568
|
-
const context = await this.findAgent(state, chainId, asset);
|
|
3569
|
-
if (!context) {
|
|
3570
|
-
throw new OwneyError(
|
|
3571
|
-
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3572
|
-
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3573
|
-
{ asset, available: "0" },
|
|
3574
|
-
this.id
|
|
3575
|
-
);
|
|
3576
|
-
}
|
|
3577
|
-
const generation = this.readGeneration;
|
|
3578
|
-
let confirmedMovement = false;
|
|
3579
|
-
let settlement = null;
|
|
3580
|
-
try {
|
|
3581
|
-
const portfolio = await this.loadPortfolioContext(
|
|
3582
|
-
state,
|
|
3583
|
-
chainId,
|
|
3584
|
-
context
|
|
3585
|
-
);
|
|
3586
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3587
|
-
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3588
|
-
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3589
|
-
);
|
|
3590
|
-
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3591
|
-
const deployed = portfolio.positions.reduce(
|
|
3592
|
-
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3593
|
-
0n
|
|
3594
|
-
);
|
|
3595
|
-
const totalAvailable = idle + deployed;
|
|
3596
|
-
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3597
|
-
const plannedMovement = this.snapshotMovement(state, chainId, context, -requested);
|
|
3598
|
-
if (requested > totalAvailable) {
|
|
3599
|
-
throw new OwneyError(
|
|
3600
|
-
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3601
|
-
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3602
|
-
{
|
|
3603
|
-
asset,
|
|
3604
|
-
requested: requested.toString(),
|
|
3605
|
-
available: totalAvailable.toString()
|
|
3606
|
-
},
|
|
3607
|
-
this.id
|
|
3608
|
-
);
|
|
3609
|
-
}
|
|
3610
|
-
let remaining = requested > idle ? requested - idle : 0n;
|
|
3611
|
-
for (const position2 of portfolio.positions) {
|
|
3612
|
-
if (remaining === 0n) break;
|
|
3613
|
-
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3614
|
-
if (available <= 0n) continue;
|
|
3615
|
-
const assetsRaw = available < remaining ? available : remaining;
|
|
3616
|
-
const response = await this.walletRequest(
|
|
3617
|
-
state,
|
|
3618
|
-
chainId,
|
|
3619
|
-
this.agentPath(context, "withdraw-from-position"),
|
|
3620
|
-
{
|
|
3621
|
-
method: "POST",
|
|
3622
|
-
body: {
|
|
3623
|
-
chainId,
|
|
3624
|
-
vaultAddress: position2.yieldOption.address,
|
|
3625
|
-
assetsRaw: assetsRaw.toString()
|
|
3626
|
-
}
|
|
3627
|
-
}
|
|
3628
|
-
);
|
|
3629
|
-
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3630
|
-
throw this.invalidResponse("position withdrawal");
|
|
3631
|
-
}
|
|
3632
|
-
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3633
|
-
confirmedMovement = true;
|
|
3634
|
-
remaining -= assetsRaw;
|
|
3635
|
-
}
|
|
3636
|
-
if (remaining > 0n) {
|
|
3637
|
-
throw this.invalidResponse("yield positions", {
|
|
3638
|
-
reason: "Withdrawable positions could not cover the request.",
|
|
3639
|
-
remaining: remaining.toString()
|
|
3640
|
-
});
|
|
3641
|
-
}
|
|
3642
|
-
const account = (0, import_viem7.getAddress)(state.walletAddress);
|
|
3643
|
-
const txHash = await this.submitTransaction(state, chainId, {
|
|
3644
|
-
from: account,
|
|
3645
|
-
to: (0, import_viem7.getAddress)(context.wallet.walletAddress),
|
|
3646
|
-
data: amount === void 0 ? (0, import_viem7.encodeFunctionData)({
|
|
3647
|
-
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3648
|
-
functionName: "withdrawAllAssetToUser",
|
|
3649
|
-
args: [account, metadata.address]
|
|
3650
|
-
}) : (0, import_viem7.encodeFunctionData)({
|
|
3651
|
-
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3652
|
-
functionName: "withdrawAssetToUser",
|
|
3653
|
-
args: [account, metadata.address, requested]
|
|
3654
|
-
}),
|
|
3655
|
-
value: "0",
|
|
3656
|
-
chainId
|
|
3657
|
-
});
|
|
3658
|
-
confirmedMovement = true;
|
|
3659
|
-
settlement = plannedMovement;
|
|
3660
|
-
return {
|
|
3661
|
-
txHash,
|
|
3662
|
-
type: amount === void 0 ? "full" : "partial",
|
|
3663
|
-
amount: requested.toString()
|
|
3664
|
-
};
|
|
3665
|
-
} finally {
|
|
3666
|
-
if (confirmedMovement && this.readGeneration === generation) {
|
|
3667
|
-
await this.refreshSnapshotAfterMovement(
|
|
3668
|
-
state,
|
|
3669
|
-
chainId,
|
|
3670
|
-
context,
|
|
3671
|
-
"withdrawal",
|
|
3672
|
-
settlement
|
|
3673
|
-
);
|
|
3674
|
-
}
|
|
3675
|
-
}
|
|
3676
|
-
}
|
|
3677
|
-
async getBalances(state, chainId) {
|
|
3678
|
-
this.assertChain(chainId);
|
|
3679
|
-
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3680
|
-
}
|
|
3681
|
-
async getEarnings(state, chainId) {
|
|
3682
|
-
this.assertChain(chainId);
|
|
3683
|
-
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3684
|
-
}
|
|
3685
|
-
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3686
|
-
this.assertChain(chainId);
|
|
3687
|
-
const asset = tokenSymbol?.toUpperCase();
|
|
3688
|
-
if (asset !== void 0) this.assertAsset(asset);
|
|
3689
|
-
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3690
|
-
...asset ? { asset } : {},
|
|
3691
|
-
historic: true
|
|
3692
|
-
});
|
|
3693
|
-
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3694
|
-
}
|
|
3695
|
-
async getHistory(state, chainId, options) {
|
|
3696
|
-
this.assertChain(chainId);
|
|
3697
|
-
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3698
|
-
if (asset !== void 0) this.assertAsset(asset);
|
|
3699
|
-
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3700
|
-
...asset ? { asset } : {},
|
|
3701
|
-
historic: true,
|
|
3702
|
-
actions: true
|
|
3703
|
-
});
|
|
3704
|
-
const catalog = await Promise.all(
|
|
3705
|
-
[...new Set(contexts.map((context) => context.asset))].map(
|
|
3706
|
-
(contextAsset) => this.loadYieldOptions(contextAsset)
|
|
3707
|
-
)
|
|
3708
|
-
);
|
|
3709
|
-
const vaultAddresses = new Set(
|
|
3710
|
-
catalog.flat().filter(
|
|
3711
|
-
(yieldOption) => yieldOption.chainId === chainId && (0, import_viem7.isAddress)(yieldOption.address)
|
|
3712
|
-
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
3713
|
-
);
|
|
3714
|
-
return mapYieldseekerHistory(contexts, {
|
|
3715
|
-
limit: options?.limit ?? 10,
|
|
3716
|
-
ownerAddress: state.walletAddress,
|
|
3717
|
-
vaultAddresses,
|
|
3718
|
-
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3719
|
-
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3720
|
-
});
|
|
3721
|
-
}
|
|
3722
|
-
async getUserProfile(state, chainId) {
|
|
3723
|
-
this.assertChain(chainId);
|
|
3724
|
-
return mapYieldseekerProfile(
|
|
3725
|
-
state.walletAddress,
|
|
3726
|
-
await this.loadPortfolio(state, chainId, {})
|
|
3727
|
-
);
|
|
3728
|
-
}
|
|
3729
|
-
async getAgentApy(days, options) {
|
|
3730
|
-
this.assertOptionalChain(options?.chainId);
|
|
3731
|
-
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3732
|
-
if (requested !== void 0) this.assertAsset(requested);
|
|
3733
|
-
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3734
|
-
const values = await Promise.all(
|
|
3735
|
-
assets.map(async (asset) => {
|
|
3736
|
-
return { asset, yieldOptions: await this.loadYieldOptions(asset) };
|
|
3737
|
-
})
|
|
3738
|
-
);
|
|
3739
|
-
return mapYieldseekerAgentApy(values, days);
|
|
3740
|
-
}
|
|
3741
|
-
async loadYieldOptions(asset) {
|
|
3742
|
-
const cached = this.yieldOptions.get(asset);
|
|
3743
|
-
if (cached && cached.expiresAt > Date.now()) return cached.value;
|
|
3744
|
-
const pending = this.pendingYieldOptions.get(asset);
|
|
3745
|
-
if (pending) return pending;
|
|
3746
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3747
|
-
const request = this.api.request(
|
|
3748
|
-
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3749
|
-
).then((response) => {
|
|
3750
|
-
if (!Array.isArray(response?.yieldOptions)) {
|
|
3751
|
-
throw this.invalidResponse("yield options");
|
|
3752
|
-
}
|
|
3753
|
-
this.yieldOptions.set(asset, {
|
|
3754
|
-
expiresAt: Date.now() + YIELDSEEKER_YIELD_OPTIONS_CACHE_MS,
|
|
3755
|
-
value: response.yieldOptions
|
|
3756
|
-
});
|
|
3757
|
-
return response.yieldOptions;
|
|
3758
|
-
}).finally(() => this.pendingYieldOptions.delete(asset));
|
|
3759
|
-
this.pendingYieldOptions.set(asset, request);
|
|
3760
|
-
return request;
|
|
3761
|
-
}
|
|
3762
|
-
userKey(state, chainId) {
|
|
3763
|
-
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3764
|
-
}
|
|
3765
|
-
contextKey(state, chainId, asset) {
|
|
3766
|
-
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3767
|
-
}
|
|
3768
|
-
assertSession(generation, chainId, asset) {
|
|
3769
|
-
if (this.readGeneration !== generation) {
|
|
3770
|
-
throw new OwneyError(
|
|
3771
|
-
"NOT_CONNECTED",
|
|
3772
|
-
"Wallet session changed during the Yieldseeker request.",
|
|
3773
|
-
{ rpcSource: "agent-api", chainId, ...asset ? { asset } : {} },
|
|
3774
|
-
this.id
|
|
3775
|
-
);
|
|
3776
|
-
}
|
|
3777
|
-
}
|
|
3778
|
-
cachedRead(key2, ttlMs, read2) {
|
|
3779
|
-
const cached = this.readCache.get(key2);
|
|
3780
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
3781
|
-
return Promise.resolve(cached.value);
|
|
3782
|
-
}
|
|
3783
|
-
const pending = this.pendingReads.get(key2);
|
|
3784
|
-
if (pending) return pending;
|
|
3785
|
-
const generation = this.readGeneration;
|
|
3786
|
-
const request = Promise.resolve().then(read2).then((value) => {
|
|
3787
|
-
if (this.readGeneration === generation && this.pendingReads.get(key2) === request) {
|
|
3788
|
-
this.readCache.set(key2, {
|
|
3789
|
-
expiresAt: Date.now() + ttlMs,
|
|
3790
|
-
value
|
|
3791
|
-
});
|
|
3792
|
-
}
|
|
3793
|
-
return value;
|
|
3794
|
-
}).finally(() => {
|
|
3795
|
-
if (this.pendingReads.get(key2) === request) {
|
|
3796
|
-
this.pendingReads.delete(key2);
|
|
3797
|
-
}
|
|
3798
|
-
});
|
|
3799
|
-
this.pendingReads.set(key2, request);
|
|
3800
|
-
return request;
|
|
3801
|
-
}
|
|
3802
|
-
agentListKey(state, chainId) {
|
|
3803
|
-
return `agents:${this.userKey(state, chainId)}`;
|
|
3804
|
-
}
|
|
3805
|
-
async listAgents(state, chainId, user) {
|
|
3806
|
-
const response = await this.cachedRead(
|
|
3807
|
-
this.agentListKey(state, chainId),
|
|
3808
|
-
YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
3809
|
-
async () => {
|
|
3810
|
-
const response2 = await this.walletRequest(
|
|
3811
|
-
state,
|
|
3812
|
-
chainId,
|
|
3813
|
-
`/users/${user.userId}/agents`
|
|
3814
|
-
);
|
|
3815
|
-
if (!Array.isArray(response2?.agents)) {
|
|
3816
|
-
throw this.invalidResponse("agent list");
|
|
3817
|
-
}
|
|
3818
|
-
return response2;
|
|
3819
|
-
}
|
|
3820
|
-
);
|
|
3821
|
-
return response.agents;
|
|
3822
|
-
}
|
|
3823
|
-
async contextForAgent(state, chainId, user, agent, asset) {
|
|
3824
|
-
const key2 = this.contextKey(state, chainId, asset);
|
|
3825
|
-
const cached = this.agentContexts.get(key2);
|
|
3826
|
-
if (cached?.agent.agentId === agent.agentId) return cached;
|
|
3827
|
-
const pending = this.pendingWalletContexts.get(key2);
|
|
3828
|
-
if (pending) return pending;
|
|
3829
|
-
const generation = this.readGeneration;
|
|
3830
|
-
const request = this.walletRequest(
|
|
3831
|
-
state,
|
|
3832
|
-
chainId,
|
|
3833
|
-
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3834
|
-
).then((walletResponse) => {
|
|
3835
|
-
this.assertSession(generation, chainId, asset);
|
|
3836
|
-
if (!walletResponse?.agentWallet || !(0, import_viem7.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3837
|
-
throw this.invalidResponse("agent wallet");
|
|
3838
|
-
}
|
|
3839
|
-
const context = {
|
|
3840
|
-
user,
|
|
3841
|
-
agent,
|
|
3842
|
-
wallet: walletResponse.agentWallet,
|
|
3843
|
-
asset
|
|
3844
|
-
};
|
|
3845
|
-
this.agentContexts.set(key2, context);
|
|
3846
|
-
return context;
|
|
3847
|
-
});
|
|
3848
|
-
this.pendingWalletContexts.set(key2, request);
|
|
3849
|
-
try {
|
|
3850
|
-
return await request;
|
|
3851
|
-
} finally {
|
|
3852
|
-
if (this.pendingWalletContexts.get(key2) === request) {
|
|
3853
|
-
this.pendingWalletContexts.delete(key2);
|
|
3854
|
-
}
|
|
3855
|
-
}
|
|
3856
|
-
}
|
|
3857
|
-
async resolveUser(state, chainId) {
|
|
3858
|
-
const key2 = this.userKey(state, chainId);
|
|
3859
|
-
const inMemory = this.users.get(key2);
|
|
3860
|
-
if (inMemory) return inMemory;
|
|
3861
|
-
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3862
|
-
if (persisted) {
|
|
3863
|
-
this.users.set(key2, persisted);
|
|
3864
|
-
return persisted;
|
|
3865
|
-
}
|
|
3866
|
-
const walletAddress = (0, import_viem7.getAddress)(state.walletAddress);
|
|
3867
|
-
const generation = this.readGeneration;
|
|
3868
|
-
let user = null;
|
|
3869
|
-
try {
|
|
3870
|
-
const login = await this.providerRequest(
|
|
3871
|
-
state,
|
|
3872
|
-
chainId,
|
|
3873
|
-
"/users/login-with-wallet",
|
|
3874
|
-
{ method: "POST", body: { walletAddress } }
|
|
3875
|
-
);
|
|
3876
|
-
user = login?.user ?? null;
|
|
3877
|
-
if (!user) {
|
|
3878
|
-
throw this.invalidResponse("wallet login", {
|
|
3879
|
-
reason: "A successful login returned no user."
|
|
3880
|
-
});
|
|
3881
|
-
}
|
|
3882
|
-
} catch (error) {
|
|
3883
|
-
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3884
|
-
if (error instanceof OwneyError) throw error;
|
|
3885
|
-
throw this.mapApiError(error);
|
|
3886
|
-
}
|
|
3887
|
-
let created;
|
|
3888
|
-
for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
|
|
3889
|
-
try {
|
|
3890
|
-
created = await this.providerRequest(
|
|
3891
|
-
state,
|
|
3892
|
-
chainId,
|
|
3893
|
-
"/users",
|
|
3894
|
-
{
|
|
3895
|
-
method: "POST",
|
|
3896
|
-
body: {
|
|
3897
|
-
walletAddress,
|
|
3898
|
-
username: generateYieldseekerUsername()
|
|
3899
|
-
}
|
|
3900
|
-
}
|
|
3901
|
-
);
|
|
3902
|
-
break;
|
|
3903
|
-
} catch (createError) {
|
|
3904
|
-
const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
|
|
3905
|
-
if (canRetry) continue;
|
|
3906
|
-
throw this.mapApiError(createError);
|
|
3907
|
-
}
|
|
3908
|
-
}
|
|
3909
|
-
user = created?.user ?? null;
|
|
3910
|
-
}
|
|
3911
|
-
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3912
|
-
throw this.invalidResponse("wallet identity");
|
|
3913
|
-
}
|
|
3914
|
-
this.assertSession(generation, chainId);
|
|
3915
|
-
const resolved = { userId: user.userId };
|
|
3916
|
-
this.users.set(key2, resolved);
|
|
3917
|
-
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3918
|
-
return resolved;
|
|
3919
|
-
}
|
|
3920
|
-
forgetUser(state, chainId) {
|
|
3921
|
-
this.users.delete(this.userKey(state, chainId));
|
|
3922
|
-
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3923
|
-
}
|
|
3924
|
-
async ensureAgent(state, chainId, asset) {
|
|
3925
|
-
const key2 = this.contextKey(state, chainId, asset);
|
|
3926
|
-
const cached = this.agentContexts.get(key2);
|
|
3927
|
-
if (cached) return cached;
|
|
3928
|
-
const pending = this.pendingAgents.get(key2);
|
|
3929
|
-
if (pending) return pending;
|
|
3930
|
-
const generation = this.readGeneration;
|
|
3931
|
-
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3932
|
-
async (context) => {
|
|
3933
|
-
this.assertSession(generation, chainId, asset);
|
|
3934
|
-
if (!context) throw this.invalidResponse("agent creation");
|
|
3935
|
-
await this.deployAgent(state, chainId, context);
|
|
3936
|
-
this.assertSession(generation, chainId, asset);
|
|
3937
|
-
this.agentContexts.set(key2, context);
|
|
3938
|
-
return context;
|
|
3939
|
-
}
|
|
3940
|
-
);
|
|
3941
|
-
this.pendingAgents.set(key2, request);
|
|
3942
|
-
try {
|
|
3943
|
-
return await request;
|
|
3944
|
-
} finally {
|
|
3945
|
-
if (this.pendingAgents.get(key2) === request) this.pendingAgents.delete(key2);
|
|
3946
|
-
}
|
|
3947
|
-
}
|
|
3948
|
-
async findAgent(state, chainId, asset) {
|
|
3949
|
-
const key2 = this.contextKey(state, chainId, asset);
|
|
3950
|
-
const cached = this.agentContexts.get(key2);
|
|
3951
|
-
if (cached) return cached;
|
|
3952
|
-
const generation = this.readGeneration;
|
|
3953
|
-
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3954
|
-
this.assertSession(generation, chainId, asset);
|
|
3955
|
-
if (context) this.agentContexts.set(key2, context);
|
|
3956
|
-
return context;
|
|
3957
|
-
}
|
|
3958
|
-
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3959
|
-
const generation = this.readGeneration;
|
|
3960
|
-
const user = await this.resolveUser(state, chainId);
|
|
3961
|
-
this.assertSession(generation, chainId, asset);
|
|
3962
|
-
const agents = await this.listAgents(state, chainId, user);
|
|
3963
|
-
this.assertSession(generation, chainId, asset);
|
|
3964
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3965
|
-
let agent = agents.find(
|
|
3966
|
-
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3967
|
-
);
|
|
3968
|
-
if (!agent && createIfMissing) {
|
|
3969
|
-
const created = await this.walletRequest(
|
|
3970
|
-
state,
|
|
3971
|
-
chainId,
|
|
3972
|
-
`/users/${user.userId}/agents`,
|
|
3973
|
-
{
|
|
3974
|
-
method: "POST",
|
|
3975
|
-
body: {
|
|
3976
|
-
name: OWNEY_AGENT_NAME,
|
|
3977
|
-
emoji: "\u{1F989}",
|
|
3978
|
-
chainId,
|
|
3979
|
-
assetAddress: metadata.address,
|
|
3980
|
-
type: "vault",
|
|
3981
|
-
rulePreset: null
|
|
3982
|
-
}
|
|
3983
|
-
}
|
|
3984
|
-
);
|
|
3985
|
-
this.assertSession(generation, chainId, asset);
|
|
3986
|
-
agent = created?.agent;
|
|
3987
|
-
if (agent) {
|
|
3988
|
-
this.readCache.set(this.agentListKey(state, chainId), {
|
|
3989
|
-
expiresAt: Date.now() + YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
3990
|
-
value: {
|
|
3991
|
-
agents: [...agents, agent]
|
|
3992
|
-
}
|
|
3993
|
-
});
|
|
3994
|
-
}
|
|
3995
|
-
}
|
|
3996
|
-
if (!agent) return null;
|
|
3997
|
-
this.assertAgent(agent);
|
|
3998
|
-
return this.contextForAgent(state, chainId, user, agent, asset);
|
|
3999
|
-
}
|
|
4000
|
-
async loadPortfolio(state, chainId, options) {
|
|
4001
|
-
const generation = this.readGeneration;
|
|
4002
|
-
const user = await this.resolveUser(state, chainId);
|
|
4003
|
-
this.assertSession(generation, chainId);
|
|
4004
|
-
const agents = await this.listAgents(state, chainId, user);
|
|
4005
|
-
this.assertSession(generation, chainId);
|
|
4006
|
-
const contexts = [];
|
|
4007
|
-
for (const agent of agents) {
|
|
4008
|
-
const asset = this.assetForAgent(agent);
|
|
4009
|
-
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
4010
|
-
continue;
|
|
4011
|
-
}
|
|
4012
|
-
this.assertAgent(agent);
|
|
4013
|
-
contexts.push(this.contextForAgent(state, chainId, user, agent, asset));
|
|
4014
|
-
}
|
|
4015
|
-
const resolvedContexts = await Promise.all(contexts);
|
|
4016
|
-
this.assertSession(generation, chainId);
|
|
4017
|
-
return Promise.all(
|
|
4018
|
-
resolvedContexts.map(
|
|
4019
|
-
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
4020
|
-
)
|
|
4021
|
-
);
|
|
4022
|
-
}
|
|
4023
|
-
portfolioVersion(contextKey) {
|
|
4024
|
-
return this.portfolioVersions.get(contextKey) ?? 0;
|
|
4025
|
-
}
|
|
4026
|
-
portfolioCacheMs(contextKey) {
|
|
4027
|
-
return (this.reconcileUntil.get(contextKey) ?? 0) > Date.now() ? YIELDSEEKER_SETTLEMENT_CACHE_MS : YIELDSEEKER_PORTFOLIO_CACHE_MS;
|
|
4028
|
-
}
|
|
4029
|
-
snapshotMovement(state, chainId, context, delta) {
|
|
4030
|
-
const key2 = this.contextKey(state, chainId, context.asset);
|
|
4031
|
-
const previous = this.snapshotMovements.get(key2);
|
|
4032
|
-
if (this.snapshotMovements.has(key2)) {
|
|
4033
|
-
if (!previous || previous.delta > 0n !== delta > 0n) return null;
|
|
4034
|
-
return {
|
|
4035
|
-
...previous,
|
|
4036
|
-
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4037
|
-
delta: previous.delta + delta,
|
|
4038
|
-
expectedBalance: previous.expectedBalance + delta,
|
|
4039
|
-
expectedNetDeposits: previous.expectedNetDeposits + delta
|
|
4040
|
-
};
|
|
4041
|
-
}
|
|
4042
|
-
const cached = this.readCache.get(`snapshot:${key2}`);
|
|
4043
|
-
if (!cached || cached.expiresAt <= Date.now()) return null;
|
|
4044
|
-
const snapshot = cached.value.agentSnapshot;
|
|
4045
|
-
if (snapshot.baseAssetDecimals !== YIELDSEEKER_ASSET_METADATA[context.asset].decimals) return null;
|
|
4046
|
-
if (!/^-?\d+$/.test(snapshot.netDepositsBase) || !/^\d+$/.test(snapshot.totalValueBase)) return null;
|
|
4047
|
-
return {
|
|
4048
|
-
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4049
|
-
delta,
|
|
4050
|
-
decimals: snapshot.baseAssetDecimals,
|
|
4051
|
-
expectedBalance: BigInt(snapshot.totalValueBase) + delta,
|
|
4052
|
-
expectedNetDeposits: BigInt(snapshot.netDepositsBase) + delta
|
|
4053
|
-
};
|
|
4054
|
-
}
|
|
4055
|
-
advancePortfolioVersion(contextKey) {
|
|
4056
|
-
const version = this.portfolioVersion(contextKey) + 1;
|
|
4057
|
-
this.portfolioVersions.set(contextKey, version);
|
|
4058
|
-
this.snapshotFailures.delete(contextKey);
|
|
4059
|
-
for (const kind of ["snapshot", "positions", "historic", "actions"]) {
|
|
4060
|
-
const key2 = `${kind}:${contextKey}`;
|
|
4061
|
-
this.readCache.delete(key2);
|
|
4062
|
-
this.pendingReads.delete(key2);
|
|
4063
|
-
}
|
|
4064
|
-
return version;
|
|
4065
|
-
}
|
|
4066
|
-
requestPortfolioSnapshot(state, chainId, context, contextKey, version, generation, forceRefresh = false) {
|
|
4067
|
-
return this.walletRequest(
|
|
4068
|
-
state,
|
|
4069
|
-
chainId,
|
|
4070
|
-
`${this.agentPath(context, "snapshot")}${query(forceRefresh ? { shouldForceRefresh: true } : { shouldOnlyUseRecentValue: true, shouldAllowStaleOnError: true })}`
|
|
4071
|
-
).then((response) => {
|
|
4072
|
-
if (!response?.agentSnapshot) {
|
|
4073
|
-
throw this.invalidResponse("agent snapshot");
|
|
4074
|
-
}
|
|
4075
|
-
if (this.readGeneration === generation && this.portfolioVersion(contextKey) === version) {
|
|
4076
|
-
this.snapshotFailures.delete(contextKey);
|
|
4077
|
-
const movement = this.snapshotMovements.get(contextKey);
|
|
4078
|
-
const snapshot = response.agentSnapshot;
|
|
4079
|
-
if (movement && snapshot.baseAssetDecimals === movement.decimals && /^-?\d+$/.test(snapshot.netDepositsBase) && /^\d+$/.test(snapshot.totalValueBase)) {
|
|
4080
|
-
const balance = BigInt(snapshot.totalValueBase);
|
|
4081
|
-
const netDeposits = BigInt(snapshot.netDepositsBase);
|
|
4082
|
-
const reconciled = movement.delta > 0n ? balance >= movement.expectedBalance && netDeposits >= movement.expectedNetDeposits : balance <= movement.expectedBalance && netDeposits <= movement.expectedNetDeposits;
|
|
4083
|
-
if (reconciled) {
|
|
4084
|
-
this.snapshotMovements.delete(contextKey);
|
|
4085
|
-
this.reconcileUntil.delete(contextKey);
|
|
4086
|
-
}
|
|
4087
|
-
}
|
|
4088
|
-
}
|
|
4089
|
-
return response;
|
|
4090
|
-
}).catch((error) => {
|
|
4091
|
-
if (this.readGeneration === generation && this.portfolioVersion(contextKey) === version) {
|
|
4092
|
-
this.snapshotFailures.set(contextKey, {
|
|
4093
|
-
retryAt: Date.now() + Math.max(YIELDSEEKER_READ_FAILURE_COOLDOWN_MS, rateLimitDelay(error) ?? 0),
|
|
4094
|
-
error
|
|
4095
|
-
});
|
|
4096
|
-
}
|
|
4097
|
-
throw error;
|
|
4098
|
-
});
|
|
4099
|
-
}
|
|
4100
|
-
portfolioSnapshot(state, chainId, context, contextKey) {
|
|
4101
|
-
const key2 = `snapshot:${contextKey}`;
|
|
4102
|
-
const cached = this.readCache.get(key2);
|
|
4103
|
-
if ((!cached || cached.expiresAt <= Date.now()) && !this.pendingReads.has(key2)) {
|
|
4104
|
-
const failure = this.snapshotFailures.get(contextKey);
|
|
4105
|
-
if (failure && failure.retryAt > Date.now()) throw failure.error;
|
|
4106
|
-
this.advancePortfolioVersion(contextKey);
|
|
4107
|
-
}
|
|
4108
|
-
const version = this.portfolioVersion(contextKey);
|
|
4109
|
-
const generation = this.readGeneration;
|
|
4110
|
-
return {
|
|
4111
|
-
version,
|
|
4112
|
-
read: this.cachedRead(key2, this.portfolioCacheMs(contextKey), () => (
|
|
4113
|
-
// Force only during the bounded post-transaction window. Keep unknown
|
|
4114
|
-
// or unreconciled movements for verification, but do not let them turn
|
|
4115
|
-
// routine polling into forced refreshes indefinitely.
|
|
4116
|
-
this.requestPortfolioSnapshot(
|
|
4117
|
-
state,
|
|
4118
|
-
chainId,
|
|
4119
|
-
context,
|
|
4120
|
-
contextKey,
|
|
4121
|
-
version,
|
|
4122
|
-
generation,
|
|
4123
|
-
this.snapshotMovements.has(contextKey) && (this.reconcileUntil.get(contextKey) ?? 0) > Date.now()
|
|
4124
|
-
)
|
|
4125
|
-
))
|
|
4126
|
-
};
|
|
4127
|
-
}
|
|
4128
|
-
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
4129
|
-
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
4130
|
-
const generation = this.readGeneration;
|
|
4131
|
-
const assertSession = () => this.assertSession(generation, chainId, context.asset);
|
|
4132
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
4133
|
-
const { version, read: read2 } = this.portfolioSnapshot(state, chainId, context, contextKey);
|
|
4134
|
-
try {
|
|
4135
|
-
const snapshot = await read2;
|
|
4136
|
-
assertSession();
|
|
4137
|
-
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4138
|
-
const [positions, historic, actions] = await Promise.all([
|
|
4139
|
-
this.cachedRead(
|
|
4140
|
-
`positions:${contextKey}`,
|
|
4141
|
-
this.portfolioCacheMs(contextKey),
|
|
4142
|
-
async () => {
|
|
4143
|
-
const response = await this.walletRequest(
|
|
4144
|
-
state,
|
|
4145
|
-
chainId,
|
|
4146
|
-
this.agentPath(context, "yield-positions")
|
|
4147
|
-
);
|
|
4148
|
-
if (!Array.isArray(response?.yieldPositions)) {
|
|
4149
|
-
throw this.invalidResponse("yield positions");
|
|
4150
|
-
}
|
|
4151
|
-
return response;
|
|
4152
|
-
}
|
|
4153
|
-
),
|
|
4154
|
-
options.historic ? this.cachedRead(
|
|
4155
|
-
`historic:${contextKey}`,
|
|
4156
|
-
(this.activityRefreshUntil.get(contextKey) ?? 0) > Date.now() ? YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS : YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
4157
|
-
() => this.walletRequest(
|
|
4158
|
-
state,
|
|
4159
|
-
chainId,
|
|
4160
|
-
this.agentPath(context, "wallet/historic-position")
|
|
4161
|
-
)
|
|
4162
|
-
) : Promise.resolve(void 0),
|
|
4163
|
-
options.actions ? this.cachedRead(
|
|
4164
|
-
`actions:${contextKey}`,
|
|
4165
|
-
(this.activityRefreshUntil.get(contextKey) ?? 0) > Date.now() ? YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS : YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
4166
|
-
() => this.walletRequest(
|
|
4167
|
-
state,
|
|
4168
|
-
chainId,
|
|
4169
|
-
this.agentPath(context, "actions")
|
|
4170
|
-
)
|
|
4171
|
-
) : Promise.resolve(void 0)
|
|
4172
|
-
]);
|
|
4173
|
-
assertSession();
|
|
4174
|
-
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4175
|
-
if (!Array.isArray(positions?.yieldPositions)) {
|
|
4176
|
-
throw this.invalidResponse("yield positions");
|
|
4177
|
-
}
|
|
4178
|
-
return {
|
|
4179
|
-
...context,
|
|
4180
|
-
snapshot: snapshot.agentSnapshot,
|
|
4181
|
-
positions: positions.yieldPositions,
|
|
4182
|
-
...historic?.position ? { historic: historic.position } : {},
|
|
4183
|
-
...actions?.actions ? { actions: actions.actions } : {}
|
|
4184
|
-
};
|
|
4185
|
-
} catch (error) {
|
|
4186
|
-
assertSession();
|
|
4187
|
-
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4188
|
-
throw error;
|
|
4189
|
-
}
|
|
4190
|
-
}
|
|
4191
|
-
throw new OwneyError(
|
|
4192
|
-
"AGENT_API_ERROR",
|
|
4193
|
-
"Yieldseeker portfolio changed during the read. Please retry.",
|
|
4194
|
-
{ rpcSource: "agent-api", chainId, asset: context.asset },
|
|
4195
|
-
this.id
|
|
4196
|
-
);
|
|
4197
|
-
}
|
|
4198
|
-
async deployAgent(state, chainId, context) {
|
|
4199
|
-
if (context.wallet.initializedDate != null) return;
|
|
4200
|
-
const walletAddress = context.wallet.walletAddress.toLowerCase();
|
|
4201
|
-
const deployed = await this.walletRequest(
|
|
4202
|
-
state,
|
|
4203
|
-
chainId,
|
|
4204
|
-
this.agentPath(context, "deploy"),
|
|
4205
|
-
{ method: "POST", body: {} }
|
|
4206
|
-
);
|
|
4207
|
-
if (!deployed?.agentWallet || !(0, import_viem7.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
4208
|
-
throw this.invalidResponse("agent deployment", {
|
|
4209
|
-
reason: "Deploy did not return the expected Agent Wallet."
|
|
4210
|
-
});
|
|
4211
|
-
}
|
|
4212
|
-
context.wallet = deployed.agentWallet;
|
|
4213
|
-
}
|
|
4214
|
-
async refreshSnapshotAfterMovement(state, chainId, context, movement, settlement) {
|
|
4215
|
-
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
4216
|
-
this.reconcileUntil.set(contextKey, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
4217
|
-
this.activityRefreshUntil.set(contextKey, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
4218
|
-
const epoch = this.movementEpochs.get(contextKey) ?? 0;
|
|
4219
|
-
this.snapshotMovements.set(contextKey, settlement?.epoch === epoch ? settlement : null);
|
|
4220
|
-
this.movementEpochs.set(contextKey, epoch + 1);
|
|
4221
|
-
const version = this.advancePortfolioVersion(contextKey);
|
|
4222
|
-
const generation = this.readGeneration;
|
|
4223
|
-
try {
|
|
4224
|
-
await this.cachedRead(
|
|
4225
|
-
`snapshot:${contextKey}`,
|
|
4226
|
-
YIELDSEEKER_SETTLEMENT_CACHE_MS,
|
|
4227
|
-
() => this.requestPortfolioSnapshot(state, chainId, context, contextKey, version, generation, true)
|
|
4228
|
-
);
|
|
4229
|
-
} catch (error) {
|
|
4230
|
-
console.warn(
|
|
4231
|
-
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
4232
|
-
error
|
|
4233
|
-
);
|
|
4234
|
-
}
|
|
4235
|
-
}
|
|
4236
|
-
agentPath(context, suffix) {
|
|
4237
|
-
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
4238
|
-
}
|
|
4239
|
-
async walletRequest(state, chainId, path, options = {}) {
|
|
4240
|
-
const read2 = (options.method ?? "GET") === "GET";
|
|
4241
|
-
const userKey = this.userKey(state, chainId);
|
|
4242
|
-
if (read2) {
|
|
4243
|
-
const failure = this.standardReadFailures.get(userKey);
|
|
4244
|
-
if (failure && failure.retryAt > Date.now()) throw failure.error;
|
|
4245
|
-
if (failure) this.standardReadFailures.delete(userKey);
|
|
4246
|
-
}
|
|
4247
|
-
const generation = this.readGeneration;
|
|
4248
|
-
try {
|
|
4249
|
-
return await this.providerRequest(state, chainId, path, options);
|
|
4250
|
-
} catch (error) {
|
|
4251
|
-
const mapped = this.mapApiError(error);
|
|
4252
|
-
const delay = read2 ? rateLimitDelay(mapped) : void 0;
|
|
4253
|
-
if (delay !== void 0 && this.readGeneration === generation) {
|
|
4254
|
-
this.standardReadFailures.set(userKey, {
|
|
4255
|
-
retryAt: Date.now() + Math.max(YIELDSEEKER_READ_FAILURE_COOLDOWN_MS, delay),
|
|
4256
|
-
error: mapped
|
|
4257
|
-
});
|
|
4258
|
-
}
|
|
4259
|
-
throw mapped;
|
|
4260
|
-
}
|
|
4261
|
-
}
|
|
4262
|
-
async providerRequest(state, chainId, path, options = {}) {
|
|
4263
|
-
this.assertChain(chainId);
|
|
4264
|
-
const request = (signature2) => this.api.request(path, {
|
|
4265
|
-
...options,
|
|
4266
|
-
signature: signature2
|
|
4267
|
-
});
|
|
4268
|
-
let signature = await this.auth.getToken(state, chainId);
|
|
4269
|
-
try {
|
|
4270
|
-
return await request(signature);
|
|
4271
|
-
} catch (error) {
|
|
4272
|
-
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
4273
|
-
if (error.providerCode === "NO_USER") throw error;
|
|
4274
|
-
if (!error.isAuthenticationError) throw error;
|
|
4275
|
-
signature = await this.auth.refreshToken(state, chainId, signature);
|
|
4276
|
-
try {
|
|
4277
|
-
return await request(signature);
|
|
4278
|
-
} catch (retryError) {
|
|
4279
|
-
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
4280
|
-
this.forgetUser(state, chainId);
|
|
4281
|
-
}
|
|
4282
|
-
throw retryError;
|
|
4283
|
-
}
|
|
4284
|
-
}
|
|
4285
|
-
}
|
|
4286
|
-
mapApiError(error) {
|
|
4287
|
-
if (!(error instanceof YieldseekerApiError)) {
|
|
4288
|
-
return new OwneyError(
|
|
4289
|
-
"AGENT_API_ERROR",
|
|
4290
|
-
"Yieldseeker request failed.",
|
|
4291
|
-
{ cause: error instanceof Error ? error.message : String(error) },
|
|
4292
|
-
this.id
|
|
4293
|
-
);
|
|
4294
|
-
}
|
|
4295
|
-
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
4296
|
-
return new OwneyError(
|
|
4297
|
-
code,
|
|
4298
|
-
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
4299
|
-
{
|
|
4300
|
-
rpcSource: "agent-api",
|
|
4301
|
-
statusCode: error.status,
|
|
4302
|
-
providerCode: error.providerCode,
|
|
4303
|
-
...error.responseFields ? { fields: error.responseFields } : {}
|
|
4304
|
-
},
|
|
4305
|
-
this.id
|
|
4306
|
-
);
|
|
4307
|
-
}
|
|
4308
|
-
async submitTransaction(state, chainId, transaction) {
|
|
4309
|
-
if (this.transactionExecutor) {
|
|
4310
|
-
return this.transactionExecutor(state, chainId, transaction);
|
|
4311
|
-
}
|
|
4312
|
-
this.assertTransaction(transaction, state, chainId);
|
|
4313
|
-
const account = (0, import_viem7.getAddress)(state.walletAddress);
|
|
4314
|
-
const walletClient = (0, import_viem7.createWalletClient)({
|
|
4315
|
-
account,
|
|
4316
|
-
chain: import_chains3.base,
|
|
4317
|
-
transport: (0, import_viem7.custom)(state.provider)
|
|
4318
|
-
});
|
|
4319
|
-
const walletChainClient = (0, import_viem7.createPublicClient)({
|
|
4320
|
-
chain: import_chains3.base,
|
|
4321
|
-
transport: (0, import_viem7.custom)(state.provider)
|
|
4322
|
-
});
|
|
4323
|
-
await ensureWalletOnChain(
|
|
4324
|
-
walletChainClient,
|
|
4325
|
-
walletClient,
|
|
4326
|
-
8453
|
|
4327
|
-
);
|
|
4328
|
-
const hash = await walletClient.sendTransaction({
|
|
4329
|
-
account,
|
|
4330
|
-
chain: import_chains3.base,
|
|
4331
|
-
to: (0, import_viem7.getAddress)(transaction.to),
|
|
4332
|
-
data: transaction.data,
|
|
4333
|
-
value: BigInt(transaction.value)
|
|
4334
|
-
});
|
|
4335
|
-
const receipt = await withPaidRpcDiagnostics(
|
|
4336
|
-
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4337
|
-
hash,
|
|
4338
|
-
confirmations: 1
|
|
4339
|
-
}),
|
|
4340
|
-
8453,
|
|
4341
|
-
"eth_getTransactionReceipt",
|
|
4342
|
-
this.id
|
|
4343
|
-
);
|
|
4344
|
-
if (receipt.status !== "success") {
|
|
4345
|
-
throw new OwneyError(
|
|
4346
|
-
"AGENT_TRANSACTION_REVERTED",
|
|
4347
|
-
`Yieldseeker transaction reverted (${hash}).`,
|
|
4348
|
-
{ transactionHash: hash },
|
|
4349
|
-
this.id
|
|
4350
|
-
);
|
|
4351
|
-
}
|
|
4352
|
-
return hash;
|
|
4353
|
-
}
|
|
4354
|
-
async waitForReceipt(state, chainId, transactionHash) {
|
|
4355
|
-
if (this.unwindReceiptWaiter) {
|
|
4356
|
-
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
4357
|
-
return;
|
|
4358
|
-
}
|
|
4359
|
-
const receipt = await withPaidRpcDiagnostics(
|
|
4360
|
-
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4361
|
-
hash: transactionHash,
|
|
4362
|
-
confirmations: 1
|
|
4363
|
-
}),
|
|
4364
|
-
8453,
|
|
4365
|
-
"eth_getTransactionReceipt",
|
|
4366
|
-
this.id
|
|
4367
|
-
);
|
|
4368
|
-
if (receipt.status !== "success") {
|
|
4369
|
-
throw new OwneyError(
|
|
4370
|
-
"AGENT_TRANSACTION_REVERTED",
|
|
4371
|
-
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
4372
|
-
{ transactionHash },
|
|
4373
|
-
this.id
|
|
4374
|
-
);
|
|
4375
|
-
}
|
|
4376
|
-
}
|
|
4377
|
-
assertTransaction(transaction, state, chainId) {
|
|
4378
|
-
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem7.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem7.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem7.getAddress)(transaction.from) !== (0, import_viem7.getAddress)(state.walletAddress)) {
|
|
4379
|
-
throw this.invalidResponse("transaction");
|
|
4380
|
-
}
|
|
4381
|
-
}
|
|
4382
|
-
assertAgent(agent) {
|
|
4383
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem7.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
4384
|
-
throw this.invalidResponse("agent");
|
|
4385
|
-
}
|
|
4386
|
-
}
|
|
4387
|
-
isOwneyAgent(agent) {
|
|
4388
|
-
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
4389
|
-
}
|
|
4390
|
-
assetForAgent(agent) {
|
|
4391
|
-
for (const asset of ["USDC", "WETH"]) {
|
|
4392
|
-
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
4393
|
-
return asset;
|
|
4394
|
-
}
|
|
4395
|
-
}
|
|
4396
|
-
return null;
|
|
4397
|
-
}
|
|
4398
|
-
isTransactionHash(value) {
|
|
4399
|
-
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
4400
|
-
}
|
|
4401
|
-
assertChain(chainId) {
|
|
4402
|
-
if (chainId !== 8453) {
|
|
4403
|
-
throw new OwneyError(
|
|
4404
|
-
"CHAIN_UNSUPPORTED",
|
|
4405
|
-
`Yieldseeker does not support chain ${chainId}.`,
|
|
4406
|
-
{ chainId, supportedChainIds: [8453] },
|
|
4407
|
-
this.id
|
|
4408
|
-
);
|
|
4409
|
-
}
|
|
4410
|
-
}
|
|
4411
|
-
assertOptionalChain(chainId) {
|
|
4412
|
-
if (chainId !== void 0) this.assertChain(chainId);
|
|
4413
|
-
}
|
|
4414
|
-
assertAsset(asset) {
|
|
4415
|
-
if (asset !== "USDC" && asset !== "WETH") {
|
|
4416
|
-
throw new OwneyError(
|
|
4417
|
-
"ASSET_UNSUPPORTED",
|
|
4418
|
-
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
4419
|
-
{
|
|
4420
|
-
asset,
|
|
4421
|
-
supportedAssets: ["USDC", "WETH"],
|
|
4422
|
-
providerAlsoAdvertises: ["cbBTC"]
|
|
4423
|
-
},
|
|
4424
|
-
this.id
|
|
4425
|
-
);
|
|
4426
|
-
}
|
|
4427
|
-
}
|
|
4428
|
-
invalidResponse(operation, details = {}) {
|
|
4429
|
-
return new OwneyError(
|
|
4430
|
-
"AGENT_INVALID_RESPONSE",
|
|
4431
|
-
`Yieldseeker returned an invalid ${operation} response.`,
|
|
4432
|
-
details,
|
|
4433
|
-
this.id
|
|
4434
|
-
);
|
|
4435
|
-
}
|
|
4436
|
-
};
|
|
4437
|
-
|
|
4438
|
-
// src/lib/routing-api.ts
|
|
4439
|
-
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
4440
|
-
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
4441
|
-
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
4442
|
-
try {
|
|
4443
|
-
const res = await fetch(url, {
|
|
4444
|
-
method: "GET",
|
|
4445
|
-
headers: {
|
|
4446
|
-
"Content-Type": "application/json",
|
|
4447
|
-
"x-owney-api-key": `${apiKey}`
|
|
4448
|
-
}
|
|
4449
|
-
});
|
|
4450
|
-
if (!res.ok) {
|
|
4451
|
-
if (res.status !== 404) {
|
|
4452
|
-
console.warn(
|
|
4453
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
4454
|
-
);
|
|
4455
|
-
}
|
|
4456
|
-
return null;
|
|
4457
|
-
}
|
|
4458
|
-
const json = await res.json();
|
|
4459
|
-
const policy = json.success ? json.data ?? null : null;
|
|
4460
|
-
debugLog(
|
|
4461
|
-
"owney-sdk",
|
|
4462
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
4463
|
-
policy ?? void 0
|
|
4464
|
-
);
|
|
4465
|
-
return policy;
|
|
4466
|
-
} catch (error) {
|
|
4467
|
-
console.warn(
|
|
4468
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
4469
|
-
error instanceof Error ? error.message : String(error)
|
|
4470
|
-
);
|
|
4471
|
-
return null;
|
|
4472
|
-
}
|
|
4473
|
-
}
|
|
4474
|
-
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
4475
|
-
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
4476
|
-
const res = await fetch(url, {
|
|
4477
|
-
method: "GET",
|
|
4478
|
-
headers: {
|
|
4479
|
-
"Content-Type": "application/json",
|
|
4480
|
-
"x-owney-api-key": `${apiKey}`
|
|
4481
|
-
}
|
|
4482
|
-
});
|
|
4483
|
-
if (!res.ok) {
|
|
4484
|
-
const text = await res.text().catch(() => "");
|
|
4485
|
-
throw new OwneyError(
|
|
4486
|
-
"API_ROUTING_ERROR",
|
|
4487
|
-
`Routing API error ${res.status}: ${text}`,
|
|
4488
|
-
{ statusCode: res.status, responseBody: text }
|
|
4489
|
-
);
|
|
4490
|
-
}
|
|
4491
|
-
const json = await res.json();
|
|
4492
|
-
if (!json.success) {
|
|
4493
|
-
throw new OwneyError(
|
|
4494
|
-
"API_ROUTING_FAILED",
|
|
4495
|
-
`Routing API request failed: ${json.message}`,
|
|
4496
|
-
{ message: json.message }
|
|
4497
|
-
);
|
|
4498
|
-
}
|
|
4499
|
-
return json.data;
|
|
4500
|
-
}
|
|
4501
|
-
|
|
4502
|
-
// src/lib/health-report.ts
|
|
4503
|
-
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
4504
|
-
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
4505
|
-
try {
|
|
4506
|
-
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
4507
|
-
method: "POST",
|
|
4508
|
-
headers: {
|
|
4509
|
-
"Content-Type": "application/json",
|
|
4510
|
-
"x-owney-api-key": apiKey
|
|
4511
|
-
},
|
|
4512
|
-
body: JSON.stringify({
|
|
4513
|
-
agent_type: agentType,
|
|
4514
|
-
error_code: errorCode,
|
|
4515
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4516
|
-
})
|
|
4517
|
-
});
|
|
4518
|
-
} catch (err) {
|
|
4519
|
-
console.warn(
|
|
4520
|
-
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
4521
|
-
err instanceof Error ? err.message : err
|
|
4522
|
-
);
|
|
4523
|
-
}
|
|
4524
|
-
}
|
|
4525
|
-
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
4526
|
-
try {
|
|
4527
|
-
return await fn();
|
|
4528
|
-
} catch (err) {
|
|
4529
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
4530
|
-
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
4531
|
-
throw err;
|
|
2226
|
+
return await fn();
|
|
2227
|
+
} catch (err) {
|
|
2228
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
2229
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
2230
|
+
throw err;
|
|
4532
2231
|
}
|
|
4533
2232
|
}
|
|
4534
2233
|
|
|
4535
2234
|
// src/lib/helpers/withdraw-helper.ts
|
|
4536
|
-
var
|
|
2235
|
+
var import_viem2 = require("viem");
|
|
4537
2236
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
4538
2237
|
const target = asset.toUpperCase();
|
|
4539
2238
|
return agents.map((agent) => {
|
|
@@ -4541,29 +2240,8 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
4541
2240
|
const tokenBalance = agentBalance?.tokens.find(
|
|
4542
2241
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4543
2242
|
);
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
const chainNameById = {
|
|
4547
|
-
1: "ETHEREUM",
|
|
4548
|
-
8453: "BASE",
|
|
4549
|
-
42161: "ARBITRUM"
|
|
4550
|
-
};
|
|
4551
|
-
const targetChain = chainNameById[chainId];
|
|
4552
|
-
for (const position2 of agentBalance?.positions ?? []) {
|
|
4553
|
-
const positionChain = position2.chain.trim().toUpperCase();
|
|
4554
|
-
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
4555
|
-
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
4556
|
-
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem8.parseUnits)(position2.amount, decimals);
|
|
4557
|
-
}
|
|
4558
|
-
}
|
|
4559
|
-
const assetTotal = agentBalance?.assetBalances?.find(
|
|
4560
|
-
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4561
|
-
);
|
|
4562
|
-
if (assetTotal) {
|
|
4563
|
-
const total = (0, import_viem8.parseUnits)(assetTotal.amount, decimals);
|
|
4564
|
-
if (total < balance) balance = total > 0n ? total : 0n;
|
|
4565
|
-
}
|
|
4566
|
-
return { agent, balance };
|
|
2243
|
+
if (!tokenBalance) return { agent, balance: 0n };
|
|
2244
|
+
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
4567
2245
|
});
|
|
4568
2246
|
}
|
|
4569
2247
|
function planProportionalShares(balances, requested, totalAvailable) {
|
|
@@ -4589,9 +2267,7 @@ function planProportionalShares(balances, requested, totalAvailable) {
|
|
|
4589
2267
|
return plans;
|
|
4590
2268
|
}
|
|
4591
2269
|
function planDisabledDrain(disabled, requested) {
|
|
4592
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
4593
|
-
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
4594
|
-
);
|
|
2270
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
4595
2271
|
const plans = [];
|
|
4596
2272
|
let remaining = requested;
|
|
4597
2273
|
for (const { agent, balance } of sorted) {
|
|
@@ -4642,13 +2318,6 @@ function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
|
4642
2318
|
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
4643
2319
|
}
|
|
4644
2320
|
const normalizedToken = tokenSymbol.toUpperCase();
|
|
4645
|
-
const snapshots = balance.assetBalances?.filter(
|
|
4646
|
-
(token) => Number(token.chainId) === chainId && String(token.asset).toUpperCase() === normalizedToken
|
|
4647
|
-
);
|
|
4648
|
-
if (snapshots?.length) {
|
|
4649
|
-
const amount = snapshots.reduce((sum, token) => sum + Number(token.amount), 0);
|
|
4650
|
-
if (Number.isFinite(amount)) return Math.max(0, amount);
|
|
4651
|
-
}
|
|
4652
2321
|
return balance.tokens.reduce((total, token) => {
|
|
4653
2322
|
if (Number(token.chainId) !== chainId || String(token.asset).toUpperCase() !== normalizedToken) {
|
|
4654
2323
|
return total;
|
|
@@ -4719,379 +2388,438 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
4719
2388
|
}
|
|
4720
2389
|
|
|
4721
2390
|
// src/client.ts
|
|
4722
|
-
var
|
|
4723
|
-
var
|
|
4724
|
-
|
|
4725
|
-
// src/lib/sponsored-token-batch.ts
|
|
4726
|
-
var import_viem10 = require("viem");
|
|
2391
|
+
var import_viem6 = require("viem");
|
|
2392
|
+
var import_chains2 = require("viem/chains");
|
|
4727
2393
|
|
|
4728
|
-
// src/lib/
|
|
4729
|
-
var
|
|
4730
|
-
var
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
{ name: "owner", type: "address" },
|
|
4736
|
-
{ name: "spender", type: "address" },
|
|
4737
|
-
{ name: "value", type: "uint256" },
|
|
4738
|
-
{ name: "nonce", type: "uint256" },
|
|
4739
|
-
{ name: "deadline", type: "uint256" }
|
|
4740
|
-
]
|
|
4741
|
-
};
|
|
4742
|
-
var ERC2612_READ_ABI = (0, import_viem9.parseAbi)([
|
|
4743
|
-
"function name() view returns (string)",
|
|
4744
|
-
"function version() view returns (string)",
|
|
4745
|
-
"function nonces(address owner) view returns (uint256)"
|
|
4746
|
-
]);
|
|
4747
|
-
function erc2612TypedData(input) {
|
|
2394
|
+
// src/lib/transfer-auth.ts
|
|
2395
|
+
var import_viem3 = require("viem");
|
|
2396
|
+
var ERC20_META_ABI = [
|
|
2397
|
+
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2398
|
+
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
2399
|
+
];
|
|
2400
|
+
function buildTransferWithAuthorizationTypedData(input) {
|
|
4748
2401
|
return {
|
|
4749
|
-
domain: {
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
2402
|
+
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
2403
|
+
types: {
|
|
2404
|
+
TransferWithAuthorization: [
|
|
2405
|
+
{ name: "from", type: "address" },
|
|
2406
|
+
{ name: "to", type: "address" },
|
|
2407
|
+
{ name: "value", type: "uint256" },
|
|
2408
|
+
{ name: "validAfter", type: "uint256" },
|
|
2409
|
+
{ name: "validBefore", type: "uint256" },
|
|
2410
|
+
{ name: "nonce", type: "bytes32" }
|
|
2411
|
+
]
|
|
4754
2412
|
},
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
message: {
|
|
4758
|
-
owner: input.owner,
|
|
4759
|
-
spender: BATCH_PERMIT2_ADDRESS,
|
|
4760
|
-
value: BigInt(input.permit.value),
|
|
4761
|
-
nonce: BigInt(input.permit.nonce),
|
|
4762
|
-
deadline: BigInt(input.permit.deadline)
|
|
4763
|
-
}
|
|
2413
|
+
primaryType: "TransferWithAuthorization",
|
|
2414
|
+
message: input.message
|
|
4764
2415
|
};
|
|
4765
2416
|
}
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
{
|
|
4769
|
-
{
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
var
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
})
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
2417
|
+
async function readTokenMeta(publicClient, token) {
|
|
2418
|
+
const [tokenName, tokenVersion] = await Promise.all([
|
|
2419
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
2420
|
+
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
2421
|
+
]);
|
|
2422
|
+
return { tokenName, tokenVersion };
|
|
2423
|
+
}
|
|
2424
|
+
function randomAuthNonce() {
|
|
2425
|
+
const bytes = new Uint8Array(32);
|
|
2426
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2427
|
+
return (0, import_viem3.bytesToHex)(bytes);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
// src/lib/sponsor-client.ts
|
|
2431
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2432
|
+
async function postSponsorTransferAuth(input) {
|
|
2433
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2434
|
+
let res;
|
|
2435
|
+
try {
|
|
2436
|
+
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2437
|
+
method: "POST",
|
|
2438
|
+
headers: {
|
|
2439
|
+
"content-type": "application/json",
|
|
2440
|
+
"x-owney-api-key": input.apiKey
|
|
2441
|
+
},
|
|
2442
|
+
body: JSON.stringify(input.body)
|
|
2443
|
+
});
|
|
2444
|
+
} catch (networkError) {
|
|
2445
|
+
throw new OwneyError(
|
|
2446
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2447
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2448
|
+
{ cause: String(networkError) }
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2451
|
+
const text = await res.text();
|
|
2452
|
+
let parsed = null;
|
|
2453
|
+
try {
|
|
2454
|
+
parsed = JSON.parse(text);
|
|
2455
|
+
} catch {
|
|
2456
|
+
}
|
|
2457
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2458
|
+
throw new OwneyError(
|
|
2459
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2460
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2461
|
+
{
|
|
2462
|
+
statusCode: res.status,
|
|
2463
|
+
responseBody: text.slice(0, 500),
|
|
2464
|
+
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2465
|
+
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2466
|
+
safeToFallback: res.status === 503
|
|
2467
|
+
}
|
|
2468
|
+
);
|
|
2469
|
+
}
|
|
2470
|
+
return parsed.data;
|
|
2471
|
+
}
|
|
2472
|
+
async function postSponsorPermit2Transfer(input) {
|
|
2473
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2474
|
+
let res;
|
|
2475
|
+
try {
|
|
2476
|
+
res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
|
|
2477
|
+
method: "POST",
|
|
2478
|
+
headers: {
|
|
2479
|
+
"content-type": "application/json",
|
|
2480
|
+
"x-owney-api-key": input.apiKey
|
|
2481
|
+
},
|
|
2482
|
+
body: JSON.stringify(input.body)
|
|
2483
|
+
});
|
|
2484
|
+
} catch (networkError) {
|
|
2485
|
+
throw new OwneyError(
|
|
2486
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2487
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2488
|
+
{ cause: String(networkError), safeToFallback: false }
|
|
2489
|
+
);
|
|
2490
|
+
}
|
|
2491
|
+
const text = await res.text();
|
|
2492
|
+
let parsed = null;
|
|
2493
|
+
try {
|
|
2494
|
+
parsed = JSON.parse(text);
|
|
2495
|
+
} catch {
|
|
2496
|
+
}
|
|
2497
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2498
|
+
throw new OwneyError(
|
|
2499
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2500
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2501
|
+
{
|
|
2502
|
+
statusCode: res.status,
|
|
2503
|
+
responseBody: text.slice(0, 500),
|
|
2504
|
+
safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
|
|
2505
|
+
}
|
|
2506
|
+
);
|
|
2507
|
+
}
|
|
2508
|
+
return parsed.data;
|
|
2509
|
+
}
|
|
2510
|
+
async function getSponsorRelayerAddress(input) {
|
|
2511
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
2512
|
+
let res;
|
|
2513
|
+
try {
|
|
2514
|
+
res = await fetch(
|
|
2515
|
+
`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2516
|
+
{
|
|
2517
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
2518
|
+
}
|
|
2519
|
+
);
|
|
2520
|
+
} catch (networkError) {
|
|
2521
|
+
throw new OwneyError(
|
|
2522
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2523
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2524
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
2525
|
+
);
|
|
2526
|
+
}
|
|
2527
|
+
const text = await res.text();
|
|
2528
|
+
let parsed = null;
|
|
2529
|
+
try {
|
|
2530
|
+
parsed = JSON.parse(text);
|
|
2531
|
+
} catch {
|
|
2532
|
+
}
|
|
2533
|
+
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
2534
|
+
throw new OwneyError(
|
|
2535
|
+
"SPONSOR_REQUEST_FAILED",
|
|
2536
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2537
|
+
{
|
|
2538
|
+
statusCode: res.status,
|
|
2539
|
+
responseBody: text.slice(0, 500),
|
|
2540
|
+
safeToFallback: true
|
|
2541
|
+
}
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2544
|
+
return parsed.data.relayer;
|
|
4796
2545
|
}
|
|
4797
|
-
|
|
2546
|
+
|
|
2547
|
+
// src/lib/permit2.ts
|
|
2548
|
+
var import_viem4 = require("viem");
|
|
2549
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2550
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2551
|
+
var ERC20_ALLOWANCE_ABI = [
|
|
2552
|
+
{
|
|
2553
|
+
type: "function",
|
|
2554
|
+
name: "allowance",
|
|
2555
|
+
stateMutability: "view",
|
|
2556
|
+
inputs: [
|
|
2557
|
+
{ name: "owner", type: "address" },
|
|
2558
|
+
{ name: "spender", type: "address" }
|
|
2559
|
+
],
|
|
2560
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
2561
|
+
},
|
|
2562
|
+
{
|
|
2563
|
+
type: "function",
|
|
2564
|
+
name: "approve",
|
|
2565
|
+
stateMutability: "nonpayable",
|
|
2566
|
+
inputs: [
|
|
2567
|
+
{ name: "spender", type: "address" },
|
|
2568
|
+
{ name: "amount", type: "uint256" }
|
|
2569
|
+
],
|
|
2570
|
+
outputs: [{ name: "", type: "bool" }]
|
|
2571
|
+
},
|
|
2572
|
+
{
|
|
2573
|
+
type: "function",
|
|
2574
|
+
name: "balanceOf",
|
|
2575
|
+
stateMutability: "view",
|
|
2576
|
+
inputs: [{ name: "account", type: "address" }],
|
|
2577
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
2578
|
+
}
|
|
2579
|
+
];
|
|
2580
|
+
function buildPermitTransferFromTypedData(input) {
|
|
4798
2581
|
return {
|
|
4799
2582
|
domain: {
|
|
4800
2583
|
name: "Permit2",
|
|
4801
|
-
chainId:
|
|
4802
|
-
verifyingContract:
|
|
2584
|
+
chainId: input.chainId,
|
|
2585
|
+
verifyingContract: PERMIT2_ADDRESS
|
|
4803
2586
|
},
|
|
4804
|
-
types:
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
2587
|
+
types: {
|
|
2588
|
+
PermitTransferFrom: [
|
|
2589
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
2590
|
+
{ name: "spender", type: "address" },
|
|
2591
|
+
{ name: "nonce", type: "uint256" },
|
|
2592
|
+
{ name: "deadline", type: "uint256" }
|
|
2593
|
+
],
|
|
2594
|
+
TokenPermissions: [
|
|
2595
|
+
{ name: "token", type: "address" },
|
|
2596
|
+
{ name: "amount", type: "uint256" }
|
|
2597
|
+
]
|
|
2598
|
+
},
|
|
2599
|
+
primaryType: "PermitTransferFrom",
|
|
2600
|
+
message: input.message
|
|
4811
2601
|
};
|
|
4812
2602
|
}
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
var planOf = (transfers) => JSON.stringify(
|
|
4818
|
-
transfers.map((t) => ({
|
|
4819
|
-
to: t.to.toLowerCase(),
|
|
4820
|
-
amount: BigInt(t.amount).toString()
|
|
4821
|
-
}))
|
|
4822
|
-
);
|
|
4823
|
-
function read(key2) {
|
|
4824
|
-
return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
|
|
2603
|
+
function randomPermit2Nonce() {
|
|
2604
|
+
const bytes = new Uint8Array(32);
|
|
2605
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2606
|
+
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
4825
2607
|
}
|
|
4826
|
-
function
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
2608
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2609
|
+
return publicClient.readContract({
|
|
2610
|
+
address: token,
|
|
2611
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2612
|
+
functionName: "allowance",
|
|
2613
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
4830
2614
|
});
|
|
4831
|
-
if (typeof window === "undefined") memory.set(key2, value);
|
|
4832
|
-
else window.localStorage.setItem(key2, value);
|
|
4833
2615
|
}
|
|
4834
|
-
function
|
|
4835
|
-
|
|
4836
|
-
|
|
2616
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
2617
|
+
return publicClient.readContract({
|
|
2618
|
+
address: token,
|
|
2619
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2620
|
+
functionName: "balanceOf",
|
|
2621
|
+
args: [owner]
|
|
2622
|
+
});
|
|
4837
2623
|
}
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
)
|
|
4848
|
-
);
|
|
4849
|
-
return active.promise;
|
|
4850
|
-
}
|
|
4851
|
-
const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
|
|
4852
|
-
inflight.set(key2, { plan, promise });
|
|
4853
|
-
return promise;
|
|
2624
|
+
|
|
2625
|
+
// src/lib/chain-guard.ts
|
|
2626
|
+
var CHAIN_NAMES = {
|
|
2627
|
+
1: "Ethereum",
|
|
2628
|
+
8453: "Base",
|
|
2629
|
+
42161: "Arbitrum"
|
|
2630
|
+
};
|
|
2631
|
+
function chainName(chainId) {
|
|
2632
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
4854
2633
|
}
|
|
4855
|
-
async function
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
let body = initial;
|
|
4862
|
-
save(key2, body);
|
|
4863
|
-
try {
|
|
4864
|
-
if (!body.serializedTransaction) {
|
|
4865
|
-
const prepared = await postSponsorBatchTransfer({
|
|
4866
|
-
apiKey: i.apiKey,
|
|
4867
|
-
baseUrl: i.baseUrl,
|
|
4868
|
-
body
|
|
4869
|
-
});
|
|
4870
|
-
if (!prepared.serializedTransaction || (0, import_viem10.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
|
|
4871
|
-
throw new Error(
|
|
4872
|
-
"Sponsorship API did not return a valid prepared transaction."
|
|
4873
|
-
);
|
|
4874
|
-
body = {
|
|
4875
|
-
...body,
|
|
4876
|
-
serializedTransaction: prepared.serializedTransaction
|
|
4877
|
-
};
|
|
4878
|
-
save(key2, body);
|
|
4879
|
-
}
|
|
4880
|
-
const result = await postSponsorBatchTransfer({
|
|
4881
|
-
apiKey: i.apiKey,
|
|
4882
|
-
baseUrl: i.baseUrl,
|
|
4883
|
-
body
|
|
4884
|
-
});
|
|
4885
|
-
if (result.txHash !== (0, import_viem10.keccak256)(body.serializedTransaction))
|
|
4886
|
-
throw new Error(
|
|
4887
|
-
"Sponsorship receipt does not match the pending transaction."
|
|
4888
|
-
);
|
|
4889
|
-
clear(key2);
|
|
4890
|
-
return result.txHash;
|
|
4891
|
-
} catch (error) {
|
|
4892
|
-
if (!body.serializedTransaction || error instanceof OwneyError && error.details?.notSubmitted === true)
|
|
4893
|
-
clear(key2);
|
|
4894
|
-
throw error;
|
|
4895
|
-
}
|
|
4896
|
-
};
|
|
4897
|
-
const saved = read(key2);
|
|
4898
|
-
if (saved) {
|
|
4899
|
-
const previous = JSON.parse(saved);
|
|
4900
|
-
if (previous.chainId !== i.chainId || !(0, import_viem10.isAddressEqual)(previous.from, i.owner) || !(0, import_viem10.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
4901
|
-
throw new Error(
|
|
4902
|
-
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
4903
|
-
);
|
|
4904
|
-
i.onApproved?.();
|
|
4905
|
-
return send({ ...previous, transfers: i.transfers });
|
|
4906
|
-
}
|
|
4907
|
-
const total = i.transfers.reduce((sum, t) => sum + BigInt(t.amount), 0n);
|
|
4908
|
-
const [balance, allowance] = await Promise.all([
|
|
4909
|
-
readErc20Balance(i.pub, i.token, i.owner),
|
|
4910
|
-
readPermit2Allowance(i.pub, i.token, i.owner)
|
|
4911
|
-
]);
|
|
4912
|
-
if (balance < total)
|
|
2634
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2635
|
+
const actual = await pub.getChainId();
|
|
2636
|
+
if (actual === expected) return;
|
|
2637
|
+
try {
|
|
2638
|
+
await wallet.switchChain({ id: expected });
|
|
2639
|
+
} catch (error) {
|
|
4913
2640
|
throw new OwneyError(
|
|
4914
|
-
"
|
|
4915
|
-
|
|
2641
|
+
"CHAIN_MISMATCH",
|
|
2642
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2643
|
+
{
|
|
2644
|
+
expectedChainId: expected,
|
|
2645
|
+
actualChainId: actual,
|
|
2646
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2647
|
+
}
|
|
4916
2648
|
);
|
|
4917
|
-
|
|
2649
|
+
}
|
|
2650
|
+
const after = await pub.getChainId();
|
|
2651
|
+
if (after !== expected) {
|
|
4918
2652
|
throw new OwneyError(
|
|
4919
|
-
"
|
|
4920
|
-
|
|
2653
|
+
"CHAIN_MISMATCH",
|
|
2654
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2655
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
4921
2656
|
);
|
|
4922
|
-
const now = (await i.pub.getBlock()).timestamp;
|
|
4923
|
-
let erc2612Permit;
|
|
4924
|
-
if (allowance < total) {
|
|
4925
|
-
const [tokenName, tokenVersion, permitNonce] = await Promise.all([
|
|
4926
|
-
i.pub.readContract({
|
|
4927
|
-
address: i.token,
|
|
4928
|
-
abi: ERC2612_READ_ABI,
|
|
4929
|
-
functionName: "name"
|
|
4930
|
-
}),
|
|
4931
|
-
i.pub.readContract({
|
|
4932
|
-
address: i.token,
|
|
4933
|
-
abi: ERC2612_READ_ABI,
|
|
4934
|
-
functionName: "version"
|
|
4935
|
-
}),
|
|
4936
|
-
i.pub.readContract({
|
|
4937
|
-
address: i.token,
|
|
4938
|
-
abi: ERC2612_READ_ABI,
|
|
4939
|
-
functionName: "nonces",
|
|
4940
|
-
args: [i.owner]
|
|
4941
|
-
})
|
|
4942
|
-
]);
|
|
4943
|
-
const unsignedPermit = {
|
|
4944
|
-
value: MAX_UINT256.toString(),
|
|
4945
|
-
nonce: permitNonce.toString(),
|
|
4946
|
-
deadline: (now + 900n).toString(),
|
|
4947
|
-
tokenName,
|
|
4948
|
-
tokenVersion
|
|
4949
|
-
};
|
|
4950
|
-
const signature2 = await i.wallet.signTypedData({
|
|
4951
|
-
account: i.owner,
|
|
4952
|
-
...erc2612TypedData({
|
|
4953
|
-
chainId: 8453,
|
|
4954
|
-
token: i.token,
|
|
4955
|
-
owner: i.owner,
|
|
4956
|
-
permit: unsignedPermit
|
|
4957
|
-
})
|
|
4958
|
-
});
|
|
4959
|
-
erc2612Permit = { ...unsignedPermit, signature: signature2 };
|
|
4960
2657
|
}
|
|
4961
|
-
const spender = erc2612Permit ? MULTICALL3_ADDRESS : await getSponsorRelayerAddress({
|
|
4962
|
-
apiKey: i.apiKey,
|
|
4963
|
-
baseUrl: i.baseUrl,
|
|
4964
|
-
chainId: i.chainId
|
|
4965
|
-
});
|
|
4966
|
-
const unsigned = {
|
|
4967
|
-
chainId: i.chainId,
|
|
4968
|
-
token: i.token,
|
|
4969
|
-
from: i.owner,
|
|
4970
|
-
transfers: i.transfers,
|
|
4971
|
-
nonce: randomPermit2Nonce().toString(),
|
|
4972
|
-
deadline: (now + 900n).toString(),
|
|
4973
|
-
...erc2612Permit ? { erc2612Permit } : {}
|
|
4974
|
-
};
|
|
4975
|
-
const signature = await i.wallet.signTypedData({
|
|
4976
|
-
account: i.owner,
|
|
4977
|
-
...batchTypedData(unsigned, spender)
|
|
4978
|
-
});
|
|
4979
|
-
i.onApproved?.();
|
|
4980
|
-
return send({ ...unsigned, signature });
|
|
4981
2658
|
}
|
|
4982
2659
|
|
|
4983
|
-
// src/lib/sponsored-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
2660
|
+
// src/lib/sponsored-deposit.ts
|
|
2661
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2662
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2663
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2664
|
+
return async (smartWallet, chainId, amount) => {
|
|
2665
|
+
const cid = chainId;
|
|
2666
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2667
|
+
if (!token) {
|
|
4987
2668
|
throw new OwneyError(
|
|
4988
2669
|
"CHAIN_UNSUPPORTED",
|
|
4989
2670
|
`No sponsored token configured for chain ${chainId}`
|
|
4990
2671
|
);
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
2672
|
+
}
|
|
2673
|
+
const pub = deps.getPublicClient(cid);
|
|
2674
|
+
const wallet = deps.getWalletClient(cid);
|
|
2675
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2676
|
+
try {
|
|
2677
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2678
|
+
if (balance < BigInt(amount)) {
|
|
2679
|
+
throw new OwneyError(
|
|
2680
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2681
|
+
"Insufficient balance for this deposit.",
|
|
2682
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2683
|
+
);
|
|
2684
|
+
}
|
|
2685
|
+
} catch (err) {
|
|
2686
|
+
if (err instanceof OwneyError) throw err;
|
|
2687
|
+
console.warn(
|
|
2688
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2689
|
+
err instanceof Error ? err.message : String(err)
|
|
4996
2690
|
);
|
|
4997
|
-
|
|
4998
|
-
await
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
2691
|
+
}
|
|
2692
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2693
|
+
const validAfter = 0n;
|
|
2694
|
+
const validBefore = BigInt(
|
|
2695
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2696
|
+
);
|
|
2697
|
+
const nonce = randomAuthNonce();
|
|
2698
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
5003
2699
|
token,
|
|
5004
|
-
chainId,
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
2700
|
+
chainId: cid,
|
|
2701
|
+
tokenName,
|
|
2702
|
+
tokenVersion,
|
|
2703
|
+
message: {
|
|
2704
|
+
from: deps.ownerAddress,
|
|
2705
|
+
to: smartWallet,
|
|
2706
|
+
value: BigInt(amount),
|
|
2707
|
+
validAfter,
|
|
2708
|
+
validBefore,
|
|
2709
|
+
nonce
|
|
2710
|
+
}
|
|
5009
2711
|
});
|
|
2712
|
+
const authSignature = await wallet.signTypedData({
|
|
2713
|
+
account: deps.ownerAddress,
|
|
2714
|
+
...typedData
|
|
2715
|
+
});
|
|
2716
|
+
deps.onApproved?.();
|
|
2717
|
+
const result = await post({
|
|
2718
|
+
baseUrl: deps.baseUrl,
|
|
2719
|
+
apiKey: deps.apiKey,
|
|
2720
|
+
body: {
|
|
2721
|
+
chainId: cid,
|
|
2722
|
+
token,
|
|
2723
|
+
from: deps.ownerAddress,
|
|
2724
|
+
to: smartWallet,
|
|
2725
|
+
value: amount,
|
|
2726
|
+
validAfter: validAfter.toString(),
|
|
2727
|
+
validBefore: validBefore.toString(),
|
|
2728
|
+
nonce,
|
|
2729
|
+
authSignature,
|
|
2730
|
+
tokenName,
|
|
2731
|
+
tokenVersion
|
|
2732
|
+
}
|
|
2733
|
+
});
|
|
2734
|
+
return result.txHash;
|
|
5010
2735
|
};
|
|
5011
|
-
const callback = makeVerificationAwareDepositCallback(
|
|
5012
|
-
(to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
|
|
5013
|
-
);
|
|
5014
|
-
registerDepositBatch(callback, batch);
|
|
5015
|
-
return callback;
|
|
5016
2736
|
}
|
|
5017
2737
|
|
|
5018
|
-
// src/lib/
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
const
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
const funding = deferred();
|
|
5031
|
-
const tasks = [];
|
|
5032
|
-
const transfers = [];
|
|
5033
|
-
try {
|
|
5034
|
-
for (const leg of legs) {
|
|
5035
|
-
const ready = deferred();
|
|
5036
|
-
let entered = false;
|
|
5037
|
-
const callback = makeVerificationAwareDepositCallback(
|
|
5038
|
-
(to, cid, amount, verification) => {
|
|
5039
|
-
if (entered || cid !== chainId || BigInt(amount) !== BigInt(leg.amount)) {
|
|
5040
|
-
const error = new Error(
|
|
5041
|
-
"Agent changed its prepared deposit share."
|
|
5042
|
-
);
|
|
5043
|
-
ready.reject(error);
|
|
5044
|
-
throw error;
|
|
5045
|
-
}
|
|
5046
|
-
entered = true;
|
|
5047
|
-
ready.resolve(toBatchTransfer(to, amount, verification));
|
|
5048
|
-
return funding.promise;
|
|
5049
|
-
}
|
|
5050
|
-
);
|
|
5051
|
-
const task = Promise.resolve().then(() => leg.run(callback));
|
|
5052
|
-
tasks.push(task);
|
|
5053
|
-
void task.then(
|
|
5054
|
-
() => {
|
|
5055
|
-
if (!entered)
|
|
5056
|
-
ready.reject(
|
|
5057
|
-
new Error("Agent did not prepare a deposit transfer.")
|
|
5058
|
-
);
|
|
5059
|
-
},
|
|
5060
|
-
(error) => ready.reject(error)
|
|
2738
|
+
// src/lib/sponsored-weth-deposit.ts
|
|
2739
|
+
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2740
|
+
function makeSponsoredWethCallback(deps) {
|
|
2741
|
+
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
2742
|
+
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
2743
|
+
return async (smartWallet, chainId, amount) => {
|
|
2744
|
+
const cid = chainId;
|
|
2745
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2746
|
+
if (!token) {
|
|
2747
|
+
throw new OwneyError(
|
|
2748
|
+
"CHAIN_UNSUPPORTED",
|
|
2749
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
5061
2750
|
);
|
|
5062
|
-
transfers.push(await ready.promise);
|
|
5063
2751
|
}
|
|
5064
|
-
const
|
|
5065
|
-
|
|
5066
|
-
const
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
if (
|
|
5071
|
-
|
|
5072
|
-
|
|
2752
|
+
const amountWei = BigInt(amount);
|
|
2753
|
+
const pub = deps.getPublicClient(cid);
|
|
2754
|
+
const wallet = deps.getWalletClient(cid);
|
|
2755
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2756
|
+
try {
|
|
2757
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2758
|
+
if (balance < amountWei) {
|
|
2759
|
+
throw new OwneyError(
|
|
2760
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2761
|
+
"Insufficient WETH balance for this deposit.",
|
|
2762
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
} catch (err) {
|
|
2766
|
+
if (err instanceof OwneyError) throw err;
|
|
2767
|
+
console.warn(
|
|
2768
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
2769
|
+
err instanceof Error ? err.message : String(err)
|
|
2770
|
+
);
|
|
5073
2771
|
}
|
|
5074
|
-
|
|
2772
|
+
const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
|
|
2773
|
+
if (allowance < amountWei) {
|
|
5075
2774
|
throw new OwneyError(
|
|
5076
|
-
"
|
|
5077
|
-
"
|
|
5078
|
-
{
|
|
5079
|
-
txHash,
|
|
5080
|
-
fundsSubmitted: true,
|
|
5081
|
-
agentResults,
|
|
5082
|
-
failedAgentIds: failures
|
|
5083
|
-
}
|
|
2775
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
2776
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
2777
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
5084
2778
|
);
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
2779
|
+
}
|
|
2780
|
+
const relayer = await get({
|
|
2781
|
+
baseUrl: deps.baseUrl,
|
|
2782
|
+
apiKey: deps.apiKey,
|
|
2783
|
+
chainId: cid
|
|
2784
|
+
});
|
|
2785
|
+
const nonce = randomPermit2Nonce();
|
|
2786
|
+
const deadline = BigInt(
|
|
2787
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2788
|
+
);
|
|
2789
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
2790
|
+
chainId: cid,
|
|
2791
|
+
message: {
|
|
2792
|
+
permitted: { token, amount: amountWei },
|
|
2793
|
+
spender: relayer,
|
|
2794
|
+
nonce,
|
|
2795
|
+
deadline
|
|
2796
|
+
}
|
|
2797
|
+
});
|
|
2798
|
+
const signature = await wallet.signTypedData({
|
|
2799
|
+
account: deps.ownerAddress,
|
|
2800
|
+
...typedData
|
|
2801
|
+
});
|
|
2802
|
+
deps.onApproved?.();
|
|
2803
|
+
const result = await post({
|
|
2804
|
+
baseUrl: deps.baseUrl,
|
|
2805
|
+
apiKey: deps.apiKey,
|
|
2806
|
+
body: {
|
|
2807
|
+
chainId: cid,
|
|
2808
|
+
token,
|
|
2809
|
+
from: deps.ownerAddress,
|
|
2810
|
+
to: smartWallet,
|
|
2811
|
+
amount,
|
|
2812
|
+
nonce: nonce.toString(),
|
|
2813
|
+
deadline: deadline.toString(),
|
|
2814
|
+
signature
|
|
2815
|
+
}
|
|
2816
|
+
});
|
|
2817
|
+
return result.txHash;
|
|
2818
|
+
};
|
|
5091
2819
|
}
|
|
5092
2820
|
|
|
5093
2821
|
// src/lib/sponsored-calls-deposit.ts
|
|
5094
|
-
var
|
|
2822
|
+
var import_viem5 = require("viem");
|
|
5095
2823
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
5096
2824
|
var DEFAULT_MAX_POLLS = 30;
|
|
5097
2825
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -5099,7 +2827,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
5099
2827
|
method: "wallet_getCapabilities",
|
|
5100
2828
|
params: [owner]
|
|
5101
2829
|
});
|
|
5102
|
-
const forChain = caps?.[(0,
|
|
2830
|
+
const forChain = caps?.[(0, import_viem5.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
5103
2831
|
return Boolean(forChain?.paymasterService?.supported);
|
|
5104
2832
|
}
|
|
5105
2833
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -5117,7 +2845,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5117
2845
|
}
|
|
5118
2846
|
return new URL(configured, origin).toString();
|
|
5119
2847
|
};
|
|
5120
|
-
|
|
2848
|
+
return async (smartWallet, chainId, amount) => {
|
|
5121
2849
|
const cid = chainId;
|
|
5122
2850
|
const token = deps.tokenAddressByChain[cid];
|
|
5123
2851
|
if (!token) {
|
|
@@ -5133,53 +2861,22 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5133
2861
|
{ chainId }
|
|
5134
2862
|
);
|
|
5135
2863
|
}
|
|
5136
|
-
const
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
functionName: "transfer",
|
|
5142
|
-
args: [transfer.to, BigInt(transfer.amount)]
|
|
5143
|
-
})
|
|
5144
|
-
}));
|
|
5145
|
-
let paymasterUrl = absolutePaymasterUrl();
|
|
5146
|
-
for (const transfer of transfers) {
|
|
5147
|
-
const verification = transfer.yieldseeker;
|
|
5148
|
-
if (!verification) continue;
|
|
5149
|
-
if (chainId !== 8453)
|
|
5150
|
-
throw new OwneyError(
|
|
5151
|
-
"CHAIN_UNSUPPORTED",
|
|
5152
|
-
`Yieldseeker sponsorship is not available on chain ${chainId}.`
|
|
5153
|
-
);
|
|
5154
|
-
const { intent } = await postPaymasterIntent({
|
|
5155
|
-
baseUrl: deps.routingApiBaseUrl,
|
|
5156
|
-
apiKey: deps.apiKey,
|
|
5157
|
-
yieldseekerSignature: verification.signature,
|
|
5158
|
-
body: {
|
|
5159
|
-
chainId,
|
|
5160
|
-
token,
|
|
5161
|
-
from: deps.ownerAddress,
|
|
5162
|
-
to: transfer.to,
|
|
5163
|
-
amount: transfer.amount,
|
|
5164
|
-
yieldseekerUserId: verification.userId,
|
|
5165
|
-
yieldseekerAgentId: verification.agentId
|
|
5166
|
-
}
|
|
5167
|
-
});
|
|
5168
|
-
const url = new URL(paymasterUrl);
|
|
5169
|
-
url.searchParams.append("owneyIntent", intent);
|
|
5170
|
-
paymasterUrl = url.toString();
|
|
5171
|
-
}
|
|
2864
|
+
const data = (0, import_viem5.encodeFunctionData)({
|
|
2865
|
+
abi: import_viem5.erc20Abi,
|
|
2866
|
+
functionName: "transfer",
|
|
2867
|
+
args: [smartWallet, BigInt(amount)]
|
|
2868
|
+
});
|
|
5172
2869
|
const sendResult = await deps.provider.request({
|
|
5173
2870
|
method: "wallet_sendCalls",
|
|
5174
2871
|
params: [
|
|
5175
2872
|
{
|
|
5176
2873
|
version: "2.0.0",
|
|
5177
2874
|
from: deps.ownerAddress,
|
|
5178
|
-
chainId: (0,
|
|
5179
|
-
atomicRequired:
|
|
5180
|
-
calls,
|
|
2875
|
+
chainId: (0, import_viem5.toHex)(chainId),
|
|
2876
|
+
atomicRequired: false,
|
|
2877
|
+
calls: [{ to: token, value: "0x0", data }],
|
|
5181
2878
|
capabilities: {
|
|
5182
|
-
paymasterService: { url:
|
|
2879
|
+
paymasterService: { url: absolutePaymasterUrl() }
|
|
5183
2880
|
}
|
|
5184
2881
|
}
|
|
5185
2882
|
]
|
|
@@ -5199,24 +2896,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5199
2896
|
params: [callsId]
|
|
5200
2897
|
});
|
|
5201
2898
|
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
5202
|
-
if (
|
|
5203
|
-
throw new OwneyError(
|
|
5204
|
-
"SPONSOR_REQUEST_FAILED",
|
|
5205
|
-
"The sponsored deposit did not complete successfully.",
|
|
5206
|
-
{ chainId, callsId, safeToFallback: false }
|
|
5207
|
-
);
|
|
5208
|
-
}
|
|
5209
|
-
if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
|
|
5210
|
-
if (status?.receipts?.some(
|
|
5211
|
-
(receipt) => receipt.transactionHash !== txHash
|
|
5212
|
-
))
|
|
5213
|
-
throw new OwneyError(
|
|
5214
|
-
"SPONSORED_CALLS_NO_RECEIPT",
|
|
5215
|
-
"The wallet returned multiple transactions for an atomic deposit. Check activity before trying again.",
|
|
5216
|
-
{ chainId, callsId }
|
|
5217
|
-
);
|
|
5218
|
-
return txHash;
|
|
5219
|
-
}
|
|
2899
|
+
if (txHash) return txHash;
|
|
5220
2900
|
if (pollIntervalMs > 0) {
|
|
5221
2901
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
5222
2902
|
}
|
|
@@ -5227,16 +2907,9 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5227
2907
|
{ chainId, callsId }
|
|
5228
2908
|
);
|
|
5229
2909
|
};
|
|
5230
|
-
const callback = makeVerificationAwareDepositCallback(
|
|
5231
|
-
(to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
|
|
5232
|
-
);
|
|
5233
|
-
registerDepositBatch(callback, batch);
|
|
5234
|
-
return callback;
|
|
5235
2910
|
}
|
|
5236
2911
|
|
|
5237
2912
|
// src/client.ts
|
|
5238
|
-
var PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS = 6;
|
|
5239
|
-
var PERMIT2_ALLOWANCE_VERIFY_DELAY_MS = 250;
|
|
5240
2913
|
function encodeMultiAgentCursor(map) {
|
|
5241
2914
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
5242
2915
|
}
|
|
@@ -5263,20 +2936,15 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
5263
2936
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
5264
2937
|
};
|
|
5265
2938
|
var VIEM_CHAIN2 = {
|
|
5266
|
-
8453:
|
|
5267
|
-
42161:
|
|
5268
|
-
1:
|
|
2939
|
+
8453: import_chains2.base,
|
|
2940
|
+
42161: import_chains2.arbitrum,
|
|
2941
|
+
1: import_chains2.mainnet
|
|
5269
2942
|
};
|
|
5270
2943
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
5271
2944
|
8453: "0x4200000000000000000000000000000000000006",
|
|
5272
2945
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
5273
2946
|
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
5274
2947
|
};
|
|
5275
|
-
var SPONSORED_TOKENS_BY_ASSET = { USDC: SPONSORED_USDC_BY_CHAIN, WETH: SPONSORED_WETH_BY_CHAIN };
|
|
5276
|
-
function sponsoredTokensFor(asset) {
|
|
5277
|
-
if (!Object.prototype.hasOwnProperty.call(SPONSORED_TOKENS_BY_ASSET, asset)) throw new OwneyError("ASSET_UNSUPPORTED", `No sponsored token configured for ${asset}`);
|
|
5278
|
-
return SPONSORED_TOKENS_BY_ASSET[asset];
|
|
5279
|
-
}
|
|
5280
2948
|
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
5281
2949
|
return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
5282
2950
|
}
|
|
@@ -5297,10 +2965,7 @@ var OwneySDK = class {
|
|
|
5297
2965
|
// leave every user's agent profile alone.
|
|
5298
2966
|
orgAgentConfig;
|
|
5299
2967
|
orgAgentConfigPromise = null;
|
|
5300
|
-
rpcUrls;
|
|
5301
2968
|
zyfaiRpcUrls;
|
|
5302
|
-
yieldseekerApiBaseUrl;
|
|
5303
|
-
yieldseekerSiweOrigin;
|
|
5304
2969
|
routingApiBaseUrl;
|
|
5305
2970
|
referralSource;
|
|
5306
2971
|
cachedSponsoredCallback = null;
|
|
@@ -5322,14 +2987,7 @@ var OwneySDK = class {
|
|
|
5322
2987
|
constructor(config) {
|
|
5323
2988
|
this.apiKey = config.apiKey;
|
|
5324
2989
|
if (config.debug) setOwneyDebug(true);
|
|
5325
|
-
this.
|
|
5326
|
-
config.rpcUrls,
|
|
5327
|
-
config.apiKey,
|
|
5328
|
-
config.routingApiBaseUrl
|
|
5329
|
-
);
|
|
5330
|
-
this.zyfaiRpcUrls = config.rpcUrls ? void 0 : config.zyfaiRpcUrls;
|
|
5331
|
-
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
5332
|
-
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
2990
|
+
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
5333
2991
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
5334
2992
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
5335
2993
|
this.referralSource = config.referralSource;
|
|
@@ -5363,7 +3021,6 @@ var OwneySDK = class {
|
|
|
5363
3021
|
* After calling this, `connect()` must be called again before using agent methods.
|
|
5364
3022
|
*/
|
|
5365
3023
|
async disconnect() {
|
|
5366
|
-
this.state = null;
|
|
5367
3024
|
for (const agent of this.agents.values()) {
|
|
5368
3025
|
await agent.disconnect();
|
|
5369
3026
|
}
|
|
@@ -5417,16 +3074,18 @@ var OwneySDK = class {
|
|
|
5417
3074
|
}
|
|
5418
3075
|
return this.state.provider;
|
|
5419
3076
|
}
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
3077
|
+
/**
|
|
3078
|
+
* Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
|
|
3079
|
+
* used when the caller omits `depositCallback`. Wraps the connected EIP-1193
|
|
3080
|
+
* provider with viem `custom(provider)` to read token meta and sign the
|
|
3081
|
+
* `TransferWithAuthorization`, then POSTs to the sponsor API.
|
|
3082
|
+
*/
|
|
5424
3083
|
getDefaultSponsoredCallback(onApproved) {
|
|
5425
3084
|
if (!onApproved && this.cachedSponsoredCallback)
|
|
5426
3085
|
return this.cachedSponsoredCallback;
|
|
5427
3086
|
const provider = this.requireConnectedProvider();
|
|
5428
3087
|
const owner = this.state.walletAddress;
|
|
5429
|
-
const callback =
|
|
3088
|
+
const callback = makeSponsoredDepositCallback({
|
|
5430
3089
|
apiKey: this.apiKey,
|
|
5431
3090
|
baseUrl: this.routingApiBaseUrl,
|
|
5432
3091
|
ownerAddress: owner,
|
|
@@ -5435,33 +3094,35 @@ var OwneySDK = class {
|
|
|
5435
3094
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
5436
3095
|
// PublicClient/WalletClient param types — structurally identical at
|
|
5437
3096
|
// runtime, but the two share a name TS treats as unrelated.
|
|
5438
|
-
getPublicClient: (cid) =>
|
|
5439
|
-
getWalletChainClient: (cid) => (0, import_viem12.createPublicClient)({
|
|
3097
|
+
getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
|
|
5440
3098
|
chain: VIEM_CHAIN2[cid],
|
|
5441
|
-
transport: (0,
|
|
3099
|
+
transport: (0, import_viem6.custom)(provider)
|
|
5442
3100
|
}),
|
|
5443
|
-
getWalletClient: (cid) => (0,
|
|
3101
|
+
getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
|
|
5444
3102
|
account: owner,
|
|
5445
3103
|
chain: VIEM_CHAIN2[cid],
|
|
5446
|
-
transport: (0,
|
|
3104
|
+
transport: (0, import_viem6.custom)(provider)
|
|
5447
3105
|
})
|
|
5448
3106
|
});
|
|
5449
3107
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
5450
3108
|
return callback;
|
|
5451
3109
|
}
|
|
5452
|
-
/**
|
|
3110
|
+
/**
|
|
3111
|
+
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
3112
|
+
* callback used when the caller omits `depositCallback` for a WETH
|
|
3113
|
+
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
3114
|
+
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
3115
|
+
*/
|
|
5453
3116
|
getDefaultSponsoredCallsCallback(asset, onApproved) {
|
|
5454
3117
|
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
5455
3118
|
if (!onApproved && cached) return cached;
|
|
5456
3119
|
const provider = this.requireConnectedProvider();
|
|
5457
3120
|
const callback = makeSponsoredCallsCallback({
|
|
5458
|
-
apiKey: this.apiKey,
|
|
5459
|
-
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
5460
3121
|
provider,
|
|
5461
3122
|
ownerAddress: this.state.walletAddress,
|
|
5462
3123
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
5463
3124
|
onApproved,
|
|
5464
|
-
tokenAddressByChain:
|
|
3125
|
+
tokenAddressByChain: asset === "WETH" ? SPONSORED_WETH_BY_CHAIN : SPONSORED_USDC_BY_CHAIN
|
|
5465
3126
|
});
|
|
5466
3127
|
if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
5467
3128
|
return callback;
|
|
@@ -5470,14 +3131,14 @@ var OwneySDK = class {
|
|
|
5470
3131
|
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
5471
3132
|
* callback used when the caller omits `depositCallback` for a WETH deposit.
|
|
5472
3133
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
5473
|
-
*
|
|
3134
|
+
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
5474
3135
|
*/
|
|
5475
3136
|
getDefaultWethSponsoredCallback(onApproved) {
|
|
5476
3137
|
if (!onApproved && this.cachedWethSponsoredCallback)
|
|
5477
3138
|
return this.cachedWethSponsoredCallback;
|
|
5478
3139
|
const provider = this.requireConnectedProvider();
|
|
5479
3140
|
const owner = this.state.walletAddress;
|
|
5480
|
-
const callback =
|
|
3141
|
+
const callback = makeSponsoredWethCallback({
|
|
5481
3142
|
apiKey: this.apiKey,
|
|
5482
3143
|
baseUrl: this.routingApiBaseUrl,
|
|
5483
3144
|
ownerAddress: owner,
|
|
@@ -5486,15 +3147,14 @@ var OwneySDK = class {
|
|
|
5486
3147
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
5487
3148
|
// PublicClient/WalletClient param types — structurally identical at
|
|
5488
3149
|
// runtime, but the two share a name TS treats as unrelated.
|
|
5489
|
-
getPublicClient: (cid) =>
|
|
5490
|
-
getWalletChainClient: (cid) => (0, import_viem12.createPublicClient)({
|
|
3150
|
+
getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
|
|
5491
3151
|
chain: VIEM_CHAIN2[cid],
|
|
5492
|
-
transport: (0,
|
|
3152
|
+
transport: (0, import_viem6.custom)(provider)
|
|
5493
3153
|
}),
|
|
5494
|
-
getWalletClient: (cid) => (0,
|
|
3154
|
+
getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
|
|
5495
3155
|
account: owner,
|
|
5496
3156
|
chain: VIEM_CHAIN2[cid],
|
|
5497
|
-
transport: (0,
|
|
3157
|
+
transport: (0, import_viem6.custom)(provider)
|
|
5498
3158
|
})
|
|
5499
3159
|
});
|
|
5500
3160
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -5525,10 +3185,12 @@ var OwneySDK = class {
|
|
|
5525
3185
|
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
5526
3186
|
this.apiKey,
|
|
5527
3187
|
this.routingApiBaseUrl
|
|
5528
|
-
).then(
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
3188
|
+
).then(
|
|
3189
|
+
(config) => {
|
|
3190
|
+
this.orgAgentConfig = config;
|
|
3191
|
+
return config;
|
|
3192
|
+
}
|
|
3193
|
+
);
|
|
5532
3194
|
}
|
|
5533
3195
|
return this.orgAgentConfigPromise;
|
|
5534
3196
|
}
|
|
@@ -5568,14 +3230,7 @@ var OwneySDK = class {
|
|
|
5568
3230
|
this.routingApiBaseUrl
|
|
5569
3231
|
);
|
|
5570
3232
|
this.disabledAgents.clear();
|
|
5571
|
-
for (const {
|
|
5572
|
-
key: key2,
|
|
5573
|
-
agent_type,
|
|
5574
|
-
is_enabled,
|
|
5575
|
-
is_configured
|
|
5576
|
-
} of agentKeys) {
|
|
5577
|
-
const configured = is_configured ?? Boolean(key2);
|
|
5578
|
-
if (!configured) continue;
|
|
3233
|
+
for (const { key: key2, agent_type, is_enabled } of agentKeys) {
|
|
5579
3234
|
const agent = this.createAgent(agent_type, key2);
|
|
5580
3235
|
if (!agent) continue;
|
|
5581
3236
|
this.agents.set(agent_type, agent);
|
|
@@ -5599,23 +3254,7 @@ var OwneySDK = class {
|
|
|
5599
3254
|
}
|
|
5600
3255
|
createAgent(agentId, key2) {
|
|
5601
3256
|
if (agentId === "zyfai") {
|
|
5602
|
-
|
|
5603
|
-
return new ZyfaiAgent(
|
|
5604
|
-
key2,
|
|
5605
|
-
this.zyfaiRpcUrls ? resolveRpcUrls(
|
|
5606
|
-
this.zyfaiRpcUrls,
|
|
5607
|
-
this.apiKey,
|
|
5608
|
-
this.routingApiBaseUrl
|
|
5609
|
-
) : this.rpcUrls,
|
|
5610
|
-
this.referralSource
|
|
5611
|
-
);
|
|
5612
|
-
}
|
|
5613
|
-
if (agentId === "yieldseeker") {
|
|
5614
|
-
return new YieldseekerAgent(this.apiKey, {
|
|
5615
|
-
auth: { origin: this.yieldseekerSiweOrigin },
|
|
5616
|
-
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl),
|
|
5617
|
-
rpcUrls: this.rpcUrls
|
|
5618
|
-
});
|
|
3257
|
+
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
5619
3258
|
}
|
|
5620
3259
|
return null;
|
|
5621
3260
|
}
|
|
@@ -5661,10 +3300,9 @@ var OwneySDK = class {
|
|
|
5661
3300
|
* If provided, ALL specified agents must support the chainId or the call
|
|
5662
3301
|
* throws before activating any agent.
|
|
5663
3302
|
*/
|
|
5664
|
-
async activateAgent(chainId, agentId
|
|
3303
|
+
async activateAgent(chainId, agentId) {
|
|
5665
3304
|
const state = this.requireState();
|
|
5666
3305
|
await this.ensureAgentsInitialized();
|
|
5667
|
-
this.assertActivationSession(state);
|
|
5668
3306
|
if (agentId !== void 0) {
|
|
5669
3307
|
if (agentId.length === 0) {
|
|
5670
3308
|
throw new OwneyError(
|
|
@@ -5698,7 +3336,7 @@ var OwneySDK = class {
|
|
|
5698
3336
|
this.activeAgents.add(id);
|
|
5699
3337
|
}
|
|
5700
3338
|
state.chainId = chainId;
|
|
5701
|
-
await this.activateAgentsInTurn(agents, state, chainId
|
|
3339
|
+
await this.activateAgentsInTurn(agents, state, chainId);
|
|
5702
3340
|
return;
|
|
5703
3341
|
}
|
|
5704
3342
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -5719,12 +3357,7 @@ var OwneySDK = class {
|
|
|
5719
3357
|
const enabledCompatible = compatible.filter(
|
|
5720
3358
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
5721
3359
|
);
|
|
5722
|
-
await this.activateAgentsInTurn(enabledCompatible, state, chainId
|
|
5723
|
-
}
|
|
5724
|
-
assertActivationSession(state) {
|
|
5725
|
-
if (this.state !== state) {
|
|
5726
|
-
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed. Please try again.");
|
|
5727
|
-
}
|
|
3360
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId);
|
|
5728
3361
|
}
|
|
5729
3362
|
/**
|
|
5730
3363
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -5739,51 +3372,26 @@ var OwneySDK = class {
|
|
|
5739
3372
|
* Serializing costs no real wall-clock: the user can only approve one prompt
|
|
5740
3373
|
* at a time anyway.
|
|
5741
3374
|
*
|
|
5742
|
-
*
|
|
5743
|
-
*
|
|
5744
|
-
*
|
|
3375
|
+
* Every agent is attempted even if an earlier one fails, so one declined
|
|
3376
|
+
* signature can't deny the remaining agents their turn. The first failure is
|
|
3377
|
+
* rethrown (matching the previous `Promise.all` rejection) once all agents
|
|
3378
|
+
* have had a chance to activate.
|
|
5745
3379
|
*/
|
|
5746
|
-
async activateAgentsInTurn(agents, state, chainId
|
|
3380
|
+
async activateAgentsInTurn(agents, state, chainId) {
|
|
5747
3381
|
let firstError = null;
|
|
5748
|
-
const activatedAgentIds = [];
|
|
5749
|
-
const failedAgents = [];
|
|
5750
3382
|
for (const agent of agents) {
|
|
5751
|
-
this.assertActivationSession(state);
|
|
5752
3383
|
try {
|
|
5753
|
-
await agent.activateAgent(state, chainId
|
|
5754
|
-
this.assertActivationSession(state);
|
|
3384
|
+
await agent.activateAgent(state, chainId);
|
|
5755
3385
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
5756
|
-
this.assertActivationSession(state);
|
|
5757
|
-
activatedAgentIds.push(agent.id);
|
|
5758
3386
|
} catch (error) {
|
|
5759
|
-
this.assertActivationSession(state);
|
|
5760
|
-
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.";
|
|
5761
|
-
failedAgents.push({
|
|
5762
|
-
agentId: agent.id,
|
|
5763
|
-
code: error instanceof OwneyError ? error.code : void 0,
|
|
5764
|
-
message,
|
|
5765
|
-
...error instanceof OwneyError && error.details ? { details: error.details } : {}
|
|
5766
|
-
});
|
|
5767
3387
|
if (firstError === null) {
|
|
5768
3388
|
firstError = error;
|
|
5769
3389
|
} else {
|
|
5770
3390
|
console.error(`activateAgent(${agent.id}) failed:`, error);
|
|
5771
3391
|
}
|
|
5772
|
-
break;
|
|
5773
3392
|
}
|
|
5774
3393
|
}
|
|
5775
|
-
if (firstError
|
|
5776
|
-
if (activatedAgentIds.length === 0) throw firstError;
|
|
5777
|
-
const failedAgentIds = failedAgents.map(({ agentId }) => agentId);
|
|
5778
|
-
const activeNames = activatedAgentIds.map(this.formatAgentName).join(", ");
|
|
5779
|
-
const failureMessages = failedAgents.map(
|
|
5780
|
-
({ agentId, message }) => `${this.formatAgentName(agentId)} activation failed: ${message}`
|
|
5781
|
-
).join(" ");
|
|
5782
|
-
throw new OwneyError(
|
|
5783
|
-
"AGENT_ACTIVATION_PARTIAL_FAILURE",
|
|
5784
|
-
`${activeNames} activated. ${failureMessages}`,
|
|
5785
|
-
{ activatedAgentIds, failedAgentIds, failures: failedAgents }
|
|
5786
|
-
);
|
|
3394
|
+
if (firstError !== null) throw firstError;
|
|
5787
3395
|
}
|
|
5788
3396
|
/**
|
|
5789
3397
|
* Deposit into a specific agent, or distribute across all eligible agents.
|
|
@@ -5793,8 +3401,7 @@ var OwneySDK = class {
|
|
|
5793
3401
|
* @param options.asset - Asset symbol to deposit (e.g. "USDC")
|
|
5794
3402
|
* @param options.depositCallback - Callback that performs the token transfer and returns a tx hash.
|
|
5795
3403
|
* When agentId is omitted, this callback is invoked once per eligible agent with that agent's
|
|
5796
|
-
* split amount and smart wallet address
|
|
5797
|
-
* all shares into one signature; custom callbacks still run once per agent.
|
|
3404
|
+
* split amount and smart wallet address — expect multiple wallet prompts.
|
|
5798
3405
|
* @param options.agentId - Optional explicit target. Otherwise split equally,
|
|
5799
3406
|
* or fund remaining agents when a recovery deposit cannot meet every minimum.
|
|
5800
3407
|
* @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
|
|
@@ -5879,40 +3486,6 @@ var OwneySDK = class {
|
|
|
5879
3486
|
}
|
|
5880
3487
|
);
|
|
5881
3488
|
}
|
|
5882
|
-
const batchTransfer = getDepositBatchTransfer(effectiveCallback);
|
|
5883
|
-
if (!depositCallback && batchTransfer) {
|
|
5884
|
-
return runAgentDepositBatch(
|
|
5885
|
-
chainId,
|
|
5886
|
-
agentAmounts.map(({ agent, amount: amount2 }) => ({
|
|
5887
|
-
id: agent.id,
|
|
5888
|
-
amount: amount2,
|
|
5889
|
-
run: (callback) => withFailureReporting(
|
|
5890
|
-
this.apiKey,
|
|
5891
|
-
agent.id,
|
|
5892
|
-
() => agent.deposit(state, chainId, amount2, asset, callback),
|
|
5893
|
-
this.routingApiBaseUrl
|
|
5894
|
-
)
|
|
5895
|
-
})),
|
|
5896
|
-
async (cid, transfers) => {
|
|
5897
|
-
try {
|
|
5898
|
-
return await batchTransfer(cid, transfers);
|
|
5899
|
-
} catch (error) {
|
|
5900
|
-
if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
|
|
5901
|
-
throw error;
|
|
5902
|
-
const requiredAmount = transfers.reduce(
|
|
5903
|
-
(sum, transfer) => sum + BigInt(transfer.amount),
|
|
5904
|
-
0n
|
|
5905
|
-
);
|
|
5906
|
-
await this.approvePermit2(
|
|
5907
|
-
asset,
|
|
5908
|
-
requiredAmount,
|
|
5909
|
-
cid
|
|
5910
|
-
);
|
|
5911
|
-
return batchTransfer(cid, transfers);
|
|
5912
|
-
}
|
|
5913
|
-
}
|
|
5914
|
-
);
|
|
5915
|
-
}
|
|
5916
3489
|
const agentResults = {};
|
|
5917
3490
|
for (const [
|
|
5918
3491
|
index,
|
|
@@ -5950,12 +3523,10 @@ var OwneySDK = class {
|
|
|
5950
3523
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
5951
3524
|
* two independent auto-recovery mechanisms:
|
|
5952
3525
|
*
|
|
5953
|
-
* 1. Missing Permit2 allowance
|
|
5954
|
-
*
|
|
5955
|
-
*
|
|
5956
|
-
*
|
|
5957
|
-
* its sponsored deposit and never reaches this branch. Other tokens send
|
|
5958
|
-
* the one-time user-paid Permit2 approval via `approvePermit2()` and
|
|
3526
|
+
* 1. Missing Permit2 allowance: when the app did not supply its own
|
|
3527
|
+
* callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
|
|
3528
|
+
* WETH deposit, this is the wallet's first gasless WETH deposit. We send
|
|
3529
|
+
* the one-time (user-paid) Permit2 approval via `approvePermit2()` and
|
|
5959
3530
|
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
5960
3531
|
* per call so a wallet/agent that keeps reporting the allowance as
|
|
5961
3532
|
* missing can't loop forever. If `approvePermit2()` itself throws (e.g.
|
|
@@ -5991,16 +3562,12 @@ var OwneySDK = class {
|
|
|
5991
3562
|
try {
|
|
5992
3563
|
return await attempt(effectiveCallback);
|
|
5993
3564
|
} catch (error) {
|
|
5994
|
-
if (!approvalAttempted && appCallback === void 0 &&
|
|
3565
|
+
if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
5995
3566
|
approvalAttempted = true;
|
|
5996
3567
|
console.warn(
|
|
5997
|
-
"[owney-sdk] First
|
|
5998
|
-
);
|
|
5999
|
-
await this.approvePermit2(
|
|
6000
|
-
asset,
|
|
6001
|
-
BigInt(amount),
|
|
6002
|
-
chainId
|
|
3568
|
+
"[owney-sdk] First WETH deposit: sending one-time Permit2 approval..."
|
|
6003
3569
|
);
|
|
3570
|
+
await this.approvePermit2();
|
|
6004
3571
|
continue;
|
|
6005
3572
|
}
|
|
6006
3573
|
if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
|
|
@@ -6038,10 +3605,10 @@ var OwneySDK = class {
|
|
|
6038
3605
|
agent,
|
|
6039
3606
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
6040
3607
|
}));
|
|
6041
|
-
const
|
|
3608
|
+
const valid = splits.filter(
|
|
6042
3609
|
(s) => s.amount > 0n && (exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset))
|
|
6043
3610
|
);
|
|
6044
|
-
if (
|
|
3611
|
+
if (valid.length === agents.length) {
|
|
6045
3612
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
6046
3613
|
}
|
|
6047
3614
|
const targets = agentsRequiringActivation.length > 0 ? agentsRequiringActivation : agents;
|
|
@@ -6060,11 +3627,6 @@ var OwneySDK = class {
|
|
|
6060
3627
|
)
|
|
6061
3628
|
}));
|
|
6062
3629
|
}
|
|
6063
|
-
formatAgentName(agentId) {
|
|
6064
|
-
if (agentId === "zyfai") return "Zyfai";
|
|
6065
|
-
if (agentId === "yieldseeker") return "Yieldseeker";
|
|
6066
|
-
return agentId;
|
|
6067
|
-
}
|
|
6068
3630
|
async validateMinDepositAmount(agent, state, chainId, asset, amount) {
|
|
6069
3631
|
const minDepositAmount = this.getMinDepositAmount(agent, chainId, asset);
|
|
6070
3632
|
const parsedAmount = BigInt(amount);
|
|
@@ -6096,12 +3658,12 @@ var OwneySDK = class {
|
|
|
6096
3658
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
6097
3659
|
);
|
|
6098
3660
|
const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
|
|
6099
|
-
const
|
|
3661
|
+
const position = (balance.positions ?? []).find((p) => {
|
|
6100
3662
|
const positionChain = p.chain.trim().toUpperCase();
|
|
6101
3663
|
const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
|
|
6102
3664
|
return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
|
|
6103
3665
|
});
|
|
6104
|
-
return !!token && Number(token.amount) > 0 || !!
|
|
3666
|
+
return !!token && Number(token.amount) > 0 || !!position;
|
|
6105
3667
|
} catch (error) {
|
|
6106
3668
|
if (requireReliableRead) {
|
|
6107
3669
|
throw new OwneyError(
|
|
@@ -6146,32 +3708,6 @@ var OwneySDK = class {
|
|
|
6146
3708
|
}
|
|
6147
3709
|
return eligible;
|
|
6148
3710
|
}
|
|
6149
|
-
/**
|
|
6150
|
-
* Run owner-approved withdrawals before relayer-only withdrawals. Wallet
|
|
6151
|
-
* approval is the only point at which the user can cancel the aggregate
|
|
6152
|
-
* operation, so no relayer leg should commit before it has completed.
|
|
6153
|
-
*/
|
|
6154
|
-
orderAgentsForWithdrawal(agents) {
|
|
6155
|
-
return agents.map((agent, index) => ({ agent, index })).sort((left, right) => {
|
|
6156
|
-
const approvalOrder = Number(Boolean(right.agent.withdrawalRequiresWalletApproval)) - Number(Boolean(left.agent.withdrawalRequiresWalletApproval));
|
|
6157
|
-
return approvalOrder || left.index - right.index;
|
|
6158
|
-
}).map(({ agent }) => agent);
|
|
6159
|
-
}
|
|
6160
|
-
isUserRejectedWithdrawal(error) {
|
|
6161
|
-
let current = error;
|
|
6162
|
-
const seen = /* @__PURE__ */ new Set();
|
|
6163
|
-
while (current && typeof current === "object" && !seen.has(current)) {
|
|
6164
|
-
seen.add(current);
|
|
6165
|
-
const candidate = current;
|
|
6166
|
-
if (candidate.code === 4001 || candidate.code === "4001") return true;
|
|
6167
|
-
const message = [candidate.message, candidate.shortMessage].filter((value) => typeof value === "string").join(" ");
|
|
6168
|
-
if (/user (?:rejected|denied)|rejected by user/i.test(message)) {
|
|
6169
|
-
return true;
|
|
6170
|
-
}
|
|
6171
|
-
current = candidate.cause;
|
|
6172
|
-
}
|
|
6173
|
-
return false;
|
|
6174
|
-
}
|
|
6175
3711
|
// --- Fund operations ---
|
|
6176
3712
|
/**
|
|
6177
3713
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -6183,14 +3719,7 @@ var OwneySDK = class {
|
|
|
6183
3719
|
* @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
|
|
6184
3720
|
*/
|
|
6185
3721
|
async withdraw(options) {
|
|
6186
|
-
const { asset, amount, agentId
|
|
6187
|
-
const notifyAgentResult = (id, result) => {
|
|
6188
|
-
try {
|
|
6189
|
-
onAgentResult?.(id, result);
|
|
6190
|
-
} catch (error) {
|
|
6191
|
-
console.warn("Withdrawal result observer failed:", error);
|
|
6192
|
-
}
|
|
6193
|
-
};
|
|
3722
|
+
const { asset, amount, agentId } = options;
|
|
6194
3723
|
const state = this.requireState();
|
|
6195
3724
|
const chainId = this.requireChainId();
|
|
6196
3725
|
const token = asset;
|
|
@@ -6206,26 +3735,21 @@ var OwneySDK = class {
|
|
|
6206
3735
|
if (agentId) {
|
|
6207
3736
|
const agent = this.getAgent(agentId);
|
|
6208
3737
|
this.validateAssetSupport(agent, chainId, asset);
|
|
6209
|
-
|
|
3738
|
+
return withFailureReporting(
|
|
6210
3739
|
this.apiKey,
|
|
6211
3740
|
agent.id,
|
|
6212
3741
|
() => agent.withdraw(state, chainId, token, amount),
|
|
6213
3742
|
this.routingApiBaseUrl
|
|
6214
3743
|
);
|
|
6215
|
-
notifyAgentResult(agent.id, result);
|
|
6216
|
-
return result;
|
|
6217
3744
|
}
|
|
6218
3745
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
6219
|
-
const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
|
|
6220
3746
|
if (!amount) {
|
|
6221
3747
|
const results2 = {};
|
|
6222
3748
|
const agentErrors2 = {};
|
|
6223
|
-
for (const agent of
|
|
3749
|
+
for (const agent of eligibleAgents) {
|
|
6224
3750
|
try {
|
|
6225
3751
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
6226
|
-
notifyAgentResult(agent.id, results2[agent.id]);
|
|
6227
3752
|
} catch (err) {
|
|
6228
|
-
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
6229
3753
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
6230
3754
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
6231
3755
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -6254,10 +3778,6 @@ var OwneySDK = class {
|
|
|
6254
3778
|
}
|
|
6255
3779
|
const requested = BigInt(amount);
|
|
6256
3780
|
const aggregated = await this.getBalances();
|
|
6257
|
-
const unavailableAgents = eligibleAgents.filter(
|
|
6258
|
-
(agent) => !(agent.id in aggregated.agentBalances)
|
|
6259
|
-
);
|
|
6260
|
-
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
6261
3781
|
const balances = projectAgentBalancesForAsset(
|
|
6262
3782
|
eligibleAgents,
|
|
6263
3783
|
aggregated.agentBalances,
|
|
@@ -6266,18 +3786,7 @@ var OwneySDK = class {
|
|
|
6266
3786
|
assetInfo.decimals
|
|
6267
3787
|
);
|
|
6268
3788
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
6269
|
-
if (totalAvailable
|
|
6270
|
-
throw new OwneyError(
|
|
6271
|
-
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
6272
|
-
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
6273
|
-
{
|
|
6274
|
-
asset,
|
|
6275
|
-
unavailableAgents: unavailableAgentIds,
|
|
6276
|
-
agentErrors: aggregated.agentErrors
|
|
6277
|
-
}
|
|
6278
|
-
);
|
|
6279
|
-
}
|
|
6280
|
-
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3789
|
+
if (totalAvailable < requested) {
|
|
6281
3790
|
throw new OwneyError(
|
|
6282
3791
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
6283
3792
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -6288,7 +3797,6 @@ var OwneySDK = class {
|
|
|
6288
3797
|
}
|
|
6289
3798
|
);
|
|
6290
3799
|
}
|
|
6291
|
-
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
6292
3800
|
const disabledBalances = balances.filter(
|
|
6293
3801
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
6294
3802
|
);
|
|
@@ -6297,7 +3805,7 @@ var OwneySDK = class {
|
|
|
6297
3805
|
);
|
|
6298
3806
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
6299
3807
|
disabledBalances,
|
|
6300
|
-
|
|
3808
|
+
requested
|
|
6301
3809
|
);
|
|
6302
3810
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
6303
3811
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -6306,13 +3814,10 @@ var OwneySDK = class {
|
|
|
6306
3814
|
planned: 0n
|
|
6307
3815
|
}));
|
|
6308
3816
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
6309
|
-
const orderedPlans = this.orderAgentsForWithdrawal(plans.map((p) => p.agent)).map((agent) => plans.find((plan) => plan.agent === agent));
|
|
6310
3817
|
const results = {};
|
|
6311
|
-
const agentErrors = {
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
for (let i = 0; i < orderedPlans.length; i++) {
|
|
6315
|
-
const p = orderedPlans[i];
|
|
3818
|
+
const agentErrors = {};
|
|
3819
|
+
for (let i = 0; i < plans.length; i++) {
|
|
3820
|
+
const p = plans[i];
|
|
6316
3821
|
if (p.planned === 0n) continue;
|
|
6317
3822
|
try {
|
|
6318
3823
|
results[p.agent.id] = await p.agent.withdraw(
|
|
@@ -6321,9 +3826,7 @@ var OwneySDK = class {
|
|
|
6321
3826
|
token,
|
|
6322
3827
|
p.planned.toString()
|
|
6323
3828
|
);
|
|
6324
|
-
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
6325
3829
|
} catch (err) {
|
|
6326
|
-
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
6327
3830
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
6328
3831
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
6329
3832
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -6335,7 +3838,7 @@ var OwneySDK = class {
|
|
|
6335
3838
|
);
|
|
6336
3839
|
const failedAmount = p.planned;
|
|
6337
3840
|
p.planned = 0n;
|
|
6338
|
-
redistributeShare(
|
|
3841
|
+
redistributeShare(plans, i, failedAmount);
|
|
6339
3842
|
}
|
|
6340
3843
|
}
|
|
6341
3844
|
if (Object.keys(results).length === 0) {
|
|
@@ -6359,8 +3862,7 @@ var OwneySDK = class {
|
|
|
6359
3862
|
requested: amount,
|
|
6360
3863
|
withdrawn: withdrawn.toString(),
|
|
6361
3864
|
partialResults: results,
|
|
6362
|
-
agentErrors
|
|
6363
|
-
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3865
|
+
agentErrors
|
|
6364
3866
|
}
|
|
6365
3867
|
);
|
|
6366
3868
|
}
|
|
@@ -6378,10 +3880,7 @@ var OwneySDK = class {
|
|
|
6378
3880
|
if (agentId) {
|
|
6379
3881
|
const agent = this.getAgent(agentId);
|
|
6380
3882
|
const result = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
6381
|
-
return
|
|
6382
|
-
...result,
|
|
6383
|
-
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
6384
|
-
};
|
|
3883
|
+
return result;
|
|
6385
3884
|
}
|
|
6386
3885
|
let totalBalance = 0;
|
|
6387
3886
|
const results = {};
|
|
@@ -6389,13 +3888,7 @@ var OwneySDK = class {
|
|
|
6389
3888
|
const balanceResults = await Promise.allSettled(
|
|
6390
3889
|
entries.map(async ([id, agent]) => {
|
|
6391
3890
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
6392
|
-
return [
|
|
6393
|
-
id,
|
|
6394
|
-
{
|
|
6395
|
-
...b,
|
|
6396
|
-
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
6397
|
-
}
|
|
6398
|
-
];
|
|
3891
|
+
return [id, b];
|
|
6399
3892
|
})
|
|
6400
3893
|
);
|
|
6401
3894
|
let successCount = 0;
|
|
@@ -6417,7 +3910,6 @@ var OwneySDK = class {
|
|
|
6417
3910
|
const retryDelay = rateLimitDelay(reason);
|
|
6418
3911
|
if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
|
|
6419
3912
|
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
6420
|
-
console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
|
|
6421
3913
|
}
|
|
6422
3914
|
if (successCount === 0) {
|
|
6423
3915
|
throw new OwneyError(
|
|
@@ -6596,10 +4088,7 @@ var OwneySDK = class {
|
|
|
6596
4088
|
Promise.all(
|
|
6597
4089
|
entries.map(async ([id, agent]) => {
|
|
6598
4090
|
const b = await this.readAgent(agent, "balances", () => agent.getBalances(state, chainId));
|
|
6599
|
-
return [
|
|
6600
|
-
id,
|
|
6601
|
-
balanceForApyScope(b, chainId, tokenSymbol)
|
|
6602
|
-
];
|
|
4091
|
+
return [id, balanceForApyScope(b, chainId, tokenSymbol)];
|
|
6603
4092
|
})
|
|
6604
4093
|
)
|
|
6605
4094
|
]);
|
|
@@ -6760,95 +4249,51 @@ var OwneySDK = class {
|
|
|
6760
4249
|
return pending;
|
|
6761
4250
|
}
|
|
6762
4251
|
/**
|
|
6763
|
-
*
|
|
6764
|
-
*
|
|
6765
|
-
*
|
|
6766
|
-
*
|
|
6767
|
-
*
|
|
6768
|
-
*
|
|
6769
|
-
* @param requiredAmount Raw base-unit amount the pending deposit must cover.
|
|
6770
|
-
* @param expectedChainId Chain captured by the deposit that requested approval.
|
|
4252
|
+
* One-time, user-paid approval of Permit2 on the sponsored WETH token for
|
|
4253
|
+
* the active chain. Required once per wallet per chain before gasless WETH
|
|
4254
|
+
* deposits; afterwards deposit() is signature-only. Resolves only after the
|
|
4255
|
+
* approval transaction is mined (1 confirmation), so a subsequent deposit()
|
|
4256
|
+
* will see the new allowance; throws if the transaction reverted.
|
|
6771
4257
|
* @returns the approval transaction hash.
|
|
6772
4258
|
*/
|
|
6773
|
-
async approvePermit2(asset = "WETH"
|
|
4259
|
+
async approvePermit2(asset = "WETH") {
|
|
4260
|
+
void asset;
|
|
6774
4261
|
const state = this.requireState();
|
|
6775
|
-
const chainId =
|
|
6776
|
-
this.
|
|
6777
|
-
const token =
|
|
4262
|
+
const chainId = this.requireChainId();
|
|
4263
|
+
this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
|
|
4264
|
+
const token = SPONSORED_WETH_BY_CHAIN[chainId];
|
|
6778
4265
|
if (!token) {
|
|
6779
4266
|
throw new OwneyError(
|
|
6780
4267
|
"CHAIN_UNSUPPORTED",
|
|
6781
|
-
`No sponsored
|
|
4268
|
+
`No sponsored WETH on chain ${chainId}`
|
|
6782
4269
|
);
|
|
6783
4270
|
}
|
|
6784
4271
|
const provider = this.requireConnectedProvider();
|
|
6785
|
-
const
|
|
6786
|
-
chain: VIEM_CHAIN2[chainId],
|
|
6787
|
-
transport: (0, import_viem12.custom)(provider)
|
|
6788
|
-
});
|
|
6789
|
-
const publicClient = this.getPaidRpcClient(chainId);
|
|
6790
|
-
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
6791
|
-
const wallet = (0, import_viem12.createWalletClient)({
|
|
4272
|
+
const wallet = (0, import_viem6.createWalletClient)({
|
|
6792
4273
|
account: state.walletAddress,
|
|
6793
4274
|
chain: VIEM_CHAIN2[chainId],
|
|
6794
|
-
transport: (0,
|
|
4275
|
+
transport: (0, import_viem6.custom)(provider)
|
|
6795
4276
|
});
|
|
6796
|
-
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
6797
4277
|
const hash = await wallet.writeContract({
|
|
6798
4278
|
address: token,
|
|
6799
4279
|
abi: ERC20_ALLOWANCE_ABI,
|
|
6800
4280
|
functionName: "approve",
|
|
6801
|
-
args: [PERMIT2_ADDRESS,
|
|
4281
|
+
args: [PERMIT2_ADDRESS, MAX_UINT256],
|
|
6802
4282
|
account: state.walletAddress,
|
|
6803
4283
|
chain: VIEM_CHAIN2[chainId]
|
|
6804
4284
|
});
|
|
6805
|
-
const
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
);
|
|
4285
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
4286
|
+
chain: VIEM_CHAIN2[chainId],
|
|
4287
|
+
transport: (0, import_viem6.custom)(provider)
|
|
4288
|
+
});
|
|
4289
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
4290
|
+
hash,
|
|
4291
|
+
confirmations: 1
|
|
4292
|
+
});
|
|
6813
4293
|
if (receipt.status !== "success") {
|
|
6814
4294
|
throw new Error(`Permit2 approval reverted (tx ${hash})`);
|
|
6815
4295
|
}
|
|
6816
|
-
|
|
6817
|
-
let verificationError;
|
|
6818
|
-
for (let attempt = 0; attempt < PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS; attempt += 1) {
|
|
6819
|
-
try {
|
|
6820
|
-
observedAllowance = await readPermit2Allowance(
|
|
6821
|
-
publicClient,
|
|
6822
|
-
token,
|
|
6823
|
-
state.walletAddress,
|
|
6824
|
-
attempt === 0 ? receipt.blockNumber : void 0
|
|
6825
|
-
);
|
|
6826
|
-
verificationError = void 0;
|
|
6827
|
-
if (observedAllowance >= requiredAmount) return hash;
|
|
6828
|
-
} catch (error) {
|
|
6829
|
-
verificationError = error;
|
|
6830
|
-
}
|
|
6831
|
-
if (attempt + 1 < PERMIT2_ALLOWANCE_VERIFY_ATTEMPTS) {
|
|
6832
|
-
await new Promise(
|
|
6833
|
-
(resolve) => setTimeout(resolve, PERMIT2_ALLOWANCE_VERIFY_DELAY_MS)
|
|
6834
|
-
);
|
|
6835
|
-
}
|
|
6836
|
-
}
|
|
6837
|
-
throw new OwneyError(
|
|
6838
|
-
"PERMIT2_APPROVAL_REQUIRED",
|
|
6839
|
-
"Permit2 approval was confirmed, but the required token allowance was not observable.",
|
|
6840
|
-
{
|
|
6841
|
-
approvalConfirmed: true,
|
|
6842
|
-
approvalTxHash: hash,
|
|
6843
|
-
owner: state.walletAddress,
|
|
6844
|
-
token,
|
|
6845
|
-
spender: PERMIT2_ADDRESS,
|
|
6846
|
-
chainId,
|
|
6847
|
-
requiredAmount: requiredAmount.toString(),
|
|
6848
|
-
observedAllowance: observedAllowance.toString(),
|
|
6849
|
-
...verificationError instanceof Error ? { verificationError: verificationError.message } : {}
|
|
6850
|
-
}
|
|
6851
|
-
);
|
|
4296
|
+
return hash;
|
|
6852
4297
|
}
|
|
6853
4298
|
// --- Discovery (no wallet required) ---
|
|
6854
4299
|
/**
|
|
@@ -6876,9 +4321,7 @@ var OwneySDK = class {
|
|
|
6876
4321
|
return this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
6877
4322
|
}
|
|
6878
4323
|
const results = {};
|
|
6879
|
-
const agentEntries = [...this.agents.entries()]
|
|
6880
|
-
([id]) => !this.isAgentDisabled(id)
|
|
6881
|
-
);
|
|
4324
|
+
const agentEntries = [...this.agents.entries()];
|
|
6882
4325
|
const apyResults = await Promise.all(
|
|
6883
4326
|
agentEntries.map(async ([id, agent]) => {
|
|
6884
4327
|
const apy = await this.readAgent(agent, "agentApy", () => agent.getAgentApy(days, agentOptions), { days, ...agentOptions });
|
|
@@ -6953,13 +4396,13 @@ var OwneySDK = class {
|
|
|
6953
4396
|
};
|
|
6954
4397
|
|
|
6955
4398
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
6956
|
-
var
|
|
6957
|
-
var
|
|
4399
|
+
var import_viem7 = require("viem");
|
|
4400
|
+
var import_siwe = require("siwe");
|
|
6958
4401
|
var import_sdk2 = require("@zyfai/sdk");
|
|
6959
4402
|
|
|
6960
4403
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
6961
|
-
var
|
|
6962
|
-
var
|
|
4404
|
+
var KEY_PREFIX2 = "owney.siwx.session";
|
|
4405
|
+
var storage2 = () => {
|
|
6963
4406
|
if (typeof window === "undefined") return null;
|
|
6964
4407
|
try {
|
|
6965
4408
|
return window.localStorage;
|
|
@@ -6967,8 +4410,8 @@ var storage4 = () => {
|
|
|
6967
4410
|
return null;
|
|
6968
4411
|
}
|
|
6969
4412
|
};
|
|
6970
|
-
var
|
|
6971
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
4413
|
+
var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
|
|
4414
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
|
|
6972
4415
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
6973
4416
|
var readLegacySiwxSession = (store, address) => {
|
|
6974
4417
|
if (!store) return null;
|
|
@@ -6999,17 +4442,17 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
6999
4442
|
};
|
|
7000
4443
|
var readSiwxSession = (address, chainId) => {
|
|
7001
4444
|
if (typeof window === "undefined") return null;
|
|
7002
|
-
const key2 =
|
|
7003
|
-
const store =
|
|
7004
|
-
let
|
|
4445
|
+
const key2 = buildKey2(address);
|
|
4446
|
+
const store = storage2();
|
|
4447
|
+
let raw = null;
|
|
7005
4448
|
try {
|
|
7006
|
-
|
|
4449
|
+
raw = store?.getItem(key2) ?? null;
|
|
7007
4450
|
} catch {
|
|
7008
|
-
|
|
4451
|
+
raw = null;
|
|
7009
4452
|
}
|
|
7010
|
-
if (
|
|
4453
|
+
if (raw) {
|
|
7011
4454
|
try {
|
|
7012
|
-
return JSON.parse(
|
|
4455
|
+
return JSON.parse(raw);
|
|
7013
4456
|
} catch {
|
|
7014
4457
|
memorySiwxSessions.delete(key2);
|
|
7015
4458
|
try {
|
|
@@ -7028,18 +4471,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
7028
4471
|
};
|
|
7029
4472
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
7030
4473
|
if (typeof window === "undefined") return;
|
|
7031
|
-
const key2 =
|
|
4474
|
+
const key2 = buildKey2(address);
|
|
7032
4475
|
memorySiwxSessions.set(key2, session);
|
|
7033
|
-
const store =
|
|
4476
|
+
const store = storage2();
|
|
7034
4477
|
try {
|
|
7035
4478
|
store?.setItem(key2, JSON.stringify(session));
|
|
7036
4479
|
} catch {
|
|
7037
4480
|
}
|
|
7038
4481
|
};
|
|
7039
4482
|
var clearSiwxSession = (address, _chainId) => {
|
|
7040
|
-
const key2 =
|
|
4483
|
+
const key2 = buildKey2(address);
|
|
7041
4484
|
memorySiwxSessions.delete(key2);
|
|
7042
|
-
const store =
|
|
4485
|
+
const store = storage2();
|
|
7043
4486
|
try {
|
|
7044
4487
|
store?.removeItem(key2);
|
|
7045
4488
|
} catch {
|
|
@@ -7079,8 +4522,8 @@ function buildSIWXConfig(deps) {
|
|
|
7079
4522
|
statement: STATEMENT,
|
|
7080
4523
|
issuedAt,
|
|
7081
4524
|
toString() {
|
|
7082
|
-
return new
|
|
7083
|
-
address: (0,
|
|
4525
|
+
return new import_siwe.SiweMessage({
|
|
4526
|
+
address: (0, import_viem7.getAddress)(accountAddress),
|
|
7084
4527
|
chainId: numericChainId(chainId),
|
|
7085
4528
|
domain,
|
|
7086
4529
|
uri,
|
|
@@ -7122,7 +4565,7 @@ function buildSIWXConfig(deps) {
|
|
|
7122
4565
|
const persistSession = async (session) => {
|
|
7123
4566
|
const address = session.data.accountAddress;
|
|
7124
4567
|
const id = numericChainId(session.data.chainId);
|
|
7125
|
-
const message = new
|
|
4568
|
+
const message = new import_siwe.SiweMessage(session.message);
|
|
7126
4569
|
const login = await post("/auth/login", {
|
|
7127
4570
|
message,
|
|
7128
4571
|
signature: session.signature,
|
|
@@ -7172,7 +4615,6 @@ function createOwneySIWX(config) {
|
|
|
7172
4615
|
NotConnectedError,
|
|
7173
4616
|
OwneyError,
|
|
7174
4617
|
OwneySDK,
|
|
7175
|
-
YieldseekerAgent,
|
|
7176
4618
|
createOwneySIWX,
|
|
7177
4619
|
setOwneyDebug
|
|
7178
4620
|
});
|