@owney/sdk 0.7.21-beta.2 → 0.7.22-beta.0

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.
@@ -0,0 +1,638 @@
1
+ import {
2
+ OwneyError,
3
+ ROUTING_API_BASE_URL
4
+ } from "./chunk-XBDJWZXY.js";
5
+ import {
6
+ SURFLIQUID_ACTION_MAP,
7
+ SURFLIQUID_CHAIN_ID,
8
+ SURFLIQUID_CHAIN_NAME,
9
+ SURFLIQUID_SUPPORTED_ASSETS,
10
+ SubmittedError,
11
+ VaultNotSponsorableError,
12
+ depositSponsored,
13
+ withdrawSponsored
14
+ } from "./chunk-W3FYRJLJ.js";
15
+ import "./chunk-5LU2SHO7.js";
16
+
17
+ // src/agents/surfliquid/surfliquid.agent.ts
18
+ import { createWalletClient, custom } from "viem";
19
+ import { base } from "viem/chains";
20
+
21
+ // src/agents/surfliquid/surfliquid.session-store.ts
22
+ var KEY_PREFIX = "owney.surfliquid.session";
23
+ var EXPIRY_SKEW_MS = 3e4;
24
+ var memorySessions = /* @__PURE__ */ new Map();
25
+ var storage = () => {
26
+ if (typeof window === "undefined") return null;
27
+ try {
28
+ return window.localStorage;
29
+ } catch {
30
+ return null;
31
+ }
32
+ };
33
+ var buildKey = (apiKey, wallet) => `${KEY_PREFIX}:${apiKey}:${wallet.toLowerCase()}`;
34
+ var isFresh = (session) => {
35
+ if (!session?.token) return false;
36
+ if (!session.expiresAt) return true;
37
+ const expiresAt = Date.parse(session.expiresAt);
38
+ if (Number.isNaN(expiresAt)) return true;
39
+ return Date.now() < expiresAt - EXPIRY_SKEW_MS;
40
+ };
41
+ var readSurfSession = (apiKey, wallet) => {
42
+ const key = buildKey(apiKey, wallet);
43
+ let raw = null;
44
+ try {
45
+ raw = storage()?.getItem(key) ?? null;
46
+ } catch {
47
+ raw = null;
48
+ }
49
+ if (raw) {
50
+ try {
51
+ const parsed = JSON.parse(raw);
52
+ if (isFresh(parsed)) return parsed;
53
+ } catch {
54
+ }
55
+ clearSurfSession(apiKey, wallet);
56
+ return null;
57
+ }
58
+ const cached = memorySessions.get(key);
59
+ if (isFresh(cached)) return cached;
60
+ if (cached) memorySessions.delete(key);
61
+ return null;
62
+ };
63
+ var writeSurfSession = (apiKey, wallet, session) => {
64
+ const key = buildKey(apiKey, wallet);
65
+ memorySessions.set(key, session);
66
+ try {
67
+ storage()?.setItem(key, JSON.stringify(session));
68
+ } catch {
69
+ }
70
+ };
71
+ var clearSurfSession = (apiKey, wallet) => {
72
+ const key = buildKey(apiKey, wallet);
73
+ memorySessions.delete(key);
74
+ try {
75
+ storage()?.removeItem(key);
76
+ } catch {
77
+ }
78
+ };
79
+
80
+ // src/agents/surfliquid/surfliquid.broker.ts
81
+ var SURF_SESSION_HEADER = "x-surf-session";
82
+ var SURF_BROKER_TIMEOUT_MS = 7e4;
83
+ var VAULT_MEMO_TTL_MS = 1e4;
84
+ var AGENT_ID = "surfliquid";
85
+ var DISCOVERY_WALLET = "0x";
86
+ var SurfSessionExpiredError = class extends Error {
87
+ constructor() {
88
+ super("SurfLiquid session expired");
89
+ this.name = "SurfSessionExpiredError";
90
+ }
91
+ };
92
+ var asRecord = (value) => value && typeof value === "object" ? value : {};
93
+ var unavailable = (message, details) => new OwneyError("SURF_UNAVAILABLE", message, details, AGENT_ID);
94
+ function parseJson(text) {
95
+ try {
96
+ return JSON.parse(text);
97
+ } catch {
98
+ return void 0;
99
+ }
100
+ }
101
+ function normalizeVault(json) {
102
+ const root = asRecord(json);
103
+ const data = asRecord(root.data);
104
+ const vault = asRecord(root.vault ?? data.vault ?? root.data ?? root);
105
+ const userVaultAddress = vault.userVaultAddress ?? null;
106
+ return {
107
+ userVaultAddress,
108
+ deploymentSalt: vault.deploymentSalt ?? null,
109
+ exists: Boolean(userVaultAddress),
110
+ walletAddress: vault.walletAddress ?? null,
111
+ homeChainId: vault.homeChainId ?? null,
112
+ vaultVersion: vault.vaultVersion ?? null,
113
+ isActive: vault.isActive,
114
+ totalValueUSD: vault.totalValueUSD ?? null,
115
+ totalDepositedUSD: vault.totalDepositedUSD ?? null,
116
+ earned: vault.earned ?? null,
117
+ apyBreakdown: vault.apyBreakdown ?? null,
118
+ league: vault.league ?? null,
119
+ assets: vault.assets ?? [],
120
+ chainAddresses: vault.chainAddresses ?? []
121
+ };
122
+ }
123
+ function normalizeSupportedAssets(json, chainId) {
124
+ const root = asRecord(json);
125
+ const data = asRecord(root.data);
126
+ const vault = asRecord(root.vault ?? data.vault ?? root.data ?? root);
127
+ const source = vault.assets ?? vault.defaultAssets ?? root.defaultAssets ?? data.defaultAssets ?? [];
128
+ const assets = source.map((asset) => ({
129
+ assetAddress: asset.assetAddress,
130
+ assetSymbol: asset.assetSymbol,
131
+ assetDecimals: asset.assetDecimals,
132
+ chainId: asset.chainId,
133
+ chainStatus: asset.chainStatus,
134
+ currentAPY: asset.currentAPY,
135
+ nativeAPY: asset.nativeAPY,
136
+ merklAPY: asset.merklAPY,
137
+ leagueAPY: asset.leagueAPY,
138
+ apy7d: asset.apy7d ?? null,
139
+ apy14d: asset.apy14d ?? null,
140
+ apy30d: asset.apy30d ?? null
141
+ }));
142
+ return chainId === void 0 ? assets : assets.filter((asset) => asset.chainId === chainId);
143
+ }
144
+ function createSurfBroker(options) {
145
+ const fetchImpl = options.fetchImpl ?? fetch;
146
+ const now = options.now ?? Date.now;
147
+ const base2 = `${options.routingApiBaseUrl.replace(/\/$/, "")}/api/v1/surf`;
148
+ const wallet = options.walletAddress;
149
+ let session = readSurfSession(options.apiKey, wallet);
150
+ const vaultMemo = /* @__PURE__ */ new Map();
151
+ const storeSession = (next) => {
152
+ session = next;
153
+ writeSurfSession(options.apiKey, wallet, next);
154
+ };
155
+ const dropSession = () => {
156
+ session = null;
157
+ clearSurfSession(options.apiKey, wallet);
158
+ };
159
+ async function call(method, path, body, isAuthCall = false) {
160
+ const headers = { "x-owney-api-key": options.apiKey };
161
+ if (session?.token) headers[SURF_SESSION_HEADER] = session.token;
162
+ if (body !== void 0) headers["content-type"] = "application/json";
163
+ if (options.origin) headers["origin"] = options.origin;
164
+ let response;
165
+ try {
166
+ response = await fetchImpl(`${base2}${path}`, {
167
+ method,
168
+ headers,
169
+ body: body === void 0 ? void 0 : JSON.stringify(body),
170
+ signal: AbortSignal.timeout(SURF_BROKER_TIMEOUT_MS)
171
+ });
172
+ } catch (error) {
173
+ throw unavailable(
174
+ `SurfLiquid broker unreachable: ${error instanceof Error ? error.message : String(error)}`,
175
+ { path }
176
+ );
177
+ }
178
+ const rotated = response.headers.get(SURF_SESSION_HEADER);
179
+ if (rotated && rotated !== session?.token) {
180
+ storeSession({ token: rotated, expiresAt: session?.expiresAt ?? null });
181
+ }
182
+ if (response.status === 401 && !isAuthCall) {
183
+ dropSession();
184
+ throw new SurfSessionExpiredError();
185
+ }
186
+ const text = await response.text();
187
+ const json = parseJson(text);
188
+ if (!response.ok) {
189
+ throw unavailable(`SurfLiquid broker responded ${response.status} on ${path}`, {
190
+ status: response.status,
191
+ body: json ?? text
192
+ });
193
+ }
194
+ return json;
195
+ }
196
+ const sessionFrom = (json) => {
197
+ const body = asRecord(json);
198
+ if (typeof body.token !== "string") throw unavailable("SurfLiquid login returned no session token");
199
+ const next = { token: body.token, expiresAt: typeof body.expiresAt === "string" ? body.expiresAt : null };
200
+ storeSession(next);
201
+ return next;
202
+ };
203
+ return {
204
+ hasSession: () => session !== null,
205
+ clearSession: dropSession,
206
+ nonce: async () => {
207
+ const json = await call("POST", "/auth/nonce", { walletAddress: wallet }, true);
208
+ const message = json?.data?.message;
209
+ if (typeof message !== "string") throw unavailable("SurfLiquid nonce response had no message");
210
+ return message;
211
+ },
212
+ login: async (message, signature) => sessionFrom(await call("POST", "/auth/login", { walletAddress: wallet, message, signature }, true)),
213
+ getVault: (walletAddress) => {
214
+ const key = walletAddress.toLowerCase();
215
+ const hit = vaultMemo.get(key);
216
+ if (hit && now() - hit.at < VAULT_MEMO_TTL_MS) return hit.value;
217
+ const query = new URLSearchParams({ walletAddress });
218
+ const value = (async () => normalizeVault(await call("GET", `/vault?${query.toString()}`)))();
219
+ vaultMemo.set(key, { at: now(), value });
220
+ value.catch(() => vaultMemo.delete(key));
221
+ return value;
222
+ },
223
+ invalidateVault: () => vaultMemo.clear(),
224
+ prepare: async (homeChainId) => {
225
+ const json = asRecord(await call("POST", "/vault/prepare", { homeChainId }));
226
+ const prepared = asRecord(json.data ?? json);
227
+ if (typeof prepared.salt !== "string") throw unavailable("SurfLiquid prepare returned no salt");
228
+ return { salt: prepared.salt };
229
+ },
230
+ confirm: async (input) => {
231
+ await call("POST", "/vault/confirm", input);
232
+ vaultMemo.clear();
233
+ },
234
+ getBestVault: async (assetSymbol) => {
235
+ const json = await call("GET", `/vaults/best?assetSymbol=${encodeURIComponent(assetSymbol)}`);
236
+ const root = asRecord(json);
237
+ const list = root.vaults ?? root.data ?? json;
238
+ return Array.isArray(list) ? list : [];
239
+ },
240
+ getAgentMessages: async (walletAddress, page, limit, fromDate, toDate) => {
241
+ const query = new URLSearchParams({ walletAddress, page: String(page), limit: String(limit) });
242
+ if (fromDate) query.set("from", fromDate);
243
+ if (toDate) query.set("to", toDate);
244
+ const json = asRecord(await call("GET", `/agent-messages?${query.toString()}`));
245
+ const data = asRecord(json.data ?? json);
246
+ return {
247
+ page: data.page ?? page,
248
+ limit: data.limit ?? limit,
249
+ total: data.total ?? 0,
250
+ pages: data.pages ?? 1,
251
+ messages: data.messages ?? []
252
+ };
253
+ },
254
+ getSupportedAssets: async (chainId) => normalizeSupportedAssets(
255
+ await call("GET", `/vault?${new URLSearchParams({ walletAddress: DISCOVERY_WALLET }).toString()}`),
256
+ chainId
257
+ )
258
+ };
259
+ }
260
+
261
+ // src/agents/surfliquid/surfliquid.mapper.ts
262
+ function mapMessage(message) {
263
+ const action = SURFLIQUID_ACTION_MAP[message.transactionType];
264
+ if (!action) return null;
265
+ return {
266
+ agent: "surfliquid",
267
+ action,
268
+ date: message.timestamp,
269
+ oldApy: message.apyBefore != null ? String(message.apyBefore) : null,
270
+ newApy: message.apyAfter != null ? String(message.apyAfter) : null,
271
+ transactions: [
272
+ {
273
+ txHashes: [message.txHash],
274
+ chainId: message.chainId,
275
+ tokenSymbol: message.token ?? void 0,
276
+ amount: message.amount != null ? String(message.amount) : void 0
277
+ }
278
+ ],
279
+ rebalanceLog: []
280
+ };
281
+ }
282
+ function mapHistory(result, _chainId) {
283
+ const data = result.messages.map(mapMessage).filter((entry) => entry !== null);
284
+ const hasMore = result.page < result.pages;
285
+ return {
286
+ data,
287
+ hasMore,
288
+ nextCursor: hasMore ? String(result.page + 1) : void 0
289
+ };
290
+ }
291
+ function mapBalances(vault, chainId, morphoStats) {
292
+ const assets = (vault.assets ?? []).filter(
293
+ (asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId
294
+ );
295
+ const tokens = assets.map((asset) => ({
296
+ chain: SURFLIQUID_CHAIN_NAME,
297
+ chainId: SURFLIQUID_CHAIN_ID,
298
+ asset: "USDC",
299
+ amount: String(asset.currentValueUSD)
300
+ }));
301
+ const positions = assets.map((asset) => {
302
+ const stats = morphoStats?.get(asset.morphoVaultAddress?.toLowerCase() ?? "");
303
+ return {
304
+ chain: SURFLIQUID_CHAIN_NAME,
305
+ protocol: "Morpho",
306
+ // The specific MetaMorpho vault (e.g. "Gauntlet USDC Frontier"), read from
307
+ // Morpho — renders as "Morpho <vault>" alongside zyfai's vault detail.
308
+ pool: stats?.name ?? void 0,
309
+ asset: "USDC",
310
+ amount: String(asset.currentValueUSD),
311
+ apy: asset.currentAPY,
312
+ // TVL + liquidity come straight from Morpho (SurfLiquid's API omits them).
313
+ tvl: stats?.tvlUsd,
314
+ liquidity: stats?.liquidityUsd
315
+ };
316
+ });
317
+ const total = assets.reduce(
318
+ (sum, asset) => sum + asset.currentValueUSD,
319
+ 0
320
+ );
321
+ return {
322
+ smartWallet: vault.userVaultAddress ?? void 0,
323
+ totalBalance: String(total),
324
+ totalBalanceAsset: "usdc",
325
+ tokens,
326
+ positions
327
+ };
328
+ }
329
+ var APY_WINDOW_KEY = {
330
+ "7D": "apy7d",
331
+ "14D": "apy14d",
332
+ "30D": "apy30d"
333
+ };
334
+ function mapAccountApy(vault, days) {
335
+ const breakdown = vault.apyBreakdown;
336
+ const windowed = breakdown ? breakdown[APY_WINDOW_KEY[days]] : void 0;
337
+ const apy = windowed != null ? windowed : breakdown?.currentAPY ?? 0;
338
+ return {
339
+ walletAddress: vault.walletAddress ?? "",
340
+ weightedApyAfterFee: apy,
341
+ apyByChainAndAsset: { [SURFLIQUID_CHAIN_ID]: { USDC: apy } },
342
+ // Parity with Sail: SurfLiquid's core-sdk exposes no daily net-APY series.
343
+ history: []
344
+ };
345
+ }
346
+ function mapEarnings(vault, vaultAddress) {
347
+ return {
348
+ smartWallet: vaultAddress,
349
+ lifetimeEarnings: vault.earned?.totalEarningsUSD ?? 0,
350
+ tokens: []
351
+ };
352
+ }
353
+ function mapUserProfile(vault, address) {
354
+ return {
355
+ address,
356
+ smartWallet: vault.userVaultAddress ?? "",
357
+ chains: [SURFLIQUID_CHAIN_ID],
358
+ // SurfLiquid uses cookie-based session auth — no session-key concept.
359
+ hasActiveSessionKey: false,
360
+ protocols: ["SurfLiquid"]
361
+ };
362
+ }
363
+
364
+ // src/agents/surfliquid/surfliquid.morpho.ts
365
+ var MORPHO_API_URL = "https://api.morpho.org/graphql";
366
+ var VAULT_STATS_QUERY = `
367
+ query VaultStats($address: String!, $chainId: Int!) {
368
+ vaultByAddress(address: $address, chainId: $chainId) {
369
+ name
370
+ state { totalAssetsUsd }
371
+ liquidity { usd }
372
+ }
373
+ }
374
+ `;
375
+ var VAULT_V2_STATS_QUERY = `
376
+ query VaultV2Stats($address: String!, $chainId: Int!) {
377
+ vaultV2ByAddress(address: $address, chainId: $chainId) {
378
+ name
379
+ totalAssetsUsd
380
+ liquidityUsd
381
+ }
382
+ }
383
+ `;
384
+ async function queryMorpho(query, vaultAddress, chainId) {
385
+ try {
386
+ const res = await fetch(MORPHO_API_URL, {
387
+ method: "POST",
388
+ headers: { "Content-Type": "application/json" },
389
+ body: JSON.stringify({
390
+ query,
391
+ variables: { address: vaultAddress, chainId }
392
+ })
393
+ });
394
+ if (!res.ok) return null;
395
+ const json = await res.json();
396
+ if (json.errors) return null;
397
+ return json.data ?? null;
398
+ } catch (error) {
399
+ console.warn("surfliquid: Morpho vault stats fetch failed", {
400
+ vaultAddress,
401
+ chainId,
402
+ error
403
+ });
404
+ return null;
405
+ }
406
+ }
407
+ function toStats(name, tvlUsd, liquidityUsd) {
408
+ if (typeof tvlUsd !== "number" || typeof liquidityUsd !== "number") {
409
+ return null;
410
+ }
411
+ return {
412
+ name: typeof name === "string" ? name : null,
413
+ tvlUsd,
414
+ liquidityUsd
415
+ };
416
+ }
417
+ async function fetchMorphoVaultStats(vaultAddress, chainId) {
418
+ const v1 = await queryMorpho(VAULT_STATS_QUERY, vaultAddress, chainId);
419
+ const vault = v1?.vaultByAddress;
420
+ const v1Stats = toStats(
421
+ vault?.name,
422
+ vault?.state?.totalAssetsUsd,
423
+ vault?.liquidity?.usd
424
+ );
425
+ if (v1Stats) return v1Stats;
426
+ const v2 = await queryMorpho(VAULT_V2_STATS_QUERY, vaultAddress, chainId);
427
+ const vaultV2 = v2?.vaultV2ByAddress;
428
+ return toStats(vaultV2?.name, vaultV2?.totalAssetsUsd, vaultV2?.liquidityUsd);
429
+ }
430
+ async function fetchMorphoStatsByVault(vaultAddresses, chainId) {
431
+ const unique = [...new Set(vaultAddresses.map((a) => a.toLowerCase()))];
432
+ const entries = await Promise.all(
433
+ unique.map(
434
+ async (address) => [address, await fetchMorphoVaultStats(address, chainId)]
435
+ )
436
+ );
437
+ const byVault = /* @__PURE__ */ new Map();
438
+ for (const [address, stats] of entries) {
439
+ if (stats) byVault.set(address, stats);
440
+ }
441
+ return byVault;
442
+ }
443
+
444
+ // src/agents/surfliquid/surfliquid.agent.ts
445
+ var AGENT_ID2 = "surfliquid";
446
+ var WALLET_REJECTED_CODE = 4001;
447
+ var DISCOVERY_WALLET2 = "0x";
448
+ var HISTORY_DEFAULT_LIMIT = 10;
449
+ var APY_WINDOW_KEY2 = {
450
+ "7D": "apy7d",
451
+ "14D": "apy14d",
452
+ "30D": "apy30d"
453
+ };
454
+ function toOwneyError(error) {
455
+ if (error instanceof OwneyError) return error;
456
+ if (error instanceof SubmittedError) {
457
+ return new OwneyError("OPERATION_PENDING", error.message, { userOpHash: error.userOpHash }, AGENT_ID2);
458
+ }
459
+ if (error instanceof VaultNotSponsorableError) {
460
+ return new OwneyError("VAULT_NOT_SPONSORABLE", error.message, void 0, AGENT_ID2);
461
+ }
462
+ const message = error instanceof Error ? error.message : String(error);
463
+ const code = error?.code;
464
+ if (code === WALLET_REJECTED_CODE || /user rejected|user denied/i.test(message)) {
465
+ return new OwneyError("USER_REJECTED", message, void 0, AGENT_ID2);
466
+ }
467
+ return new OwneyError("SPONSORSHIP_UNAVAILABLE", message, void 0, AGENT_ID2);
468
+ }
469
+ var SurfLiquidAgent = class {
470
+ constructor(config) {
471
+ this.config = config;
472
+ }
473
+ config;
474
+ id = AGENT_ID2;
475
+ supportedChainIds = [SURFLIQUID_CHAIN_ID];
476
+ supportedAssets = SURFLIQUID_SUPPORTED_ASSETS;
477
+ broker = null;
478
+ brokerWallet = null;
479
+ // Dedicated instance for the anonymous "0x" asset probe — never shares a slot
480
+ // with the user's broker, so discovery can never evict or overwrite a session.
481
+ discoveryBroker = null;
482
+ // In-flight SIWE, shared by concurrent callers: SurfLiquid's nonce is single-use.
483
+ connectPromise = null;
484
+ get routingApiBaseUrl() {
485
+ return this.config.routingApiBaseUrl ?? ROUTING_API_BASE_URL;
486
+ }
487
+ // Synchronous so the check-and-assign below cannot interleave with a concurrent call.
488
+ brokerFor(walletAddress) {
489
+ if (this.broker && this.brokerWallet === walletAddress.toLowerCase()) return this.broker;
490
+ this.broker = createSurfBroker({
491
+ routingApiBaseUrl: this.routingApiBaseUrl,
492
+ apiKey: this.config.apiKey,
493
+ walletAddress,
494
+ fetchImpl: this.config.fetchImpl
495
+ });
496
+ this.brokerWallet = walletAddress.toLowerCase();
497
+ return this.broker;
498
+ }
499
+ async connect(state, shouldForce = false) {
500
+ const broker = this.brokerFor(state.walletAddress);
501
+ if (!shouldForce && broker.hasSession()) return broker;
502
+ if (this.connectPromise) return this.connectPromise;
503
+ this.connectPromise = (async () => {
504
+ const message = await broker.nonce();
505
+ const walletClient = createWalletClient({
506
+ account: state.walletAddress,
507
+ chain: base,
508
+ transport: custom(state.provider)
509
+ });
510
+ const signature = await walletClient.signMessage({ account: state.walletAddress, message });
511
+ await broker.login(message, signature);
512
+ return broker;
513
+ })();
514
+ try {
515
+ return await this.connectPromise;
516
+ } finally {
517
+ this.connectPromise = null;
518
+ }
519
+ }
520
+ /** A 401 can only surface before any signature (SurfLiquid is never called between sign and submit). */
521
+ async withSession(state, run) {
522
+ try {
523
+ return await run();
524
+ } catch (error) {
525
+ if (!(error instanceof SurfSessionExpiredError)) throw error;
526
+ await this.connect(state, true);
527
+ try {
528
+ return await run();
529
+ } catch (retryError) {
530
+ if (!(retryError instanceof SurfSessionExpiredError)) throw retryError;
531
+ throw new OwneyError("SURF_UNAVAILABLE", "SurfLiquid session could not be re-established", void 0, AGENT_ID2);
532
+ }
533
+ }
534
+ }
535
+ async sponsoredWallet(state) {
536
+ const { createSponsoredWallet } = await import("./surfliquid.smart-account-DWV5B3D5.js");
537
+ return createSponsoredWallet({
538
+ provider: state.provider,
539
+ ownerAddress: state.walletAddress,
540
+ routingApiBaseUrl: this.routingApiBaseUrl,
541
+ apiKey: this.config.apiKey,
542
+ rpcUrl: this.config.rpcUrl
543
+ });
544
+ }
545
+ // --- IAgent: lifecycle ---
546
+ async disconnect() {
547
+ this.broker?.clearSession();
548
+ this.broker = null;
549
+ this.brokerWallet = null;
550
+ }
551
+ async activateAgent(state, _chainId) {
552
+ await this.connect(state);
553
+ }
554
+ // --- IAgent: funds (gasless only) ---
555
+ async deposit(state, _chainId, amount, _asset, _depositCallback) {
556
+ const broker = await this.connect(state);
557
+ const wallet = await this.sponsoredWallet(state);
558
+ const { createSponsoredChain } = await import("./surfliquid.smart-account-DWV5B3D5.js");
559
+ try {
560
+ const { txHash, vault } = await this.withSession(
561
+ state,
562
+ () => depositSponsored({ amount: BigInt(amount), api: broker, chain: createSponsoredChain(this.config.rpcUrl), wallet })
563
+ );
564
+ broker.invalidateVault();
565
+ return { txHash, smartWallet: vault, amount };
566
+ } catch (error) {
567
+ throw toOwneyError(error);
568
+ }
569
+ }
570
+ async withdraw(state, _chainId, _token, amount) {
571
+ const broker = await this.connect(state);
572
+ const wallet = await this.sponsoredWallet(state);
573
+ const { createSponsoredChain } = await import("./surfliquid.smart-account-DWV5B3D5.js");
574
+ try {
575
+ const result = await this.withSession(
576
+ state,
577
+ () => withdrawSponsored({
578
+ amount: amount != null ? BigInt(amount) : void 0,
579
+ api: broker,
580
+ chain: createSponsoredChain(this.config.rpcUrl),
581
+ wallet
582
+ })
583
+ );
584
+ broker.invalidateVault();
585
+ return { txHash: result.txHash, type: amount != null ? "partial" : "full", amount: result.amount };
586
+ } catch (error) {
587
+ throw toOwneyError(error);
588
+ }
589
+ }
590
+ // --- IAgent: portfolio reads ---
591
+ async vault(state) {
592
+ const broker = await this.connect(state);
593
+ return this.withSession(state, () => broker.getVault(state.walletAddress));
594
+ }
595
+ async getBalances(state, chainId) {
596
+ const vault = await this.vault(state);
597
+ const morphoVaultAddresses = (vault.assets ?? []).filter((asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId).map((asset) => asset.morphoVaultAddress).filter((address) => Boolean(address));
598
+ const morphoStats = await fetchMorphoStatsByVault(morphoVaultAddresses, chainId);
599
+ return mapBalances(vault, chainId, morphoStats);
600
+ }
601
+ async getEarnings(state) {
602
+ const vault = await this.vault(state);
603
+ return mapEarnings(vault, vault.userVaultAddress ?? state.walletAddress);
604
+ }
605
+ async getAccountApy(state, _chainId, days) {
606
+ return mapAccountApy(await this.vault(state), days);
607
+ }
608
+ async getHistory(state, chainId, options) {
609
+ const broker = await this.connect(state);
610
+ const page = options?.cursor ? Number(options.cursor) : 1;
611
+ const limit = options?.limit ?? HISTORY_DEFAULT_LIMIT;
612
+ const result = await this.withSession(
613
+ state,
614
+ () => broker.getAgentMessages(state.walletAddress, page, limit, options?.fromDate, options?.toDate)
615
+ );
616
+ return mapHistory(result, chainId);
617
+ }
618
+ async getUserProfile(state) {
619
+ return mapUserProfile(await this.vault(state), state.walletAddress);
620
+ }
621
+ // --- IAgent: discovery (no wallet, no session) ---
622
+ async getAgentApy(days, _options) {
623
+ this.discoveryBroker ??= createSurfBroker({
624
+ routingApiBaseUrl: this.routingApiBaseUrl,
625
+ apiKey: this.config.apiKey,
626
+ walletAddress: DISCOVERY_WALLET2,
627
+ fetchImpl: this.config.fetchImpl
628
+ });
629
+ const assets = await this.discoveryBroker.getSupportedAssets(SURFLIQUID_CHAIN_ID);
630
+ const usdc = assets.find((asset) => asset.assetSymbol === "USDC");
631
+ if (!usdc) return { averageApy: 0 };
632
+ const windowed = usdc[APY_WINDOW_KEY2[days]];
633
+ return { averageApy: windowed != null ? windowed : usdc.currentAPY };
634
+ }
635
+ };
636
+ export {
637
+ SurfLiquidAgent
638
+ };