@owney/sdk 0.7.16-beta.4 → 0.7.16-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/chunk-5LU2SHO7.js +50 -0
- package/dist/chunk-DBRXNDSB.js +87 -0
- package/dist/chunk-GMNQBWUB.js +55 -0
- package/dist/chunk-QA7JUSDB.js +92 -0
- package/dist/chunk-V2LNMTE6.js +222 -0
- package/dist/chunk-VHQ4VXY7.js +39 -0
- package/dist/index.cjs +3350 -3881
- package/dist/index.d.cts +33 -132
- package/dist/index.d.ts +33 -132
- package/dist/index.js +647 -2428
- package/dist/routing-api-KAILDWMD.js +11 -0
- package/dist/surfliquid.agent-5CWKYMRL.js +532 -0
- package/dist/surfliquid.smart-account-L3AWN52C.js +125 -0
- package/dist/surfliquid.sponsorship-BFAL2O3B.js +14 -0
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
debugLog,
|
|
3
|
+
fetchAgentKeys,
|
|
4
|
+
fetchOrgAgentConfig,
|
|
5
|
+
setOwneyDebug
|
|
6
|
+
} from "./chunk-QA7JUSDB.js";
|
|
7
|
+
import {
|
|
8
|
+
buildTransferWithAuthorizationTypedData,
|
|
9
|
+
randomAuthNonce,
|
|
10
|
+
readTokenMeta
|
|
11
|
+
} from "./chunk-5LU2SHO7.js";
|
|
12
|
+
import {
|
|
13
|
+
ERC20_ALLOWANCE_ABI,
|
|
14
|
+
MAX_UINT256,
|
|
15
|
+
PERMIT2_ADDRESS,
|
|
16
|
+
buildPermitTransferFromTypedData,
|
|
17
|
+
randomPermit2Nonce,
|
|
18
|
+
readErc20Balance,
|
|
19
|
+
readPermit2Allowance
|
|
20
|
+
} from "./chunk-DBRXNDSB.js";
|
|
21
|
+
import {
|
|
22
|
+
AgentChainIncompatibleError,
|
|
23
|
+
AgentNotFoundError,
|
|
24
|
+
NotConnectedError,
|
|
25
|
+
OwneyError
|
|
26
|
+
} from "./chunk-GMNQBWUB.js";
|
|
27
|
+
|
|
1
28
|
// src/agents/zyfai/zyfai.agent.ts
|
|
2
29
|
import { SMART_SESSIONS_VALIDATOR, ZyfaiSDK } from "@zyfai/sdk";
|
|
3
30
|
import { createPublicClient, http, parseAbi } from "viem";
|
|
@@ -50,55 +77,6 @@ var SupportedAssets = [
|
|
|
50
77
|
}
|
|
51
78
|
];
|
|
52
79
|
|
|
53
|
-
// src/errors.ts
|
|
54
|
-
var OwneyError = class extends Error {
|
|
55
|
-
code;
|
|
56
|
-
details;
|
|
57
|
-
agentId;
|
|
58
|
-
constructor(code, message, details, agentId) {
|
|
59
|
-
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
60
|
-
super(`${prefix} ${message}`);
|
|
61
|
-
this.name = "OwneyError";
|
|
62
|
-
this.code = code;
|
|
63
|
-
this.details = details;
|
|
64
|
-
this.agentId = agentId;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
var AgentNotFoundError = class extends OwneyError {
|
|
68
|
-
constructor(agentId, available) {
|
|
69
|
-
super(
|
|
70
|
-
"AGENT_NOT_FOUND",
|
|
71
|
-
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
72
|
-
{ agentId, available },
|
|
73
|
-
agentId
|
|
74
|
-
);
|
|
75
|
-
this.name = "AgentNotFoundError";
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
var NotConnectedError = class extends OwneyError {
|
|
79
|
-
constructor() {
|
|
80
|
-
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
81
|
-
this.name = "NotConnectedError";
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
var AgentChainIncompatibleError = class extends OwneyError {
|
|
85
|
-
incompatibleAgents;
|
|
86
|
-
connectedChainId;
|
|
87
|
-
constructor(incompatibleAgents, connectedChainId) {
|
|
88
|
-
const details = incompatibleAgents.map(
|
|
89
|
-
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
90
|
-
).join("; ");
|
|
91
|
-
super(
|
|
92
|
-
"AGENT_CHAIN_INCOMPATIBLE",
|
|
93
|
-
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
94
|
-
{ incompatibleAgents, connectedChainId }
|
|
95
|
-
);
|
|
96
|
-
this.name = "AgentChainIncompatibleError";
|
|
97
|
-
this.incompatibleAgents = incompatibleAgents;
|
|
98
|
-
this.connectedChainId = connectedChainId;
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
80
|
// src/lib/utils.ts
|
|
103
81
|
var isValidChainId = (chainId) => {
|
|
104
82
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
|
|
@@ -110,6 +88,12 @@ var isValidChainId = (chainId) => {
|
|
|
110
88
|
}
|
|
111
89
|
return chainId;
|
|
112
90
|
};
|
|
91
|
+
function isSameAsset(tokenSymbol, asset) {
|
|
92
|
+
const token = tokenSymbol.toLowerCase();
|
|
93
|
+
const target = asset.toLowerCase();
|
|
94
|
+
if (token === target) return true;
|
|
95
|
+
return target === "weth" && token === "eth" || target === "eth" && token === "weth";
|
|
96
|
+
}
|
|
113
97
|
function hexToDecimal(hex, decimals = 6) {
|
|
114
98
|
const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
|
|
115
99
|
const parsed = BigInt(normalized);
|
|
@@ -147,18 +131,18 @@ function tokenDecimals(symbol, explicit) {
|
|
|
147
131
|
return explicit;
|
|
148
132
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
149
133
|
}
|
|
150
|
-
function mapDeposit(
|
|
134
|
+
function mapDeposit(raw) {
|
|
151
135
|
return {
|
|
152
|
-
txHash:
|
|
153
|
-
smartWallet:
|
|
154
|
-
amount:
|
|
136
|
+
txHash: raw.txHash,
|
|
137
|
+
smartWallet: raw.smartWallet,
|
|
138
|
+
amount: raw.amount
|
|
155
139
|
};
|
|
156
140
|
}
|
|
157
|
-
function mapWithdraw(
|
|
141
|
+
function mapWithdraw(raw) {
|
|
158
142
|
return {
|
|
159
|
-
txHash:
|
|
160
|
-
type:
|
|
161
|
-
amount:
|
|
143
|
+
txHash: raw.txHash,
|
|
144
|
+
type: raw.type,
|
|
145
|
+
amount: raw.amount
|
|
162
146
|
};
|
|
163
147
|
}
|
|
164
148
|
var CHAIN_ID_TO_NAME = {
|
|
@@ -177,10 +161,10 @@ function resolveChainId(chain) {
|
|
|
177
161
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
178
162
|
return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
|
|
179
163
|
}
|
|
180
|
-
function mapPendingAllocations(
|
|
181
|
-
if (!Array.isArray(
|
|
164
|
+
function mapPendingAllocations(raw) {
|
|
165
|
+
if (!Array.isArray(raw)) return void 0;
|
|
182
166
|
const pending = [];
|
|
183
|
-
for (const entry of
|
|
167
|
+
for (const entry of raw) {
|
|
184
168
|
if (typeof entry !== "object" || entry === null) continue;
|
|
185
169
|
const e = entry;
|
|
186
170
|
if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
|
|
@@ -203,8 +187,8 @@ function mapPendingAllocations(raw2) {
|
|
|
203
187
|
}
|
|
204
188
|
return pending.length > 0 ? pending : void 0;
|
|
205
189
|
}
|
|
206
|
-
function mapBalances(
|
|
207
|
-
const portfolio =
|
|
190
|
+
function mapBalances(raw, _chainId, smartWallet) {
|
|
191
|
+
const portfolio = raw.portfolio;
|
|
208
192
|
const portfolioByChain = portfolio.portfolioByChain ?? {};
|
|
209
193
|
let totalBalance = 0;
|
|
210
194
|
const tokens = [];
|
|
@@ -249,7 +233,7 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
249
233
|
)
|
|
250
234
|
)
|
|
251
235
|
),
|
|
252
|
-
apy: p.pool_apy,
|
|
236
|
+
apy: netApy(p.pool_apy_withFee, p.pool_apy, "pool_apy_withFee"),
|
|
253
237
|
tvl: p.pool_tvl,
|
|
254
238
|
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
255
239
|
// structural read keeps it undefined today and auto-populates later.
|
|
@@ -273,8 +257,8 @@ function sumTokenValues(tokens) {
|
|
|
273
257
|
function sumTokenEarnings(tokens) {
|
|
274
258
|
return Object.values(tokens).reduce((sum, val) => sum + Number(val), 0);
|
|
275
259
|
}
|
|
276
|
-
function mapEarnings(
|
|
277
|
-
const totalEarningsByChain =
|
|
260
|
+
function mapEarnings(raw, smartWallet) {
|
|
261
|
+
const totalEarningsByChain = raw.data.totalEarningsByChainWithFee ?? raw.data.totalEarningsByChain ?? {};
|
|
278
262
|
const tokens = [];
|
|
279
263
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(
|
|
280
264
|
totalEarningsByChain
|
|
@@ -293,15 +277,15 @@ function mapEarnings(raw2, smartWallet) {
|
|
|
293
277
|
return {
|
|
294
278
|
smartWallet,
|
|
295
279
|
lifetimeEarnings: sumTokenEarnings(
|
|
296
|
-
|
|
280
|
+
raw.data.totalEarningsByTokenWithFee ?? raw.data.totalEarningsByToken
|
|
297
281
|
),
|
|
298
282
|
tokens
|
|
299
283
|
};
|
|
300
284
|
}
|
|
301
|
-
function mapWeightedApyByChain(
|
|
302
|
-
if (!
|
|
285
|
+
function mapWeightedApyByChain(raw) {
|
|
286
|
+
if (!raw) return void 0;
|
|
303
287
|
const out = {};
|
|
304
|
-
for (const [chainKey, tokenApy] of Object.entries(
|
|
288
|
+
for (const [chainKey, tokenApy] of Object.entries(raw)) {
|
|
305
289
|
const chainId = Number(chainKey);
|
|
306
290
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
307
291
|
const perAsset = {};
|
|
@@ -314,6 +298,19 @@ function mapWeightedApyByChain(raw2) {
|
|
|
314
298
|
}
|
|
315
299
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
316
300
|
}
|
|
301
|
+
var warnedGrossApyFallbacks = /* @__PURE__ */ new Set();
|
|
302
|
+
function netApy(net, gross, field) {
|
|
303
|
+
if (net !== void 0 && net !== null) return net;
|
|
304
|
+
if (gross === void 0 || gross === null) return void 0;
|
|
305
|
+
if (!warnedGrossApyFallbacks.has(field)) {
|
|
306
|
+
warnedGrossApyFallbacks.add(field);
|
|
307
|
+
console.warn(
|
|
308
|
+
`[owney] @zyfai/sdk omitted "${field}"; falling back to the gross APY, which does not deduct Zyfai's performance fee and so reads high.`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
debugLog("zyfai:apy", `gross fallback for ${field}`, { gross });
|
|
312
|
+
return gross;
|
|
313
|
+
}
|
|
317
314
|
function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
318
315
|
let weightedSum = 0;
|
|
319
316
|
let totalBalance = 0;
|
|
@@ -321,21 +318,22 @@ function rawPoolApyForChain(entry, chainId, tokenSymbol) {
|
|
|
321
318
|
if (p.chainId !== chainId) continue;
|
|
322
319
|
if (tokenSymbol && p.tokenSymbol !== tokenSymbol) continue;
|
|
323
320
|
const balance = p.balance ?? 0;
|
|
324
|
-
|
|
325
|
-
|
|
321
|
+
const apy = netApy(p.apy_withFee, p.apy, "apy_withFee");
|
|
322
|
+
if (balance <= 0 || typeof apy !== "number") continue;
|
|
323
|
+
weightedSum += apy * balance;
|
|
326
324
|
totalBalance += balance;
|
|
327
325
|
}
|
|
328
326
|
return totalBalance > 0 ? weightedSum / totalBalance : null;
|
|
329
327
|
}
|
|
330
|
-
function mapApyHistory(
|
|
331
|
-
const history = Object.entries(
|
|
328
|
+
function mapApyHistory(raw, chainId, tokenSymbol) {
|
|
329
|
+
const history = Object.entries(raw.history ?? {}).map(([date, entry]) => ({
|
|
332
330
|
date,
|
|
333
331
|
apy: rawPoolApyForChain(entry, chainId, tokenSymbol)
|
|
334
332
|
})).filter((p) => p.apy !== null).sort((a, b) => a.date.localeCompare(b.date));
|
|
335
333
|
return {
|
|
336
|
-
walletAddress:
|
|
337
|
-
weightedApyAfterFee:
|
|
338
|
-
apyByChainAndAsset: mapWeightedApyByChain(
|
|
334
|
+
walletAddress: raw.walletAddress,
|
|
335
|
+
weightedApyAfterFee: raw.weightedApyAfterFee ? sumTokenValues(raw.weightedApyAfterFee) : void 0,
|
|
336
|
+
apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain),
|
|
339
337
|
history
|
|
340
338
|
};
|
|
341
339
|
}
|
|
@@ -370,8 +368,12 @@ function mapEntries(rawEntries, chainId) {
|
|
|
370
368
|
const matched = positions.find(
|
|
371
369
|
(p) => p.protocol_name ? rawLog.newOpportunity.includes(`${p.protocol_name} (${p.pool})`) : false
|
|
372
370
|
);
|
|
373
|
-
oldApy = Number(
|
|
374
|
-
|
|
371
|
+
oldApy = Number(
|
|
372
|
+
netApy(rawLog.oldApy_withFee, rawLog.oldApy, "oldApy_withFee")
|
|
373
|
+
);
|
|
374
|
+
newApy = Number(
|
|
375
|
+
netApy(rawLog.newApy_withFee, rawLog.newApy, "newApy_withFee")
|
|
376
|
+
);
|
|
375
377
|
const from = splitOpportunity(rawLog.oldOpportunity);
|
|
376
378
|
const to = splitOpportunity(rawLog.newOpportunity);
|
|
377
379
|
rebalanceLog = [
|
|
@@ -427,23 +429,23 @@ function mapEntries(rawEntries, chainId) {
|
|
|
427
429
|
};
|
|
428
430
|
});
|
|
429
431
|
}
|
|
430
|
-
function mapUserProfile(
|
|
432
|
+
function mapUserProfile(raw, userAddress) {
|
|
431
433
|
return {
|
|
432
434
|
address: userAddress,
|
|
433
|
-
smartWallet:
|
|
434
|
-
chains:
|
|
435
|
-
strategy:
|
|
436
|
-
hasActiveSessionKey:
|
|
437
|
-
protocols:
|
|
438
|
-
splitting:
|
|
439
|
-
minSplits:
|
|
435
|
+
smartWallet: raw.smartWallet || "",
|
|
436
|
+
chains: raw.chains || [],
|
|
437
|
+
strategy: raw.strategy,
|
|
438
|
+
hasActiveSessionKey: raw.hasActiveSessionKey || false,
|
|
439
|
+
protocols: raw.protocols || [],
|
|
440
|
+
splitting: raw.splitting,
|
|
441
|
+
minSplits: raw.minSplits
|
|
440
442
|
};
|
|
441
443
|
}
|
|
442
|
-
function mapApyByStrategy(
|
|
444
|
+
function mapApyByStrategy(raw) {
|
|
443
445
|
const apyPerAsset = {};
|
|
444
446
|
let apySum = 0;
|
|
445
447
|
let apyCount = 0;
|
|
446
|
-
for (const entry of
|
|
448
|
+
for (const entry of raw.data) {
|
|
447
449
|
const supported = SupportedAssets.find(
|
|
448
450
|
(asset) => asset.chainId === entry.chain_id && asset.symbol === entry.token_symbol
|
|
449
451
|
);
|
|
@@ -451,7 +453,11 @@ function mapApyByStrategy(raw2) {
|
|
|
451
453
|
const chainKey = supported.chainId;
|
|
452
454
|
const symbolKey = supported.symbol;
|
|
453
455
|
const bucket = apyPerAsset[chainKey] ?? {};
|
|
454
|
-
const apy =
|
|
456
|
+
const apy = netApy(
|
|
457
|
+
entry.average_apy_withFee,
|
|
458
|
+
entry.average_apy,
|
|
459
|
+
"average_apy_withFee"
|
|
460
|
+
) ?? entry.average_apy;
|
|
455
461
|
bucket[symbolKey] = apy;
|
|
456
462
|
apyPerAsset[chainKey] = bucket;
|
|
457
463
|
apySum += apy;
|
|
@@ -610,15 +616,15 @@ var readSession = (address, _chainId) => {
|
|
|
610
616
|
if (typeof window === "undefined") return null;
|
|
611
617
|
const key2 = buildKey(address);
|
|
612
618
|
const store = storage();
|
|
613
|
-
let
|
|
619
|
+
let raw = null;
|
|
614
620
|
try {
|
|
615
|
-
|
|
621
|
+
raw = store?.getItem(key2) ?? null;
|
|
616
622
|
} catch {
|
|
617
|
-
|
|
623
|
+
raw = null;
|
|
618
624
|
}
|
|
619
|
-
if (
|
|
625
|
+
if (raw) {
|
|
620
626
|
try {
|
|
621
|
-
const parsed = JSON.parse(
|
|
627
|
+
const parsed = JSON.parse(raw);
|
|
622
628
|
if (isFreshSession(parsed)) return parsed;
|
|
623
629
|
} catch {
|
|
624
630
|
}
|
|
@@ -786,8 +792,8 @@ function buildChainsByProtocol(protocols, dashboardChainIds) {
|
|
|
786
792
|
}
|
|
787
793
|
return result;
|
|
788
794
|
}
|
|
789
|
-
function flattenAvailablePools(
|
|
790
|
-
const byChain =
|
|
795
|
+
function flattenAvailablePools(raw) {
|
|
796
|
+
const byChain = raw ?? {};
|
|
791
797
|
const names = [];
|
|
792
798
|
for (const byToken of Object.values(byChain ?? {})) {
|
|
793
799
|
for (const entry of Object.values(byToken ?? {})) {
|
|
@@ -811,23 +817,6 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
811
817
|
return !protocolListsEqual(current.protocols, desiredProtocols);
|
|
812
818
|
}
|
|
813
819
|
|
|
814
|
-
// src/lib/debug.ts
|
|
815
|
-
var configuredDebug = false;
|
|
816
|
-
function setOwneyDebug(enabled) {
|
|
817
|
-
configuredDebug = enabled;
|
|
818
|
-
}
|
|
819
|
-
function isOwneyDebug() {
|
|
820
|
-
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
821
|
-
}
|
|
822
|
-
function debugLog(scope, message, data) {
|
|
823
|
-
if (!isOwneyDebug()) return;
|
|
824
|
-
if (data === void 0) {
|
|
825
|
-
console.log(`[${scope}] ${message}`);
|
|
826
|
-
} else {
|
|
827
|
-
console.log(`[${scope}] ${message}`, data);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
820
|
// src/agents/zyfai/zyfai.agent.ts
|
|
832
821
|
var ERC7579_IS_MODULE_INSTALLED_ABI = parseAbi([
|
|
833
822
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
@@ -1298,8 +1287,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1298
1287
|
const poolResults = await Promise.all(
|
|
1299
1288
|
universe.map(async (protocol) => {
|
|
1300
1289
|
try {
|
|
1301
|
-
const
|
|
1302
|
-
return [protocol.id, flattenAvailablePools(
|
|
1290
|
+
const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
|
|
1291
|
+
return [protocol.id, flattenAvailablePools(raw)];
|
|
1303
1292
|
} catch (error) {
|
|
1304
1293
|
console.warn(
|
|
1305
1294
|
`[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
|
|
@@ -1338,6 +1327,37 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1338
1327
|
);
|
|
1339
1328
|
}
|
|
1340
1329
|
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Records the deposit with Zyfai, retrying transient failures.
|
|
1332
|
+
*
|
|
1333
|
+
* This runs AFTER the transfer has already landed on-chain, so it must never
|
|
1334
|
+
* fail the deposit — the funds moved. But it is also the ONLY source of the
|
|
1335
|
+
* "Top up wallet" entry the history is built from: Zyfai auto-deploys the
|
|
1336
|
+
* balance it detects either way, so when this call is lost the user's deposit
|
|
1337
|
+
* never appears in Activity (an earlier withdrawal stays the newest row) and
|
|
1338
|
+
* nothing ever backfills it. Retry, then log loudly enough to be recoverable.
|
|
1339
|
+
*/
|
|
1340
|
+
async logDepositWithRetry(chainId, txHash, amount, tokenAddress) {
|
|
1341
|
+
const ATTEMPTS = 3;
|
|
1342
|
+
const RETRY_DELAY_MS = 1e3;
|
|
1343
|
+
for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
|
|
1344
|
+
try {
|
|
1345
|
+
await (tokenAddress ? this.sdk.logDeposit(chainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(chainId, txHash, amount));
|
|
1346
|
+
return;
|
|
1347
|
+
} catch (logError) {
|
|
1348
|
+
if (attempt === ATTEMPTS) {
|
|
1349
|
+
console.error(
|
|
1350
|
+
"[owney-sdk] Deposit landed on-chain but logDeposit failed \u2014 it will be missing from Zyfai history:",
|
|
1351
|
+
{ txHash, chainId, amount, tokenAddress, error: logError }
|
|
1352
|
+
);
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
await new Promise(
|
|
1356
|
+
(resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt)
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1341
1361
|
/**
|
|
1342
1362
|
* True when `smartWallet` is the backend-managed pool wallet Zyfai assigned
|
|
1343
1363
|
* to this EOA — the "new wallet" kind that is predeployed on the chains it
|
|
@@ -1365,14 +1385,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1365
1385
|
async readWalletState(ownerAddress) {
|
|
1366
1386
|
try {
|
|
1367
1387
|
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1368
|
-
const
|
|
1388
|
+
const raw = portfolio;
|
|
1369
1389
|
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1370
|
-
predeployed:
|
|
1371
|
-
hasActiveSessionKey:
|
|
1390
|
+
predeployed: raw?.predeployed,
|
|
1391
|
+
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1372
1392
|
});
|
|
1373
1393
|
return {
|
|
1374
|
-
predeployed:
|
|
1375
|
-
hasActiveSessionKey:
|
|
1394
|
+
predeployed: raw?.predeployed,
|
|
1395
|
+
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1376
1396
|
};
|
|
1377
1397
|
} catch (error) {
|
|
1378
1398
|
console.warn(
|
|
@@ -1641,25 +1661,23 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1641
1661
|
);
|
|
1642
1662
|
const txHash = await depositCallback(smartWallet, validChainId, amount);
|
|
1643
1663
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
);
|
|
1651
|
-
}
|
|
1664
|
+
await this.logDepositWithRetry(
|
|
1665
|
+
validChainId,
|
|
1666
|
+
txHash,
|
|
1667
|
+
amount,
|
|
1668
|
+
tokenAddress
|
|
1669
|
+
);
|
|
1652
1670
|
return { txHash, smartWallet, amount };
|
|
1653
1671
|
}
|
|
1654
1672
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
1655
|
-
const
|
|
1673
|
+
const raw = await this.sdk.depositFunds(
|
|
1656
1674
|
this.getAddress(),
|
|
1657
1675
|
validChainId,
|
|
1658
1676
|
amount,
|
|
1659
1677
|
asset,
|
|
1660
1678
|
"aggressive"
|
|
1661
1679
|
);
|
|
1662
|
-
return mapDeposit(
|
|
1680
|
+
return mapDeposit(raw);
|
|
1663
1681
|
} catch (error) {
|
|
1664
1682
|
throw error;
|
|
1665
1683
|
}
|
|
@@ -1668,32 +1686,32 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1668
1686
|
async withdraw(state, chainId, token, amount) {
|
|
1669
1687
|
const validChainId = isValidChainId(chainId);
|
|
1670
1688
|
await this.ensureConnected(state, validChainId);
|
|
1671
|
-
const
|
|
1689
|
+
const raw = await this.sdk.withdrawFunds(
|
|
1672
1690
|
this.getAddress(),
|
|
1673
1691
|
validChainId,
|
|
1674
1692
|
amount,
|
|
1675
1693
|
token
|
|
1676
1694
|
);
|
|
1677
|
-
if (!
|
|
1695
|
+
if (!raw.success) {
|
|
1678
1696
|
throw new OwneyError(
|
|
1679
1697
|
"WITHDRAW_FAILED",
|
|
1680
|
-
|
|
1681
|
-
{ chainId: validChainId, token, amount, response:
|
|
1698
|
+
raw.message || "Zyfai withdraw failed.",
|
|
1699
|
+
{ chainId: validChainId, token, amount, response: raw },
|
|
1682
1700
|
this.id
|
|
1683
1701
|
);
|
|
1684
1702
|
}
|
|
1685
|
-
return mapWithdraw(
|
|
1703
|
+
return mapWithdraw(raw);
|
|
1686
1704
|
}
|
|
1687
1705
|
// --- IAgent: Portfolio reads ---
|
|
1688
1706
|
async getBalances(state, chainId) {
|
|
1689
1707
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
1690
|
-
const
|
|
1691
|
-
return mapBalances(
|
|
1708
|
+
const raw = await this.sdk.getPortfolio(this.getAddress());
|
|
1709
|
+
return mapBalances(raw, validChainId, smartWallet);
|
|
1692
1710
|
}
|
|
1693
1711
|
async getEarnings(state, chainId) {
|
|
1694
1712
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1695
|
-
const
|
|
1696
|
-
return mapEarnings(
|
|
1713
|
+
const raw = await this.sdk.getOnchainEarnings(smartWallet);
|
|
1714
|
+
return mapEarnings(raw, smartWallet);
|
|
1697
1715
|
}
|
|
1698
1716
|
async refreshEarnings(state, chainId) {
|
|
1699
1717
|
if (this.earningsRefreshInFlight) return this.earningsRefreshInFlight;
|
|
@@ -1713,8 +1731,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1713
1731
|
}
|
|
1714
1732
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
1715
1733
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1716
|
-
const
|
|
1717
|
-
return mapApyHistory(
|
|
1734
|
+
const raw = await this.sdk.getDailyApyHistory(smartWallet, days);
|
|
1735
|
+
return mapApyHistory(raw, chainId, tokenSymbol);
|
|
1718
1736
|
}
|
|
1719
1737
|
/**
|
|
1720
1738
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
@@ -1749,7 +1767,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1749
1767
|
const matched = [];
|
|
1750
1768
|
let backendExhausted = false;
|
|
1751
1769
|
for (let i = 0; i < MAX_BACKEND_CALLS && matched.length < limit; i++) {
|
|
1752
|
-
const
|
|
1770
|
+
const raw = await this.sdk.getHistory(smartWallet, validChainId, {
|
|
1753
1771
|
limit: backendPageSize,
|
|
1754
1772
|
offset,
|
|
1755
1773
|
fromDate: options?.fromDate,
|
|
@@ -1760,13 +1778,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1760
1778
|
// asset's rows and handing back a page that filters to nothing.
|
|
1761
1779
|
assetType
|
|
1762
1780
|
});
|
|
1763
|
-
|
|
1781
|
+
raw.data.forEach((entry, idx) => {
|
|
1764
1782
|
if (entry.chainId === validChainId) {
|
|
1765
1783
|
matched.push({ entry, rawIdx: offset + idx });
|
|
1766
1784
|
}
|
|
1767
1785
|
});
|
|
1768
|
-
offset +=
|
|
1769
|
-
if (
|
|
1786
|
+
offset += raw.data.length;
|
|
1787
|
+
if (raw.data.length < backendPageSize) {
|
|
1770
1788
|
backendExhausted = true;
|
|
1771
1789
|
break;
|
|
1772
1790
|
}
|
|
@@ -1788,18 +1806,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1788
1806
|
}
|
|
1789
1807
|
async getUserProfile(state, chainId) {
|
|
1790
1808
|
await this.connectAuth(state, chainId);
|
|
1791
|
-
const
|
|
1809
|
+
const raw = await this.sdk.getUserDetails();
|
|
1792
1810
|
debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
|
|
1793
1811
|
asset: "USDC (default \u2014 no asset passed)",
|
|
1794
|
-
splitting:
|
|
1795
|
-
minSplits:
|
|
1796
|
-
strategy:
|
|
1797
|
-
chains:
|
|
1798
|
-
protocolCount:
|
|
1799
|
-
hasActiveSessionKey:
|
|
1800
|
-
smartWallet:
|
|
1812
|
+
splitting: raw.splitting,
|
|
1813
|
+
minSplits: raw.minSplits,
|
|
1814
|
+
strategy: raw.strategy,
|
|
1815
|
+
chains: raw.chains,
|
|
1816
|
+
protocolCount: raw.protocols?.length,
|
|
1817
|
+
hasActiveSessionKey: raw.hasActiveSessionKey,
|
|
1818
|
+
smartWallet: raw.smartWallet
|
|
1801
1819
|
});
|
|
1802
|
-
return mapUserProfile(
|
|
1820
|
+
return mapUserProfile(raw, this.connectedAddress);
|
|
1803
1821
|
}
|
|
1804
1822
|
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
1805
1823
|
await this.connectAuth(state, chainId);
|
|
@@ -1818,151 +1836,184 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1818
1836
|
}
|
|
1819
1837
|
// --- IAgent: Discovery (no wallet required) ---
|
|
1820
1838
|
async getAgentApy(days, options) {
|
|
1821
|
-
const
|
|
1839
|
+
const raw = await this.sdk.getAPYPerStrategy(
|
|
1822
1840
|
false,
|
|
1823
1841
|
DayFilterMapping[days],
|
|
1824
1842
|
"aggressive",
|
|
1825
1843
|
options?.chainId,
|
|
1826
1844
|
options?.tokenSymbol
|
|
1827
1845
|
);
|
|
1828
|
-
return mapApyByStrategy(
|
|
1846
|
+
return mapApyByStrategy(raw);
|
|
1829
1847
|
}
|
|
1830
1848
|
};
|
|
1831
1849
|
|
|
1832
|
-
// src/
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
createWalletClient as createWalletClient2,
|
|
1836
|
-
custom as custom2,
|
|
1837
|
-
encodeFunctionData,
|
|
1838
|
-
erc20Abi,
|
|
1839
|
-
getAddress as getAddress2,
|
|
1840
|
-
isAddress as isAddress2
|
|
1841
|
-
} from "viem";
|
|
1842
|
-
import { base as base3 } from "viem/chains";
|
|
1843
|
-
|
|
1844
|
-
// src/lib/chain-guard.ts
|
|
1845
|
-
var CHAIN_NAMES = {
|
|
1846
|
-
1: "Ethereum",
|
|
1847
|
-
8453: "Base",
|
|
1848
|
-
42161: "Arbitrum"
|
|
1849
|
-
};
|
|
1850
|
-
function chainName(chainId) {
|
|
1851
|
-
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
1852
|
-
}
|
|
1853
|
-
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
1854
|
-
const actual = await pub.getChainId();
|
|
1855
|
-
if (actual === expected) return;
|
|
1850
|
+
// src/lib/health-report.ts
|
|
1851
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1852
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL) {
|
|
1856
1853
|
try {
|
|
1857
|
-
await
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1854
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
1855
|
+
method: "POST",
|
|
1856
|
+
headers: {
|
|
1857
|
+
"Content-Type": "application/json",
|
|
1858
|
+
"x-owney-api-key": apiKey
|
|
1859
|
+
},
|
|
1860
|
+
body: JSON.stringify({
|
|
1861
|
+
agent_type: agentType,
|
|
1862
|
+
error_code: errorCode,
|
|
1863
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1864
|
+
})
|
|
1865
|
+
});
|
|
1866
|
+
} catch (err) {
|
|
1867
|
+
console.warn(
|
|
1868
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
1869
|
+
err instanceof Error ? err.message : err
|
|
1867
1870
|
);
|
|
1868
1871
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
);
|
|
1872
|
+
}
|
|
1873
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
1874
|
+
try {
|
|
1875
|
+
return await fn();
|
|
1876
|
+
} catch (err) {
|
|
1877
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
1878
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
1879
|
+
throw err;
|
|
1876
1880
|
}
|
|
1877
1881
|
}
|
|
1878
1882
|
|
|
1879
|
-
// src/lib/
|
|
1880
|
-
import {
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
{ name: "from", type: "address" },
|
|
1891
|
-
{ name: "to", type: "address" },
|
|
1892
|
-
{ name: "value", type: "uint256" },
|
|
1893
|
-
{ name: "validAfter", type: "uint256" },
|
|
1894
|
-
{ name: "validBefore", type: "uint256" },
|
|
1895
|
-
{ name: "nonce", type: "bytes32" }
|
|
1896
|
-
]
|
|
1897
|
-
},
|
|
1898
|
-
primaryType: "TransferWithAuthorization",
|
|
1899
|
-
message: input.message
|
|
1900
|
-
};
|
|
1883
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
1884
|
+
import { parseUnits } from "viem";
|
|
1885
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
1886
|
+
return agents.map((agent) => {
|
|
1887
|
+
const agentBalance = aggregated[agent.id];
|
|
1888
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
1889
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
1890
|
+
);
|
|
1891
|
+
if (!tokenBalance) return { agent, balance: 0n };
|
|
1892
|
+
return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
|
|
1893
|
+
});
|
|
1901
1894
|
}
|
|
1902
|
-
|
|
1903
|
-
const
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1895
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
1896
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
1897
|
+
agent,
|
|
1898
|
+
balance,
|
|
1899
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
1900
|
+
}));
|
|
1901
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
1902
|
+
let remainder = requested - assigned;
|
|
1903
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
1904
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
1905
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
1906
|
+
});
|
|
1907
|
+
for (const p of byHeadroom) {
|
|
1908
|
+
if (remainder === 0n) break;
|
|
1909
|
+
const headroom = p.balance - p.planned;
|
|
1910
|
+
if (headroom <= 0n) continue;
|
|
1911
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
1912
|
+
p.planned += take;
|
|
1913
|
+
remainder -= take;
|
|
1914
|
+
}
|
|
1915
|
+
return plans;
|
|
1908
1916
|
}
|
|
1909
|
-
function
|
|
1910
|
-
const
|
|
1911
|
-
|
|
1912
|
-
|
|
1917
|
+
function planDisabledDrain(disabled, requested) {
|
|
1918
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
1919
|
+
const plans = [];
|
|
1920
|
+
let remaining = requested;
|
|
1921
|
+
for (const { agent, balance } of sorted) {
|
|
1922
|
+
if (remaining === 0n) {
|
|
1923
|
+
plans.push({ agent, balance, planned: 0n });
|
|
1924
|
+
continue;
|
|
1925
|
+
}
|
|
1926
|
+
const take = balance < remaining ? balance : remaining;
|
|
1927
|
+
plans.push({ agent, balance, planned: take });
|
|
1928
|
+
remaining -= take;
|
|
1929
|
+
}
|
|
1930
|
+
return { plans, remaining };
|
|
1913
1931
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
});
|
|
1930
|
-
} catch (networkError) {
|
|
1931
|
-
throw new OwneyError(
|
|
1932
|
-
"SPONSOR_REQUEST_FAILED",
|
|
1933
|
-
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1934
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
1935
|
-
);
|
|
1932
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
1933
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
1934
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
1935
|
+
const totalHeadroom = candidates.reduce(
|
|
1936
|
+
(s, c) => s + (c.balance - c.planned),
|
|
1937
|
+
0n
|
|
1938
|
+
);
|
|
1939
|
+
if (totalHeadroom === 0n) return;
|
|
1940
|
+
let distributed = 0n;
|
|
1941
|
+
for (const c of candidates) {
|
|
1942
|
+
const headroom = c.balance - c.planned;
|
|
1943
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
1944
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
1945
|
+
c.planned += give;
|
|
1946
|
+
distributed += give;
|
|
1936
1947
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1948
|
+
let leftover = amount - distributed;
|
|
1949
|
+
for (const c of candidates) {
|
|
1950
|
+
if (leftover === 0n) break;
|
|
1951
|
+
const headroom = c.balance - c.planned;
|
|
1952
|
+
if (headroom <= 0n) continue;
|
|
1953
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
1954
|
+
c.planned += take;
|
|
1955
|
+
leftover -= take;
|
|
1942
1956
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1957
|
+
}
|
|
1958
|
+
function sumWithdrawnAmount(results) {
|
|
1959
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
1963
|
+
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
1964
|
+
const sums = {};
|
|
1965
|
+
const weights = {};
|
|
1966
|
+
for (const id of Object.keys(agentApys)) {
|
|
1967
|
+
const cells = agentApys[id].apyByChainAndAsset;
|
|
1968
|
+
const balance = agentBalances[id] ?? 0;
|
|
1969
|
+
if (!cells || balance <= 0) continue;
|
|
1970
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
1971
|
+
if (!perAsset) continue;
|
|
1972
|
+
const chainId = Number(chainKey);
|
|
1973
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
1974
|
+
const apy = Number(apyValue ?? 0);
|
|
1975
|
+
if (apy === 0) continue;
|
|
1976
|
+
sums[chainId] ??= {};
|
|
1977
|
+
weights[chainId] ??= {};
|
|
1978
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
1979
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
1951
1980
|
}
|
|
1952
|
-
|
|
1981
|
+
}
|
|
1953
1982
|
}
|
|
1954
|
-
|
|
1983
|
+
const out = {};
|
|
1984
|
+
for (const chainKey of Object.keys(sums)) {
|
|
1985
|
+
const chainId = Number(chainKey);
|
|
1986
|
+
const perAssetOut = {};
|
|
1987
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
1988
|
+
const w = weights[chainId][asset];
|
|
1989
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
1990
|
+
}
|
|
1991
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
1992
|
+
out[chainId] = perAssetOut;
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
return out;
|
|
1955
1996
|
}
|
|
1997
|
+
|
|
1998
|
+
// src/client.ts
|
|
1999
|
+
import {
|
|
2000
|
+
createPublicClient as createPublicClient2,
|
|
2001
|
+
createWalletClient,
|
|
2002
|
+
custom
|
|
2003
|
+
} from "viem";
|
|
2004
|
+
import { base as base2, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
2005
|
+
|
|
2006
|
+
// src/lib/sponsor-client.ts
|
|
2007
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1956
2008
|
async function postSponsorTransferAuth(input) {
|
|
1957
|
-
const
|
|
2009
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL2;
|
|
1958
2010
|
let res;
|
|
1959
2011
|
try {
|
|
1960
|
-
res = await fetch(`${
|
|
2012
|
+
res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
1961
2013
|
method: "POST",
|
|
1962
2014
|
headers: {
|
|
1963
2015
|
"content-type": "application/json",
|
|
1964
|
-
"x-owney-api-key": input.apiKey
|
|
1965
|
-
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2016
|
+
"x-owney-api-key": input.apiKey
|
|
1966
2017
|
},
|
|
1967
2018
|
body: JSON.stringify(input.body)
|
|
1968
2019
|
});
|
|
@@ -1995,15 +2046,14 @@ async function postSponsorTransferAuth(input) {
|
|
|
1995
2046
|
return parsed.data;
|
|
1996
2047
|
}
|
|
1997
2048
|
async function postSponsorPermit2Transfer(input) {
|
|
1998
|
-
const
|
|
2049
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL2;
|
|
1999
2050
|
let res;
|
|
2000
2051
|
try {
|
|
2001
|
-
res = await fetch(`${
|
|
2052
|
+
res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
|
|
2002
2053
|
method: "POST",
|
|
2003
2054
|
headers: {
|
|
2004
2055
|
"content-type": "application/json",
|
|
2005
|
-
"x-owney-api-key": input.apiKey
|
|
2006
|
-
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2056
|
+
"x-owney-api-key": input.apiKey
|
|
2007
2057
|
},
|
|
2008
2058
|
body: JSON.stringify(input.body)
|
|
2009
2059
|
});
|
|
@@ -2034,11 +2084,11 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2034
2084
|
return parsed.data;
|
|
2035
2085
|
}
|
|
2036
2086
|
async function getSponsorRelayerAddress(input) {
|
|
2037
|
-
const
|
|
2087
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL2;
|
|
2038
2088
|
let res;
|
|
2039
2089
|
try {
|
|
2040
2090
|
res = await fetch(
|
|
2041
|
-
`${
|
|
2091
|
+
`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2042
2092
|
{
|
|
2043
2093
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2044
2094
|
}
|
|
@@ -2070,2001 +2120,204 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2070
2120
|
return parsed.data.relayer;
|
|
2071
2121
|
}
|
|
2072
2122
|
|
|
2073
|
-
// src/lib/
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
stateMutability: "view",
|
|
2082
|
-
inputs: [
|
|
2083
|
-
{ name: "owner", type: "address" },
|
|
2084
|
-
{ name: "spender", type: "address" }
|
|
2085
|
-
],
|
|
2086
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2087
|
-
},
|
|
2088
|
-
{
|
|
2089
|
-
type: "function",
|
|
2090
|
-
name: "approve",
|
|
2091
|
-
stateMutability: "nonpayable",
|
|
2092
|
-
inputs: [
|
|
2093
|
-
{ name: "spender", type: "address" },
|
|
2094
|
-
{ name: "amount", type: "uint256" }
|
|
2095
|
-
],
|
|
2096
|
-
outputs: [{ name: "", type: "bool" }]
|
|
2097
|
-
},
|
|
2098
|
-
{
|
|
2099
|
-
type: "function",
|
|
2100
|
-
name: "balanceOf",
|
|
2101
|
-
stateMutability: "view",
|
|
2102
|
-
inputs: [{ name: "account", type: "address" }],
|
|
2103
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2104
|
-
}
|
|
2105
|
-
];
|
|
2106
|
-
function buildPermitTransferFromTypedData(input) {
|
|
2107
|
-
return {
|
|
2108
|
-
domain: {
|
|
2109
|
-
name: "Permit2",
|
|
2110
|
-
chainId: input.chainId,
|
|
2111
|
-
verifyingContract: PERMIT2_ADDRESS
|
|
2112
|
-
},
|
|
2113
|
-
types: {
|
|
2114
|
-
PermitTransferFrom: [
|
|
2115
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
2116
|
-
{ name: "spender", type: "address" },
|
|
2117
|
-
{ name: "nonce", type: "uint256" },
|
|
2118
|
-
{ name: "deadline", type: "uint256" }
|
|
2119
|
-
],
|
|
2120
|
-
TokenPermissions: [
|
|
2121
|
-
{ name: "token", type: "address" },
|
|
2122
|
-
{ name: "amount", type: "uint256" }
|
|
2123
|
-
]
|
|
2124
|
-
},
|
|
2125
|
-
primaryType: "PermitTransferFrom",
|
|
2126
|
-
message: input.message
|
|
2127
|
-
};
|
|
2128
|
-
}
|
|
2129
|
-
function randomPermit2Nonce() {
|
|
2130
|
-
const bytes = new Uint8Array(32);
|
|
2131
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2132
|
-
return BigInt(bytesToHex2(bytes));
|
|
2133
|
-
}
|
|
2134
|
-
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2135
|
-
return publicClient.readContract({
|
|
2136
|
-
address: token,
|
|
2137
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2138
|
-
functionName: "allowance",
|
|
2139
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
2140
|
-
});
|
|
2141
|
-
}
|
|
2142
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
2143
|
-
return publicClient.readContract({
|
|
2144
|
-
address: token,
|
|
2145
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2146
|
-
functionName: "balanceOf",
|
|
2147
|
-
args: [owner]
|
|
2148
|
-
});
|
|
2149
|
-
}
|
|
2150
|
-
|
|
2151
|
-
// src/lib/sponsored-deposit.ts
|
|
2152
|
-
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2153
|
-
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2154
|
-
function provideDepositVerificationContext(callback, context) {
|
|
2155
|
-
callback[verificationSetter]?.(context);
|
|
2156
|
-
}
|
|
2157
|
-
function makeVerificationAwareDepositCallback(implementation) {
|
|
2158
|
-
let nextVerification;
|
|
2159
|
-
const callback = async (smartWallet, chainId, amount) => {
|
|
2160
|
-
const verification = nextVerification;
|
|
2161
|
-
nextVerification = void 0;
|
|
2162
|
-
return implementation(smartWallet, chainId, amount, verification);
|
|
2163
|
-
};
|
|
2164
|
-
Object.defineProperty(callback, verificationSetter, {
|
|
2165
|
-
value: (context) => {
|
|
2166
|
-
nextVerification = context;
|
|
2167
|
-
}
|
|
2168
|
-
});
|
|
2169
|
-
return callback;
|
|
2170
|
-
}
|
|
2171
|
-
function makeSponsoredDepositCallback(deps) {
|
|
2172
|
-
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2173
|
-
return makeVerificationAwareDepositCallback(
|
|
2174
|
-
async (smartWallet, chainId, amount, verification) => {
|
|
2175
|
-
const cid = chainId;
|
|
2176
|
-
const token = deps.tokenAddressByChain[cid];
|
|
2177
|
-
if (!token) {
|
|
2178
|
-
throw new OwneyError(
|
|
2179
|
-
"CHAIN_UNSUPPORTED",
|
|
2180
|
-
`No sponsored token configured for chain ${chainId}`
|
|
2181
|
-
);
|
|
2182
|
-
}
|
|
2183
|
-
const pub = deps.getPublicClient(cid);
|
|
2184
|
-
const wallet = deps.getWalletClient(cid);
|
|
2185
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2186
|
-
try {
|
|
2187
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2188
|
-
if (balance < BigInt(amount)) {
|
|
2189
|
-
throw new OwneyError(
|
|
2190
|
-
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2191
|
-
"Insufficient balance for this deposit.",
|
|
2192
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2193
|
-
);
|
|
2194
|
-
}
|
|
2195
|
-
} catch (err) {
|
|
2196
|
-
if (err instanceof OwneyError) throw err;
|
|
2197
|
-
console.warn(
|
|
2198
|
-
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2199
|
-
err instanceof Error ? err.message : String(err)
|
|
2200
|
-
);
|
|
2201
|
-
}
|
|
2202
|
-
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2203
|
-
const validAfter = 0n;
|
|
2204
|
-
const validBefore = BigInt(
|
|
2205
|
-
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2206
|
-
);
|
|
2207
|
-
const nonce = randomAuthNonce();
|
|
2208
|
-
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2209
|
-
token,
|
|
2210
|
-
chainId: cid,
|
|
2211
|
-
tokenName,
|
|
2212
|
-
tokenVersion,
|
|
2213
|
-
message: {
|
|
2214
|
-
from: deps.ownerAddress,
|
|
2215
|
-
to: smartWallet,
|
|
2216
|
-
value: BigInt(amount),
|
|
2217
|
-
validAfter,
|
|
2218
|
-
validBefore,
|
|
2219
|
-
nonce
|
|
2220
|
-
}
|
|
2221
|
-
});
|
|
2222
|
-
const authSignature = await wallet.signTypedData({
|
|
2223
|
-
account: deps.ownerAddress,
|
|
2224
|
-
...typedData
|
|
2225
|
-
});
|
|
2226
|
-
deps.onApproved?.();
|
|
2227
|
-
const result = await post({
|
|
2228
|
-
baseUrl: deps.baseUrl,
|
|
2229
|
-
apiKey: deps.apiKey,
|
|
2230
|
-
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2231
|
-
body: {
|
|
2232
|
-
chainId: cid,
|
|
2233
|
-
token,
|
|
2234
|
-
from: deps.ownerAddress,
|
|
2235
|
-
to: smartWallet,
|
|
2236
|
-
value: amount,
|
|
2237
|
-
validAfter: validAfter.toString(),
|
|
2238
|
-
validBefore: validBefore.toString(),
|
|
2239
|
-
nonce,
|
|
2240
|
-
authSignature,
|
|
2241
|
-
tokenName,
|
|
2242
|
-
tokenVersion,
|
|
2243
|
-
...verification?.agentId === "yieldseeker" ? {
|
|
2244
|
-
yieldseekerUserId: verification.userId,
|
|
2245
|
-
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
2246
|
-
} : {}
|
|
2247
|
-
}
|
|
2248
|
-
});
|
|
2249
|
-
return result.txHash;
|
|
2250
|
-
}
|
|
2251
|
-
);
|
|
2252
|
-
}
|
|
2253
|
-
|
|
2254
|
-
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2255
|
-
import { SiweMessage, generateNonce } from "siwe";
|
|
2256
|
-
import {
|
|
2257
|
-
createPublicClient as createPublicClient2,
|
|
2258
|
-
createWalletClient,
|
|
2259
|
-
custom,
|
|
2260
|
-
getAddress
|
|
2261
|
-
} from "viem";
|
|
2262
|
-
import { base as base2 } from "viem/chains";
|
|
2263
|
-
|
|
2264
|
-
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2265
|
-
var KEY_PREFIX2 = "owney.yieldseeker.session.v4";
|
|
2266
|
-
var INVALIDATED_KEY_PREFIXES = [
|
|
2267
|
-
"owney.yieldseeker.session",
|
|
2268
|
-
"owney.yieldseeker.session.v3"
|
|
2269
|
-
];
|
|
2270
|
-
var storage2 = () => {
|
|
2271
|
-
if (typeof window === "undefined") return null;
|
|
2272
|
-
try {
|
|
2273
|
-
return window.localStorage;
|
|
2274
|
-
} catch {
|
|
2275
|
-
return null;
|
|
2276
|
-
}
|
|
2277
|
-
};
|
|
2278
|
-
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2279
|
-
var invalidatedKeys = (address, chainId) => INVALIDATED_KEY_PREFIXES.map(
|
|
2280
|
-
(prefix) => `${prefix}:${address.toLowerCase()}:${chainId}`
|
|
2281
|
-
);
|
|
2282
|
-
var clearInvalidatedSessions = (store, address, chainId) => {
|
|
2283
|
-
for (const key2 of invalidatedKeys(address, chainId)) {
|
|
2284
|
-
memorySessions2.delete(key2);
|
|
2285
|
-
try {
|
|
2286
|
-
store?.removeItem(key2);
|
|
2287
|
-
} catch {
|
|
2288
|
-
}
|
|
2289
|
-
}
|
|
2290
|
-
};
|
|
2291
|
-
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2292
|
-
var isValidSession = (session) => {
|
|
2293
|
-
if (!session?.token) return false;
|
|
2294
|
-
try {
|
|
2295
|
-
const parsed = JSON.parse(atob(session.token));
|
|
2296
|
-
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2297
|
-
} catch {
|
|
2298
|
-
return false;
|
|
2299
|
-
}
|
|
2300
|
-
};
|
|
2301
|
-
var readYieldseekerSession = (address, chainId) => {
|
|
2302
|
-
if (typeof window === "undefined") return null;
|
|
2303
|
-
const key2 = buildKey2(address, chainId);
|
|
2304
|
-
const store = storage2();
|
|
2305
|
-
clearInvalidatedSessions(store, address, chainId);
|
|
2306
|
-
let raw2 = null;
|
|
2307
|
-
try {
|
|
2308
|
-
raw2 = store?.getItem(key2) ?? null;
|
|
2309
|
-
} catch {
|
|
2310
|
-
raw2 = null;
|
|
2311
|
-
}
|
|
2312
|
-
if (raw2) {
|
|
2313
|
-
try {
|
|
2314
|
-
const parsed = JSON.parse(raw2);
|
|
2315
|
-
if (isValidSession(parsed)) return parsed.token;
|
|
2316
|
-
} catch {
|
|
2317
|
-
}
|
|
2318
|
-
memorySessions2.delete(key2);
|
|
2319
|
-
try {
|
|
2320
|
-
store?.removeItem(key2);
|
|
2321
|
-
} catch {
|
|
2322
|
-
}
|
|
2323
|
-
return null;
|
|
2324
|
-
}
|
|
2325
|
-
const cached = memorySessions2.get(key2);
|
|
2326
|
-
if (isValidSession(cached)) return cached.token;
|
|
2327
|
-
if (cached) memorySessions2.delete(key2);
|
|
2328
|
-
return null;
|
|
2329
|
-
};
|
|
2330
|
-
var writeYieldseekerSession = (address, chainId, token) => {
|
|
2331
|
-
if (typeof window === "undefined") return;
|
|
2332
|
-
const session = { token };
|
|
2333
|
-
if (!isValidSession(session)) return;
|
|
2334
|
-
const key2 = buildKey2(address, chainId);
|
|
2335
|
-
memorySessions2.set(key2, session);
|
|
2336
|
-
const store = storage2();
|
|
2337
|
-
try {
|
|
2338
|
-
store?.setItem(key2, JSON.stringify(session));
|
|
2339
|
-
} catch {
|
|
2340
|
-
}
|
|
2341
|
-
};
|
|
2342
|
-
var clearYieldseekerSession = (address, chainId) => {
|
|
2343
|
-
const key2 = buildKey2(address, chainId);
|
|
2344
|
-
memorySessions2.delete(key2);
|
|
2345
|
-
const store = storage2();
|
|
2346
|
-
clearInvalidatedSessions(store, address, chainId);
|
|
2347
|
-
try {
|
|
2348
|
-
store?.removeItem(key2);
|
|
2349
|
-
} catch {
|
|
2350
|
-
}
|
|
2351
|
-
};
|
|
2352
|
-
|
|
2353
|
-
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2354
|
-
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2355
|
-
return new SiweMessage({
|
|
2356
|
-
domain: "yieldseeker.xyz",
|
|
2357
|
-
address: getAddress(address),
|
|
2358
|
-
uri: "https://yieldseeker.xyz",
|
|
2359
|
-
version: "1",
|
|
2360
|
-
chainId,
|
|
2361
|
-
nonce: (dependencies.nonce ?? generateNonce)(),
|
|
2362
|
-
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2363
|
-
}).prepareMessage();
|
|
2364
|
-
}
|
|
2365
|
-
function encodeYieldseekerAuthToken(token) {
|
|
2366
|
-
const bytes = new TextEncoder().encode(JSON.stringify(token));
|
|
2367
|
-
let binary = "";
|
|
2368
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2369
|
-
return btoa(binary);
|
|
2370
|
-
}
|
|
2371
|
-
var YieldseekerAuth = class {
|
|
2372
|
-
constructor(dependencies = {}) {
|
|
2373
|
-
this.dependencies = dependencies;
|
|
2374
|
-
}
|
|
2375
|
-
dependencies;
|
|
2376
|
-
tokens = /* @__PURE__ */ new Map();
|
|
2377
|
-
pending = /* @__PURE__ */ new Map();
|
|
2378
|
-
scopes = /* @__PURE__ */ new Map();
|
|
2379
|
-
key(state, chainId) {
|
|
2380
|
-
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
2381
|
-
}
|
|
2382
|
-
async getToken(state, chainId) {
|
|
2383
|
-
const key2 = this.key(state, chainId);
|
|
2384
|
-
const scope = { address: state.walletAddress, chainId };
|
|
2385
|
-
this.scopes.set(key2, scope);
|
|
2386
|
-
const cached = this.tokens.get(key2);
|
|
2387
|
-
if (cached) return cached;
|
|
2388
|
-
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2389
|
-
if (persisted) {
|
|
2390
|
-
this.tokens.set(key2, persisted);
|
|
2391
|
-
return persisted;
|
|
2392
|
-
}
|
|
2393
|
-
const inFlight = this.pending.get(key2);
|
|
2394
|
-
if (inFlight) return inFlight;
|
|
2395
|
-
const request = this.sign(state, chainId).then((token) => {
|
|
2396
|
-
this.tokens.set(key2, token);
|
|
2397
|
-
writeYieldseekerSession(scope.address, scope.chainId, token);
|
|
2398
|
-
return token;
|
|
2399
|
-
});
|
|
2400
|
-
this.pending.set(key2, request);
|
|
2401
|
-
try {
|
|
2402
|
-
return await request;
|
|
2403
|
-
} finally {
|
|
2404
|
-
this.pending.delete(key2);
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
clear(state, chainId) {
|
|
2408
|
-
if (!state || chainId === void 0) {
|
|
2409
|
-
for (const scope of this.scopes.values()) {
|
|
2410
|
-
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2411
|
-
}
|
|
2412
|
-
this.tokens.clear();
|
|
2413
|
-
this.pending.clear();
|
|
2414
|
-
this.scopes.clear();
|
|
2415
|
-
return;
|
|
2416
|
-
}
|
|
2417
|
-
const key2 = this.key(state, chainId);
|
|
2418
|
-
this.tokens.delete(key2);
|
|
2419
|
-
this.pending.delete(key2);
|
|
2420
|
-
this.scopes.delete(key2);
|
|
2421
|
-
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2422
|
-
}
|
|
2423
|
-
async sign(state, chainId) {
|
|
2424
|
-
const account = getAddress(state.walletAddress);
|
|
2425
|
-
const publicClient = createPublicClient2({
|
|
2426
|
-
chain: base2,
|
|
2427
|
-
transport: custom(state.provider)
|
|
2428
|
-
});
|
|
2429
|
-
const walletClient = createWalletClient({
|
|
2430
|
-
account,
|
|
2431
|
-
chain: base2,
|
|
2432
|
-
transport: custom(state.provider)
|
|
2433
|
-
});
|
|
2434
|
-
await ensureWalletOnChain(
|
|
2435
|
-
publicClient,
|
|
2436
|
-
walletClient,
|
|
2437
|
-
8453
|
|
2438
|
-
);
|
|
2439
|
-
const message = createYieldseekerSiweMessage(
|
|
2440
|
-
account,
|
|
2441
|
-
chainId,
|
|
2442
|
-
this.dependencies
|
|
2443
|
-
);
|
|
2444
|
-
const signature = await walletClient.signMessage({ account, message });
|
|
2445
|
-
return encodeYieldseekerAuthToken({ message, signature });
|
|
2446
|
-
}
|
|
2447
|
-
};
|
|
2448
|
-
|
|
2449
|
-
// src/agents/yieldseeker/yieldseeker.identity-cache.ts
|
|
2450
|
-
var KEY_PREFIX3 = "owney.yieldseeker.identity.v1";
|
|
2451
|
-
var YIELDSEEKER_IDENTITY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2452
|
-
var memoryIdentities = /* @__PURE__ */ new Map();
|
|
2453
|
-
var storage3 = () => {
|
|
2454
|
-
if (typeof window === "undefined") return null;
|
|
2455
|
-
try {
|
|
2456
|
-
return window.localStorage;
|
|
2457
|
-
} catch {
|
|
2458
|
-
return null;
|
|
2459
|
-
}
|
|
2460
|
-
};
|
|
2461
|
-
var keyFor = (walletAddress, chainId) => `${KEY_PREFIX3}:${walletAddress.toLowerCase()}:${chainId}`;
|
|
2462
|
-
function valid(value, walletAddress, chainId, now) {
|
|
2463
|
-
return Boolean(
|
|
2464
|
-
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
|
|
2465
|
-
);
|
|
2466
|
-
}
|
|
2467
|
-
function readYieldseekerIdentity(walletAddress, chainId, now = Date.now()) {
|
|
2468
|
-
if (typeof window === "undefined") return null;
|
|
2469
|
-
const key2 = keyFor(walletAddress, chainId);
|
|
2470
|
-
const store = storage3();
|
|
2471
|
-
let parsed = null;
|
|
2472
|
-
try {
|
|
2473
|
-
const raw2 = store?.getItem(key2);
|
|
2474
|
-
parsed = raw2 ? JSON.parse(raw2) : null;
|
|
2475
|
-
} catch {
|
|
2476
|
-
parsed = null;
|
|
2477
|
-
}
|
|
2478
|
-
const candidate = parsed ?? memoryIdentities.get(key2);
|
|
2479
|
-
if (valid(candidate, walletAddress, chainId, now)) {
|
|
2480
|
-
memoryIdentities.set(key2, candidate);
|
|
2481
|
-
return { userId: candidate.userId };
|
|
2482
|
-
}
|
|
2483
|
-
memoryIdentities.delete(key2);
|
|
2484
|
-
try {
|
|
2485
|
-
store?.removeItem(key2);
|
|
2486
|
-
} catch {
|
|
2487
|
-
}
|
|
2488
|
-
return null;
|
|
2489
|
-
}
|
|
2490
|
-
function writeYieldseekerIdentity(walletAddress, chainId, userId, now = Date.now()) {
|
|
2491
|
-
if (typeof window === "undefined") return;
|
|
2492
|
-
const identity = {
|
|
2493
|
-
userId,
|
|
2494
|
-
walletAddress,
|
|
2495
|
-
chainId,
|
|
2496
|
-
expiresAt: now + YIELDSEEKER_IDENTITY_TTL_MS
|
|
2497
|
-
};
|
|
2498
|
-
if (!valid(identity, walletAddress, chainId, now)) return;
|
|
2499
|
-
const key2 = keyFor(walletAddress, chainId);
|
|
2500
|
-
memoryIdentities.set(key2, identity);
|
|
2501
|
-
try {
|
|
2502
|
-
storage3()?.setItem(key2, JSON.stringify(identity));
|
|
2503
|
-
} catch {
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
function clearYieldseekerIdentity(walletAddress, chainId) {
|
|
2507
|
-
const key2 = keyFor(walletAddress, chainId);
|
|
2508
|
-
memoryIdentities.delete(key2);
|
|
2509
|
-
try {
|
|
2510
|
-
storage3()?.removeItem(key2);
|
|
2511
|
-
} catch {
|
|
2512
|
-
}
|
|
2513
|
-
}
|
|
2514
|
-
|
|
2515
|
-
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2516
|
-
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2517
|
-
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2518
|
-
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2519
|
-
}
|
|
2520
|
-
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2521
|
-
var YieldseekerApiError = class extends Error {
|
|
2522
|
-
constructor(status, providerCode, responseFields) {
|
|
2523
|
-
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2524
|
-
this.status = status;
|
|
2525
|
-
this.providerCode = providerCode;
|
|
2526
|
-
this.responseFields = responseFields;
|
|
2527
|
-
this.name = "YieldseekerApiError";
|
|
2528
|
-
}
|
|
2529
|
-
status;
|
|
2530
|
-
providerCode;
|
|
2531
|
-
responseFields;
|
|
2532
|
-
get isAuthenticationError() {
|
|
2533
|
-
return this.status === 401 || this.status === 403;
|
|
2534
|
-
}
|
|
2535
|
-
};
|
|
2536
|
-
function providerError(body, fallback) {
|
|
2537
|
-
if (!body || typeof body !== "object") return { code: fallback };
|
|
2538
|
-
const record = body;
|
|
2539
|
-
return {
|
|
2540
|
-
code: typeof record.message === "string" ? record.message : fallback,
|
|
2541
|
-
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2542
|
-
};
|
|
2543
|
-
}
|
|
2544
|
-
var YieldseekerApiClient = class {
|
|
2545
|
-
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
|
|
2546
|
-
this.owneyApiKey = owneyApiKey;
|
|
2547
|
-
this.baseUrl = baseUrl;
|
|
2548
|
-
this.fetchFn = fetchFn;
|
|
2549
|
-
}
|
|
2550
|
-
owneyApiKey;
|
|
2551
|
-
baseUrl;
|
|
2552
|
-
fetchFn;
|
|
2553
|
-
async request(path, options = {}) {
|
|
2554
|
-
const controller = new AbortController();
|
|
2555
|
-
const timer = setTimeout(
|
|
2556
|
-
() => controller.abort(),
|
|
2557
|
-
options.timeoutMs ?? 15e3
|
|
2558
|
-
);
|
|
2559
|
-
try {
|
|
2560
|
-
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2561
|
-
method: options.method ?? "GET",
|
|
2562
|
-
headers: {
|
|
2563
|
-
"Content-Type": "application/json",
|
|
2564
|
-
"x-owney-api-key": this.owneyApiKey,
|
|
2565
|
-
...options.signature ? { Authorization: `Signature ${options.signature}` } : {}
|
|
2566
|
-
},
|
|
2567
|
-
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2568
|
-
signal: controller.signal
|
|
2569
|
-
});
|
|
2570
|
-
const payload = await response.json().catch(() => null);
|
|
2571
|
-
if (!response.ok) {
|
|
2572
|
-
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2573
|
-
throw new YieldseekerApiError(
|
|
2574
|
-
response.status,
|
|
2575
|
-
error.code,
|
|
2576
|
-
error.fields
|
|
2577
|
-
);
|
|
2578
|
-
}
|
|
2579
|
-
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2580
|
-
return payload.data;
|
|
2581
|
-
}
|
|
2582
|
-
return payload;
|
|
2583
|
-
} catch (error) {
|
|
2584
|
-
if (error instanceof YieldseekerApiError) throw error;
|
|
2585
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2586
|
-
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2587
|
-
}
|
|
2588
|
-
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2589
|
-
cause: error instanceof Error ? error.message : String(error)
|
|
2590
|
-
});
|
|
2591
|
-
} finally {
|
|
2592
|
-
clearTimeout(timer);
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
};
|
|
2596
|
-
|
|
2597
|
-
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2598
|
-
import { formatUnits, isAddress } from "viem";
|
|
2599
|
-
|
|
2600
|
-
// src/agents/yieldseeker/yieldseeker.types.ts
|
|
2601
|
-
var YIELDSEEKER_ASSET_METADATA = {
|
|
2602
|
-
USDC: {
|
|
2603
|
-
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2604
|
-
decimals: 6
|
|
2605
|
-
},
|
|
2606
|
-
WETH: {
|
|
2607
|
-
address: "0x4200000000000000000000000000000000000006",
|
|
2608
|
-
decimals: 18
|
|
2609
|
-
}
|
|
2610
|
-
};
|
|
2611
|
-
|
|
2612
|
-
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2613
|
-
function invalid(endpoint, detail) {
|
|
2614
|
-
throw new OwneyError(
|
|
2615
|
-
"AGENT_INVALID_RESPONSE",
|
|
2616
|
-
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2617
|
-
{ endpoint, detail },
|
|
2618
|
-
"yieldseeker"
|
|
2619
|
-
);
|
|
2620
|
-
}
|
|
2621
|
-
function raw(value, endpoint) {
|
|
2622
|
-
if (typeof value !== "string" || !/^-?[0-9]+$/.test(value)) {
|
|
2623
|
-
return invalid(endpoint, "expected a base-10 integer string");
|
|
2624
|
-
}
|
|
2625
|
-
return BigInt(value);
|
|
2626
|
-
}
|
|
2627
|
-
function decimal(value, decimals, endpoint) {
|
|
2628
|
-
return formatUnits(raw(value, endpoint), decimals);
|
|
2629
|
-
}
|
|
2630
|
-
function usd(rawAmount, decimals, price) {
|
|
2631
|
-
return Number(formatUnits(rawAmount, decimals)) * price;
|
|
2632
|
-
}
|
|
2633
|
-
function percent(value) {
|
|
2634
|
-
const result = Number(value);
|
|
2635
|
-
return Number.isFinite(result) ? result * 100 : 0;
|
|
2636
|
-
}
|
|
2637
|
-
var YIELDSEEKER_POSITIVE_YIELD_FEE_RATE = 0.1;
|
|
2638
|
-
function publicApyAfterYieldseekerFee(value) {
|
|
2639
|
-
const grossPercent = percent(value);
|
|
2640
|
-
if (grossPercent <= 0) return grossPercent;
|
|
2641
|
-
const netPercent = grossPercent * (1 - YIELDSEEKER_POSITIVE_YIELD_FEE_RATE);
|
|
2642
|
-
return Math.round(netPercent * 1e12) / 1e12;
|
|
2643
|
-
}
|
|
2644
|
-
function riskAdjustedApyForDays(option, days) {
|
|
2645
|
-
if (days === "7D") return option.riskAdjustedApy7dAverage;
|
|
2646
|
-
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
2647
|
-
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
2648
|
-
}
|
|
2649
|
-
function assetAddressValue(record, address) {
|
|
2650
|
-
const entry = Object.entries(record).find(
|
|
2651
|
-
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
2652
|
-
);
|
|
2653
|
-
return entry?.[1] ?? "0";
|
|
2654
|
-
}
|
|
2655
|
-
function position(value, asset) {
|
|
2656
|
-
const option = value?.yieldOption;
|
|
2657
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress(option.address)) {
|
|
2658
|
-
return invalid("yield positions", "missing vault metadata");
|
|
2659
|
-
}
|
|
2660
|
-
const decimals = YIELDSEEKER_ASSET_METADATA[asset].decimals;
|
|
2661
|
-
return {
|
|
2662
|
-
chain: "BASE",
|
|
2663
|
-
protocol: option.provider,
|
|
2664
|
-
protocolId: option.address,
|
|
2665
|
-
pool: option.name,
|
|
2666
|
-
asset,
|
|
2667
|
-
amount: decimal(value.assetsRaw, decimals, "yield positions"),
|
|
2668
|
-
amountRaw: String(value.assetsRaw),
|
|
2669
|
-
apy: percent(option.riskAdjustedApy),
|
|
2670
|
-
tvl: Number(option.totalDepositsUsd),
|
|
2671
|
-
liquidity: Number(option.withdrawableDepositsUsd)
|
|
2672
|
-
};
|
|
2673
|
-
}
|
|
2674
|
-
function mapYieldseekerBalances(contexts) {
|
|
2675
|
-
const tokens = [];
|
|
2676
|
-
const positions = [];
|
|
2677
|
-
let totalUsd = 0;
|
|
2678
|
-
for (const context of contexts) {
|
|
2679
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
2680
|
-
const idle = assetAddressValue(
|
|
2681
|
-
context.snapshot.tokenBalances,
|
|
2682
|
-
metadata.address
|
|
2683
|
-
);
|
|
2684
|
-
tokens.push({
|
|
2685
|
-
chain: "BASE",
|
|
2686
|
-
chainId: 8453,
|
|
2687
|
-
asset: context.asset,
|
|
2688
|
-
amount: decimal(idle, metadata.decimals, "snapshot")
|
|
2689
|
-
});
|
|
2690
|
-
positions.push(
|
|
2691
|
-
...context.positions.map((entry) => position(entry, context.asset))
|
|
2692
|
-
);
|
|
2693
|
-
totalUsd += usd(
|
|
2694
|
-
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2695
|
-
context.snapshot.baseAssetDecimals,
|
|
2696
|
-
context.snapshot.baseAssetPriceUsd
|
|
2697
|
-
);
|
|
2698
|
-
}
|
|
2699
|
-
return {
|
|
2700
|
-
...contexts.length === 1 ? { smartWallet: contexts[0].wallet.walletAddress } : {},
|
|
2701
|
-
totalBalance: String(totalUsd),
|
|
2702
|
-
totalBalanceAsset: "usdc",
|
|
2703
|
-
tokens,
|
|
2704
|
-
positions
|
|
2705
|
-
};
|
|
2706
|
-
}
|
|
2707
|
-
function mapYieldseekerEarnings(contexts) {
|
|
2708
|
-
const tokens = [];
|
|
2709
|
-
let lifetimeEarnings = 0;
|
|
2710
|
-
for (const context of contexts) {
|
|
2711
|
-
const amount = raw(context.snapshot.cumulativeYieldBase, "snapshot");
|
|
2712
|
-
tokens.push({
|
|
2713
|
-
chain: "BASE",
|
|
2714
|
-
chainId: 8453,
|
|
2715
|
-
asset: context.asset,
|
|
2716
|
-
amount: formatUnits(amount, context.snapshot.baseAssetDecimals)
|
|
2717
|
-
});
|
|
2718
|
-
lifetimeEarnings += usd(
|
|
2719
|
-
amount,
|
|
2720
|
-
context.snapshot.baseAssetDecimals,
|
|
2721
|
-
context.snapshot.baseAssetPriceUsd
|
|
2722
|
-
);
|
|
2723
|
-
}
|
|
2724
|
-
return {
|
|
2725
|
-
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2726
|
-
lifetimeEarnings,
|
|
2727
|
-
tokens
|
|
2728
|
-
};
|
|
2729
|
-
}
|
|
2730
|
-
function apyForDays(snapshot, days) {
|
|
2731
|
-
if (days === "7D") return percent(snapshot.apy7d);
|
|
2732
|
-
if (days === "30D") return percent(snapshot.apy30d);
|
|
2733
|
-
return percent((snapshot.apy7d + snapshot.apy30d) / 2);
|
|
2734
|
-
}
|
|
2735
|
-
function dailyApy(point) {
|
|
2736
|
-
const total = raw(point.totalValueBase, "historic position");
|
|
2737
|
-
const earned = raw(point.dailyYieldBase, "historic position");
|
|
2738
|
-
const principal = total - earned;
|
|
2739
|
-
if (principal <= 0n || earned === 0n) return 0;
|
|
2740
|
-
return Number(earned) / Number(principal) * 365 * 100;
|
|
2741
|
-
}
|
|
2742
|
-
function aggregateHistory(contexts, dayCount) {
|
|
2743
|
-
const byDate = /* @__PURE__ */ new Map();
|
|
2744
|
-
for (const context of contexts) {
|
|
2745
|
-
const points = context.historic?.dailyYieldSnapshots ?? [];
|
|
2746
|
-
for (const point of points.slice(-dayCount)) {
|
|
2747
|
-
const valueUsd = usd(
|
|
2748
|
-
raw(point.totalValueBase, "historic position"),
|
|
2749
|
-
point.baseAssetDecimals,
|
|
2750
|
-
point.baseAssetPriceUsd
|
|
2751
|
-
);
|
|
2752
|
-
const current = byDate.get(point.date) ?? { weighted: 0, valueUsd: 0 };
|
|
2753
|
-
current.weighted += dailyApy(point) * valueUsd;
|
|
2754
|
-
current.valueUsd += valueUsd;
|
|
2755
|
-
byDate.set(point.date, current);
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
2759
|
-
date,
|
|
2760
|
-
apy: value.valueUsd > 0 ? value.weighted / value.valueUsd : 0
|
|
2761
|
-
}));
|
|
2762
|
-
}
|
|
2763
|
-
function mapYieldseekerApy(walletAddress, contexts, days) {
|
|
2764
|
-
let weighted = 0;
|
|
2765
|
-
let totalUsd = 0;
|
|
2766
|
-
const byAsset = {};
|
|
2767
|
-
for (const context of contexts) {
|
|
2768
|
-
const valueUsd = usd(
|
|
2769
|
-
raw(context.snapshot.totalValueBase, "snapshot"),
|
|
2770
|
-
context.snapshot.baseAssetDecimals,
|
|
2771
|
-
context.snapshot.baseAssetPriceUsd
|
|
2772
|
-
);
|
|
2773
|
-
const apy = apyForDays(context.snapshot, days);
|
|
2774
|
-
weighted += apy * valueUsd;
|
|
2775
|
-
totalUsd += valueUsd;
|
|
2776
|
-
byAsset[context.asset] = apy;
|
|
2777
|
-
}
|
|
2778
|
-
const dayCount = Number(days.slice(0, -1));
|
|
2779
|
-
return {
|
|
2780
|
-
walletAddress,
|
|
2781
|
-
weightedApyAfterFee: totalUsd > 0 ? weighted / totalUsd : 0,
|
|
2782
|
-
apyByChainAndAsset: { 8453: byAsset },
|
|
2783
|
-
history: aggregateHistory(contexts, dayCount)
|
|
2784
|
-
};
|
|
2785
|
-
}
|
|
2786
|
-
function actionType(value) {
|
|
2787
|
-
const normalized = value.toLowerCase();
|
|
2788
|
-
if (normalized.includes("deposit")) return "Deposit";
|
|
2789
|
-
if (normalized.includes("withdraw")) return "Withdraw";
|
|
2790
|
-
if (normalized.includes("yield") || normalized.includes("earn"))
|
|
2791
|
-
return "Earned";
|
|
2792
|
-
return "Rebalance";
|
|
2793
|
-
}
|
|
2794
|
-
function transactionHashes(details) {
|
|
2795
|
-
if (!details) return [];
|
|
2796
|
-
const values = [
|
|
2797
|
-
details.transactionHash,
|
|
2798
|
-
details.txHash,
|
|
2799
|
-
...Array.isArray(details.transactionHashes) ? details.transactionHashes : [],
|
|
2800
|
-
...Array.isArray(details.txHashes) ? details.txHashes : []
|
|
2801
|
-
];
|
|
2802
|
-
return values.filter(
|
|
2803
|
-
(value) => typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value)
|
|
2804
|
-
).filter((value, index, all) => all.indexOf(value) === index);
|
|
2805
|
-
}
|
|
2806
|
-
function actionEntry(action) {
|
|
2807
|
-
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
2808
|
-
return {
|
|
2809
|
-
agent: "yieldseeker",
|
|
2810
|
-
action: actionType(action.actionType),
|
|
2811
|
-
date: action.createdDate,
|
|
2812
|
-
oldApy: null,
|
|
2813
|
-
newApy: null,
|
|
2814
|
-
transactions: [
|
|
2815
|
-
{
|
|
2816
|
-
txHashes: transactionHashes(action.details),
|
|
2817
|
-
chainId: 8453
|
|
2818
|
-
}
|
|
2819
|
-
],
|
|
2820
|
-
rebalanceLog: []
|
|
2821
|
-
};
|
|
2822
|
-
}
|
|
2823
|
-
function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddresses) {
|
|
2824
|
-
const from = movement.fromAddress.toLowerCase();
|
|
2825
|
-
const to = movement.toAddress.toLowerCase();
|
|
2826
|
-
const owner = ownerAddress.toLowerCase();
|
|
2827
|
-
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
2828
|
-
const baseAsset = agent.assetAddress.toLowerCase();
|
|
2829
|
-
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
2830
|
-
return void 0;
|
|
2831
|
-
}
|
|
2832
|
-
let action;
|
|
2833
|
-
if (from === owner && to === agentWallet) {
|
|
2834
|
-
action = "Top up";
|
|
2835
|
-
} else if (from === agentWallet && to === owner) {
|
|
2836
|
-
action = "Withdraw";
|
|
2837
|
-
} else if (from === agentWallet && vaultAddresses.has(to)) {
|
|
2838
|
-
action = "Deposit";
|
|
2839
|
-
}
|
|
2840
|
-
if (!action) return void 0;
|
|
2841
|
-
return {
|
|
2842
|
-
agent: "yieldseeker",
|
|
2843
|
-
action,
|
|
2844
|
-
date: movement.blockDate,
|
|
2845
|
-
oldApy: null,
|
|
2846
|
-
newApy: null,
|
|
2847
|
-
transactions: [
|
|
2848
|
-
{
|
|
2849
|
-
txHashes: [movement.transactionHash],
|
|
2850
|
-
chainId: agent.chainId,
|
|
2851
|
-
tokenSymbol: asset,
|
|
2852
|
-
amount: decimal(
|
|
2853
|
-
movement.assetAmount,
|
|
2854
|
-
YIELDSEEKER_ASSET_METADATA[asset].decimals,
|
|
2855
|
-
"historic position"
|
|
2856
|
-
)
|
|
2857
|
-
}
|
|
2858
|
-
],
|
|
2859
|
-
rebalanceLog: []
|
|
2860
|
-
};
|
|
2861
|
-
}
|
|
2862
|
-
function mapYieldseekerHistory(contexts, options) {
|
|
2863
|
-
const entries = contexts.flatMap((context) => {
|
|
2864
|
-
const vaultAddresses = new Set(
|
|
2865
|
-
context.positions.map(
|
|
2866
|
-
(position2) => position2.yieldOption.address.toLowerCase()
|
|
2867
|
-
)
|
|
2868
|
-
);
|
|
2869
|
-
return [
|
|
2870
|
-
...(context.historic?.movements ?? []).map(
|
|
2871
|
-
(movement) => movementEntry(
|
|
2872
|
-
movement,
|
|
2873
|
-
context.wallet,
|
|
2874
|
-
context.agent,
|
|
2875
|
-
context.asset,
|
|
2876
|
-
options.ownerAddress,
|
|
2877
|
-
vaultAddresses
|
|
2878
|
-
)
|
|
2879
|
-
),
|
|
2880
|
-
...(context.actions ?? []).map(actionEntry)
|
|
2881
|
-
].filter((entry) => entry !== void 0);
|
|
2882
|
-
});
|
|
2883
|
-
const filtered = entries.filter(
|
|
2884
|
-
(entry) => (!options.fromDate || entry.date >= options.fromDate) && (!options.toDate || entry.date <= options.toDate)
|
|
2885
|
-
).filter((entry, index, all) => {
|
|
2886
|
-
const transactionHash = entry.transactions[0]?.txHashes[0];
|
|
2887
|
-
if (!transactionHash) return true;
|
|
2888
|
-
return all.findIndex(
|
|
2889
|
-
(candidate) => candidate.action === entry.action && candidate.transactions[0]?.txHashes[0] === transactionHash
|
|
2890
|
-
) === index;
|
|
2891
|
-
}).sort((left, right) => right.date.localeCompare(left.date));
|
|
2892
|
-
return {
|
|
2893
|
-
data: filtered.slice(0, options.limit),
|
|
2894
|
-
// v1 returns the whole action/movement collection and defines no cursor.
|
|
2895
|
-
// Report a terminal page so callers never loop over the same prefix.
|
|
2896
|
-
hasMore: false
|
|
2897
|
-
};
|
|
2898
|
-
}
|
|
2899
|
-
function mapYieldseekerProfile(address, contexts) {
|
|
2900
|
-
const protocols = /* @__PURE__ */ new Set();
|
|
2901
|
-
for (const context of contexts) {
|
|
2902
|
-
for (const current of context.positions) {
|
|
2903
|
-
if (current.yieldOption?.provider) {
|
|
2904
|
-
protocols.add(String(current.yieldOption.provider));
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
|
-
}
|
|
2908
|
-
return {
|
|
2909
|
-
address,
|
|
2910
|
-
smartWallet: contexts[0]?.wallet.walletAddress ?? "0x0000000000000000000000000000000000000000",
|
|
2911
|
-
chains: contexts.length > 0 ? [8453] : [],
|
|
2912
|
-
hasActiveSessionKey: contexts.some(
|
|
2913
|
-
(context) => context.wallet.initializedDate != null
|
|
2914
|
-
),
|
|
2915
|
-
protocols: [...protocols]
|
|
2916
|
-
};
|
|
2917
|
-
}
|
|
2918
|
-
function mapYieldseekerAgentApy(options, days) {
|
|
2919
|
-
const perAsset = {};
|
|
2920
|
-
const all = [];
|
|
2921
|
-
for (const entry of options) {
|
|
2922
|
-
const apys = entry.yieldOptions.map(
|
|
2923
|
-
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
2924
|
-
).filter(Number.isFinite);
|
|
2925
|
-
if (apys.length === 0) continue;
|
|
2926
|
-
const average = apys.reduce((sum, value) => sum + value, 0) / apys.length;
|
|
2927
|
-
perAsset[entry.asset] = average;
|
|
2928
|
-
all.push(average);
|
|
2929
|
-
}
|
|
2930
|
-
return {
|
|
2931
|
-
averageApy: all.length > 0 ? all.reduce((sum, value) => sum + value, 0) / all.length : 0,
|
|
2932
|
-
detailedApys: { apyPerAsset: { 8453: perAsset } }
|
|
2933
|
-
};
|
|
2934
|
-
}
|
|
2935
|
-
|
|
2936
|
-
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2937
|
-
var OWNEY_AGENT_NAME = "owney";
|
|
2938
|
-
var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
2939
|
-
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
2940
|
-
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
2941
|
-
function generateYieldseekerUsername() {
|
|
2942
|
-
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
2943
|
-
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
2944
|
-
}
|
|
2945
|
-
function isUsernameConflict(error) {
|
|
2946
|
-
if (!(error instanceof YieldseekerApiError)) return false;
|
|
2947
|
-
const code = error.providerCode.toUpperCase();
|
|
2948
|
-
return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
|
|
2949
|
-
}
|
|
2950
|
-
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
2951
|
-
{
|
|
2952
|
-
type: "function",
|
|
2953
|
-
name: "withdrawAssetToUser",
|
|
2954
|
-
stateMutability: "nonpayable",
|
|
2955
|
-
inputs: [
|
|
2956
|
-
{ name: "recipient", type: "address" },
|
|
2957
|
-
{ name: "asset", type: "address" },
|
|
2958
|
-
{ name: "amount", type: "uint256" }
|
|
2959
|
-
],
|
|
2960
|
-
outputs: []
|
|
2961
|
-
},
|
|
2962
|
-
{
|
|
2963
|
-
type: "function",
|
|
2964
|
-
name: "withdrawAllAssetToUser",
|
|
2965
|
-
stateMutability: "nonpayable",
|
|
2966
|
-
inputs: [
|
|
2967
|
-
{ name: "recipient", type: "address" },
|
|
2968
|
-
{ name: "asset", type: "address" }
|
|
2969
|
-
],
|
|
2970
|
-
outputs: []
|
|
2971
|
-
}
|
|
2972
|
-
];
|
|
2973
|
-
function query(params) {
|
|
2974
|
-
const search = new URLSearchParams();
|
|
2975
|
-
for (const [key2, value] of Object.entries(params)) {
|
|
2976
|
-
if (value !== void 0) search.set(key2, String(value));
|
|
2977
|
-
}
|
|
2978
|
-
const encoded = search.toString();
|
|
2979
|
-
return encoded ? `?${encoded}` : "";
|
|
2980
|
-
}
|
|
2981
|
-
var YieldseekerAgent = class {
|
|
2982
|
-
id = "yieldseeker";
|
|
2983
|
-
balanceComposition = "tokens-plus-positions";
|
|
2984
|
-
supportedChainIds = [8453];
|
|
2985
|
-
supportedAssets = [
|
|
2986
|
-
{
|
|
2987
|
-
chainId: 8453,
|
|
2988
|
-
chain: "BASE",
|
|
2989
|
-
assets: [
|
|
2990
|
-
{ symbol: "USDC", minDepositAmount: "10000000" },
|
|
2991
|
-
{ symbol: "WETH", minDepositAmount: "1" }
|
|
2992
|
-
]
|
|
2993
|
-
}
|
|
2994
|
-
];
|
|
2995
|
-
api;
|
|
2996
|
-
auth;
|
|
2997
|
-
transactionExecutor;
|
|
2998
|
-
unwindReceiptWaiter;
|
|
2999
|
-
agentContexts = /* @__PURE__ */ new Map();
|
|
3000
|
-
users = /* @__PURE__ */ new Map();
|
|
3001
|
-
pendingAgents = /* @__PURE__ */ new Map();
|
|
3002
|
-
constructor(owneyApiKey, options = {}) {
|
|
3003
|
-
this.api = new YieldseekerApiClient(
|
|
3004
|
-
owneyApiKey,
|
|
3005
|
-
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
3006
|
-
options.fetchFn
|
|
3007
|
-
);
|
|
3008
|
-
this.auth = new YieldseekerAuth(options.auth);
|
|
3009
|
-
this.transactionExecutor = options.transactionExecutor;
|
|
3010
|
-
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3011
|
-
}
|
|
3012
|
-
async disconnect() {
|
|
3013
|
-
this.auth.clear();
|
|
3014
|
-
for (const key2 of this.users.keys()) {
|
|
3015
|
-
const [walletAddress, chainId] = key2.split(":");
|
|
3016
|
-
clearYieldseekerIdentity(walletAddress, Number(chainId));
|
|
3017
|
-
}
|
|
3018
|
-
this.users.clear();
|
|
3019
|
-
this.agentContexts.clear();
|
|
3020
|
-
this.pendingAgents.clear();
|
|
3021
|
-
}
|
|
3022
|
-
async activateAgent(state, chainId, asset) {
|
|
3023
|
-
this.assertChain(chainId);
|
|
3024
|
-
const targetAsset = asset ?? "USDC";
|
|
3025
|
-
this.assertAsset(targetAsset);
|
|
3026
|
-
await this.ensureAgent(state, chainId, targetAsset);
|
|
3027
|
-
}
|
|
3028
|
-
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3029
|
-
this.assertChain(chainId);
|
|
3030
|
-
this.assertAsset(asset);
|
|
3031
|
-
if (BigInt(amount) <= 0n) {
|
|
3032
|
-
throw new OwneyError(
|
|
3033
|
-
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
3034
|
-
"Yieldseeker deposits must be greater than zero.",
|
|
3035
|
-
{ amount, minDepositAmount: "1" },
|
|
3036
|
-
this.id
|
|
3037
|
-
);
|
|
3038
|
-
}
|
|
3039
|
-
const context = await this.ensureAgent(state, chainId, asset);
|
|
3040
|
-
let txHash;
|
|
3041
|
-
try {
|
|
3042
|
-
if (depositCallback) {
|
|
3043
|
-
provideDepositVerificationContext(depositCallback, {
|
|
3044
|
-
agentId: "yieldseeker",
|
|
3045
|
-
signature: await this.auth.getToken(state, chainId),
|
|
3046
|
-
userId: context.user.userId,
|
|
3047
|
-
yieldseekerAgentId: context.agent.agentId
|
|
3048
|
-
});
|
|
3049
|
-
txHash = await depositCallback(
|
|
3050
|
-
context.wallet.walletAddress,
|
|
3051
|
-
chainId,
|
|
3052
|
-
amount
|
|
3053
|
-
);
|
|
3054
|
-
await this.waitForReceipt(state, chainId, txHash);
|
|
3055
|
-
} else {
|
|
3056
|
-
txHash = await this.submitTransaction(state, chainId, {
|
|
3057
|
-
from: getAddress2(state.walletAddress),
|
|
3058
|
-
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3059
|
-
data: encodeFunctionData({
|
|
3060
|
-
abi: erc20Abi,
|
|
3061
|
-
functionName: "transfer",
|
|
3062
|
-
args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
|
|
3063
|
-
}),
|
|
3064
|
-
value: "0",
|
|
3065
|
-
chainId
|
|
3066
|
-
});
|
|
3067
|
-
}
|
|
3068
|
-
await this.deployAfterFunding(state, chainId, context);
|
|
3069
|
-
} finally {
|
|
3070
|
-
await this.refreshSnapshotAfterMovement(
|
|
3071
|
-
state,
|
|
3072
|
-
chainId,
|
|
3073
|
-
context,
|
|
3074
|
-
"deposit"
|
|
3075
|
-
);
|
|
3076
|
-
}
|
|
3077
|
-
return {
|
|
3078
|
-
txHash,
|
|
3079
|
-
smartWallet: context.wallet.walletAddress,
|
|
3080
|
-
amount
|
|
3081
|
-
};
|
|
3082
|
-
}
|
|
3083
|
-
async withdraw(state, chainId, asset, amount) {
|
|
3084
|
-
this.assertChain(chainId);
|
|
3085
|
-
this.assertAsset(asset);
|
|
3086
|
-
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
3087
|
-
throw new OwneyError(
|
|
3088
|
-
"WITHDRAW_FAILED",
|
|
3089
|
-
"Yieldseeker withdrawals must be greater than zero.",
|
|
3090
|
-
{ amount },
|
|
3091
|
-
this.id
|
|
3092
|
-
);
|
|
3093
|
-
}
|
|
3094
|
-
const context = await this.findAgent(state, chainId, asset);
|
|
3095
|
-
if (!context) {
|
|
3096
|
-
throw new OwneyError(
|
|
3097
|
-
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3098
|
-
`No Yieldseeker ${asset} agent exists for this wallet.`,
|
|
3099
|
-
{ asset, available: "0" },
|
|
3100
|
-
this.id
|
|
3101
|
-
);
|
|
3102
|
-
}
|
|
3103
|
-
try {
|
|
3104
|
-
const portfolio = await this.loadPortfolioContext(
|
|
3105
|
-
state,
|
|
3106
|
-
chainId,
|
|
3107
|
-
context
|
|
3108
|
-
);
|
|
3109
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3110
|
-
const idleEntry = Object.entries(portfolio.snapshot.tokenBalances).find(
|
|
3111
|
-
([address]) => address.toLowerCase() === metadata.address.toLowerCase()
|
|
3112
|
-
);
|
|
3113
|
-
const idle = BigInt(idleEntry?.[1] ?? "0");
|
|
3114
|
-
const deployed = portfolio.positions.reduce(
|
|
3115
|
-
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3116
|
-
0n
|
|
3117
|
-
);
|
|
3118
|
-
const totalAvailable = idle + deployed;
|
|
3119
|
-
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3120
|
-
if (requested > totalAvailable) {
|
|
3121
|
-
throw new OwneyError(
|
|
3122
|
-
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3123
|
-
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
3124
|
-
{
|
|
3125
|
-
asset,
|
|
3126
|
-
requested: requested.toString(),
|
|
3127
|
-
available: totalAvailable.toString()
|
|
3128
|
-
},
|
|
3129
|
-
this.id
|
|
3130
|
-
);
|
|
3131
|
-
}
|
|
3132
|
-
let remaining = requested > idle ? requested - idle : 0n;
|
|
3133
|
-
for (const position2 of portfolio.positions) {
|
|
3134
|
-
if (remaining === 0n) break;
|
|
3135
|
-
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
3136
|
-
if (available <= 0n) continue;
|
|
3137
|
-
const assetsRaw = available < remaining ? available : remaining;
|
|
3138
|
-
const response = await this.walletRequest(
|
|
3139
|
-
state,
|
|
3140
|
-
chainId,
|
|
3141
|
-
this.agentPath(context, "withdraw-from-position"),
|
|
3142
|
-
{
|
|
3143
|
-
method: "POST",
|
|
3144
|
-
body: {
|
|
3145
|
-
chainId,
|
|
3146
|
-
vaultAddress: position2.yieldOption.address,
|
|
3147
|
-
assetsRaw: assetsRaw.toString()
|
|
3148
|
-
}
|
|
3149
|
-
}
|
|
3150
|
-
);
|
|
3151
|
-
if (!this.isTransactionHash(response?.transactionHash)) {
|
|
3152
|
-
throw this.invalidResponse("position withdrawal");
|
|
3153
|
-
}
|
|
3154
|
-
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3155
|
-
remaining -= assetsRaw;
|
|
3156
|
-
}
|
|
3157
|
-
if (remaining > 0n) {
|
|
3158
|
-
throw this.invalidResponse("yield positions", {
|
|
3159
|
-
reason: "Withdrawable positions could not cover the request.",
|
|
3160
|
-
remaining: remaining.toString()
|
|
3161
|
-
});
|
|
3162
|
-
}
|
|
3163
|
-
const account = getAddress2(state.walletAddress);
|
|
3164
|
-
const txHash = await this.submitTransaction(state, chainId, {
|
|
3165
|
-
from: account,
|
|
3166
|
-
to: getAddress2(context.wallet.walletAddress),
|
|
3167
|
-
data: amount === void 0 ? encodeFunctionData({
|
|
3168
|
-
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3169
|
-
functionName: "withdrawAllAssetToUser",
|
|
3170
|
-
args: [account, metadata.address]
|
|
3171
|
-
}) : encodeFunctionData({
|
|
3172
|
-
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3173
|
-
functionName: "withdrawAssetToUser",
|
|
3174
|
-
args: [account, metadata.address, requested]
|
|
3175
|
-
}),
|
|
3176
|
-
value: "0",
|
|
3177
|
-
chainId
|
|
3178
|
-
});
|
|
3179
|
-
return {
|
|
3180
|
-
txHash,
|
|
3181
|
-
type: amount === void 0 ? "full" : "partial",
|
|
3182
|
-
amount: requested.toString()
|
|
3183
|
-
};
|
|
3184
|
-
} finally {
|
|
3185
|
-
await this.refreshSnapshotAfterMovement(
|
|
3186
|
-
state,
|
|
3187
|
-
chainId,
|
|
3188
|
-
context,
|
|
3189
|
-
"withdrawal"
|
|
3190
|
-
);
|
|
3191
|
-
}
|
|
3192
|
-
}
|
|
3193
|
-
async getBalances(state, chainId) {
|
|
3194
|
-
this.assertChain(chainId);
|
|
3195
|
-
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, {}));
|
|
3196
|
-
}
|
|
3197
|
-
async getEarnings(state, chainId) {
|
|
3198
|
-
this.assertChain(chainId);
|
|
3199
|
-
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
3200
|
-
}
|
|
3201
|
-
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
3202
|
-
this.assertChain(chainId);
|
|
3203
|
-
const asset = tokenSymbol?.toUpperCase();
|
|
3204
|
-
if (asset !== void 0) this.assertAsset(asset);
|
|
3205
|
-
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3206
|
-
...asset ? { asset } : {},
|
|
3207
|
-
historic: true
|
|
3208
|
-
});
|
|
3209
|
-
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
3210
|
-
}
|
|
3211
|
-
async getHistory(state, chainId, options) {
|
|
3212
|
-
this.assertChain(chainId);
|
|
3213
|
-
const asset = options?.tokenSymbol?.toUpperCase();
|
|
3214
|
-
if (asset !== void 0) this.assertAsset(asset);
|
|
3215
|
-
const contexts = await this.loadPortfolio(state, chainId, {
|
|
3216
|
-
...asset ? { asset } : {},
|
|
3217
|
-
historic: true,
|
|
3218
|
-
actions: true
|
|
3219
|
-
});
|
|
3220
|
-
return mapYieldseekerHistory(contexts, {
|
|
3221
|
-
limit: options?.limit ?? 10,
|
|
3222
|
-
ownerAddress: state.walletAddress,
|
|
3223
|
-
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
3224
|
-
...options?.toDate ? { toDate: options.toDate } : {}
|
|
3225
|
-
});
|
|
3226
|
-
}
|
|
3227
|
-
async getUserProfile(state, chainId) {
|
|
3228
|
-
this.assertChain(chainId);
|
|
3229
|
-
return mapYieldseekerProfile(
|
|
3230
|
-
state.walletAddress,
|
|
3231
|
-
await this.loadPortfolio(state, chainId, {})
|
|
3232
|
-
);
|
|
3233
|
-
}
|
|
3234
|
-
async getAgentApy(days, options) {
|
|
3235
|
-
this.assertOptionalChain(options?.chainId);
|
|
3236
|
-
const requested = options?.tokenSymbol?.toUpperCase();
|
|
3237
|
-
if (requested !== void 0) this.assertAsset(requested);
|
|
3238
|
-
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
3239
|
-
const values = await Promise.all(
|
|
3240
|
-
assets.map(async (asset) => {
|
|
3241
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3242
|
-
const response = await this.api.request(
|
|
3243
|
-
`/chains/8453/assets/${metadata.address}/yield-options`
|
|
3244
|
-
);
|
|
3245
|
-
if (!Array.isArray(response?.yieldOptions)) {
|
|
3246
|
-
throw this.invalidResponse("yield options");
|
|
3247
|
-
}
|
|
3248
|
-
return { asset, yieldOptions: response.yieldOptions };
|
|
3249
|
-
})
|
|
3250
|
-
);
|
|
3251
|
-
return mapYieldseekerAgentApy(values, days);
|
|
3252
|
-
}
|
|
3253
|
-
userKey(state, chainId) {
|
|
3254
|
-
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
3255
|
-
}
|
|
3256
|
-
contextKey(state, chainId, asset) {
|
|
3257
|
-
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3258
|
-
}
|
|
3259
|
-
async resolveUser(state, chainId) {
|
|
3260
|
-
const key2 = this.userKey(state, chainId);
|
|
3261
|
-
const inMemory = this.users.get(key2);
|
|
3262
|
-
if (inMemory) return inMemory;
|
|
3263
|
-
const persisted = readYieldseekerIdentity(state.walletAddress, chainId);
|
|
3264
|
-
if (persisted) {
|
|
3265
|
-
this.users.set(key2, persisted);
|
|
3266
|
-
return persisted;
|
|
3267
|
-
}
|
|
3268
|
-
const walletAddress = getAddress2(state.walletAddress);
|
|
3269
|
-
let user = null;
|
|
3270
|
-
try {
|
|
3271
|
-
const login = await this.providerRequest(
|
|
3272
|
-
state,
|
|
3273
|
-
chainId,
|
|
3274
|
-
"/users/login-with-wallet",
|
|
3275
|
-
{ method: "POST", body: { walletAddress } }
|
|
3276
|
-
);
|
|
3277
|
-
user = login?.user ?? null;
|
|
3278
|
-
if (!user) {
|
|
3279
|
-
throw this.invalidResponse("wallet login", {
|
|
3280
|
-
reason: "A successful login returned no user."
|
|
3281
|
-
});
|
|
3282
|
-
}
|
|
3283
|
-
} catch (error) {
|
|
3284
|
-
if (!(error instanceof YieldseekerApiError) || error.providerCode !== "NO_USER") {
|
|
3285
|
-
if (error instanceof OwneyError) throw error;
|
|
3286
|
-
throw this.mapApiError(error);
|
|
3287
|
-
}
|
|
3288
|
-
let created;
|
|
3289
|
-
for (let attempt = 0; attempt < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS; attempt += 1) {
|
|
3290
|
-
try {
|
|
3291
|
-
created = await this.providerRequest(
|
|
3292
|
-
state,
|
|
3293
|
-
chainId,
|
|
3294
|
-
"/users",
|
|
3295
|
-
{
|
|
3296
|
-
method: "POST",
|
|
3297
|
-
body: {
|
|
3298
|
-
walletAddress,
|
|
3299
|
-
username: generateYieldseekerUsername()
|
|
3300
|
-
}
|
|
3301
|
-
}
|
|
3302
|
-
);
|
|
3303
|
-
break;
|
|
3304
|
-
} catch (createError) {
|
|
3305
|
-
const canRetry = isUsernameConflict(createError) && attempt + 1 < YIELDSEEKER_USERNAME_CREATE_ATTEMPTS;
|
|
3306
|
-
if (canRetry) continue;
|
|
3307
|
-
throw this.mapApiError(createError);
|
|
3308
|
-
}
|
|
3309
|
-
}
|
|
3310
|
-
user = created?.user ?? null;
|
|
3311
|
-
}
|
|
3312
|
-
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3313
|
-
throw this.invalidResponse("wallet identity");
|
|
3314
|
-
}
|
|
3315
|
-
const resolved = { userId: user.userId };
|
|
3316
|
-
this.users.set(key2, resolved);
|
|
3317
|
-
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
3318
|
-
return resolved;
|
|
3319
|
-
}
|
|
3320
|
-
forgetUser(state, chainId) {
|
|
3321
|
-
this.users.delete(this.userKey(state, chainId));
|
|
3322
|
-
clearYieldseekerIdentity(state.walletAddress, chainId);
|
|
3323
|
-
}
|
|
3324
|
-
async ensureAgent(state, chainId, asset) {
|
|
3325
|
-
const key2 = this.contextKey(state, chainId, asset);
|
|
3326
|
-
const cached = this.agentContexts.get(key2);
|
|
3327
|
-
if (cached) return cached;
|
|
3328
|
-
const pending = this.pendingAgents.get(key2);
|
|
3329
|
-
if (pending) return pending;
|
|
3330
|
-
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3331
|
-
(context) => {
|
|
3332
|
-
if (!context) throw this.invalidResponse("agent creation");
|
|
3333
|
-
this.agentContexts.set(key2, context);
|
|
3334
|
-
return context;
|
|
3335
|
-
}
|
|
3336
|
-
);
|
|
3337
|
-
this.pendingAgents.set(key2, request);
|
|
3338
|
-
try {
|
|
3339
|
-
return await request;
|
|
3340
|
-
} finally {
|
|
3341
|
-
this.pendingAgents.delete(key2);
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
async findAgent(state, chainId, asset) {
|
|
3345
|
-
const key2 = this.contextKey(state, chainId, asset);
|
|
3346
|
-
const cached = this.agentContexts.get(key2);
|
|
3347
|
-
if (cached) return cached;
|
|
3348
|
-
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3349
|
-
if (context) this.agentContexts.set(key2, context);
|
|
3350
|
-
return context;
|
|
3351
|
-
}
|
|
3352
|
-
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3353
|
-
const user = await this.resolveUser(state, chainId);
|
|
3354
|
-
const response = await this.walletRequest(
|
|
3355
|
-
state,
|
|
3356
|
-
chainId,
|
|
3357
|
-
`/users/${user.userId}/agents`
|
|
3358
|
-
);
|
|
3359
|
-
if (!Array.isArray(response?.agents)) {
|
|
3360
|
-
throw this.invalidResponse("agent list");
|
|
3361
|
-
}
|
|
3362
|
-
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3363
|
-
let agent = response.agents.find(
|
|
3364
|
-
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
3365
|
-
);
|
|
3366
|
-
if (!agent && createIfMissing) {
|
|
3367
|
-
const created = await this.walletRequest(
|
|
3368
|
-
state,
|
|
3369
|
-
chainId,
|
|
3370
|
-
`/users/${user.userId}/agents`,
|
|
3371
|
-
{
|
|
3372
|
-
method: "POST",
|
|
3373
|
-
body: {
|
|
3374
|
-
name: OWNEY_AGENT_NAME,
|
|
3375
|
-
emoji: "\u{1F989}",
|
|
3376
|
-
chainId,
|
|
3377
|
-
assetAddress: metadata.address,
|
|
3378
|
-
type: "vault",
|
|
3379
|
-
rulePreset: null
|
|
3380
|
-
}
|
|
3381
|
-
}
|
|
3382
|
-
);
|
|
3383
|
-
agent = created?.agent;
|
|
3384
|
-
}
|
|
3385
|
-
if (!agent) return null;
|
|
3386
|
-
this.assertAgent(agent);
|
|
3387
|
-
const walletResponse = await this.walletRequest(
|
|
3388
|
-
state,
|
|
3389
|
-
chainId,
|
|
3390
|
-
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3391
|
-
);
|
|
3392
|
-
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3393
|
-
throw this.invalidResponse("agent wallet");
|
|
3394
|
-
}
|
|
3395
|
-
return { user, agent, wallet: walletResponse.agentWallet, asset };
|
|
3396
|
-
}
|
|
3397
|
-
async loadPortfolio(state, chainId, options) {
|
|
3398
|
-
const user = await this.resolveUser(state, chainId);
|
|
3399
|
-
const response = await this.walletRequest(
|
|
3400
|
-
state,
|
|
3401
|
-
chainId,
|
|
3402
|
-
`/users/${user.userId}/agents`
|
|
3403
|
-
);
|
|
3404
|
-
if (!Array.isArray(response?.agents)) {
|
|
3405
|
-
throw this.invalidResponse("agent list");
|
|
3406
|
-
}
|
|
3407
|
-
const contexts = [];
|
|
3408
|
-
for (const agent of response.agents) {
|
|
3409
|
-
const asset = this.assetForAgent(agent);
|
|
3410
|
-
if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
|
|
3411
|
-
continue;
|
|
3412
|
-
}
|
|
3413
|
-
this.assertAgent(agent);
|
|
3414
|
-
const walletResponse = await this.walletRequest(
|
|
3415
|
-
state,
|
|
3416
|
-
chainId,
|
|
3417
|
-
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3418
|
-
);
|
|
3419
|
-
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3420
|
-
throw this.invalidResponse("agent wallet");
|
|
3421
|
-
}
|
|
3422
|
-
const context = {
|
|
3423
|
-
user,
|
|
3424
|
-
agent,
|
|
3425
|
-
wallet: walletResponse.agentWallet,
|
|
3426
|
-
asset
|
|
3427
|
-
};
|
|
3428
|
-
this.agentContexts.set(this.contextKey(state, chainId, asset), context);
|
|
3429
|
-
contexts.push(context);
|
|
3430
|
-
}
|
|
3431
|
-
return Promise.all(
|
|
3432
|
-
contexts.map(
|
|
3433
|
-
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3434
|
-
)
|
|
3435
|
-
);
|
|
3436
|
-
}
|
|
3437
|
-
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
3438
|
-
const [snapshot, positions, historic, actions] = await Promise.all([
|
|
3439
|
-
this.walletRequest(
|
|
3440
|
-
state,
|
|
3441
|
-
chainId,
|
|
3442
|
-
`${this.agentPath(context, "snapshot")}${query({
|
|
3443
|
-
shouldOnlyUseRecentValue: true,
|
|
3444
|
-
shouldAllowStaleOnError: true
|
|
3445
|
-
})}`
|
|
3446
|
-
),
|
|
3447
|
-
this.walletRequest(
|
|
3448
|
-
state,
|
|
3449
|
-
chainId,
|
|
3450
|
-
this.agentPath(context, "yield-positions")
|
|
3451
|
-
),
|
|
3452
|
-
options.historic ? this.walletRequest(
|
|
3453
|
-
state,
|
|
3454
|
-
chainId,
|
|
3455
|
-
this.agentPath(context, "wallet/historic-position")
|
|
3456
|
-
) : Promise.resolve(void 0),
|
|
3457
|
-
options.actions ? this.walletRequest(
|
|
3458
|
-
state,
|
|
3459
|
-
chainId,
|
|
3460
|
-
this.agentPath(context, "actions")
|
|
3461
|
-
) : Promise.resolve(void 0)
|
|
3462
|
-
]);
|
|
3463
|
-
if (!snapshot?.agentSnapshot) {
|
|
3464
|
-
throw this.invalidResponse("agent snapshot");
|
|
3465
|
-
}
|
|
3466
|
-
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3467
|
-
throw this.invalidResponse("yield positions");
|
|
3468
|
-
}
|
|
3469
|
-
return {
|
|
3470
|
-
...context,
|
|
3471
|
-
snapshot: snapshot.agentSnapshot,
|
|
3472
|
-
positions: positions.yieldPositions,
|
|
3473
|
-
...historic?.position ? { historic: historic.position } : {},
|
|
3474
|
-
...actions?.actions ? { actions: actions.actions } : {}
|
|
3475
|
-
};
|
|
3476
|
-
}
|
|
3477
|
-
async deployAfterFunding(state, chainId, context) {
|
|
3478
|
-
if (context.wallet.initializedDate != null) return;
|
|
3479
|
-
try {
|
|
3480
|
-
const deployed = await this.walletRequest(
|
|
3481
|
-
state,
|
|
3482
|
-
chainId,
|
|
3483
|
-
this.agentPath(context, "deploy"),
|
|
3484
|
-
{ method: "POST", body: {} }
|
|
3485
|
-
);
|
|
3486
|
-
if (deployed?.agentWallet) {
|
|
3487
|
-
context.wallet = deployed.agentWallet;
|
|
3488
|
-
}
|
|
3489
|
-
} catch (error) {
|
|
3490
|
-
console.warn(
|
|
3491
|
-
"[owney-sdk] Yieldseeker deposit is funded but not deployable yet:",
|
|
3492
|
-
error
|
|
3493
|
-
);
|
|
3494
|
-
}
|
|
3495
|
-
}
|
|
3496
|
-
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
3497
|
-
try {
|
|
3498
|
-
const response = await this.walletRequest(
|
|
3499
|
-
state,
|
|
3500
|
-
chainId,
|
|
3501
|
-
`${this.agentPath(context, "snapshot")}${query({
|
|
3502
|
-
shouldForceRefresh: true
|
|
3503
|
-
})}`
|
|
3504
|
-
);
|
|
3505
|
-
if (!response?.agentSnapshot) {
|
|
3506
|
-
throw this.invalidResponse("agent snapshot refresh");
|
|
3507
|
-
}
|
|
3508
|
-
} catch (error) {
|
|
3509
|
-
console.warn(
|
|
3510
|
-
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
3511
|
-
error
|
|
3512
|
-
);
|
|
3513
|
-
}
|
|
3514
|
-
}
|
|
3515
|
-
agentPath(context, suffix) {
|
|
3516
|
-
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
3517
|
-
}
|
|
3518
|
-
async walletRequest(state, chainId, path, options = {}) {
|
|
3519
|
-
try {
|
|
3520
|
-
return await this.providerRequest(state, chainId, path, options);
|
|
3521
|
-
} catch (error) {
|
|
3522
|
-
throw this.mapApiError(error);
|
|
3523
|
-
}
|
|
3524
|
-
}
|
|
3525
|
-
async providerRequest(state, chainId, path, options = {}) {
|
|
3526
|
-
this.assertChain(chainId);
|
|
3527
|
-
const request = (signature2) => this.api.request(path, {
|
|
3528
|
-
...options,
|
|
3529
|
-
signature: signature2
|
|
3530
|
-
});
|
|
3531
|
-
let signature = await this.auth.getToken(state, chainId);
|
|
3532
|
-
try {
|
|
3533
|
-
return await request(signature);
|
|
3534
|
-
} catch (error) {
|
|
3535
|
-
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
3536
|
-
if (error.providerCode === "NO_USER") throw error;
|
|
3537
|
-
if (!error.isAuthenticationError) throw error;
|
|
3538
|
-
this.auth.clear(state, chainId);
|
|
3539
|
-
signature = await this.auth.getToken(state, chainId);
|
|
3540
|
-
try {
|
|
3541
|
-
return await request(signature);
|
|
3542
|
-
} catch (retryError) {
|
|
3543
|
-
if (retryError instanceof YieldseekerApiError && retryError.isAuthenticationError) {
|
|
3544
|
-
this.forgetUser(state, chainId);
|
|
3545
|
-
}
|
|
3546
|
-
throw retryError;
|
|
3547
|
-
}
|
|
3548
|
-
}
|
|
3549
|
-
}
|
|
3550
|
-
mapApiError(error) {
|
|
3551
|
-
if (!(error instanceof YieldseekerApiError)) {
|
|
3552
|
-
return new OwneyError(
|
|
3553
|
-
"AGENT_API_ERROR",
|
|
3554
|
-
"Yieldseeker request failed.",
|
|
3555
|
-
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3556
|
-
this.id
|
|
3557
|
-
);
|
|
3558
|
-
}
|
|
3559
|
-
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3560
|
-
return new OwneyError(
|
|
3561
|
-
code,
|
|
3562
|
-
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3563
|
-
{
|
|
3564
|
-
statusCode: error.status,
|
|
3565
|
-
providerCode: error.providerCode,
|
|
3566
|
-
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3567
|
-
},
|
|
3568
|
-
this.id
|
|
3569
|
-
);
|
|
3570
|
-
}
|
|
3571
|
-
async submitTransaction(state, chainId, transaction) {
|
|
3572
|
-
if (this.transactionExecutor) {
|
|
3573
|
-
return this.transactionExecutor(state, chainId, transaction);
|
|
3574
|
-
}
|
|
3575
|
-
this.assertTransaction(transaction, state, chainId);
|
|
3576
|
-
const account = getAddress2(state.walletAddress);
|
|
3577
|
-
const walletClient = createWalletClient2({
|
|
3578
|
-
account,
|
|
3579
|
-
chain: base3,
|
|
3580
|
-
transport: custom2(state.provider)
|
|
3581
|
-
});
|
|
3582
|
-
const publicClient = createPublicClient3({
|
|
3583
|
-
chain: base3,
|
|
3584
|
-
transport: custom2(state.provider)
|
|
3585
|
-
});
|
|
3586
|
-
await ensureWalletOnChain(
|
|
3587
|
-
publicClient,
|
|
3588
|
-
walletClient,
|
|
3589
|
-
8453
|
|
3590
|
-
);
|
|
3591
|
-
const hash = await walletClient.sendTransaction({
|
|
3592
|
-
account,
|
|
3593
|
-
chain: base3,
|
|
3594
|
-
to: getAddress2(transaction.to),
|
|
3595
|
-
data: transaction.data,
|
|
3596
|
-
value: BigInt(transaction.value)
|
|
3597
|
-
});
|
|
3598
|
-
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3599
|
-
hash,
|
|
3600
|
-
confirmations: 1
|
|
3601
|
-
});
|
|
3602
|
-
if (receipt.status !== "success") {
|
|
3603
|
-
throw new OwneyError(
|
|
3604
|
-
"AGENT_TRANSACTION_REVERTED",
|
|
3605
|
-
`Yieldseeker transaction reverted (${hash}).`,
|
|
3606
|
-
{ transactionHash: hash },
|
|
3607
|
-
this.id
|
|
3608
|
-
);
|
|
3609
|
-
}
|
|
3610
|
-
return hash;
|
|
3611
|
-
}
|
|
3612
|
-
async waitForReceipt(state, chainId, transactionHash) {
|
|
3613
|
-
if (this.unwindReceiptWaiter) {
|
|
3614
|
-
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3615
|
-
return;
|
|
3616
|
-
}
|
|
3617
|
-
const publicClient = createPublicClient3({
|
|
3618
|
-
chain: base3,
|
|
3619
|
-
transport: custom2(state.provider)
|
|
3620
|
-
});
|
|
3621
|
-
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3622
|
-
hash: transactionHash,
|
|
3623
|
-
confirmations: 1
|
|
3624
|
-
});
|
|
3625
|
-
if (receipt.status !== "success") {
|
|
3626
|
-
throw new OwneyError(
|
|
3627
|
-
"AGENT_TRANSACTION_REVERTED",
|
|
3628
|
-
`Yieldseeker transaction reverted (${transactionHash}).`,
|
|
3629
|
-
{ transactionHash },
|
|
3630
|
-
this.id
|
|
3631
|
-
);
|
|
3632
|
-
}
|
|
3633
|
-
}
|
|
3634
|
-
assertTransaction(transaction, state, chainId) {
|
|
3635
|
-
if (!transaction || typeof transaction.from !== "string" || !isAddress2(transaction.from) || typeof transaction.to !== "string" || !isAddress2(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || getAddress2(transaction.from) !== getAddress2(state.walletAddress)) {
|
|
3636
|
-
throw this.invalidResponse("transaction");
|
|
3637
|
-
}
|
|
3638
|
-
}
|
|
3639
|
-
assertAgent(agent) {
|
|
3640
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress2(agent.assetAddress) || agent.chainId !== 8453) {
|
|
3641
|
-
throw this.invalidResponse("agent");
|
|
3642
|
-
}
|
|
3643
|
-
}
|
|
3644
|
-
isOwneyAgent(agent) {
|
|
3645
|
-
return typeof agent.name === "string" && agent.name.trim().toLowerCase() === OWNEY_AGENT_NAME;
|
|
3646
|
-
}
|
|
3647
|
-
assetForAgent(agent) {
|
|
3648
|
-
for (const asset of ["USDC", "WETH"]) {
|
|
3649
|
-
if (agent.assetAddress.toLowerCase() === YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase()) {
|
|
3650
|
-
return asset;
|
|
3651
|
-
}
|
|
3652
|
-
}
|
|
3653
|
-
return null;
|
|
3654
|
-
}
|
|
3655
|
-
isTransactionHash(value) {
|
|
3656
|
-
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3657
|
-
}
|
|
3658
|
-
assertChain(chainId) {
|
|
3659
|
-
if (chainId !== 8453) {
|
|
3660
|
-
throw new OwneyError(
|
|
3661
|
-
"CHAIN_UNSUPPORTED",
|
|
3662
|
-
`Yieldseeker does not support chain ${chainId}.`,
|
|
3663
|
-
{ chainId, supportedChainIds: [8453] },
|
|
3664
|
-
this.id
|
|
3665
|
-
);
|
|
3666
|
-
}
|
|
3667
|
-
}
|
|
3668
|
-
assertOptionalChain(chainId) {
|
|
3669
|
-
if (chainId !== void 0) this.assertChain(chainId);
|
|
3670
|
-
}
|
|
3671
|
-
assertAsset(asset) {
|
|
3672
|
-
if (asset !== "USDC" && asset !== "WETH") {
|
|
3673
|
-
throw new OwneyError(
|
|
3674
|
-
"ASSET_UNSUPPORTED",
|
|
3675
|
-
`Yieldseeker does not support asset ${asset} in the Owney rollout.`,
|
|
3676
|
-
{
|
|
3677
|
-
asset,
|
|
3678
|
-
supportedAssets: ["USDC", "WETH"],
|
|
3679
|
-
providerAlsoAdvertises: ["cbBTC"]
|
|
3680
|
-
},
|
|
3681
|
-
this.id
|
|
3682
|
-
);
|
|
3683
|
-
}
|
|
3684
|
-
}
|
|
3685
|
-
invalidResponse(operation, details = {}) {
|
|
3686
|
-
return new OwneyError(
|
|
3687
|
-
"AGENT_INVALID_RESPONSE",
|
|
3688
|
-
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3689
|
-
details,
|
|
3690
|
-
this.id
|
|
3691
|
-
);
|
|
3692
|
-
}
|
|
3693
|
-
};
|
|
3694
|
-
|
|
3695
|
-
// src/lib/routing-api.ts
|
|
3696
|
-
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3697
|
-
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3698
|
-
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3699
|
-
try {
|
|
3700
|
-
const res = await fetch(url, {
|
|
3701
|
-
method: "GET",
|
|
3702
|
-
headers: {
|
|
3703
|
-
"Content-Type": "application/json",
|
|
3704
|
-
"x-owney-api-key": `${apiKey}`
|
|
3705
|
-
}
|
|
3706
|
-
});
|
|
3707
|
-
if (!res.ok) {
|
|
3708
|
-
if (res.status !== 404) {
|
|
3709
|
-
console.warn(
|
|
3710
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
3711
|
-
);
|
|
3712
|
-
}
|
|
3713
|
-
return null;
|
|
3714
|
-
}
|
|
3715
|
-
const json = await res.json();
|
|
3716
|
-
const policy = json.success ? json.data ?? null : null;
|
|
3717
|
-
debugLog(
|
|
3718
|
-
"owney-sdk",
|
|
3719
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
3720
|
-
policy ?? void 0
|
|
3721
|
-
);
|
|
3722
|
-
return policy;
|
|
3723
|
-
} catch (error) {
|
|
3724
|
-
console.warn(
|
|
3725
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
3726
|
-
error instanceof Error ? error.message : String(error)
|
|
3727
|
-
);
|
|
3728
|
-
return null;
|
|
3729
|
-
}
|
|
3730
|
-
}
|
|
3731
|
-
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3732
|
-
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
3733
|
-
const res = await fetch(url, {
|
|
3734
|
-
method: "GET",
|
|
3735
|
-
headers: {
|
|
3736
|
-
"Content-Type": "application/json",
|
|
3737
|
-
"x-owney-api-key": `${apiKey}`
|
|
3738
|
-
}
|
|
3739
|
-
});
|
|
3740
|
-
if (!res.ok) {
|
|
3741
|
-
const text = await res.text().catch(() => "");
|
|
3742
|
-
throw new OwneyError(
|
|
3743
|
-
"API_ROUTING_ERROR",
|
|
3744
|
-
`Routing API error ${res.status}: ${text}`,
|
|
3745
|
-
{ statusCode: res.status, responseBody: text }
|
|
3746
|
-
);
|
|
3747
|
-
}
|
|
3748
|
-
const json = await res.json();
|
|
3749
|
-
if (!json.success) {
|
|
3750
|
-
throw new OwneyError(
|
|
3751
|
-
"API_ROUTING_FAILED",
|
|
3752
|
-
`Routing API request failed: ${json.message}`,
|
|
3753
|
-
{ message: json.message }
|
|
3754
|
-
);
|
|
3755
|
-
}
|
|
3756
|
-
return json.data;
|
|
3757
|
-
}
|
|
3758
|
-
|
|
3759
|
-
// src/lib/health-report.ts
|
|
3760
|
-
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3761
|
-
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
3762
|
-
try {
|
|
3763
|
-
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
3764
|
-
method: "POST",
|
|
3765
|
-
headers: {
|
|
3766
|
-
"Content-Type": "application/json",
|
|
3767
|
-
"x-owney-api-key": apiKey
|
|
3768
|
-
},
|
|
3769
|
-
body: JSON.stringify({
|
|
3770
|
-
agent_type: agentType,
|
|
3771
|
-
error_code: errorCode,
|
|
3772
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3773
|
-
})
|
|
3774
|
-
});
|
|
3775
|
-
} catch (err) {
|
|
3776
|
-
console.warn(
|
|
3777
|
-
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
3778
|
-
err instanceof Error ? err.message : err
|
|
3779
|
-
);
|
|
3780
|
-
}
|
|
3781
|
-
}
|
|
3782
|
-
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
3783
|
-
try {
|
|
3784
|
-
return await fn();
|
|
3785
|
-
} catch (err) {
|
|
3786
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3787
|
-
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
3788
|
-
throw err;
|
|
3789
|
-
}
|
|
3790
|
-
}
|
|
3791
|
-
|
|
3792
|
-
// src/lib/helpers/withdraw-helper.ts
|
|
3793
|
-
import { parseUnits } from "viem";
|
|
3794
|
-
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
3795
|
-
const target = asset.toUpperCase();
|
|
3796
|
-
return agents.map((agent) => {
|
|
3797
|
-
const agentBalance = aggregated[agent.id];
|
|
3798
|
-
const tokenBalance = agentBalance?.tokens.find(
|
|
3799
|
-
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
3800
|
-
);
|
|
3801
|
-
let balance = tokenBalance ? parseUnits(tokenBalance.amount, decimals) : 0n;
|
|
3802
|
-
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
3803
|
-
const chainNameById = {
|
|
3804
|
-
1: "ETHEREUM",
|
|
3805
|
-
8453: "BASE",
|
|
3806
|
-
42161: "ARBITRUM"
|
|
3807
|
-
};
|
|
3808
|
-
const targetChain = chainNameById[chainId];
|
|
3809
|
-
for (const position2 of agentBalance?.positions ?? []) {
|
|
3810
|
-
const positionChain = position2.chain.trim().toUpperCase();
|
|
3811
|
-
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
3812
|
-
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
3813
|
-
if (position2.amountRaw !== void 0) {
|
|
3814
|
-
try {
|
|
3815
|
-
balance += BigInt(position2.amountRaw);
|
|
3816
|
-
continue;
|
|
3817
|
-
} catch {
|
|
3818
|
-
}
|
|
3819
|
-
}
|
|
3820
|
-
balance += parseUnits(position2.amount, decimals);
|
|
3821
|
-
}
|
|
3822
|
-
}
|
|
3823
|
-
return { agent, balance };
|
|
3824
|
-
});
|
|
3825
|
-
}
|
|
3826
|
-
function planProportionalShares(balances, requested, totalAvailable) {
|
|
3827
|
-
const plans = balances.map(({ agent, balance }) => ({
|
|
3828
|
-
agent,
|
|
3829
|
-
balance,
|
|
3830
|
-
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
3831
|
-
}));
|
|
3832
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
3833
|
-
let remainder = requested - assigned;
|
|
3834
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
3835
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
3836
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
3837
|
-
});
|
|
3838
|
-
for (const p of byHeadroom) {
|
|
3839
|
-
if (remainder === 0n) break;
|
|
3840
|
-
const headroom = p.balance - p.planned;
|
|
3841
|
-
if (headroom <= 0n) continue;
|
|
3842
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
3843
|
-
p.planned += take;
|
|
3844
|
-
remainder -= take;
|
|
3845
|
-
}
|
|
3846
|
-
return plans;
|
|
3847
|
-
}
|
|
3848
|
-
function planDisabledDrain(disabled, requested) {
|
|
3849
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
3850
|
-
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
3851
|
-
);
|
|
3852
|
-
const plans = [];
|
|
3853
|
-
let remaining = requested;
|
|
3854
|
-
for (const { agent, balance } of sorted) {
|
|
3855
|
-
if (remaining === 0n) {
|
|
3856
|
-
plans.push({ agent, balance, planned: 0n });
|
|
3857
|
-
continue;
|
|
3858
|
-
}
|
|
3859
|
-
const take = balance < remaining ? balance : remaining;
|
|
3860
|
-
plans.push({ agent, balance, planned: take });
|
|
3861
|
-
remaining -= take;
|
|
3862
|
-
}
|
|
3863
|
-
return { plans, remaining };
|
|
2123
|
+
// src/lib/chain-guard.ts
|
|
2124
|
+
var CHAIN_NAMES = {
|
|
2125
|
+
1: "Ethereum",
|
|
2126
|
+
8453: "Base",
|
|
2127
|
+
42161: "Arbitrum"
|
|
2128
|
+
};
|
|
2129
|
+
function chainName(chainId) {
|
|
2130
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
3864
2131
|
}
|
|
3865
|
-
function
|
|
3866
|
-
const
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
(
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
2132
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2133
|
+
const actual = await pub.getChainId();
|
|
2134
|
+
if (actual === expected) return;
|
|
2135
|
+
try {
|
|
2136
|
+
await wallet.switchChain({ id: expected });
|
|
2137
|
+
} catch (error) {
|
|
2138
|
+
throw new OwneyError(
|
|
2139
|
+
"CHAIN_MISMATCH",
|
|
2140
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2141
|
+
{
|
|
2142
|
+
expectedChainId: expected,
|
|
2143
|
+
actualChainId: actual,
|
|
2144
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2145
|
+
}
|
|
2146
|
+
);
|
|
3880
2147
|
}
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
leftover -= take;
|
|
2148
|
+
const after = await pub.getChainId();
|
|
2149
|
+
if (after !== expected) {
|
|
2150
|
+
throw new OwneyError(
|
|
2151
|
+
"CHAIN_MISMATCH",
|
|
2152
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2153
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
2154
|
+
);
|
|
3889
2155
|
}
|
|
3890
2156
|
}
|
|
3891
|
-
function sumWithdrawnAmount(results) {
|
|
3892
|
-
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
3893
|
-
}
|
|
3894
2157
|
|
|
3895
|
-
// src/lib/
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
3908
|
-
}, 0);
|
|
3909
|
-
}
|
|
3910
|
-
function aggregateApyHistory(agentApys, agentBalances) {
|
|
3911
|
-
const byDate = /* @__PURE__ */ new Map();
|
|
3912
|
-
for (const [id, accountApy] of Object.entries(agentApys)) {
|
|
3913
|
-
const balance = agentBalances[id] ?? 0;
|
|
3914
|
-
if (!Number.isFinite(balance) || balance <= 0) continue;
|
|
3915
|
-
for (const point of accountApy.history ?? []) {
|
|
3916
|
-
const apy = Number(point.apy);
|
|
3917
|
-
if (!point.date || !Number.isFinite(apy)) continue;
|
|
3918
|
-
const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
|
|
3919
|
-
current.weightedSum += apy * balance;
|
|
3920
|
-
current.weight += balance;
|
|
3921
|
-
byDate.set(point.date, current);
|
|
2158
|
+
// src/lib/sponsored-deposit.ts
|
|
2159
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2160
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2161
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2162
|
+
return async (smartWallet, chainId, amount) => {
|
|
2163
|
+
const cid = chainId;
|
|
2164
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2165
|
+
if (!token) {
|
|
2166
|
+
throw new OwneyError(
|
|
2167
|
+
"CHAIN_UNSUPPORTED",
|
|
2168
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2169
|
+
);
|
|
3922
2170
|
}
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
const balance = agentBalances[id] ?? 0;
|
|
3935
|
-
if (!cells || balance <= 0) continue;
|
|
3936
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
3937
|
-
if (!perAsset) continue;
|
|
3938
|
-
const chainId = Number(chainKey);
|
|
3939
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
3940
|
-
const apy = Number(apyValue ?? 0);
|
|
3941
|
-
if (apy === 0) continue;
|
|
3942
|
-
sums[chainId] ??= {};
|
|
3943
|
-
weights[chainId] ??= {};
|
|
3944
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
3945
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2171
|
+
const pub = deps.getPublicClient(cid);
|
|
2172
|
+
const wallet = deps.getWalletClient(cid);
|
|
2173
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2174
|
+
try {
|
|
2175
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2176
|
+
if (balance < BigInt(amount)) {
|
|
2177
|
+
throw new OwneyError(
|
|
2178
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2179
|
+
"Insufficient balance for this deposit.",
|
|
2180
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2181
|
+
);
|
|
3946
2182
|
}
|
|
3947
|
-
}
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
2183
|
+
} catch (err) {
|
|
2184
|
+
if (err instanceof OwneyError) throw err;
|
|
2185
|
+
console.warn(
|
|
2186
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2187
|
+
err instanceof Error ? err.message : String(err)
|
|
2188
|
+
);
|
|
2189
|
+
}
|
|
2190
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2191
|
+
const validAfter = 0n;
|
|
2192
|
+
const validBefore = BigInt(
|
|
2193
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2194
|
+
);
|
|
2195
|
+
const nonce = randomAuthNonce();
|
|
2196
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2197
|
+
token,
|
|
2198
|
+
chainId: cid,
|
|
2199
|
+
tokenName,
|
|
2200
|
+
tokenVersion,
|
|
2201
|
+
message: {
|
|
2202
|
+
from: deps.ownerAddress,
|
|
2203
|
+
to: smartWallet,
|
|
2204
|
+
value: BigInt(amount),
|
|
2205
|
+
validAfter,
|
|
2206
|
+
validBefore,
|
|
2207
|
+
nonce
|
|
2208
|
+
}
|
|
2209
|
+
});
|
|
2210
|
+
const authSignature = await wallet.signTypedData({
|
|
2211
|
+
account: deps.ownerAddress,
|
|
2212
|
+
...typedData
|
|
2213
|
+
});
|
|
2214
|
+
deps.onApproved?.();
|
|
2215
|
+
const result = await post({
|
|
2216
|
+
baseUrl: deps.baseUrl,
|
|
2217
|
+
apiKey: deps.apiKey,
|
|
2218
|
+
body: {
|
|
2219
|
+
chainId: cid,
|
|
2220
|
+
token,
|
|
2221
|
+
from: deps.ownerAddress,
|
|
2222
|
+
to: smartWallet,
|
|
2223
|
+
value: amount,
|
|
2224
|
+
validAfter: validAfter.toString(),
|
|
2225
|
+
validBefore: validBefore.toString(),
|
|
2226
|
+
nonce,
|
|
2227
|
+
authSignature,
|
|
2228
|
+
tokenName,
|
|
2229
|
+
tokenVersion
|
|
2230
|
+
}
|
|
2231
|
+
});
|
|
2232
|
+
return result.txHash;
|
|
2233
|
+
};
|
|
3962
2234
|
}
|
|
3963
2235
|
|
|
3964
|
-
// src/client.ts
|
|
3965
|
-
import {
|
|
3966
|
-
createPublicClient as createPublicClient4,
|
|
3967
|
-
createWalletClient as createWalletClient3,
|
|
3968
|
-
custom as custom3
|
|
3969
|
-
} from "viem";
|
|
3970
|
-
import { base as base4, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
3971
|
-
|
|
3972
2236
|
// src/lib/sponsored-weth-deposit.ts
|
|
3973
2237
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
3974
2238
|
function makeSponsoredWethCallback(deps) {
|
|
3975
2239
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
3976
2240
|
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
3977
|
-
return
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
`No sponsored WETH configured for chain ${chainId}`
|
|
3985
|
-
);
|
|
3986
|
-
}
|
|
3987
|
-
const amountWei = BigInt(amount);
|
|
3988
|
-
const pub = deps.getPublicClient(cid);
|
|
3989
|
-
const wallet = deps.getWalletClient(cid);
|
|
3990
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
3991
|
-
try {
|
|
3992
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
3993
|
-
if (balance < amountWei) {
|
|
3994
|
-
throw new OwneyError(
|
|
3995
|
-
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
3996
|
-
"Insufficient WETH balance for this deposit.",
|
|
3997
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3998
|
-
);
|
|
3999
|
-
}
|
|
4000
|
-
} catch (err) {
|
|
4001
|
-
if (err instanceof OwneyError) throw err;
|
|
4002
|
-
console.warn(
|
|
4003
|
-
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
4004
|
-
err instanceof Error ? err.message : String(err)
|
|
4005
|
-
);
|
|
4006
|
-
}
|
|
4007
|
-
const allowance = await readPermit2Allowance(
|
|
4008
|
-
pub,
|
|
4009
|
-
token,
|
|
4010
|
-
deps.ownerAddress
|
|
2241
|
+
return async (smartWallet, chainId, amount) => {
|
|
2242
|
+
const cid = chainId;
|
|
2243
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2244
|
+
if (!token) {
|
|
2245
|
+
throw new OwneyError(
|
|
2246
|
+
"CHAIN_UNSUPPORTED",
|
|
2247
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
4011
2248
|
);
|
|
4012
|
-
|
|
2249
|
+
}
|
|
2250
|
+
const amountWei = BigInt(amount);
|
|
2251
|
+
const pub = deps.getPublicClient(cid);
|
|
2252
|
+
const wallet = deps.getWalletClient(cid);
|
|
2253
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2254
|
+
try {
|
|
2255
|
+
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2256
|
+
if (balance < amountWei) {
|
|
4013
2257
|
throw new OwneyError(
|
|
4014
|
-
"
|
|
4015
|
-
"WETH
|
|
4016
|
-
{ token, chainId: cid,
|
|
2258
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2259
|
+
"Insufficient WETH balance for this deposit.",
|
|
2260
|
+
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
4017
2261
|
);
|
|
4018
2262
|
}
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
const nonce = randomPermit2Nonce();
|
|
4025
|
-
const deadline = BigInt(
|
|
4026
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2263
|
+
} catch (err) {
|
|
2264
|
+
if (err instanceof OwneyError) throw err;
|
|
2265
|
+
console.warn(
|
|
2266
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
2267
|
+
err instanceof Error ? err.message : String(err)
|
|
4027
2268
|
);
|
|
4028
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
4029
|
-
chainId: cid,
|
|
4030
|
-
message: {
|
|
4031
|
-
permitted: { token, amount: amountWei },
|
|
4032
|
-
spender: relayer,
|
|
4033
|
-
nonce,
|
|
4034
|
-
deadline
|
|
4035
|
-
}
|
|
4036
|
-
});
|
|
4037
|
-
const signature = await wallet.signTypedData({
|
|
4038
|
-
account: deps.ownerAddress,
|
|
4039
|
-
...typedData
|
|
4040
|
-
});
|
|
4041
|
-
deps.onApproved?.();
|
|
4042
|
-
const result = await post({
|
|
4043
|
-
baseUrl: deps.baseUrl,
|
|
4044
|
-
apiKey: deps.apiKey,
|
|
4045
|
-
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
4046
|
-
body: {
|
|
4047
|
-
chainId: cid,
|
|
4048
|
-
token,
|
|
4049
|
-
from: deps.ownerAddress,
|
|
4050
|
-
to: smartWallet,
|
|
4051
|
-
amount,
|
|
4052
|
-
nonce: nonce.toString(),
|
|
4053
|
-
deadline: deadline.toString(),
|
|
4054
|
-
signature,
|
|
4055
|
-
...verification?.agentId === "yieldseeker" ? {
|
|
4056
|
-
yieldseekerUserId: verification.userId,
|
|
4057
|
-
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4058
|
-
} : {}
|
|
4059
|
-
}
|
|
4060
|
-
});
|
|
4061
|
-
return result.txHash;
|
|
4062
2269
|
}
|
|
4063
|
-
|
|
2270
|
+
const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
|
|
2271
|
+
if (allowance < amountWei) {
|
|
2272
|
+
throw new OwneyError(
|
|
2273
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
2274
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
2275
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
2276
|
+
);
|
|
2277
|
+
}
|
|
2278
|
+
const relayer = await get({
|
|
2279
|
+
baseUrl: deps.baseUrl,
|
|
2280
|
+
apiKey: deps.apiKey,
|
|
2281
|
+
chainId: cid
|
|
2282
|
+
});
|
|
2283
|
+
const nonce = randomPermit2Nonce();
|
|
2284
|
+
const deadline = BigInt(
|
|
2285
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2286
|
+
);
|
|
2287
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
2288
|
+
chainId: cid,
|
|
2289
|
+
message: {
|
|
2290
|
+
permitted: { token, amount: amountWei },
|
|
2291
|
+
spender: relayer,
|
|
2292
|
+
nonce,
|
|
2293
|
+
deadline
|
|
2294
|
+
}
|
|
2295
|
+
});
|
|
2296
|
+
const signature = await wallet.signTypedData({
|
|
2297
|
+
account: deps.ownerAddress,
|
|
2298
|
+
...typedData
|
|
2299
|
+
});
|
|
2300
|
+
deps.onApproved?.();
|
|
2301
|
+
const result = await post({
|
|
2302
|
+
baseUrl: deps.baseUrl,
|
|
2303
|
+
apiKey: deps.apiKey,
|
|
2304
|
+
body: {
|
|
2305
|
+
chainId: cid,
|
|
2306
|
+
token,
|
|
2307
|
+
from: deps.ownerAddress,
|
|
2308
|
+
to: smartWallet,
|
|
2309
|
+
amount,
|
|
2310
|
+
nonce: nonce.toString(),
|
|
2311
|
+
deadline: deadline.toString(),
|
|
2312
|
+
signature
|
|
2313
|
+
}
|
|
2314
|
+
});
|
|
2315
|
+
return result.txHash;
|
|
2316
|
+
};
|
|
4064
2317
|
}
|
|
4065
2318
|
|
|
4066
2319
|
// src/lib/sponsored-calls-deposit.ts
|
|
4067
|
-
import { encodeFunctionData
|
|
2320
|
+
import { encodeFunctionData, erc20Abi, toHex } from "viem";
|
|
4068
2321
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
4069
2322
|
var DEFAULT_MAX_POLLS = 30;
|
|
4070
2323
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -4090,7 +2343,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4090
2343
|
}
|
|
4091
2344
|
return new URL(configured, origin).toString();
|
|
4092
2345
|
};
|
|
4093
|
-
return
|
|
2346
|
+
return async (smartWallet, chainId, amount) => {
|
|
4094
2347
|
const cid = chainId;
|
|
4095
2348
|
const token = deps.tokenAddressByChain[cid];
|
|
4096
2349
|
if (!token) {
|
|
@@ -4106,37 +2359,11 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4106
2359
|
{ chainId }
|
|
4107
2360
|
);
|
|
4108
2361
|
}
|
|
4109
|
-
const data =
|
|
4110
|
-
abi:
|
|
2362
|
+
const data = encodeFunctionData({
|
|
2363
|
+
abi: erc20Abi,
|
|
4111
2364
|
functionName: "transfer",
|
|
4112
2365
|
args: [smartWallet, BigInt(amount)]
|
|
4113
2366
|
});
|
|
4114
|
-
let paymasterUrl = absolutePaymasterUrl();
|
|
4115
|
-
if (verification?.agentId === "yieldseeker") {
|
|
4116
|
-
if (chainId !== 8453) {
|
|
4117
|
-
throw new OwneyError(
|
|
4118
|
-
"CHAIN_UNSUPPORTED",
|
|
4119
|
-
`Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
|
|
4120
|
-
);
|
|
4121
|
-
}
|
|
4122
|
-
const { intent } = await postPaymasterIntent({
|
|
4123
|
-
baseUrl: deps.routingApiBaseUrl,
|
|
4124
|
-
apiKey: deps.apiKey,
|
|
4125
|
-
yieldseekerSignature: verification.signature,
|
|
4126
|
-
body: {
|
|
4127
|
-
chainId,
|
|
4128
|
-
token,
|
|
4129
|
-
from: deps.ownerAddress,
|
|
4130
|
-
to: smartWallet,
|
|
4131
|
-
amount,
|
|
4132
|
-
yieldseekerUserId: verification.userId,
|
|
4133
|
-
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4134
|
-
}
|
|
4135
|
-
});
|
|
4136
|
-
const url = new URL(paymasterUrl);
|
|
4137
|
-
url.searchParams.set("owneyIntent", intent);
|
|
4138
|
-
paymasterUrl = url.toString();
|
|
4139
|
-
}
|
|
4140
2367
|
const sendResult = await deps.provider.request({
|
|
4141
2368
|
method: "wallet_sendCalls",
|
|
4142
2369
|
params: [
|
|
@@ -4147,7 +2374,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4147
2374
|
atomicRequired: false,
|
|
4148
2375
|
calls: [{ to: token, value: "0x0", data }],
|
|
4149
2376
|
capabilities: {
|
|
4150
|
-
paymasterService: { url:
|
|
2377
|
+
paymasterService: { url: absolutePaymasterUrl() }
|
|
4151
2378
|
}
|
|
4152
2379
|
}
|
|
4153
2380
|
]
|
|
@@ -4177,7 +2404,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4177
2404
|
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
4178
2405
|
{ chainId, callsId }
|
|
4179
2406
|
);
|
|
4180
|
-
}
|
|
2407
|
+
};
|
|
4181
2408
|
}
|
|
4182
2409
|
|
|
4183
2410
|
// src/client.ts
|
|
@@ -4207,10 +2434,11 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
4207
2434
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
4208
2435
|
};
|
|
4209
2436
|
var VIEM_CHAIN2 = {
|
|
4210
|
-
8453:
|
|
2437
|
+
8453: base2,
|
|
4211
2438
|
42161: arbitrum2,
|
|
4212
2439
|
1: mainnet2
|
|
4213
2440
|
};
|
|
2441
|
+
var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai"];
|
|
4214
2442
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
4215
2443
|
8453: "0x4200000000000000000000000000000000000006",
|
|
4216
2444
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -4237,9 +2465,9 @@ var OwneySDK = class {
|
|
|
4237
2465
|
orgAgentConfig;
|
|
4238
2466
|
orgAgentConfigPromise = null;
|
|
4239
2467
|
zyfaiRpcUrls;
|
|
4240
|
-
yieldseekerApiBaseUrl;
|
|
4241
2468
|
routingApiBaseUrl;
|
|
4242
2469
|
referralSource;
|
|
2470
|
+
surfliquidApiBaseUrl;
|
|
4243
2471
|
cachedSponsoredCallback = null;
|
|
4244
2472
|
cachedWethSponsoredCallback = null;
|
|
4245
2473
|
paymasterServiceUrl;
|
|
@@ -4249,10 +2477,10 @@ var OwneySDK = class {
|
|
|
4249
2477
|
this.apiKey = config.apiKey;
|
|
4250
2478
|
if (config.debug) setOwneyDebug(true);
|
|
4251
2479
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
4252
|
-
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
4253
2480
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
4254
2481
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
4255
2482
|
this.referralSource = config.referralSource;
|
|
2483
|
+
this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
|
|
4256
2484
|
}
|
|
4257
2485
|
/**
|
|
4258
2486
|
* Establish connection state for all subsequent SDK calls.
|
|
@@ -4352,14 +2580,14 @@ var OwneySDK = class {
|
|
|
4352
2580
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4353
2581
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4354
2582
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4355
|
-
getPublicClient: (cid) =>
|
|
2583
|
+
getPublicClient: (cid) => createPublicClient2({
|
|
4356
2584
|
chain: VIEM_CHAIN2[cid],
|
|
4357
|
-
transport:
|
|
2585
|
+
transport: custom(provider)
|
|
4358
2586
|
}),
|
|
4359
|
-
getWalletClient: (cid) =>
|
|
2587
|
+
getWalletClient: (cid) => createWalletClient({
|
|
4360
2588
|
account: owner,
|
|
4361
2589
|
chain: VIEM_CHAIN2[cid],
|
|
4362
|
-
transport:
|
|
2590
|
+
transport: custom(provider)
|
|
4363
2591
|
})
|
|
4364
2592
|
});
|
|
4365
2593
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -4376,8 +2604,6 @@ var OwneySDK = class {
|
|
|
4376
2604
|
if (!onApproved && cached) return cached;
|
|
4377
2605
|
const provider = this.requireConnectedProvider();
|
|
4378
2606
|
const callback = makeSponsoredCallsCallback({
|
|
4379
|
-
apiKey: this.apiKey,
|
|
4380
|
-
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
4381
2607
|
provider,
|
|
4382
2608
|
ownerAddress: this.state.walletAddress,
|
|
4383
2609
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
@@ -4407,14 +2633,14 @@ var OwneySDK = class {
|
|
|
4407
2633
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
4408
2634
|
// PublicClient/WalletClient param types — structurally identical at
|
|
4409
2635
|
// runtime, but the two share a name TS treats as unrelated.
|
|
4410
|
-
getPublicClient: (cid) =>
|
|
2636
|
+
getPublicClient: (cid) => createPublicClient2({
|
|
4411
2637
|
chain: VIEM_CHAIN2[cid],
|
|
4412
|
-
transport:
|
|
2638
|
+
transport: custom(provider)
|
|
4413
2639
|
}),
|
|
4414
|
-
getWalletClient: (cid) =>
|
|
2640
|
+
getWalletClient: (cid) => createWalletClient({
|
|
4415
2641
|
account: owner,
|
|
4416
2642
|
chain: VIEM_CHAIN2[cid],
|
|
4417
|
-
transport:
|
|
2643
|
+
transport: custom(provider)
|
|
4418
2644
|
})
|
|
4419
2645
|
});
|
|
4420
2646
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -4445,10 +2671,12 @@ var OwneySDK = class {
|
|
|
4445
2671
|
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
4446
2672
|
this.apiKey,
|
|
4447
2673
|
this.routingApiBaseUrl
|
|
4448
|
-
).then(
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
2674
|
+
).then(
|
|
2675
|
+
(config) => {
|
|
2676
|
+
this.orgAgentConfig = config;
|
|
2677
|
+
return config;
|
|
2678
|
+
}
|
|
2679
|
+
);
|
|
4452
2680
|
}
|
|
4453
2681
|
return this.orgAgentConfigPromise;
|
|
4454
2682
|
}
|
|
@@ -4488,15 +2716,8 @@ var OwneySDK = class {
|
|
|
4488
2716
|
this.routingApiBaseUrl
|
|
4489
2717
|
);
|
|
4490
2718
|
this.disabledAgents.clear();
|
|
4491
|
-
for (const {
|
|
4492
|
-
|
|
4493
|
-
agent_type,
|
|
4494
|
-
is_enabled,
|
|
4495
|
-
is_configured
|
|
4496
|
-
} of agentKeys) {
|
|
4497
|
-
const configured = is_configured ?? Boolean(key2);
|
|
4498
|
-
if (!configured) continue;
|
|
4499
|
-
const agent = this.createAgent(agent_type, key2);
|
|
2719
|
+
for (const { key: key2, agent_type, project_name, is_enabled } of agentKeys) {
|
|
2720
|
+
const agent = await this.createAgent(agent_type, key2, project_name);
|
|
4500
2721
|
if (!agent) continue;
|
|
4501
2722
|
this.agents.set(agent_type, agent);
|
|
4502
2723
|
if (is_enabled === false) {
|
|
@@ -4517,49 +2738,26 @@ var OwneySDK = class {
|
|
|
4517
2738
|
this.initializingAgentsPromise = null;
|
|
4518
2739
|
}
|
|
4519
2740
|
}
|
|
4520
|
-
createAgent(agentId, key2) {
|
|
2741
|
+
async createAgent(agentId, key2, projectName) {
|
|
4521
2742
|
if (agentId === "zyfai") {
|
|
4522
|
-
if (!key2) return null;
|
|
4523
2743
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
4524
2744
|
}
|
|
4525
|
-
if (agentId === "
|
|
4526
|
-
|
|
4527
|
-
|
|
2745
|
+
if (agentId === "surfliquid") {
|
|
2746
|
+
if (!projectName) return null;
|
|
2747
|
+
const { SurfLiquidAgent } = await import("./surfliquid.agent-5CWKYMRL.js");
|
|
2748
|
+
return new SurfLiquidAgent({
|
|
2749
|
+
projectName,
|
|
2750
|
+
appId: key2,
|
|
2751
|
+
// Same-origin proxy override — SurfLiquid auth is an httpOnly cookie,
|
|
2752
|
+
// which WebKit (iOS) discards when it is third-party. See
|
|
2753
|
+
// OwneySDKConfig.surfliquidApiBaseUrl.
|
|
2754
|
+
apiBaseUrl: this.surfliquidApiBaseUrl,
|
|
2755
|
+
apiKey: this.apiKey,
|
|
2756
|
+
routingApiBaseUrl: this.routingApiBaseUrl
|
|
4528
2757
|
});
|
|
4529
2758
|
}
|
|
4530
2759
|
return null;
|
|
4531
2760
|
}
|
|
4532
|
-
/**
|
|
4533
|
-
* Discover the agents actually returned by routing for this organization.
|
|
4534
|
-
* Consumers should use this instead of hardcoding a global agent roster.
|
|
4535
|
-
*/
|
|
4536
|
-
async getAvailableAgents(options = {}) {
|
|
4537
|
-
await this.ensureAgentsInitialized();
|
|
4538
|
-
const { chainId, asset, includeDisabled = false } = options;
|
|
4539
|
-
const available = [];
|
|
4540
|
-
for (const [id, agent] of this.agents) {
|
|
4541
|
-
const isEnabled = !this.isAgentDisabled(id);
|
|
4542
|
-
if (!includeDisabled && !isEnabled) continue;
|
|
4543
|
-
if (chainId !== void 0 && !agent.supportedChainIds.includes(chainId)) {
|
|
4544
|
-
continue;
|
|
4545
|
-
}
|
|
4546
|
-
if (asset !== void 0) {
|
|
4547
|
-
const supportsAsset = agent.supportedAssets.some(
|
|
4548
|
-
(entry) => (chainId === void 0 || entry.chainId === chainId) && entry.assets.some((candidate) => candidate.symbol === asset)
|
|
4549
|
-
);
|
|
4550
|
-
if (!supportsAsset) {
|
|
4551
|
-
continue;
|
|
4552
|
-
}
|
|
4553
|
-
}
|
|
4554
|
-
available.push({
|
|
4555
|
-
id,
|
|
4556
|
-
isEnabled,
|
|
4557
|
-
supportedChainIds: agent.supportedChainIds,
|
|
4558
|
-
supportedAssets: agent.supportedAssets
|
|
4559
|
-
});
|
|
4560
|
-
}
|
|
4561
|
-
return available;
|
|
4562
|
-
}
|
|
4563
2761
|
// --- Account lifecycle ---
|
|
4564
2762
|
/**
|
|
4565
2763
|
* Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
|
|
@@ -4571,7 +2769,7 @@ var OwneySDK = class {
|
|
|
4571
2769
|
* If provided, ALL specified agents must support the chainId or the call
|
|
4572
2770
|
* throws before activating any agent.
|
|
4573
2771
|
*/
|
|
4574
|
-
async activateAgent(chainId, agentId
|
|
2772
|
+
async activateAgent(chainId, agentId) {
|
|
4575
2773
|
const state = this.requireState();
|
|
4576
2774
|
await this.ensureAgentsInitialized();
|
|
4577
2775
|
if (agentId !== void 0) {
|
|
@@ -4607,7 +2805,9 @@ var OwneySDK = class {
|
|
|
4607
2805
|
this.activeAgents.add(id);
|
|
4608
2806
|
}
|
|
4609
2807
|
state.chainId = chainId;
|
|
4610
|
-
|
|
2808
|
+
this.activateAgentsInTurn(agents, state, chainId).catch((error) => {
|
|
2809
|
+
console.error("activateAgent background init failed:", error);
|
|
2810
|
+
});
|
|
4611
2811
|
return;
|
|
4612
2812
|
}
|
|
4613
2813
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -4628,7 +2828,11 @@ var OwneySDK = class {
|
|
|
4628
2828
|
const enabledCompatible = compatible.filter(
|
|
4629
2829
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
4630
2830
|
);
|
|
4631
|
-
|
|
2831
|
+
this.activateAgentsInTurn(enabledCompatible, state, chainId).catch(
|
|
2832
|
+
(error) => {
|
|
2833
|
+
console.error("activateAgent background init failed:", error);
|
|
2834
|
+
}
|
|
2835
|
+
);
|
|
4632
2836
|
}
|
|
4633
2837
|
/**
|
|
4634
2838
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -4648,11 +2852,11 @@ var OwneySDK = class {
|
|
|
4648
2852
|
* rethrown (matching the previous `Promise.all` rejection) once all agents
|
|
4649
2853
|
* have had a chance to activate.
|
|
4650
2854
|
*/
|
|
4651
|
-
async activateAgentsInTurn(agents, state, chainId
|
|
2855
|
+
async activateAgentsInTurn(agents, state, chainId) {
|
|
4652
2856
|
let firstError = null;
|
|
4653
2857
|
for (const agent of agents) {
|
|
4654
2858
|
try {
|
|
4655
|
-
await agent.activateAgent(state, chainId
|
|
2859
|
+
await agent.activateAgent(state, chainId);
|
|
4656
2860
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
4657
2861
|
} catch (error) {
|
|
4658
2862
|
if (firstError === null) {
|
|
@@ -4732,18 +2936,30 @@ var OwneySDK = class {
|
|
|
4732
2936
|
);
|
|
4733
2937
|
}
|
|
4734
2938
|
const agentResults = {};
|
|
2939
|
+
const agentErrors = {};
|
|
4735
2940
|
for (const { agent, amount: agentAmount } of agentAmounts) {
|
|
4736
|
-
|
|
4737
|
-
agent
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
2941
|
+
try {
|
|
2942
|
+
agentResults[agent.id] = await this.depositWithFallback(
|
|
2943
|
+
agent,
|
|
2944
|
+
state,
|
|
2945
|
+
chainId,
|
|
2946
|
+
agentAmount,
|
|
2947
|
+
asset,
|
|
2948
|
+
effectiveCallback,
|
|
2949
|
+
depositCallback
|
|
2950
|
+
);
|
|
2951
|
+
} catch (error) {
|
|
2952
|
+
agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
if (Object.keys(agentResults).length === 0) {
|
|
2956
|
+
throw new OwneyError(
|
|
2957
|
+
"DEPOSIT_ALL_FAILED",
|
|
2958
|
+
`All ${agentAmounts.length} agent deposit(s) failed.`,
|
|
2959
|
+
{ agentErrors }
|
|
4744
2960
|
);
|
|
4745
2961
|
}
|
|
4746
|
-
return { agentResults };
|
|
2962
|
+
return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
|
|
4747
2963
|
}
|
|
4748
2964
|
/**
|
|
4749
2965
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
@@ -4820,15 +3036,15 @@ var OwneySDK = class {
|
|
|
4820
3036
|
agent,
|
|
4821
3037
|
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
4822
3038
|
}));
|
|
4823
|
-
const
|
|
3039
|
+
const valid = splits.filter(
|
|
4824
3040
|
(s) => exempt.has(s.agent.id) || s.amount >= this.getMinDepositAmount(s.agent, chainId, asset)
|
|
4825
3041
|
);
|
|
4826
|
-
if (
|
|
3042
|
+
if (valid.length === agents.length) {
|
|
4827
3043
|
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
4828
3044
|
}
|
|
4829
3045
|
return this.splitDepositAmount(
|
|
4830
3046
|
totalAmount,
|
|
4831
|
-
|
|
3047
|
+
valid.map((s) => s.agent),
|
|
4832
3048
|
chainId,
|
|
4833
3049
|
asset,
|
|
4834
3050
|
exempt
|
|
@@ -4860,9 +3076,8 @@ var OwneySDK = class {
|
|
|
4860
3076
|
async hasExistingBalance(agent, state, chainId, asset) {
|
|
4861
3077
|
try {
|
|
4862
3078
|
const balance = await agent.getBalances(state, chainId);
|
|
4863
|
-
const target = asset.toLowerCase();
|
|
4864
3079
|
const token = balance.tokens.find(
|
|
4865
|
-
(t) => t.chainId === chainId && t.asset
|
|
3080
|
+
(t) => t.chainId === chainId && isSameAsset(t.asset, asset)
|
|
4866
3081
|
);
|
|
4867
3082
|
return !!token && Number(token.amount) > 0;
|
|
4868
3083
|
} catch {
|
|
@@ -4901,6 +3116,34 @@ var OwneySDK = class {
|
|
|
4901
3116
|
}
|
|
4902
3117
|
return eligible;
|
|
4903
3118
|
}
|
|
3119
|
+
/**
|
|
3120
|
+
* Agent ids the routing API provisioned for this org that support the given
|
|
3121
|
+
* chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
|
|
3122
|
+
* surfliquid first). Returns `[]` when the org has no compatible agent — never
|
|
3123
|
+
* throws on an empty org. Loads agent keys on first call (apiKey only, no
|
|
3124
|
+
* wallet), so the UI can resolve which agent to use before the user connects.
|
|
3125
|
+
*
|
|
3126
|
+
* This is the source of truth for agent availability: an agent appears here
|
|
3127
|
+
* iff the routing API returned its key. No per-app feature flags.
|
|
3128
|
+
*/
|
|
3129
|
+
async getEligibleAgentIds(chainId, asset) {
|
|
3130
|
+
try {
|
|
3131
|
+
await this.ensureAgentsInitialized();
|
|
3132
|
+
} catch (error) {
|
|
3133
|
+
if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
|
|
3134
|
+
return [];
|
|
3135
|
+
}
|
|
3136
|
+
throw error;
|
|
3137
|
+
}
|
|
3138
|
+
return [...this.agents.values()].filter((agent) => {
|
|
3139
|
+
const chainAssets = agent.supportedAssets.find(
|
|
3140
|
+
(sa) => sa.chainId === chainId
|
|
3141
|
+
);
|
|
3142
|
+
return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
|
|
3143
|
+
}).map((agent) => agent.id).sort(
|
|
3144
|
+
(a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
|
|
3145
|
+
);
|
|
3146
|
+
}
|
|
4904
3147
|
// --- Fund operations ---
|
|
4905
3148
|
/**
|
|
4906
3149
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -4937,9 +3180,17 @@ var OwneySDK = class {
|
|
|
4937
3180
|
}
|
|
4938
3181
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
4939
3182
|
if (!amount) {
|
|
3183
|
+
const aggregated2 = await this.getBalances();
|
|
3184
|
+
const funded = projectAgentBalancesForAsset(
|
|
3185
|
+
eligibleAgents,
|
|
3186
|
+
aggregated2.agentBalances,
|
|
3187
|
+
chainId,
|
|
3188
|
+
asset,
|
|
3189
|
+
assetInfo.decimals
|
|
3190
|
+
).filter((b) => b.balance > 0n);
|
|
4940
3191
|
const results2 = {};
|
|
4941
3192
|
const agentErrors2 = {};
|
|
4942
|
-
for (const agent of
|
|
3193
|
+
for (const { agent } of funded) {
|
|
4943
3194
|
try {
|
|
4944
3195
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
4945
3196
|
} catch (err) {
|
|
@@ -4961,17 +3212,14 @@ var OwneySDK = class {
|
|
|
4961
3212
|
{ asset, agentErrors: agentErrors2 }
|
|
4962
3213
|
);
|
|
4963
3214
|
}
|
|
3215
|
+
const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
|
|
4964
3216
|
return {
|
|
4965
3217
|
agentResult: results2,
|
|
4966
|
-
totalWithdrawn:
|
|
3218
|
+
totalWithdrawn: totalWithdrawn.toString()
|
|
4967
3219
|
};
|
|
4968
3220
|
}
|
|
4969
3221
|
const requested = BigInt(amount);
|
|
4970
3222
|
const aggregated = await this.getBalances();
|
|
4971
|
-
const unavailableAgents = eligibleAgents.filter(
|
|
4972
|
-
(agent) => !(agent.id in aggregated.agentBalances)
|
|
4973
|
-
);
|
|
4974
|
-
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
4975
3223
|
const balances = projectAgentBalancesForAsset(
|
|
4976
3224
|
eligibleAgents,
|
|
4977
3225
|
aggregated.agentBalances,
|
|
@@ -4980,18 +3228,7 @@ var OwneySDK = class {
|
|
|
4980
3228
|
assetInfo.decimals
|
|
4981
3229
|
);
|
|
4982
3230
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
4983
|
-
if (totalAvailable
|
|
4984
|
-
throw new OwneyError(
|
|
4985
|
-
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
4986
|
-
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
4987
|
-
{
|
|
4988
|
-
asset,
|
|
4989
|
-
unavailableAgents: unavailableAgentIds,
|
|
4990
|
-
agentErrors: aggregated.agentErrors
|
|
4991
|
-
}
|
|
4992
|
-
);
|
|
4993
|
-
}
|
|
4994
|
-
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3231
|
+
if (totalAvailable < requested) {
|
|
4995
3232
|
throw new OwneyError(
|
|
4996
3233
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4997
3234
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -5002,7 +3239,6 @@ var OwneySDK = class {
|
|
|
5002
3239
|
}
|
|
5003
3240
|
);
|
|
5004
3241
|
}
|
|
5005
|
-
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
5006
3242
|
const disabledBalances = balances.filter(
|
|
5007
3243
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
5008
3244
|
);
|
|
@@ -5011,7 +3247,7 @@ var OwneySDK = class {
|
|
|
5011
3247
|
);
|
|
5012
3248
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
5013
3249
|
disabledBalances,
|
|
5014
|
-
|
|
3250
|
+
requested
|
|
5015
3251
|
);
|
|
5016
3252
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
5017
3253
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -5021,9 +3257,7 @@ var OwneySDK = class {
|
|
|
5021
3257
|
}));
|
|
5022
3258
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
5023
3259
|
const results = {};
|
|
5024
|
-
const agentErrors = {
|
|
5025
|
-
...aggregated.agentErrors ?? {}
|
|
5026
|
-
};
|
|
3260
|
+
const agentErrors = {};
|
|
5027
3261
|
for (let i = 0; i < plans.length; i++) {
|
|
5028
3262
|
const p = plans[i];
|
|
5029
3263
|
if (p.planned === 0n) continue;
|
|
@@ -5070,8 +3304,7 @@ var OwneySDK = class {
|
|
|
5070
3304
|
requested: amount,
|
|
5071
3305
|
withdrawn: withdrawn.toString(),
|
|
5072
3306
|
partialResults: results,
|
|
5073
|
-
agentErrors
|
|
5074
|
-
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3307
|
+
agentErrors
|
|
5075
3308
|
}
|
|
5076
3309
|
);
|
|
5077
3310
|
}
|
|
@@ -5112,12 +3345,7 @@ var OwneySDK = class {
|
|
|
5112
3345
|
continue;
|
|
5113
3346
|
}
|
|
5114
3347
|
const reason = settledResult.reason;
|
|
5115
|
-
|
|
5116
|
-
agentErrors[agentId2] = message;
|
|
5117
|
-
console.error(
|
|
5118
|
-
`[owney-sdk] Balance fetch failed for agent "${agentId2}":`,
|
|
5119
|
-
reason
|
|
5120
|
-
);
|
|
3348
|
+
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
5121
3349
|
}
|
|
5122
3350
|
if (successCount === 0) {
|
|
5123
3351
|
throw new OwneyError(
|
|
@@ -5129,8 +3357,7 @@ var OwneySDK = class {
|
|
|
5129
3357
|
return {
|
|
5130
3358
|
totalBalance: String(totalBalance),
|
|
5131
3359
|
totalBalanceAsset: "usdc",
|
|
5132
|
-
agentBalances: results
|
|
5133
|
-
...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
|
|
3360
|
+
agentBalances: results
|
|
5134
3361
|
};
|
|
5135
3362
|
}
|
|
5136
3363
|
/**
|
|
@@ -5224,10 +3451,7 @@ var OwneySDK = class {
|
|
|
5224
3451
|
Promise.all(
|
|
5225
3452
|
entries.map(async ([id, agent]) => {
|
|
5226
3453
|
const b = await agent.getBalances(state, chainId);
|
|
5227
|
-
return [
|
|
5228
|
-
id,
|
|
5229
|
-
balanceForApyScope(b, chainId, tokenSymbol)
|
|
5230
|
-
];
|
|
3454
|
+
return [id, Number(b.totalBalance)];
|
|
5231
3455
|
})
|
|
5232
3456
|
)
|
|
5233
3457
|
]);
|
|
@@ -5255,12 +3479,10 @@ var OwneySDK = class {
|
|
|
5255
3479
|
}
|
|
5256
3480
|
}
|
|
5257
3481
|
const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
|
|
5258
|
-
const history = aggregateApyHistory(results, balances);
|
|
5259
3482
|
return {
|
|
5260
3483
|
totalApy: String(totalApy),
|
|
5261
3484
|
agentApy: results,
|
|
5262
|
-
apyByChainAndAsset
|
|
5263
|
-
history
|
|
3485
|
+
apyByChainAndAsset
|
|
5264
3486
|
};
|
|
5265
3487
|
}
|
|
5266
3488
|
/**
|
|
@@ -5390,10 +3612,10 @@ var OwneySDK = class {
|
|
|
5390
3612
|
);
|
|
5391
3613
|
}
|
|
5392
3614
|
const provider = this.requireConnectedProvider();
|
|
5393
|
-
const wallet =
|
|
3615
|
+
const wallet = createWalletClient({
|
|
5394
3616
|
account: state.walletAddress,
|
|
5395
3617
|
chain: VIEM_CHAIN2[chainId],
|
|
5396
|
-
transport:
|
|
3618
|
+
transport: custom(provider)
|
|
5397
3619
|
});
|
|
5398
3620
|
const hash = await wallet.writeContract({
|
|
5399
3621
|
address: token,
|
|
@@ -5403,9 +3625,9 @@ var OwneySDK = class {
|
|
|
5403
3625
|
account: state.walletAddress,
|
|
5404
3626
|
chain: VIEM_CHAIN2[chainId]
|
|
5405
3627
|
});
|
|
5406
|
-
const publicClient =
|
|
3628
|
+
const publicClient = createPublicClient2({
|
|
5407
3629
|
chain: VIEM_CHAIN2[chainId],
|
|
5408
|
-
transport:
|
|
3630
|
+
transport: custom(provider)
|
|
5409
3631
|
});
|
|
5410
3632
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
5411
3633
|
hash,
|
|
@@ -5441,9 +3663,7 @@ var OwneySDK = class {
|
|
|
5441
3663
|
return this.getAgent(agentId).getAgentApy(days, agentOptions);
|
|
5442
3664
|
}
|
|
5443
3665
|
const results = {};
|
|
5444
|
-
const agentEntries = [...this.agents.entries()]
|
|
5445
|
-
([id]) => !this.isAgentDisabled(id)
|
|
5446
|
-
);
|
|
3666
|
+
const agentEntries = [...this.agents.entries()];
|
|
5447
3667
|
const apyResults = await Promise.all(
|
|
5448
3668
|
agentEntries.map(async ([id, agent]) => {
|
|
5449
3669
|
const apy = await agent.getAgentApy(days, agentOptions);
|
|
@@ -5516,13 +3736,13 @@ var OwneySDK = class {
|
|
|
5516
3736
|
};
|
|
5517
3737
|
|
|
5518
3738
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
5519
|
-
import { getAddress
|
|
5520
|
-
import { SiweMessage
|
|
3739
|
+
import { getAddress } from "viem";
|
|
3740
|
+
import { SiweMessage } from "siwe";
|
|
5521
3741
|
import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
|
|
5522
3742
|
|
|
5523
3743
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
5524
|
-
var
|
|
5525
|
-
var
|
|
3744
|
+
var KEY_PREFIX2 = "owney.siwx.session";
|
|
3745
|
+
var storage2 = () => {
|
|
5526
3746
|
if (typeof window === "undefined") return null;
|
|
5527
3747
|
try {
|
|
5528
3748
|
return window.localStorage;
|
|
@@ -5530,8 +3750,8 @@ var storage4 = () => {
|
|
|
5530
3750
|
return null;
|
|
5531
3751
|
}
|
|
5532
3752
|
};
|
|
5533
|
-
var
|
|
5534
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
3753
|
+
var buildKey2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}`;
|
|
3754
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX2}:${address.toLowerCase()}:`;
|
|
5535
3755
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
5536
3756
|
var readLegacySiwxSession = (store, address) => {
|
|
5537
3757
|
if (!store) return null;
|
|
@@ -5562,17 +3782,17 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
5562
3782
|
};
|
|
5563
3783
|
var readSiwxSession = (address, chainId) => {
|
|
5564
3784
|
if (typeof window === "undefined") return null;
|
|
5565
|
-
const key2 =
|
|
5566
|
-
const store =
|
|
5567
|
-
let
|
|
3785
|
+
const key2 = buildKey2(address);
|
|
3786
|
+
const store = storage2();
|
|
3787
|
+
let raw = null;
|
|
5568
3788
|
try {
|
|
5569
|
-
|
|
3789
|
+
raw = store?.getItem(key2) ?? null;
|
|
5570
3790
|
} catch {
|
|
5571
|
-
|
|
3791
|
+
raw = null;
|
|
5572
3792
|
}
|
|
5573
|
-
if (
|
|
3793
|
+
if (raw) {
|
|
5574
3794
|
try {
|
|
5575
|
-
return JSON.parse(
|
|
3795
|
+
return JSON.parse(raw);
|
|
5576
3796
|
} catch {
|
|
5577
3797
|
memorySiwxSessions.delete(key2);
|
|
5578
3798
|
try {
|
|
@@ -5591,18 +3811,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
5591
3811
|
};
|
|
5592
3812
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
5593
3813
|
if (typeof window === "undefined") return;
|
|
5594
|
-
const key2 =
|
|
3814
|
+
const key2 = buildKey2(address);
|
|
5595
3815
|
memorySiwxSessions.set(key2, session);
|
|
5596
|
-
const store =
|
|
3816
|
+
const store = storage2();
|
|
5597
3817
|
try {
|
|
5598
3818
|
store?.setItem(key2, JSON.stringify(session));
|
|
5599
3819
|
} catch {
|
|
5600
3820
|
}
|
|
5601
3821
|
};
|
|
5602
3822
|
var clearSiwxSession = (address, _chainId) => {
|
|
5603
|
-
const key2 =
|
|
3823
|
+
const key2 = buildKey2(address);
|
|
5604
3824
|
memorySiwxSessions.delete(key2);
|
|
5605
|
-
const store =
|
|
3825
|
+
const store = storage2();
|
|
5606
3826
|
try {
|
|
5607
3827
|
store?.removeItem(key2);
|
|
5608
3828
|
} catch {
|
|
@@ -5642,8 +3862,8 @@ function buildSIWXConfig(deps) {
|
|
|
5642
3862
|
statement: STATEMENT,
|
|
5643
3863
|
issuedAt,
|
|
5644
3864
|
toString() {
|
|
5645
|
-
return new
|
|
5646
|
-
address:
|
|
3865
|
+
return new SiweMessage({
|
|
3866
|
+
address: getAddress(accountAddress),
|
|
5647
3867
|
chainId: numericChainId(chainId),
|
|
5648
3868
|
domain,
|
|
5649
3869
|
uri,
|
|
@@ -5685,7 +3905,7 @@ function buildSIWXConfig(deps) {
|
|
|
5685
3905
|
const persistSession = async (session) => {
|
|
5686
3906
|
const address = session.data.accountAddress;
|
|
5687
3907
|
const id = numericChainId(session.data.chainId);
|
|
5688
|
-
const message = new
|
|
3908
|
+
const message = new SiweMessage(session.message);
|
|
5689
3909
|
const login = await post("/auth/login", {
|
|
5690
3910
|
message,
|
|
5691
3911
|
signature: session.signature,
|
|
@@ -5733,7 +3953,6 @@ export {
|
|
|
5733
3953
|
NotConnectedError,
|
|
5734
3954
|
OwneyError,
|
|
5735
3955
|
OwneySDK,
|
|
5736
|
-
YieldseekerAgent,
|
|
5737
3956
|
createOwneySIWX,
|
|
5738
3957
|
setOwneyDebug
|
|
5739
3958
|
};
|