@owney/sdk 0.6.9-beta.9 → 0.7.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.
package/dist/index.cjs CHANGED
@@ -3,667 +3,20 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __esm = (fn, res, err) => function __init() {
7
- if (err) throw err[0];
8
- try {
9
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
- } catch (e) {
11
- throw err = [e], e;
12
- }
13
- };
14
6
  var __export = (target, all) => {
15
7
  for (var name in all)
16
8
  __defProp(target, name, { get: all[name], enumerable: true });
17
9
  };
18
10
  var __copyProps = (to, from, except, desc) => {
19
11
  if (from && typeof from === "object" || typeof from === "function") {
20
- for (let key2 of __getOwnPropNames(from))
21
- if (!__hasOwnProp.call(to, key2) && key2 !== except)
22
- __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
15
  }
24
16
  return to;
25
17
  };
26
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
19
 
28
- // src/errors.ts
29
- var OwneyError, AgentNotFoundError, NotConnectedError, AgentChainIncompatibleError;
30
- var init_errors = __esm({
31
- "src/errors.ts"() {
32
- "use strict";
33
- OwneyError = class extends Error {
34
- code;
35
- details;
36
- agentId;
37
- constructor(code, message, details, agentId) {
38
- const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
39
- super(`${prefix} ${message}`);
40
- this.name = "OwneyError";
41
- this.code = code;
42
- this.details = details;
43
- this.agentId = agentId;
44
- }
45
- };
46
- AgentNotFoundError = class extends OwneyError {
47
- constructor(agentId, available) {
48
- super(
49
- "AGENT_NOT_FOUND",
50
- `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
51
- { agentId, available },
52
- agentId
53
- );
54
- this.name = "AgentNotFoundError";
55
- }
56
- };
57
- NotConnectedError = class extends OwneyError {
58
- constructor() {
59
- super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
60
- this.name = "NotConnectedError";
61
- }
62
- };
63
- AgentChainIncompatibleError = class extends OwneyError {
64
- incompatibleAgents;
65
- connectedChainId;
66
- constructor(incompatibleAgents, connectedChainId) {
67
- const details = incompatibleAgents.map(
68
- ({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
69
- ).join("; ");
70
- super(
71
- "AGENT_CHAIN_INCOMPATIBLE",
72
- `Chain ${connectedChainId} is not supported by the following agents: ${details}`,
73
- { incompatibleAgents, connectedChainId }
74
- );
75
- this.name = "AgentChainIncompatibleError";
76
- this.incompatibleAgents = incompatibleAgents;
77
- this.connectedChainId = connectedChainId;
78
- }
79
- };
80
- }
81
- });
82
-
83
- // src/lib/permit2.ts
84
- function buildPermitTransferFromTypedData(input) {
85
- return {
86
- domain: {
87
- name: "Permit2",
88
- chainId: input.chainId,
89
- verifyingContract: PERMIT2_ADDRESS
90
- },
91
- types: {
92
- PermitTransferFrom: [
93
- { name: "permitted", type: "TokenPermissions" },
94
- { name: "spender", type: "address" },
95
- { name: "nonce", type: "uint256" },
96
- { name: "deadline", type: "uint256" }
97
- ],
98
- TokenPermissions: [
99
- { name: "token", type: "address" },
100
- { name: "amount", type: "uint256" }
101
- ]
102
- },
103
- primaryType: "PermitTransferFrom",
104
- message: input.message
105
- };
106
- }
107
- function randomPermit2Nonce() {
108
- const bytes = new Uint8Array(32);
109
- globalThis.crypto.getRandomValues(bytes);
110
- return BigInt((0, import_viem4.bytesToHex)(bytes));
111
- }
112
- async function readPermit2Allowance(publicClient, token, owner) {
113
- return publicClient.readContract({
114
- address: token,
115
- abi: ERC20_ALLOWANCE_ABI,
116
- functionName: "allowance",
117
- args: [owner, PERMIT2_ADDRESS]
118
- });
119
- }
120
- async function readErc20Balance(publicClient, token, owner) {
121
- return publicClient.readContract({
122
- address: token,
123
- abi: ERC20_ALLOWANCE_ABI,
124
- functionName: "balanceOf",
125
- args: [owner]
126
- });
127
- }
128
- var import_viem4, PERMIT2_ADDRESS, MAX_UINT256, ERC20_ALLOWANCE_ABI;
129
- var init_permit2 = __esm({
130
- "src/lib/permit2.ts"() {
131
- "use strict";
132
- import_viem4 = require("viem");
133
- PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
134
- MAX_UINT256 = 2n ** 256n - 1n;
135
- ERC20_ALLOWANCE_ABI = [
136
- {
137
- type: "function",
138
- name: "allowance",
139
- stateMutability: "view",
140
- inputs: [
141
- { name: "owner", type: "address" },
142
- { name: "spender", type: "address" }
143
- ],
144
- outputs: [{ name: "", type: "uint256" }]
145
- },
146
- {
147
- type: "function",
148
- name: "approve",
149
- stateMutability: "nonpayable",
150
- inputs: [
151
- { name: "spender", type: "address" },
152
- { name: "amount", type: "uint256" }
153
- ],
154
- outputs: [{ name: "", type: "bool" }]
155
- },
156
- {
157
- type: "function",
158
- name: "balanceOf",
159
- stateMutability: "view",
160
- inputs: [{ name: "account", type: "address" }],
161
- outputs: [{ name: "", type: "uint256" }]
162
- }
163
- ];
164
- }
165
- });
166
-
167
- // src/agents/surfliquid/surfliquid.constants.ts
168
- var SURFLIQUID_CHAIN_ID, SURFLIQUID_CHAIN_NAME, SURFLIQUID_USDC_ADDRESS, SURFLIQUID_USDC_DECIMALS, SURFLIQUID_MIN_DEPOSIT, SURFLIQUID_APPROVAL_TARGET, SURFLIQUID_SUPPORTED_ASSETS, SURFLIQUID_ACTION_MAP;
169
- var init_surfliquid_constants = __esm({
170
- "src/agents/surfliquid/surfliquid.constants.ts"() {
171
- "use strict";
172
- SURFLIQUID_CHAIN_ID = 8453;
173
- SURFLIQUID_CHAIN_NAME = "BASE";
174
- SURFLIQUID_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
175
- SURFLIQUID_USDC_DECIMALS = 6;
176
- SURFLIQUID_MIN_DEPOSIT = "0";
177
- SURFLIQUID_APPROVAL_TARGET = 100000000000n;
178
- SURFLIQUID_SUPPORTED_ASSETS = [
179
- {
180
- chainId: SURFLIQUID_CHAIN_ID,
181
- chain: SURFLIQUID_CHAIN_NAME,
182
- assets: [{ symbol: "USDC", minDepositAmount: SURFLIQUID_MIN_DEPOSIT }]
183
- }
184
- ];
185
- SURFLIQUID_ACTION_MAP = {
186
- // INITIAL_DEPOSIT is the user's real first deposit, so surface it like any
187
- // other funding ("Top up"). The mini-app intentionally hides the "Deposit"
188
- // action (it marks Zyfai's smart-account-creation event), which would
189
- // otherwise drop a SurfLiquid user's only history row.
190
- INITIAL_DEPOSIT: "Top up",
191
- DEPOSIT: "Top up",
192
- USER_DEPOSIT: "Top up",
193
- WITHDRAWAL: "Withdraw",
194
- USER_WITHDRAWAL: "Withdraw",
195
- REBALANCE: "Rebalance",
196
- REBALANCE_COMPLETED: "Rebalance",
197
- CROSS_CHAIN_REBALANCE: "Rebalance",
198
- MERKL_CLAIM: "Earned"
199
- };
200
- }
201
- });
202
-
203
- // src/agents/surfliquid/surfliquid.mapper.ts
204
- function mapMessage(message) {
205
- const action = SURFLIQUID_ACTION_MAP[message.transactionType];
206
- if (!action) return null;
207
- return {
208
- agent: "surfliquid",
209
- action,
210
- date: message.timestamp,
211
- oldApy: message.apyBefore != null ? String(message.apyBefore) : null,
212
- newApy: message.apyAfter != null ? String(message.apyAfter) : null,
213
- transactions: [
214
- {
215
- txHashes: [message.txHash],
216
- chainId: message.chainId,
217
- tokenSymbol: message.token ?? void 0,
218
- amount: message.amount != null ? String(message.amount) : void 0
219
- }
220
- ],
221
- rebalanceLog: []
222
- };
223
- }
224
- function mapHistory(result, _chainId) {
225
- const data = result.messages.map(mapMessage).filter((entry) => entry !== null);
226
- const hasMore = result.page < result.pages;
227
- return {
228
- data,
229
- hasMore,
230
- nextCursor: hasMore ? String(result.page + 1) : void 0
231
- };
232
- }
233
- function mapBalances2(vault, chainId, morphoStats) {
234
- const assets = (vault.assets ?? []).filter(
235
- (asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId
236
- );
237
- const tokens = assets.map((asset) => ({
238
- chain: SURFLIQUID_CHAIN_NAME,
239
- chainId: SURFLIQUID_CHAIN_ID,
240
- asset: "USDC",
241
- amount: String(asset.currentValueUSD)
242
- }));
243
- const positions = assets.map((asset) => {
244
- const stats = morphoStats?.get(asset.morphoVaultAddress?.toLowerCase() ?? "");
245
- return {
246
- chain: SURFLIQUID_CHAIN_NAME,
247
- protocol: "Morpho",
248
- // The specific MetaMorpho vault (e.g. "Gauntlet USDC Frontier"), read from
249
- // Morpho — renders as "Morpho <vault>" alongside zyfai's vault detail.
250
- pool: stats?.name ?? void 0,
251
- asset: "USDC",
252
- amount: String(asset.currentValueUSD),
253
- apy: asset.currentAPY,
254
- // TVL + liquidity come straight from Morpho (SurfLiquid's API omits them).
255
- tvl: stats?.tvlUsd,
256
- liquidity: stats?.liquidityUsd
257
- };
258
- });
259
- const total = assets.reduce(
260
- (sum, asset) => sum + asset.currentValueUSD,
261
- 0
262
- );
263
- return {
264
- smartWallet: vault.userVaultAddress ?? void 0,
265
- totalBalance: String(total),
266
- totalBalanceAsset: "usdc",
267
- tokens,
268
- positions
269
- };
270
- }
271
- function mapAccountApy(vault, days) {
272
- const breakdown = vault.apyBreakdown;
273
- const windowed = breakdown ? breakdown[APY_WINDOW_KEY[days]] : void 0;
274
- const apy = windowed != null ? windowed : breakdown?.currentAPY ?? 0;
275
- return {
276
- walletAddress: vault.walletAddress ?? "",
277
- weightedApyAfterFee: apy,
278
- apyByChainAndAsset: { [SURFLIQUID_CHAIN_ID]: { USDC: apy } },
279
- // Parity with Sail: SurfLiquid's core-sdk exposes no daily net-APY series.
280
- history: []
281
- };
282
- }
283
- function mapEarnings2(vault, vaultAddress) {
284
- return {
285
- smartWallet: vaultAddress,
286
- lifetimeEarnings: vault.earned?.totalEarningsUSD ?? 0,
287
- tokens: []
288
- };
289
- }
290
- function mapUserProfile2(vault, address) {
291
- return {
292
- address,
293
- smartWallet: vault.userVaultAddress ?? "",
294
- chains: [SURFLIQUID_CHAIN_ID],
295
- // SurfLiquid uses cookie-based session auth — no session-key concept.
296
- hasActiveSessionKey: false,
297
- protocols: ["SurfLiquid"]
298
- };
299
- }
300
- var APY_WINDOW_KEY;
301
- var init_surfliquid_mapper = __esm({
302
- "src/agents/surfliquid/surfliquid.mapper.ts"() {
303
- "use strict";
304
- init_surfliquid_constants();
305
- APY_WINDOW_KEY = {
306
- "7D": "apy7d",
307
- "14D": "apy14d",
308
- "30D": "apy30d"
309
- };
310
- }
311
- });
312
-
313
- // src/agents/surfliquid/surfliquid.wallet-adapter.ts
314
- var import_core_sdk, OwneyWalletAdapter;
315
- var init_surfliquid_wallet_adapter = __esm({
316
- "src/agents/surfliquid/surfliquid.wallet-adapter.ts"() {
317
- "use strict";
318
- import_core_sdk = require("@surf_liquid/core-sdk");
319
- OwneyWalletAdapter = class extends import_core_sdk.BaseWalletAdapter {
320
- constructor(eip1193Provider) {
321
- super();
322
- this.eip1193Provider = eip1193Provider;
323
- }
324
- eip1193Provider;
325
- name = "owney";
326
- get installed() {
327
- return true;
328
- }
329
- resolveProvider() {
330
- return this.eip1193Provider;
331
- }
332
- };
333
- }
334
- });
335
-
336
- // src/agents/surfliquid/surfliquid.morpho.ts
337
- async function queryMorpho(query, vaultAddress, chainId) {
338
- try {
339
- const res = await fetch(MORPHO_API_URL, {
340
- method: "POST",
341
- headers: { "Content-Type": "application/json" },
342
- body: JSON.stringify({
343
- query,
344
- variables: { address: vaultAddress, chainId }
345
- })
346
- });
347
- if (!res.ok) return null;
348
- const json = await res.json();
349
- if (json.errors) return null;
350
- return json.data ?? null;
351
- } catch (error) {
352
- console.warn("surfliquid: Morpho vault stats fetch failed", {
353
- vaultAddress,
354
- chainId,
355
- error
356
- });
357
- return null;
358
- }
359
- }
360
- function toStats(name, tvlUsd, liquidityUsd) {
361
- if (typeof tvlUsd !== "number" || typeof liquidityUsd !== "number") {
362
- return null;
363
- }
364
- return {
365
- name: typeof name === "string" ? name : null,
366
- tvlUsd,
367
- liquidityUsd
368
- };
369
- }
370
- async function fetchMorphoVaultStats(vaultAddress, chainId) {
371
- const v1 = await queryMorpho(VAULT_STATS_QUERY, vaultAddress, chainId);
372
- const vault = v1?.vaultByAddress;
373
- const v1Stats = toStats(
374
- vault?.name,
375
- vault?.state?.totalAssetsUsd,
376
- vault?.liquidity?.usd
377
- );
378
- if (v1Stats) return v1Stats;
379
- const v2 = await queryMorpho(VAULT_V2_STATS_QUERY, vaultAddress, chainId);
380
- const vaultV2 = v2?.vaultV2ByAddress;
381
- return toStats(vaultV2?.name, vaultV2?.totalAssetsUsd, vaultV2?.liquidityUsd);
382
- }
383
- async function fetchMorphoStatsByVault(vaultAddresses, chainId) {
384
- const unique = [...new Set(vaultAddresses.map((a) => a.toLowerCase()))];
385
- const entries = await Promise.all(
386
- unique.map(
387
- async (address) => [address, await fetchMorphoVaultStats(address, chainId)]
388
- )
389
- );
390
- const byVault = /* @__PURE__ */ new Map();
391
- for (const [address, stats] of entries) {
392
- if (stats) byVault.set(address, stats);
393
- }
394
- return byVault;
395
- }
396
- var MORPHO_API_URL, VAULT_STATS_QUERY, VAULT_V2_STATS_QUERY;
397
- var init_surfliquid_morpho = __esm({
398
- "src/agents/surfliquid/surfliquid.morpho.ts"() {
399
- "use strict";
400
- MORPHO_API_URL = "https://api.morpho.org/graphql";
401
- VAULT_STATS_QUERY = `
402
- query VaultStats($address: String!, $chainId: Int!) {
403
- vaultByAddress(address: $address, chainId: $chainId) {
404
- name
405
- state { totalAssetsUsd }
406
- liquidity { usd }
407
- }
408
- }
409
- `;
410
- VAULT_V2_STATS_QUERY = `
411
- query VaultV2Stats($address: String!, $chainId: Int!) {
412
- vaultV2ByAddress(address: $address, chainId: $chainId) {
413
- name
414
- totalAssetsUsd
415
- liquidityUsd
416
- }
417
- }
418
- `;
419
- }
420
- });
421
-
422
- // src/agents/surfliquid/surfliquid.agent.ts
423
- var surfliquid_agent_exports = {};
424
- __export(surfliquid_agent_exports, {
425
- SurfLiquidAgent: () => SurfLiquidAgent
426
- });
427
- var import_core_sdk2, import_viem5, import_chains2, APY_WINDOW_KEY2, SurfLiquidAgent;
428
- var init_surfliquid_agent = __esm({
429
- "src/agents/surfliquid/surfliquid.agent.ts"() {
430
- "use strict";
431
- import_core_sdk2 = require("@surf_liquid/core-sdk");
432
- import_viem5 = require("viem");
433
- import_chains2 = require("viem/chains");
434
- init_errors();
435
- init_surfliquid_constants();
436
- init_permit2();
437
- init_surfliquid_mapper();
438
- init_surfliquid_wallet_adapter();
439
- init_surfliquid_morpho();
440
- APY_WINDOW_KEY2 = {
441
- "7D": "apy7d",
442
- "14D": "apy14d",
443
- "30D": "apy30d"
444
- };
445
- SurfLiquidAgent = class {
446
- constructor(config) {
447
- this.config = config;
448
- }
449
- config;
450
- id = "surfliquid";
451
- supportedChainIds = [SURFLIQUID_CHAIN_ID];
452
- supportedAssets = SURFLIQUID_SUPPORTED_ASSETS;
453
- surf = null;
454
- connectedAddress = null;
455
- // In-flight connect+auth, shared by concurrent callers to dedupe SIWE.
456
- connectPromise = null;
457
- ensureSurf() {
458
- if (this.surf) return this.surf;
459
- this.surf = import_core_sdk2.SurfClient.create({
460
- projectName: this.config.projectName,
461
- appId: this.config.appId,
462
- environment: "mainnet",
463
- chainId: SURFLIQUID_CHAIN_ID,
464
- apiBaseUrl: this.config.apiBaseUrl,
465
- rpcUrl: this.config.rpcUrl,
466
- // Let the SDK auto-approve USDC to the vault before depositing.
467
- autoApprove: true
468
- });
469
- return this.surf;
470
- }
471
- /**
472
- * Connects + authenticates the SurfClient against owney's provider. Skips the
473
- * SIWE prompt when already authenticated for this wallet (cookie session).
474
- */
475
- async connect(state) {
476
- const surf = this.ensureSurf();
477
- const sameWallet = this.connectedAddress?.toLowerCase() === state.walletAddress.toLowerCase();
478
- if (sameWallet && surf.getAuthState().authenticated) {
479
- return surf;
480
- }
481
- if (this.connectPromise) return this.connectPromise;
482
- this.connectPromise = (async () => {
483
- surf.registerWalletAdapter(
484
- "owney",
485
- new OwneyWalletAdapter(state.provider)
486
- );
487
- await surf.connectWallet("owney");
488
- await surf.authenticate();
489
- this.connectedAddress = state.walletAddress;
490
- return surf;
491
- })();
492
- try {
493
- return await this.connectPromise;
494
- } finally {
495
- this.connectPromise = null;
496
- }
497
- }
498
- /**
499
- * Returns the user's vault address, deploying one on first use. SurfLiquid's
500
- * deposit requires the vault to exist, so this must run before depositing.
501
- * Existing vault → returned as-is; otherwise `deployVault()` is sent
502
- * (user-pays) and the freshly deployed address is returned.
503
- */
504
- async resolveVaultAddress(surf, owner) {
505
- const vault = await surf.getVault(owner).catch(() => null);
506
- if (vault?.exists && vault.userVaultAddress) {
507
- return vault.userVaultAddress;
508
- }
509
- const { vaultAddress } = await surf.deployVault();
510
- return vaultAddress;
511
- }
512
- /**
513
- * Grants the user's vault a standing USDC allowance so SurfLiquid's
514
- * `autoApprove` has nothing to do — see {@link SURFLIQUID_APPROVAL_TARGET}
515
- * for why one prompt per deposit is worth removing.
516
- *
517
- * Fails OPEN on a read error: if the allowance can't be checked we simply
518
- * return and let SurfLiquid's own per-deposit approve run, because a flaky
519
- * RPC must never block a deposit. A failed or rejected APPROVE does
520
- * propagate — the user declined a transaction, and letting SurfLiquid
521
- * immediately ask again for a smaller one is exactly the double prompt this
522
- * removes.
523
- */
524
- async ensureVaultAllowance(state, vaultAddress, amount) {
525
- const spender = vaultAddress;
526
- const transport = (0, import_viem5.custom)(state.provider);
527
- const publicClient = (0, import_viem5.createPublicClient)({ chain: import_chains2.base, transport });
528
- let allowance;
529
- try {
530
- allowance = await publicClient.readContract({
531
- address: SURFLIQUID_USDC_ADDRESS,
532
- abi: ERC20_ALLOWANCE_ABI,
533
- functionName: "allowance",
534
- args: [state.walletAddress, spender]
535
- });
536
- } catch (error) {
537
- console.warn(
538
- "[owney-sdk] SurfLiquid allowance pre-check failed; deferring to its per-deposit approve:",
539
- error instanceof Error ? error.message : String(error)
540
- );
541
- return;
542
- }
543
- if (allowance >= amount) return;
544
- const target = amount > SURFLIQUID_APPROVAL_TARGET ? amount : SURFLIQUID_APPROVAL_TARGET;
545
- const wallet = (0, import_viem5.createWalletClient)({
546
- account: state.walletAddress,
547
- chain: import_chains2.base,
548
- transport
549
- });
550
- const hash = await wallet.writeContract({
551
- address: SURFLIQUID_USDC_ADDRESS,
552
- abi: ERC20_ALLOWANCE_ABI,
553
- functionName: "approve",
554
- args: [spender, target],
555
- account: state.walletAddress,
556
- chain: import_chains2.base
557
- });
558
- const receipt = await publicClient.waitForTransactionReceipt({
559
- hash,
560
- confirmations: 1
561
- });
562
- if (receipt.status !== "success") {
563
- throw new Error(`SurfLiquid USDC approval reverted (tx ${hash})`);
564
- }
565
- }
566
- // --- IAgent: lifecycle ---
567
- async disconnect() {
568
- if (this.surf) {
569
- await this.surf.disconnectWallet();
570
- }
571
- this.connectedAddress = null;
572
- }
573
- async activateAgent(state) {
574
- await this.connect(state);
575
- }
576
- // --- IAgent: funds (user-pays; asset is always USDC, callback ignored) ---
577
- async deposit(state, _chainId, amount, _asset, _depositCallback) {
578
- const surf = await this.connect(state);
579
- const human = (0, import_viem5.formatUnits)(BigInt(amount), SURFLIQUID_USDC_DECIMALS);
580
- const smartWallet = await this.resolveVaultAddress(
581
- surf,
582
- state.walletAddress
583
- );
584
- await this.ensureVaultAllowance(state, smartWallet, BigInt(amount));
585
- const tx = await surf.deposit({
586
- asset: SURFLIQUID_USDC_ADDRESS,
587
- amount: human
588
- });
589
- await tx.wait();
590
- return { txHash: tx.hash, smartWallet, amount };
591
- }
592
- async withdraw(state, _chainId, _token, amount) {
593
- const surf = await this.connect(state);
594
- const human = amount != null ? (0, import_viem5.formatUnits)(BigInt(amount), SURFLIQUID_USDC_DECIMALS) : void 0;
595
- try {
596
- const tx = await surf.withdraw({
597
- asset: SURFLIQUID_USDC_ADDRESS,
598
- amount: human
599
- });
600
- await tx.wait();
601
- return {
602
- txHash: tx.hash,
603
- type: amount != null ? "partial" : "full",
604
- amount: amount ?? "0"
605
- };
606
- } catch (error) {
607
- throw new OwneyError(
608
- "WITHDRAW_FAILED",
609
- error instanceof Error ? error.message : "SurfLiquid withdraw failed.",
610
- { chainId: SURFLIQUID_CHAIN_ID, amount },
611
- this.id
612
- );
613
- }
614
- }
615
- // --- IAgent: portfolio reads ---
616
- async getBalances(state, chainId) {
617
- const surf = await this.connect(state);
618
- const vault = await surf.getVault(state.walletAddress);
619
- const morphoVaultAddresses = (vault.assets ?? []).filter((asset) => asset.assetSymbol === "USDC" && asset.chainId === chainId).map((asset) => asset.morphoVaultAddress).filter((address) => Boolean(address));
620
- const morphoStats = await fetchMorphoStatsByVault(
621
- morphoVaultAddresses,
622
- chainId
623
- );
624
- return mapBalances2(vault, chainId, morphoStats);
625
- }
626
- async getEarnings(state) {
627
- const surf = await this.connect(state);
628
- const vault = await surf.getVault(state.walletAddress);
629
- return mapEarnings2(vault, vault.userVaultAddress ?? state.walletAddress);
630
- }
631
- async getAccountApy(state, _chainId, days, _tokenSymbol) {
632
- const surf = await this.connect(state);
633
- const vault = await surf.getVault(state.walletAddress);
634
- return mapAccountApy(vault, days);
635
- }
636
- async getHistory(state, chainId, options) {
637
- const surf = await this.connect(state);
638
- const page = options?.cursor ? Number(options.cursor) : 1;
639
- const limit = options?.limit ?? 10;
640
- const result = await surf.getAgentMessages(
641
- state.walletAddress,
642
- page,
643
- limit,
644
- options?.fromDate,
645
- options?.toDate
646
- );
647
- return mapHistory(result, chainId);
648
- }
649
- async getUserProfile(state) {
650
- const surf = await this.connect(state);
651
- const vault = await surf.getVault(state.walletAddress);
652
- return mapUserProfile2(vault, state.walletAddress);
653
- }
654
- // --- IAgent: discovery (no wallet) ---
655
- async getAgentApy(days, _options) {
656
- const surf = this.ensureSurf();
657
- const assets = await surf.getSupportedAssets(SURFLIQUID_CHAIN_ID);
658
- const usdc = assets.find((asset) => asset.assetSymbol === "USDC");
659
- if (!usdc) return { averageApy: 0 };
660
- const windowed = usdc[APY_WINDOW_KEY2[days]];
661
- return { averageApy: windowed != null ? windowed : usdc.currentAPY };
662
- }
663
- };
664
- }
665
- });
666
-
667
20
  // src/index.ts
668
21
  var index_exports = {};
669
22
  __export(index_exports, {
@@ -673,8 +26,7 @@ __export(index_exports, {
673
26
  NotConnectedError: () => NotConnectedError,
674
27
  OwneyError: () => OwneyError,
675
28
  OwneySDK: () => OwneySDK,
676
- createOwneySIWX: () => createOwneySIWX,
677
- setOwneyDebug: () => setOwneyDebug
29
+ createOwneySIWX: () => createOwneySIWX
678
30
  });
679
31
  module.exports = __toCommonJS(index_exports);
680
32
 
@@ -736,8 +88,56 @@ var SupportedAssets = [
736
88
  }
737
89
  ];
738
90
 
91
+ // src/errors.ts
92
+ var OwneyError = class extends Error {
93
+ code;
94
+ details;
95
+ agentId;
96
+ constructor(code, message, details, agentId) {
97
+ const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
98
+ super(`${prefix} ${message}`);
99
+ this.name = "OwneyError";
100
+ this.code = code;
101
+ this.details = details;
102
+ this.agentId = agentId;
103
+ }
104
+ };
105
+ var AgentNotFoundError = class extends OwneyError {
106
+ constructor(agentId, available) {
107
+ super(
108
+ "AGENT_NOT_FOUND",
109
+ `Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
110
+ { agentId, available },
111
+ agentId
112
+ );
113
+ this.name = "AgentNotFoundError";
114
+ }
115
+ };
116
+ var NotConnectedError = class extends OwneyError {
117
+ constructor() {
118
+ super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
119
+ this.name = "NotConnectedError";
120
+ }
121
+ };
122
+ var AgentChainIncompatibleError = class extends OwneyError {
123
+ incompatibleAgents;
124
+ connectedChainId;
125
+ constructor(incompatibleAgents, connectedChainId) {
126
+ const details = incompatibleAgents.map(
127
+ ({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
128
+ ).join("; ");
129
+ super(
130
+ "AGENT_CHAIN_INCOMPATIBLE",
131
+ `Chain ${connectedChainId} is not supported by the following agents: ${details}`,
132
+ { incompatibleAgents, connectedChainId }
133
+ );
134
+ this.name = "AgentChainIncompatibleError";
135
+ this.incompatibleAgents = incompatibleAgents;
136
+ this.connectedChainId = connectedChainId;
137
+ }
138
+ };
139
+
739
140
  // src/lib/utils.ts
740
- init_errors();
741
141
  var isValidChainId = (chainId) => {
742
142
  if (!SUPPORTED_CHAIN_IDS.includes(chainId)) {
743
143
  throw new OwneyError(
@@ -748,12 +148,6 @@ var isValidChainId = (chainId) => {
748
148
  }
749
149
  return chainId;
750
150
  };
751
- function isSameAsset(tokenSymbol, asset) {
752
- const token = tokenSymbol.toLowerCase();
753
- const target = asset.toLowerCase();
754
- if (token === target) return true;
755
- return target === "weth" && token === "eth" || target === "eth" && token === "weth";
756
- }
757
151
  function hexToDecimal(hex, decimals = 6) {
758
152
  const normalized = hex.startsWith("0x") || hex.startsWith("0X") ? hex : `0x${hex}`;
759
153
  const parsed = BigInt(normalized);
@@ -779,10 +173,6 @@ var DECIMALS_BY_SYMBOL = {
779
173
  wbtc: 8,
780
174
  cbbtc: 8
781
175
  };
782
- function canonicalSymbol(symbol) {
783
- const upper = symbol.toUpperCase();
784
- return upper === "ETH" ? "WETH" : upper;
785
- }
786
176
  function tokenDecimals(symbol, explicit) {
787
177
  if (typeof explicit === "number" && Number.isFinite(explicit))
788
178
  return explicit;
@@ -821,50 +211,46 @@ function resolveChainId(chain) {
821
211
  function mapBalances(raw, _chainId, smartWallet) {
822
212
  const portfolio = raw.portfolio;
823
213
  const portfolioByChain = portfolio.portfolioByChain ?? {};
824
- const rawPositions = portfolio.positions ?? [];
825
- const detailByKey = /* @__PURE__ */ new Map();
826
- for (const p of rawPositions) {
827
- const cid = resolveChainId(p.chain ?? "");
828
- const symbol = canonicalSymbol(p.token_symbol ?? "");
829
- if (Number.isFinite(cid) && symbol) detailByKey.set(`${cid}:${symbol}`, p);
830
- }
831
214
  let totalBalance = 0;
832
215
  const tokens = [];
833
- const positions = [];
834
216
  for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
835
217
  const positionChainId = Number(chainIdKey);
836
- const chainName = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
837
218
  for (const [symbol, { balance, decimals }] of Object.entries(
838
219
  tokensBySymbol ?? {}
839
220
  )) {
840
- const amount = hexToDecimal(balance || "0", tokenDecimals(symbol, decimals));
221
+ const amount = hexToDecimal(balance || "0", decimals);
841
222
  totalBalance += amount;
842
223
  tokens.push({
843
- chain: chainName,
224
+ chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
844
225
  chainId: positionChainId,
845
226
  asset: symbol,
846
227
  amount: String(amount)
847
228
  });
848
- const detail = detailByKey.get(
849
- `${positionChainId}:${canonicalSymbol(symbol)}`
850
- );
851
- positions.push({
852
- chain: chainName,
853
- protocol: detail?.protocol_name ?? detail?.protocol_id ?? "",
854
- protocolId: detail?.protocol_id,
855
- pool: detail?.pool,
856
- // Report the token the way the rest of owney spells it ("WETH", as the
857
- // pre-portfolioByChain mapper did), not Zyfai's internal "eth".
858
- asset: canonicalSymbol(symbol),
859
- amount: String(amount),
860
- apy: detail?.pool_apy,
861
- tvl: detail?.pool_tvl,
862
- // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
863
- // structural read keeps it undefined today and auto-populates later.
864
- liquidity: detail?.liquidity
865
- });
866
229
  }
867
230
  }
231
+ const positions = (portfolio.positions ?? []).map((p) => ({
232
+ chain: p.chain ?? "",
233
+ protocol: p.protocol_name ?? p.protocol_id ?? "",
234
+ protocolId: p.protocol_id,
235
+ pool: p.pool,
236
+ asset: p.token_symbol ?? "",
237
+ // Position amount is hex-encoded base units (like the token balances
238
+ // above). `PositionSlot` doesn't type a `decimals` field, but runtime
239
+ // getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
240
+ // when present, otherwise fall back to the per-symbol lookup so
241
+ // non-6-decimal assets like WETH don't render 1e12 too large.
242
+ amount: String(
243
+ hexToDecimal(
244
+ p.amount || "0",
245
+ tokenDecimals(p.token_symbol, p.decimals)
246
+ )
247
+ ),
248
+ apy: p.pool_apy,
249
+ tvl: p.pool_tvl,
250
+ // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
251
+ // structural read keeps it undefined today and auto-populates later.
252
+ liquidity: p.liquidity
253
+ }));
868
254
  return {
869
255
  smartWallet,
870
256
  totalBalance: String(totalBalance),
@@ -1026,9 +412,7 @@ function mapUserProfile(raw, userAddress) {
1026
412
  chains: raw.chains || [],
1027
413
  strategy: raw.strategy,
1028
414
  hasActiveSessionKey: raw.hasActiveSessionKey || false,
1029
- protocols: raw.protocols || [],
1030
- splitting: raw.splitting,
1031
- minSplits: raw.minSplits
415
+ protocols: raw.protocols || []
1032
416
  };
1033
417
  }
1034
418
  function mapApyByStrategy(raw) {
@@ -1141,9 +525,6 @@ function decodeHistoryCursor(token) {
1141
525
  return parsed;
1142
526
  }
1143
527
 
1144
- // src/agents/zyfai/zyfai.agent.ts
1145
- init_errors();
1146
-
1147
528
  // src/agents/zyfai/zyfai.auth-cache.ts
1148
529
  var KEY_PREFIX = "owney.zyfai.session";
1149
530
  var storage = () => {
@@ -1178,23 +559,23 @@ var readLegacySession = (store, address) => {
1178
559
  const legacyKeys = [];
1179
560
  try {
1180
561
  for (let i = 0; i < store.length; i++) {
1181
- const key2 = store.key(i);
1182
- if (key2?.startsWith(prefix)) legacyKeys.push(key2);
562
+ const key = store.key(i);
563
+ if (key?.startsWith(prefix)) legacyKeys.push(key);
1183
564
  }
1184
565
  } catch {
1185
566
  return null;
1186
567
  }
1187
568
  let adopted = null;
1188
- for (const key2 of legacyKeys) {
569
+ for (const key of legacyKeys) {
1189
570
  if (!adopted) {
1190
571
  try {
1191
- const parsed = JSON.parse(store.getItem(key2) ?? "");
572
+ const parsed = JSON.parse(store.getItem(key) ?? "");
1192
573
  if (isFreshSession(parsed)) adopted = parsed;
1193
574
  } catch {
1194
575
  }
1195
576
  }
1196
577
  try {
1197
- store.removeItem(key2);
578
+ store.removeItem(key);
1198
579
  } catch {
1199
580
  }
1200
581
  }
@@ -1202,11 +583,11 @@ var readLegacySession = (store, address) => {
1202
583
  };
1203
584
  var readSession = (address, _chainId) => {
1204
585
  if (typeof window === "undefined") return null;
1205
- const key2 = buildKey(address);
586
+ const key = buildKey(address);
1206
587
  const store = storage();
1207
588
  let raw = null;
1208
589
  try {
1209
- raw = store?.getItem(key2) ?? null;
590
+ raw = store?.getItem(key) ?? null;
1210
591
  } catch {
1211
592
  raw = null;
1212
593
  }
@@ -1216,9 +597,9 @@ var readSession = (address, _chainId) => {
1216
597
  if (isFreshSession(parsed)) return parsed;
1217
598
  } catch {
1218
599
  }
1219
- memorySessions.delete(key2);
600
+ memorySessions.delete(key);
1220
601
  try {
1221
- store?.removeItem(key2);
602
+ store?.removeItem(key);
1222
603
  } catch {
1223
604
  }
1224
605
  return null;
@@ -1228,27 +609,27 @@ var readSession = (address, _chainId) => {
1228
609
  writeSession(address, _chainId, legacy);
1229
610
  return legacy;
1230
611
  }
1231
- const cached = memorySessions.get(key2);
612
+ const cached = memorySessions.get(key);
1232
613
  if (isFreshSession(cached)) return cached;
1233
- if (cached) memorySessions.delete(key2);
614
+ if (cached) memorySessions.delete(key);
1234
615
  return null;
1235
616
  };
1236
617
  var writeSession = (address, _chainId, session) => {
1237
618
  if (typeof window === "undefined") return;
1238
- const key2 = buildKey(address);
1239
- memorySessions.set(key2, session);
619
+ const key = buildKey(address);
620
+ memorySessions.set(key, session);
1240
621
  const store = storage();
1241
622
  try {
1242
- store?.setItem(key2, JSON.stringify(session));
623
+ store?.setItem(key, JSON.stringify(session));
1243
624
  } catch {
1244
625
  }
1245
626
  };
1246
627
  var clearSession = (address, _chainId) => {
1247
- const key2 = buildKey(address);
1248
- memorySessions.delete(key2);
628
+ const key = buildKey(address);
629
+ memorySessions.delete(key);
1249
630
  const store = storage();
1250
631
  try {
1251
- store?.removeItem(key2);
632
+ store?.removeItem(key);
1252
633
  } catch {
1253
634
  }
1254
635
  readLegacySession(store, address);
@@ -1279,149 +660,6 @@ function buildProtocolMergeUpdate(available, current, asset) {
1279
660
  };
1280
661
  }
1281
662
 
1282
- // src/agents/zyfai/splitting.ts
1283
- var MAX_MIN_SPLITS = 4;
1284
- var MIN_FORCE_SPLITS = 2;
1285
- var AUTOMATIC_MIN_SPLITS = 1;
1286
- function toZyfaiSplitting(policy) {
1287
- switch (policy.splittingMode) {
1288
- case "none":
1289
- return { splitting: false };
1290
- case "automatic":
1291
- return { splitting: true, minSplits: AUTOMATIC_MIN_SPLITS };
1292
- case "force":
1293
- return {
1294
- splitting: true,
1295
- minSplits: clampForceSplits(policy.minSplits)
1296
- };
1297
- }
1298
- }
1299
- function clampForceSplits(minSplits) {
1300
- if (typeof minSplits !== "number" || !Number.isFinite(minSplits)) {
1301
- return MIN_FORCE_SPLITS;
1302
- }
1303
- const rounded = Math.round(minSplits);
1304
- if (rounded < MIN_FORCE_SPLITS) return MIN_FORCE_SPLITS;
1305
- if (rounded > MAX_MIN_SPLITS) return MAX_MIN_SPLITS;
1306
- return rounded;
1307
- }
1308
- function splittingNeedsUpdate(current, desired) {
1309
- if (!current) return true;
1310
- if (current.splitting !== desired.splitting) return true;
1311
- if (desired.minSplits === void 0) return false;
1312
- return current.minSplits !== desired.minSplits;
1313
- }
1314
-
1315
- // src/agents/zyfai/pool-policy.ts
1316
- function toZyfaiCustomizations(policy, availableByProtocol, chainsByProtocol) {
1317
- const selectionByKey = /* @__PURE__ */ new Map();
1318
- for (const selection of policy.selections) {
1319
- selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
1320
- }
1321
- const customizations = [];
1322
- for (const [protocolId, chainIds] of Object.entries(chainsByProtocol)) {
1323
- const available = availableByProtocol.get(protocolId);
1324
- if (available === void 0) {
1325
- continue;
1326
- }
1327
- const availableSet = new Set(available);
1328
- for (const chainId of chainIds) {
1329
- const stored = selectionByKey.get(key(protocolId, chainId));
1330
- if (stored === void 0) {
1331
- customizations.push({
1332
- protocolId,
1333
- chainId,
1334
- pools: [],
1335
- autoselect: true
1336
- });
1337
- continue;
1338
- }
1339
- const pools = stored.pools.filter((pool) => availableSet.has(pool));
1340
- customizations.push({
1341
- protocolId,
1342
- chainId,
1343
- pools,
1344
- autoselect: false
1345
- });
1346
- }
1347
- }
1348
- return customizations;
1349
- }
1350
- function deriveProtocols(policy, universe, chainsByProtocol) {
1351
- if (policy.selections.length === 0) {
1352
- return null;
1353
- }
1354
- const selectionByKey = /* @__PURE__ */ new Map();
1355
- for (const selection of policy.selections) {
1356
- selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
1357
- }
1358
- return universe.filter((protocolId) => {
1359
- const chainIds = chainsByProtocol[protocolId];
1360
- if (chainIds === void 0 || chainIds.length === 0) {
1361
- return true;
1362
- }
1363
- const fullyDisabled = chainIds.every((chainId) => {
1364
- const stored = selectionByKey.get(key(protocolId, chainId));
1365
- return stored !== void 0 && stored.pools.length === 0;
1366
- });
1367
- return !fullyDisabled;
1368
- });
1369
- }
1370
- function key(protocolId, chainId) {
1371
- return `${protocolId}:${chainId}`;
1372
- }
1373
- function buildChainsByProtocol(protocols, dashboardChainIds) {
1374
- const allowed = new Set(dashboardChainIds);
1375
- const result = {};
1376
- for (const protocol of protocols) {
1377
- result[protocol.id] = (protocol.chains ?? []).filter(
1378
- (chainId) => allowed.has(chainId)
1379
- );
1380
- }
1381
- return result;
1382
- }
1383
- function flattenAvailablePools(raw) {
1384
- const byChain = raw ?? {};
1385
- const names = [];
1386
- for (const byToken of Object.values(byChain ?? {})) {
1387
- for (const entry of Object.values(byToken ?? {})) {
1388
- for (const pool of entry?.pools ?? []) {
1389
- if (pool?.name) names.push(pool.name);
1390
- }
1391
- }
1392
- }
1393
- return names;
1394
- }
1395
- function protocolListsEqual(a, b) {
1396
- const left = [...a ?? []].sort();
1397
- const right = [...b ?? []].sort();
1398
- if (left.length !== right.length) return false;
1399
- return left.every((value, index) => value === right[index]);
1400
- }
1401
- function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect) {
1402
- if (!current) return true;
1403
- if (current.autoSelectProtocols !== desiredAutoSelect) return true;
1404
- if (desiredProtocols === null) return false;
1405
- return !protocolListsEqual(current.protocols, desiredProtocols);
1406
- }
1407
-
1408
- // src/lib/debug.ts
1409
- var configuredDebug = false;
1410
- function setOwneyDebug(enabled) {
1411
- configuredDebug = enabled;
1412
- }
1413
- function isOwneyDebug() {
1414
- return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
1415
- }
1416
- function debugLog(scope, message, data) {
1417
- if (!isOwneyDebug()) return;
1418
- if (data === void 0) {
1419
- console.log(`[${scope}] ${message}`);
1420
- } else {
1421
- console.log(`[${scope}] ${message}`, data);
1422
- }
1423
- }
1424
-
1425
663
  // src/agents/zyfai/zyfai.agent.ts
1426
664
  var DEFAULT_ZYFAI_RPC_URLS = {
1427
665
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
@@ -1434,12 +672,6 @@ var WETH_ADDRESS_BY_CHAIN = {
1434
672
  1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
1435
673
  };
1436
674
  var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum, 1: import_chains.mainnet };
1437
- var SPLITTING_ASSETS = ["USDC", "WETH"];
1438
- var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
1439
- function isAlreadyActiveSessionKeyError(error) {
1440
- const message = error instanceof Error ? error.message : String(error);
1441
- return /already has an active session key/i.test(message);
1442
- }
1443
675
  var ZyfaiAgent = class {
1444
676
  id = "zyfai";
1445
677
  supportedChainIds = [8453, 42161, 1];
@@ -1666,71 +898,21 @@ var ZyfaiAgent = class {
1666
898
  return await this.sdk.connectAccount(provider, chainId);
1667
899
  }
1668
900
  }
1669
- /** Full connect: SIWE auth + Safe deploy check + session key. */
1670
- async connectFull(state, chainId) {
1671
- await this.connectAuth(state, chainId);
1672
- const validChainId = isValidChainId(chainId);
1673
- const address = this.getAddress();
1674
- const wallet = await this.sdk.getSmartWalletAddress(address, validChainId);
1675
- this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
1676
- if (!wallet.isDeployed) {
1677
- await this.sdk.deploySafe(address, validChainId, "aggressive", false);
1678
- try {
1679
- if (validChainId === 42161) {
1680
- await this.sdk.updateUserProfile({
1681
- asset: "WETH",
1682
- chains: [validChainId],
1683
- protocols: ["c24a33fa-4a87-4376-b756-93bd3957d3ea"],
1684
- // AAVE protocol ID
1685
- autoSelectProtocols: false
1686
- });
1687
- }
1688
- } catch (err) {
1689
- console.warn(
1690
- "Failed to configure AAVE protocol after Safe deployment:",
1691
- err instanceof Error ? err.message : String(err)
1692
- );
1693
- }
1694
- }
1695
- await this.ensureSessionKey(state, address, validChainId);
1696
- }
1697
901
  /**
1698
- * Ensures the user has an active session key, creating one if absent.
1699
- *
1700
- * The Zyfai session key is a single **per-user** record, not per-chain: its
1701
- * config (`/session-keys/config`) spans every supported chain and is signed
1702
- * in one shot, and the backend rejects `POST /session-keys/add` with 400
1703
- * "User already has an active session key" if one exists. So we must NOT try
1704
- * to (re)create a key per chain — that 400s and blocks the deposit. We only
1705
- * create when the user has none.
1706
- *
1707
- * The one wrapper-side hardening that IS needed: a chain/account switch can
1708
- * fire the provider's `accountsChanged` → `disconnectAccount`, tearing down
1709
- * the zyfai connection (walletClient + authenticatedUserId both null) while
1710
- * our own `connectAuth` guard still believes this chain is connected and
1711
- * early-returns without reconnecting. `createSessionKey` then fails with
1712
- * "No account connected". Detect that and reconnect first.
1713
- *
1714
- * NOTE: whether a globally-active session key actually authorizes activity on
1715
- * a freshly-deployed Safe on a new chain is a backend/zyf question — the
1716
- * wrapper cannot force per-chain activation given the global 400 guard.
1717
- * See [[zyfai-per-chain-session-key]].
902
+ * Reconnect if the Zyfai connection was torn down behind our back: a
903
+ * chain/account switch fires the provider's `accountsChanged` →
904
+ * `disconnectAccount` (nulls walletClient + authenticatedUserId) while our
905
+ * own `connectAuth` guard still believes this chain is connected and
906
+ * early-returns. Without this, authenticated calls (depositFunds,
907
+ * withdrawFunds) fail with "No account connected".
1718
908
  */
1719
- async ensureSessionKey(state, address, chainId) {
909
+ async ensureConnected(state, chainId) {
910
+ await this.connectAuth(state, chainId);
1720
911
  const internals = this.sdk;
1721
912
  if (!internals.walletClient || internals.authenticatedUserId === null) {
1722
913
  this.connectedChainId = null;
1723
914
  await this.connectAuth(state, chainId);
1724
915
  }
1725
- const user = await this.sdk.getUserDetails();
1726
- if (!user.hasActiveSessionKey) {
1727
- try {
1728
- await this.sdk.createSessionKey(address, chainId);
1729
- } catch (error) {
1730
- if (!isAlreadyActiveSessionKeyError(error)) throw error;
1731
- internals.hasActiveSessionKey = true;
1732
- }
1733
- }
1734
916
  }
1735
917
  /**
1736
918
  * Sync newly-available protocols onto an existing user's profile at wallet
@@ -1800,211 +982,10 @@ var ZyfaiAgent = class {
1800
982
  throw error;
1801
983
  }
1802
984
  }
1803
- /**
1804
- * Apply the org's capital splitting policy to this user's Zyfai profile.
1805
- *
1806
- * Per ASSET, not per chain: Zyfai stores splitting under
1807
- * `assetTypeSettings[asset]`, so one call would only configure USDC and leave a
1808
- * WETH holder's real setting untouched.
1809
- *
1810
- * Non-fatal throughout. This is a side effect of connecting, so a Zyfai hiccup
1811
- * must leave the user connected with their profile unchanged, never fail
1812
- * activation.
1813
- */
1814
- async applyOrgPolicy(_state, _chainId, policy) {
1815
- debugLog("zyfai:splitting", "applying org policy", {
1816
- policy,
1817
- // Per asset because Zyfai stores splitting under assetTypeSettings[asset].
1818
- assets: SPLITTING_ASSETS
1819
- });
1820
- await Promise.all(
1821
- SPLITTING_ASSETS.map((asset) => this.applySplitting(asset, policy))
1822
- );
1823
- if (policy.poolPolicy) {
1824
- await this.applyPoolPolicy(policy.poolPolicy);
1825
- }
1826
- }
1827
- async applySplitting(asset, policy) {
1828
- try {
1829
- const desired = toZyfaiSplitting(policy);
1830
- const current = await this.sdk.getUserDetails(asset);
1831
- const currentSplitting = {
1832
- splitting: current?.splitting,
1833
- minSplits: current?.minSplits
1834
- };
1835
- const needsUpdate = splittingNeedsUpdate(current, desired);
1836
- debugLog("zyfai:splitting", `${asset}: comparing`, {
1837
- mode: policy.splittingMode,
1838
- current: currentSplitting,
1839
- desired,
1840
- // Spelled out because the "why" is the non-obvious part: minSplits is
1841
- // what separates automatic (1) from force (2-4), not `splitting`.
1842
- decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
1843
- });
1844
- if (!needsUpdate) return;
1845
- const result = await this.sdk.updateUserProfile({ asset, ...desired });
1846
- debugLog("zyfai:splitting", `${asset}: wrote updateUserProfile`, {
1847
- sent: { asset, ...desired },
1848
- // updateUserProfile re-reads the profile after PATCHing, so this is
1849
- // Zyfai's own view of the result rather than an echo of what we sent.
1850
- zyfaiNowReports: {
1851
- splitting: result?.splitting,
1852
- minSplits: result?.minSplits
1853
- }
1854
- });
1855
- } catch (error) {
1856
- console.warn(
1857
- `[zyfai] applyOrgPolicy(${asset}) failed (non-fatal):`,
1858
- error instanceof Error ? error.message : String(error)
1859
- );
1860
- }
1861
- }
1862
- /**
1863
- * Apply the org's protocol/pool policy to this user's Zyfai profile.
1864
- *
1865
- * Two Zyfai writes, in a strict order:
1866
- * 1. `updateUserProfile({ protocols, autoSelectProtocols })` per asset —
1867
- * the explicit allow-list. Chain-agnostic, mirrors `applySplitting`.
1868
- * 2. `customizeBatch(...)` once — per (protocol, chain), asset-agnostic
1869
- * pool narrowing.
1870
- *
1871
- * Order matters: constraining a protocol's pools (step 2) while that
1872
- * protocol is not yet in the allow-list (step 1 not yet applied) lands the
1873
- * constraint on something the rebalance engine isn't considering.
1874
- *
1875
- * Wrapped so a failure anywhere is non-fatal — this runs as a side effect of
1876
- * connecting, so a Zyfai hiccup must leave the user connected with their
1877
- * profile unchanged, never fail activation.
1878
- */
1879
- async applyPoolPolicy(policy) {
1880
- try {
1881
- const { protocols: universe } = await this.sdk.getAvailableProtocols(
1882
- this.supportedChainIds[0]
1883
- );
1884
- const universeIds = universe.map((p) => p.id);
1885
- const chainsByProtocol = buildChainsByProtocol(
1886
- universe,
1887
- POOL_POLICY_CHAIN_IDS
1888
- );
1889
- debugLog("zyfai:pools", "protocol universe", {
1890
- universeIds,
1891
- chainsByProtocol
1892
- });
1893
- let strategy;
1894
- const desiredProtocols = deriveProtocols(
1895
- policy,
1896
- universeIds,
1897
- chainsByProtocol
1898
- );
1899
- const desiredAutoSelect = policy.autoApproveProtocols;
1900
- for (const asset of SPLITTING_ASSETS) {
1901
- const current = await this.sdk.getUserDetails(asset);
1902
- if (strategy === void 0 && (current.strategy === "conservative" || current.strategy === "aggressive")) {
1903
- strategy = current.strategy;
1904
- }
1905
- const needsUpdate = protocolsPolicyNeedsUpdate(
1906
- current,
1907
- desiredProtocols,
1908
- desiredAutoSelect
1909
- );
1910
- debugLog("zyfai:pools", `${asset}: comparing protocols`, {
1911
- current: {
1912
- protocols: current.protocols,
1913
- autoSelectProtocols: current.autoSelectProtocols
1914
- },
1915
- desired: {
1916
- protocols: desiredProtocols,
1917
- autoSelectProtocols: desiredAutoSelect
1918
- },
1919
- decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
1920
- });
1921
- if (!needsUpdate) continue;
1922
- const update = {
1923
- asset,
1924
- autoSelectProtocols: desiredAutoSelect
1925
- };
1926
- if (desiredProtocols !== null) update.protocols = desiredProtocols;
1927
- await this.sdk.updateUserProfile(update);
1928
- debugLog("zyfai:pools", `${asset}: wrote protocols update`, update);
1929
- }
1930
- const poolResults = await Promise.all(
1931
- universe.map(async (protocol) => {
1932
- try {
1933
- const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1934
- return [protocol.id, flattenAvailablePools(raw)];
1935
- } catch (error) {
1936
- console.warn(
1937
- `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
1938
- error instanceof Error ? error.message : String(error)
1939
- );
1940
- return null;
1941
- }
1942
- })
1943
- );
1944
- const availableByProtocol = /* @__PURE__ */ new Map();
1945
- for (const result of poolResults) {
1946
- if (result) availableByProtocol.set(result[0], result[1]);
1947
- }
1948
- const customizations = toZyfaiCustomizations(
1949
- policy,
1950
- availableByProtocol,
1951
- chainsByProtocol
1952
- );
1953
- debugLog("zyfai:pools", "derived customizeBatch payload", {
1954
- strategy,
1955
- availableProtocolCount: availableByProtocol.size,
1956
- customizations
1957
- });
1958
- if (customizations.length === 0) {
1959
- debugLog("zyfai:pools", "skipping customizeBatch \u2014 nothing to send");
1960
- return;
1961
- }
1962
- await this.sdk.customizeBatch(customizations);
1963
- debugLog("zyfai:pools", "wrote customizeBatch", {
1964
- count: customizations.length
1965
- });
1966
- } catch (error) {
1967
- console.warn(
1968
- "[zyfai] applyPoolPolicy failed (non-fatal):",
1969
- error instanceof Error ? error.message : String(error)
1970
- );
1971
- }
1972
- }
1973
- /**
1974
- * Records the deposit with Zyfai, retrying transient failures.
1975
- *
1976
- * This runs AFTER the transfer has already landed on-chain, so it must never
1977
- * fail the deposit — the funds moved. But it is also the ONLY source of the
1978
- * "Top up wallet" entry the history is built from: Zyfai auto-deploys the
1979
- * balance it detects either way, so when this call is lost the user's deposit
1980
- * never appears in Activity (an earlier withdrawal stays the newest row) and
1981
- * nothing ever backfills it. Retry, then log loudly enough to be recoverable.
1982
- */
1983
- async logDepositWithRetry(chainId, txHash, amount, tokenAddress) {
1984
- const ATTEMPTS = 3;
1985
- const RETRY_DELAY_MS = 1e3;
1986
- for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
1987
- try {
1988
- await (tokenAddress ? this.sdk.logDeposit(chainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(chainId, txHash, amount));
1989
- return;
1990
- } catch (logError) {
1991
- if (attempt === ATTEMPTS) {
1992
- console.error(
1993
- "[owney-sdk] Deposit landed on-chain but logDeposit failed \u2014 it will be missing from Zyfai history:",
1994
- { txHash, chainId, amount, tokenAddress, error: logError }
1995
- );
1996
- return;
1997
- }
1998
- await new Promise(
1999
- (resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt)
2000
- );
2001
- }
2002
- }
2003
- }
2004
985
  async deposit(state, chainId, amount, asset, depositCallback) {
2005
986
  try {
2006
- await this.connectFull(state, chainId);
2007
987
  const validChainId = isValidChainId(chainId);
988
+ await this.ensureConnected(state, validChainId);
2008
989
  if (depositCallback) {
2009
990
  const wallet = await this.sdk.getSmartWalletAddress(
2010
991
  this.getAddress(),
@@ -2016,19 +997,22 @@ var ZyfaiAgent = class {
2016
997
  amount
2017
998
  );
2018
999
  const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
2019
- await this.logDepositWithRetry(
2020
- validChainId,
2021
- txHash,
2022
- amount,
2023
- tokenAddress
2024
- );
1000
+ try {
1001
+ await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
1002
+ } catch (logError) {
1003
+ console.warn(
1004
+ "[owney-sdk] Deposit landed on-chain but logDeposit failed (non-fatal):",
1005
+ logError
1006
+ );
1007
+ }
2025
1008
  return { txHash, smartWallet: wallet.address, amount };
2026
1009
  }
2027
1010
  const raw = await this.sdk.depositFunds(
2028
1011
  this.getAddress(),
2029
1012
  validChainId,
2030
1013
  amount,
2031
- asset
1014
+ asset,
1015
+ "aggressive"
2032
1016
  );
2033
1017
  return mapDeposit(raw);
2034
1018
  } catch (error) {
@@ -2037,8 +1021,8 @@ var ZyfaiAgent = class {
2037
1021
  }
2038
1022
  // --- IAgent: Fund operations ---
2039
1023
  async withdraw(state, chainId, token, amount) {
2040
- await this.connectFull(state, chainId);
2041
1024
  const validChainId = isValidChainId(chainId);
1025
+ await this.ensureConnected(state, validChainId);
2042
1026
  const raw = await this.sdk.withdrawFunds(
2043
1027
  this.getAddress(),
2044
1028
  validChainId,
@@ -2117,19 +1101,7 @@ var ZyfaiAgent = class {
2117
1101
  }
2118
1102
  async getUserProfile(state, chainId) {
2119
1103
  await this.connectAuth(state, chainId);
2120
- const validChainId = isValidChainId(chainId);
2121
1104
  const raw = await this.sdk.getUserDetails();
2122
- debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
2123
- asset: "USDC (default \u2014 no asset passed)",
2124
- splitting: raw.splitting,
2125
- minSplits: raw.minSplits,
2126
- strategy: raw.strategy,
2127
- chains: raw.chains,
2128
- protocolCount: raw.protocols?.length,
2129
- hasActiveSessionKey: raw.hasActiveSessionKey,
2130
- smartWallet: raw.smartWallet
2131
- });
2132
- await this.ensureSessionKey(state, this.getAddress(), validChainId);
2133
1105
  return mapUserProfile(raw, this.connectedAddress);
2134
1106
  }
2135
1107
  async ensureAutoSelectProtocols(state, chainId, asset) {
@@ -2160,46 +1132,8 @@ var ZyfaiAgent = class {
2160
1132
  }
2161
1133
  };
2162
1134
 
2163
- // src/client.ts
2164
- init_errors();
2165
-
2166
1135
  // src/lib/routing-api.ts
2167
- init_errors();
2168
1136
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2169
- async function fetchOrgAgentConfig(apiKey) {
2170
- const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
2171
- try {
2172
- const res = await fetch(url, {
2173
- method: "GET",
2174
- headers: {
2175
- "Content-Type": "application/json",
2176
- "x-owney-api-key": `${apiKey}`
2177
- }
2178
- });
2179
- if (!res.ok) {
2180
- if (res.status !== 404) {
2181
- console.warn(
2182
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
2183
- );
2184
- }
2185
- return null;
2186
- }
2187
- const json = await res.json();
2188
- const policy = json.success ? json.data ?? null : null;
2189
- debugLog(
2190
- "owney-sdk",
2191
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
2192
- policy ?? void 0
2193
- );
2194
- return policy;
2195
- } catch (error) {
2196
- console.warn(
2197
- "[owney-sdk] Could not read org agent config (non-fatal):",
2198
- error instanceof Error ? error.message : String(error)
2199
- );
2200
- return null;
2201
- }
2202
- }
2203
1137
  async function fetchAgentKeys(apiKey) {
2204
1138
  const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
2205
1139
  const res = await fetch(url, {
@@ -2229,7 +1163,6 @@ async function fetchAgentKeys(apiKey) {
2229
1163
  }
2230
1164
 
2231
1165
  // src/lib/health-report.ts
2232
- init_errors();
2233
1166
  var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2234
1167
  async function reportAgentFailure(apiKey, agentType, errorCode) {
2235
1168
  try {
@@ -2265,10 +1198,11 @@ async function withFailureReporting(apiKey, agentType, fn) {
2265
1198
  // src/lib/helpers/withdraw-helper.ts
2266
1199
  var import_viem2 = require("viem");
2267
1200
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
1201
+ const target = asset.toUpperCase();
2268
1202
  return agents.map((agent) => {
2269
1203
  const agentBalance = aggregated[agent.id];
2270
1204
  const tokenBalance = agentBalance?.tokens.find(
2271
- (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
1205
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
2272
1206
  );
2273
1207
  if (!tokenBalance) return { agent, balance: 0n };
2274
1208
  return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
@@ -2378,11 +1312,8 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
2378
1312
  }
2379
1313
 
2380
1314
  // src/client.ts
2381
- var import_viem6 = require("viem");
2382
- var import_chains3 = require("viem/chains");
2383
-
2384
- // src/lib/sponsored-deposit.ts
2385
- init_errors();
1315
+ var import_viem5 = require("viem");
1316
+ var import_chains2 = require("viem/chains");
2386
1317
 
2387
1318
  // src/lib/transfer-auth.ts
2388
1319
  var import_viem3 = require("viem");
@@ -2421,13 +1352,12 @@ function randomAuthNonce() {
2421
1352
  }
2422
1353
 
2423
1354
  // src/lib/sponsor-client.ts
2424
- init_errors();
2425
1355
  var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2426
1356
  async function postSponsorTransferAuth(input) {
2427
- const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1357
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2428
1358
  let res;
2429
1359
  try {
2430
- res = await fetch(`${base4}/api/v1/sponsor/erc20-transfer-auth`, {
1360
+ res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
2431
1361
  method: "POST",
2432
1362
  headers: {
2433
1363
  "content-type": "application/json",
@@ -2464,10 +1394,10 @@ async function postSponsorTransferAuth(input) {
2464
1394
  return parsed.data;
2465
1395
  }
2466
1396
  async function postSponsorPermit2Transfer(input) {
2467
- const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1397
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2468
1398
  let res;
2469
1399
  try {
2470
- res = await fetch(`${base4}/api/v1/sponsor/permit2-transfer`, {
1400
+ res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
2471
1401
  method: "POST",
2472
1402
  headers: {
2473
1403
  "content-type": "application/json",
@@ -2502,11 +1432,11 @@ async function postSponsorPermit2Transfer(input) {
2502
1432
  return parsed.data;
2503
1433
  }
2504
1434
  async function getSponsorRelayerAddress(input) {
2505
- const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1435
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2506
1436
  let res;
2507
1437
  try {
2508
1438
  res = await fetch(
2509
- `${base4}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
1439
+ `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2510
1440
  {
2511
1441
  headers: { "x-owney-api-key": input.apiKey }
2512
1442
  }
@@ -2538,8 +1468,85 @@ async function getSponsorRelayerAddress(input) {
2538
1468
  return parsed.data.relayer;
2539
1469
  }
2540
1470
 
1471
+ // src/lib/permit2.ts
1472
+ var import_viem4 = require("viem");
1473
+ var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
1474
+ var MAX_UINT256 = 2n ** 256n - 1n;
1475
+ var ERC20_ALLOWANCE_ABI = [
1476
+ {
1477
+ type: "function",
1478
+ name: "allowance",
1479
+ stateMutability: "view",
1480
+ inputs: [
1481
+ { name: "owner", type: "address" },
1482
+ { name: "spender", type: "address" }
1483
+ ],
1484
+ outputs: [{ name: "", type: "uint256" }]
1485
+ },
1486
+ {
1487
+ type: "function",
1488
+ name: "approve",
1489
+ stateMutability: "nonpayable",
1490
+ inputs: [
1491
+ { name: "spender", type: "address" },
1492
+ { name: "amount", type: "uint256" }
1493
+ ],
1494
+ outputs: [{ name: "", type: "bool" }]
1495
+ },
1496
+ {
1497
+ type: "function",
1498
+ name: "balanceOf",
1499
+ stateMutability: "view",
1500
+ inputs: [{ name: "account", type: "address" }],
1501
+ outputs: [{ name: "", type: "uint256" }]
1502
+ }
1503
+ ];
1504
+ function buildPermitTransferFromTypedData(input) {
1505
+ return {
1506
+ domain: {
1507
+ name: "Permit2",
1508
+ chainId: input.chainId,
1509
+ verifyingContract: PERMIT2_ADDRESS
1510
+ },
1511
+ types: {
1512
+ PermitTransferFrom: [
1513
+ { name: "permitted", type: "TokenPermissions" },
1514
+ { name: "spender", type: "address" },
1515
+ { name: "nonce", type: "uint256" },
1516
+ { name: "deadline", type: "uint256" }
1517
+ ],
1518
+ TokenPermissions: [
1519
+ { name: "token", type: "address" },
1520
+ { name: "amount", type: "uint256" }
1521
+ ]
1522
+ },
1523
+ primaryType: "PermitTransferFrom",
1524
+ message: input.message
1525
+ };
1526
+ }
1527
+ function randomPermit2Nonce() {
1528
+ const bytes = new Uint8Array(32);
1529
+ globalThis.crypto.getRandomValues(bytes);
1530
+ return BigInt((0, import_viem4.bytesToHex)(bytes));
1531
+ }
1532
+ async function readPermit2Allowance(publicClient, token, owner) {
1533
+ return publicClient.readContract({
1534
+ address: token,
1535
+ abi: ERC20_ALLOWANCE_ABI,
1536
+ functionName: "allowance",
1537
+ args: [owner, PERMIT2_ADDRESS]
1538
+ });
1539
+ }
1540
+ async function readErc20Balance(publicClient, token, owner) {
1541
+ return publicClient.readContract({
1542
+ address: token,
1543
+ abi: ERC20_ALLOWANCE_ABI,
1544
+ functionName: "balanceOf",
1545
+ args: [owner]
1546
+ });
1547
+ }
1548
+
2541
1549
  // src/lib/sponsored-deposit.ts
2542
- init_permit2();
2543
1550
  var AUTH_WINDOW_SECONDS = 15 * 60;
2544
1551
  function makeSponsoredDepositCallback(deps) {
2545
1552
  const post = deps.httpPost ?? postSponsorTransferAuth;
@@ -2613,8 +1620,6 @@ function makeSponsoredDepositCallback(deps) {
2613
1620
  }
2614
1621
 
2615
1622
  // src/lib/sponsored-weth-deposit.ts
2616
- init_errors();
2617
- init_permit2();
2618
1623
  var PERMIT_WINDOW_SECONDS = 15 * 60;
2619
1624
  function makeSponsoredWethCallback(deps) {
2620
1625
  const get = deps.httpGet ?? getSponsorRelayerAddress;
@@ -2693,7 +1698,6 @@ function makeSponsoredWethCallback(deps) {
2693
1698
  }
2694
1699
 
2695
1700
  // src/client.ts
2696
- init_permit2();
2697
1701
  function encodeMultiAgentCursor(map) {
2698
1702
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
2699
1703
  }
@@ -2720,11 +1724,10 @@ var SPONSORED_USDC_BY_CHAIN = {
2720
1724
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
2721
1725
  };
2722
1726
  var VIEM_CHAIN2 = {
2723
- 8453: import_chains3.base,
2724
- 42161: import_chains3.arbitrum,
2725
- 1: import_chains3.mainnet
1727
+ 8453: import_chains2.base,
1728
+ 42161: import_chains2.arbitrum,
1729
+ 1: import_chains2.mainnet
2726
1730
  };
2727
- var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
2728
1731
  var SPONSORED_WETH_BY_CHAIN = {
2729
1732
  8453: "0x4200000000000000000000000000000000000006",
2730
1733
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -2744,26 +1747,17 @@ var OwneySDK = class {
2744
1747
  disabledAgents = /* @__PURE__ */ new Set();
2745
1748
  state = null;
2746
1749
  apiKey;
2747
- // The org's agent policy, fetched once per client. Applied to each agent on
2748
- // activation (ROUT-229). `undefined` means "not fetched yet", `null` means
2749
- // "fetched, and the partner has no policy" — which is a real answer meaning
2750
- // leave every user's agent profile alone.
2751
- orgAgentConfig;
2752
- orgAgentConfigPromise = null;
2753
1750
  zyfaiRpcUrls;
2754
1751
  routingApiBaseUrl;
2755
1752
  referralSource;
2756
- surfliquidApiBaseUrl;
2757
1753
  cachedSponsoredCallback = null;
2758
1754
  cachedWethSponsoredCallback = null;
2759
1755
  initializingAgentsPromise = null;
2760
1756
  constructor(config) {
2761
1757
  this.apiKey = config.apiKey;
2762
- if (config.debug) setOwneyDebug(true);
2763
1758
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
2764
1759
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2765
1760
  this.referralSource = config.referralSource;
2766
- this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
2767
1761
  }
2768
1762
  /**
2769
1763
  * Establish connection state for all subsequent SDK calls.
@@ -2861,14 +1855,14 @@ var OwneySDK = class {
2861
1855
  // Casts work around viem's chain-narrowed Client vs the generic
2862
1856
  // PublicClient/WalletClient param types — structurally identical at
2863
1857
  // runtime, but the two share a name TS treats as unrelated.
2864
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
1858
+ getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
2865
1859
  chain: VIEM_CHAIN2[cid],
2866
- transport: (0, import_viem6.custom)(provider)
1860
+ transport: (0, import_viem5.custom)(provider)
2867
1861
  }),
2868
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
1862
+ getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
2869
1863
  account: owner,
2870
1864
  chain: VIEM_CHAIN2[cid],
2871
- transport: (0, import_viem6.custom)(provider)
1865
+ transport: (0, import_viem5.custom)(provider)
2872
1866
  })
2873
1867
  });
2874
1868
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -2894,14 +1888,14 @@ var OwneySDK = class {
2894
1888
  // Casts work around viem's chain-narrowed Client vs the generic
2895
1889
  // PublicClient/WalletClient param types — structurally identical at
2896
1890
  // runtime, but the two share a name TS treats as unrelated.
2897
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
1891
+ getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
2898
1892
  chain: VIEM_CHAIN2[cid],
2899
- transport: (0, import_viem6.custom)(provider)
1893
+ transport: (0, import_viem5.custom)(provider)
2900
1894
  }),
2901
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
1895
+ getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
2902
1896
  account: owner,
2903
1897
  chain: VIEM_CHAIN2[cid],
2904
- transport: (0, import_viem6.custom)(provider)
1898
+ transport: (0, import_viem5.custom)(provider)
2905
1899
  })
2906
1900
  });
2907
1901
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -2922,57 +1916,14 @@ var OwneySDK = class {
2922
1916
  }
2923
1917
  return active;
2924
1918
  }
2925
- /**
2926
- * Read the org's agent policy once and reuse it. fetchOrgAgentConfig never
2927
- * throws — it degrades to null — so this cannot break activation.
2928
- */
2929
- async getOrgAgentConfig() {
2930
- if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
2931
- if (!this.orgAgentConfigPromise) {
2932
- this.orgAgentConfigPromise = fetchOrgAgentConfig(this.apiKey).then(
2933
- (config) => {
2934
- this.orgAgentConfig = config;
2935
- return config;
2936
- }
2937
- );
2938
- }
2939
- return this.orgAgentConfigPromise;
2940
- }
2941
- /**
2942
- * Apply the org policy to one agent, after it has activated.
2943
- *
2944
- * Sequenced after activation rather than alongside it because applying the
2945
- * policy needs the authenticated session activation establishes. Every failure
2946
- * mode is swallowed: a partner's splitting preference is not worth failing a
2947
- * user's connect over.
2948
- */
2949
- async applyOrgPolicyTo(agent, state, chainId) {
2950
- if (!agent.applyOrgPolicy) {
2951
- debugLog(
2952
- "owney-sdk",
2953
- `${agent.id}: no applyOrgPolicy \u2014 agent has no org-level knobs, skipping`
2954
- );
2955
- return;
2956
- }
2957
- try {
2958
- const policy = await this.getOrgAgentConfig();
2959
- if (!policy) return;
2960
- await agent.applyOrgPolicy(state, chainId, policy);
2961
- } catch (error) {
2962
- console.warn(
2963
- `[owney-sdk] applyOrgPolicy(${agent.id}) failed (non-fatal):`,
2964
- error instanceof Error ? error.message : String(error)
2965
- );
2966
- }
2967
- }
2968
1919
  async ensureAgentsInitialized() {
2969
1920
  if (this.agents.size > 0) return;
2970
1921
  if (!this.initializingAgentsPromise) {
2971
1922
  this.initializingAgentsPromise = (async () => {
2972
1923
  const agentKeys = await fetchAgentKeys(this.apiKey);
2973
1924
  this.disabledAgents.clear();
2974
- for (const { key: key2, agent_type, project_name, is_enabled } of agentKeys) {
2975
- const agent = await this.createAgent(agent_type, key2, project_name);
1925
+ for (const { key, agent_type, is_enabled } of agentKeys) {
1926
+ const agent = this.createAgent(agent_type, key);
2976
1927
  if (!agent) continue;
2977
1928
  this.agents.set(agent_type, agent);
2978
1929
  if (is_enabled === false) {
@@ -2993,21 +1944,9 @@ var OwneySDK = class {
2993
1944
  this.initializingAgentsPromise = null;
2994
1945
  }
2995
1946
  }
2996
- async createAgent(agentId, key2, projectName) {
1947
+ createAgent(agentId, key) {
2997
1948
  if (agentId === "zyfai") {
2998
- return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2999
- }
3000
- if (agentId === "surfliquid") {
3001
- if (!projectName) return null;
3002
- const { SurfLiquidAgent: SurfLiquidAgent2 } = await Promise.resolve().then(() => (init_surfliquid_agent(), surfliquid_agent_exports));
3003
- return new SurfLiquidAgent2({
3004
- projectName,
3005
- appId: key2,
3006
- // Same-origin proxy override — SurfLiquid auth is an httpOnly cookie,
3007
- // which WebKit (iOS) discards when it is third-party. See
3008
- // OwneySDKConfig.surfliquidApiBaseUrl.
3009
- apiBaseUrl: this.surfliquidApiBaseUrl
3010
- });
1949
+ return new ZyfaiAgent(key, this.zyfaiRpcUrls, this.referralSource);
3011
1950
  }
3012
1951
  return null;
3013
1952
  }
@@ -3061,7 +2000,6 @@ var OwneySDK = class {
3061
2000
  Promise.all(
3062
2001
  agents.map(async (agent) => {
3063
2002
  await agent.activateAgent(state, chainId);
3064
- await this.applyOrgPolicyTo(agent, state, chainId);
3065
2003
  })
3066
2004
  ).catch((error) => {
3067
2005
  console.error("activateAgent background init failed:", error);
@@ -3089,7 +2027,6 @@ var OwneySDK = class {
3089
2027
  Promise.all(
3090
2028
  enabledCompatible.map(async (agent) => {
3091
2029
  await agent.activateAgent(state, chainId);
3092
- await this.applyOrgPolicyTo(agent, state, chainId);
3093
2030
  })
3094
2031
  ).catch((error) => {
3095
2032
  console.error("activateAgent background init failed:", error);
@@ -3163,30 +2100,18 @@ var OwneySDK = class {
3163
2100
  );
3164
2101
  }
3165
2102
  const agentResults = {};
3166
- const agentErrors = {};
3167
2103
  for (const { agent, amount: agentAmount } of agentAmounts) {
3168
- try {
3169
- agentResults[agent.id] = await this.depositWithFallback(
3170
- agent,
3171
- state,
3172
- chainId,
3173
- agentAmount,
3174
- asset,
3175
- effectiveCallback,
3176
- depositCallback
3177
- );
3178
- } catch (error) {
3179
- agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
3180
- }
3181
- }
3182
- if (Object.keys(agentResults).length === 0) {
3183
- throw new OwneyError(
3184
- "DEPOSIT_ALL_FAILED",
3185
- `All ${agentAmounts.length} agent deposit(s) failed.`,
3186
- { agentErrors }
2104
+ agentResults[agent.id] = await this.depositWithFallback(
2105
+ agent,
2106
+ state,
2107
+ chainId,
2108
+ agentAmount,
2109
+ asset,
2110
+ effectiveCallback,
2111
+ depositCallback
3187
2112
  );
3188
2113
  }
3189
- return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
2114
+ return { agentResults };
3190
2115
  }
3191
2116
  /**
3192
2117
  * Invokes `agent.deposit` with the resolved sponsored callback, composing
@@ -3302,8 +2227,9 @@ var OwneySDK = class {
3302
2227
  async hasExistingBalance(agent, state, chainId, asset) {
3303
2228
  try {
3304
2229
  const balance = await agent.getBalances(state, chainId);
2230
+ const target = asset.toLowerCase();
3305
2231
  const token = balance.tokens.find(
3306
- (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
2232
+ (t) => t.chainId === chainId && t.asset.toLowerCase() === target
3307
2233
  );
3308
2234
  return !!token && Number(token.amount) > 0;
3309
2235
  } catch {
@@ -3342,34 +2268,6 @@ var OwneySDK = class {
3342
2268
  }
3343
2269
  return eligible;
3344
2270
  }
3345
- /**
3346
- * Agent ids the routing API provisioned for this org that support the given
3347
- * chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
3348
- * surfliquid first). Returns `[]` when the org has no compatible agent — never
3349
- * throws on an empty org. Loads agent keys on first call (apiKey only, no
3350
- * wallet), so the UI can resolve which agent to use before the user connects.
3351
- *
3352
- * This is the source of truth for agent availability: an agent appears here
3353
- * iff the routing API returned its key. No per-app feature flags.
3354
- */
3355
- async getEligibleAgentIds(chainId, asset) {
3356
- try {
3357
- await this.ensureAgentsInitialized();
3358
- } catch (error) {
3359
- if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
3360
- return [];
3361
- }
3362
- throw error;
3363
- }
3364
- return [...this.agents.values()].filter((agent) => {
3365
- const chainAssets = agent.supportedAssets.find(
3366
- (sa) => sa.chainId === chainId
3367
- );
3368
- return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
3369
- }).map((agent) => agent.id).sort(
3370
- (a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
3371
- );
3372
- }
3373
2271
  // --- Fund operations ---
3374
2272
  /**
3375
2273
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
@@ -3405,17 +2303,9 @@ var OwneySDK = class {
3405
2303
  }
3406
2304
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
3407
2305
  if (!amount) {
3408
- const aggregated2 = await this.getBalances();
3409
- const funded = projectAgentBalancesForAsset(
3410
- eligibleAgents,
3411
- aggregated2.agentBalances,
3412
- chainId,
3413
- asset,
3414
- assetInfo.decimals
3415
- ).filter((b) => b.balance > 0n);
3416
2306
  const results2 = {};
3417
2307
  const agentErrors2 = {};
3418
- for (const { agent } of funded) {
2308
+ for (const agent of eligibleAgents) {
3419
2309
  try {
3420
2310
  results2[agent.id] = await agent.withdraw(state, chainId, token);
3421
2311
  } catch (err) {
@@ -3432,10 +2322,9 @@ var OwneySDK = class {
3432
2322
  { asset, agentErrors: agentErrors2 }
3433
2323
  );
3434
2324
  }
3435
- const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
3436
2325
  return {
3437
2326
  agentResult: results2,
3438
- totalWithdrawn: totalWithdrawn.toString()
2327
+ totalWithdrawn: sumWithdrawnAmount(results2).toString()
3439
2328
  };
3440
2329
  }
3441
2330
  const requested = BigInt(amount);
@@ -3805,10 +2694,10 @@ var OwneySDK = class {
3805
2694
  );
3806
2695
  }
3807
2696
  const provider = this.requireConnectedProvider();
3808
- const wallet = (0, import_viem6.createWalletClient)({
2697
+ const wallet = (0, import_viem5.createWalletClient)({
3809
2698
  account: state.walletAddress,
3810
2699
  chain: VIEM_CHAIN2[chainId],
3811
- transport: (0, import_viem6.custom)(provider)
2700
+ transport: (0, import_viem5.custom)(provider)
3812
2701
  });
3813
2702
  const hash = await wallet.writeContract({
3814
2703
  address: token,
@@ -3818,9 +2707,9 @@ var OwneySDK = class {
3818
2707
  account: state.walletAddress,
3819
2708
  chain: VIEM_CHAIN2[chainId]
3820
2709
  });
3821
- const publicClient = (0, import_viem6.createPublicClient)({
2710
+ const publicClient = (0, import_viem5.createPublicClient)({
3822
2711
  chain: VIEM_CHAIN2[chainId],
3823
- transport: (0, import_viem6.custom)(provider)
2712
+ transport: (0, import_viem5.custom)(provider)
3824
2713
  });
3825
2714
  const receipt = await publicClient.waitForTransactionReceipt({
3826
2715
  hash,
@@ -3928,11 +2817,8 @@ var OwneySDK = class {
3928
2817
  }
3929
2818
  };
3930
2819
 
3931
- // src/index.ts
3932
- init_errors();
3933
-
3934
2820
  // src/agents/zyfai/zyfai.siwx.ts
3935
- var import_viem7 = require("viem");
2821
+ var import_viem6 = require("viem");
3936
2822
  var import_siwe = require("siwe");
3937
2823
  var import_sdk2 = require("@zyfai/sdk");
3938
2824
 
@@ -3955,22 +2841,22 @@ var readLegacySiwxSession = (store, address) => {
3955
2841
  const legacyKeys = [];
3956
2842
  try {
3957
2843
  for (let i = 0; i < store.length; i++) {
3958
- const key2 = store.key(i);
3959
- if (key2?.startsWith(prefix)) legacyKeys.push(key2);
2844
+ const key = store.key(i);
2845
+ if (key?.startsWith(prefix)) legacyKeys.push(key);
3960
2846
  }
3961
2847
  } catch {
3962
2848
  return null;
3963
2849
  }
3964
2850
  let adopted = null;
3965
- for (const key2 of legacyKeys) {
2851
+ for (const key of legacyKeys) {
3966
2852
  if (!adopted) {
3967
2853
  try {
3968
- adopted = JSON.parse(store.getItem(key2) ?? "");
2854
+ adopted = JSON.parse(store.getItem(key) ?? "");
3969
2855
  } catch {
3970
2856
  }
3971
2857
  }
3972
2858
  try {
3973
- store.removeItem(key2);
2859
+ store.removeItem(key);
3974
2860
  } catch {
3975
2861
  }
3976
2862
  }
@@ -3978,11 +2864,11 @@ var readLegacySiwxSession = (store, address) => {
3978
2864
  };
3979
2865
  var readSiwxSession = (address, chainId) => {
3980
2866
  if (typeof window === "undefined") return null;
3981
- const key2 = buildKey2(address);
2867
+ const key = buildKey2(address);
3982
2868
  const store = storage2();
3983
2869
  let raw = null;
3984
2870
  try {
3985
- raw = store?.getItem(key2) ?? null;
2871
+ raw = store?.getItem(key) ?? null;
3986
2872
  } catch {
3987
2873
  raw = null;
3988
2874
  }
@@ -3990,9 +2876,9 @@ var readSiwxSession = (address, chainId) => {
3990
2876
  try {
3991
2877
  return JSON.parse(raw);
3992
2878
  } catch {
3993
- memorySiwxSessions.delete(key2);
2879
+ memorySiwxSessions.delete(key);
3994
2880
  try {
3995
- store?.removeItem(key2);
2881
+ store?.removeItem(key);
3996
2882
  } catch {
3997
2883
  }
3998
2884
  return null;
@@ -4003,24 +2889,24 @@ var readSiwxSession = (address, chainId) => {
4003
2889
  writeSiwxSession(address, chainId, legacy);
4004
2890
  return legacy;
4005
2891
  }
4006
- return memorySiwxSessions.get(key2) ?? null;
2892
+ return memorySiwxSessions.get(key) ?? null;
4007
2893
  };
4008
2894
  var writeSiwxSession = (address, _chainId, session) => {
4009
2895
  if (typeof window === "undefined") return;
4010
- const key2 = buildKey2(address);
4011
- memorySiwxSessions.set(key2, session);
2896
+ const key = buildKey2(address);
2897
+ memorySiwxSessions.set(key, session);
4012
2898
  const store = storage2();
4013
2899
  try {
4014
- store?.setItem(key2, JSON.stringify(session));
2900
+ store?.setItem(key, JSON.stringify(session));
4015
2901
  } catch {
4016
2902
  }
4017
2903
  };
4018
2904
  var clearSiwxSession = (address, _chainId) => {
4019
- const key2 = buildKey2(address);
4020
- memorySiwxSessions.delete(key2);
2905
+ const key = buildKey2(address);
2906
+ memorySiwxSessions.delete(key);
4021
2907
  const store = storage2();
4022
2908
  try {
4023
- store?.removeItem(key2);
2909
+ store?.removeItem(key);
4024
2910
  } catch {
4025
2911
  }
4026
2912
  readLegacySiwxSession(store, address);
@@ -4059,7 +2945,7 @@ function buildSIWXConfig(deps) {
4059
2945
  issuedAt,
4060
2946
  toString() {
4061
2947
  return new import_siwe.SiweMessage({
4062
- address: (0, import_viem7.getAddress)(accountAddress),
2948
+ address: (0, import_viem6.getAddress)(accountAddress),
4063
2949
  chainId: numericChainId(chainId),
4064
2950
  domain,
4065
2951
  uri,
@@ -4150,6 +3036,5 @@ function createOwneySIWX(config) {
4150
3036
  NotConnectedError,
4151
3037
  OwneyError,
4152
3038
  OwneySDK,
4153
- createOwneySIWX,
4154
- setOwneyDebug
3039
+ createOwneySIWX
4155
3040
  });