@owney/sdk 0.6.9-beta.8 → 0.7.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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);
@@ -817,46 +211,46 @@ function resolveChainId(chain) {
817
211
  function mapBalances(raw, _chainId, smartWallet) {
818
212
  const portfolio = raw.portfolio;
819
213
  const portfolioByChain = portfolio.portfolioByChain ?? {};
820
- const rawPositions = portfolio.positions ?? [];
821
- const detailByKey = /* @__PURE__ */ new Map();
822
- for (const p of rawPositions) {
823
- const cid = resolveChainId(p.chain ?? "");
824
- const symbol = (p.token_symbol ?? "").toUpperCase();
825
- if (Number.isFinite(cid) && symbol) detailByKey.set(`${cid}:${symbol}`, p);
826
- }
827
214
  let totalBalance = 0;
828
215
  const tokens = [];
829
- const positions = [];
830
216
  for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
831
217
  const positionChainId = Number(chainIdKey);
832
- const chainName = CHAIN_ID_TO_NAME[positionChainId] ?? "BASE";
833
218
  for (const [symbol, { balance, decimals }] of Object.entries(
834
219
  tokensBySymbol ?? {}
835
220
  )) {
836
- const amount = hexToDecimal(balance || "0", tokenDecimals(symbol, decimals));
221
+ const amount = hexToDecimal(balance || "0", decimals);
837
222
  totalBalance += amount;
838
223
  tokens.push({
839
- chain: chainName,
224
+ chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
840
225
  chainId: positionChainId,
841
226
  asset: symbol,
842
227
  amount: String(amount)
843
228
  });
844
- const detail = detailByKey.get(`${positionChainId}:${symbol.toUpperCase()}`);
845
- positions.push({
846
- chain: chainName,
847
- protocol: detail?.protocol_name ?? detail?.protocol_id ?? "",
848
- protocolId: detail?.protocol_id,
849
- pool: detail?.pool,
850
- asset: symbol,
851
- amount: String(amount),
852
- apy: detail?.pool_apy,
853
- tvl: detail?.pool_tvl,
854
- // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
855
- // structural read keeps it undefined today and auto-populates later.
856
- liquidity: detail?.liquidity
857
- });
858
229
  }
859
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
+ }));
860
254
  return {
861
255
  smartWallet,
862
256
  totalBalance: String(totalBalance),
@@ -1018,9 +412,7 @@ function mapUserProfile(raw, userAddress) {
1018
412
  chains: raw.chains || [],
1019
413
  strategy: raw.strategy,
1020
414
  hasActiveSessionKey: raw.hasActiveSessionKey || false,
1021
- protocols: raw.protocols || [],
1022
- splitting: raw.splitting,
1023
- minSplits: raw.minSplits
415
+ protocols: raw.protocols || []
1024
416
  };
1025
417
  }
1026
418
  function mapApyByStrategy(raw) {
@@ -1133,9 +525,6 @@ function decodeHistoryCursor(token) {
1133
525
  return parsed;
1134
526
  }
1135
527
 
1136
- // src/agents/zyfai/zyfai.agent.ts
1137
- init_errors();
1138
-
1139
528
  // src/agents/zyfai/zyfai.auth-cache.ts
1140
529
  var KEY_PREFIX = "owney.zyfai.session";
1141
530
  var storage = () => {
@@ -1170,23 +559,23 @@ var readLegacySession = (store, address) => {
1170
559
  const legacyKeys = [];
1171
560
  try {
1172
561
  for (let i = 0; i < store.length; i++) {
1173
- const key2 = store.key(i);
1174
- if (key2?.startsWith(prefix)) legacyKeys.push(key2);
562
+ const key = store.key(i);
563
+ if (key?.startsWith(prefix)) legacyKeys.push(key);
1175
564
  }
1176
565
  } catch {
1177
566
  return null;
1178
567
  }
1179
568
  let adopted = null;
1180
- for (const key2 of legacyKeys) {
569
+ for (const key of legacyKeys) {
1181
570
  if (!adopted) {
1182
571
  try {
1183
- const parsed = JSON.parse(store.getItem(key2) ?? "");
572
+ const parsed = JSON.parse(store.getItem(key) ?? "");
1184
573
  if (isFreshSession(parsed)) adopted = parsed;
1185
574
  } catch {
1186
575
  }
1187
576
  }
1188
577
  try {
1189
- store.removeItem(key2);
578
+ store.removeItem(key);
1190
579
  } catch {
1191
580
  }
1192
581
  }
@@ -1194,11 +583,11 @@ var readLegacySession = (store, address) => {
1194
583
  };
1195
584
  var readSession = (address, _chainId) => {
1196
585
  if (typeof window === "undefined") return null;
1197
- const key2 = buildKey(address);
586
+ const key = buildKey(address);
1198
587
  const store = storage();
1199
588
  let raw = null;
1200
589
  try {
1201
- raw = store?.getItem(key2) ?? null;
590
+ raw = store?.getItem(key) ?? null;
1202
591
  } catch {
1203
592
  raw = null;
1204
593
  }
@@ -1208,9 +597,9 @@ var readSession = (address, _chainId) => {
1208
597
  if (isFreshSession(parsed)) return parsed;
1209
598
  } catch {
1210
599
  }
1211
- memorySessions.delete(key2);
600
+ memorySessions.delete(key);
1212
601
  try {
1213
- store?.removeItem(key2);
602
+ store?.removeItem(key);
1214
603
  } catch {
1215
604
  }
1216
605
  return null;
@@ -1220,27 +609,27 @@ var readSession = (address, _chainId) => {
1220
609
  writeSession(address, _chainId, legacy);
1221
610
  return legacy;
1222
611
  }
1223
- const cached = memorySessions.get(key2);
612
+ const cached = memorySessions.get(key);
1224
613
  if (isFreshSession(cached)) return cached;
1225
- if (cached) memorySessions.delete(key2);
614
+ if (cached) memorySessions.delete(key);
1226
615
  return null;
1227
616
  };
1228
617
  var writeSession = (address, _chainId, session) => {
1229
618
  if (typeof window === "undefined") return;
1230
- const key2 = buildKey(address);
1231
- memorySessions.set(key2, session);
619
+ const key = buildKey(address);
620
+ memorySessions.set(key, session);
1232
621
  const store = storage();
1233
622
  try {
1234
- store?.setItem(key2, JSON.stringify(session));
623
+ store?.setItem(key, JSON.stringify(session));
1235
624
  } catch {
1236
625
  }
1237
626
  };
1238
627
  var clearSession = (address, _chainId) => {
1239
- const key2 = buildKey(address);
1240
- memorySessions.delete(key2);
628
+ const key = buildKey(address);
629
+ memorySessions.delete(key);
1241
630
  const store = storage();
1242
631
  try {
1243
- store?.removeItem(key2);
632
+ store?.removeItem(key);
1244
633
  } catch {
1245
634
  }
1246
635
  readLegacySession(store, address);
@@ -1271,149 +660,6 @@ function buildProtocolMergeUpdate(available, current, asset) {
1271
660
  };
1272
661
  }
1273
662
 
1274
- // src/agents/zyfai/splitting.ts
1275
- var MAX_MIN_SPLITS = 4;
1276
- var MIN_FORCE_SPLITS = 2;
1277
- var AUTOMATIC_MIN_SPLITS = 1;
1278
- function toZyfaiSplitting(policy) {
1279
- switch (policy.splittingMode) {
1280
- case "none":
1281
- return { splitting: false };
1282
- case "automatic":
1283
- return { splitting: true, minSplits: AUTOMATIC_MIN_SPLITS };
1284
- case "force":
1285
- return {
1286
- splitting: true,
1287
- minSplits: clampForceSplits(policy.minSplits)
1288
- };
1289
- }
1290
- }
1291
- function clampForceSplits(minSplits) {
1292
- if (typeof minSplits !== "number" || !Number.isFinite(minSplits)) {
1293
- return MIN_FORCE_SPLITS;
1294
- }
1295
- const rounded = Math.round(minSplits);
1296
- if (rounded < MIN_FORCE_SPLITS) return MIN_FORCE_SPLITS;
1297
- if (rounded > MAX_MIN_SPLITS) return MAX_MIN_SPLITS;
1298
- return rounded;
1299
- }
1300
- function splittingNeedsUpdate(current, desired) {
1301
- if (!current) return true;
1302
- if (current.splitting !== desired.splitting) return true;
1303
- if (desired.minSplits === void 0) return false;
1304
- return current.minSplits !== desired.minSplits;
1305
- }
1306
-
1307
- // src/agents/zyfai/pool-policy.ts
1308
- function toZyfaiCustomizations(policy, availableByProtocol, chainsByProtocol) {
1309
- const selectionByKey = /* @__PURE__ */ new Map();
1310
- for (const selection of policy.selections) {
1311
- selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
1312
- }
1313
- const customizations = [];
1314
- for (const [protocolId, chainIds] of Object.entries(chainsByProtocol)) {
1315
- const available = availableByProtocol.get(protocolId);
1316
- if (available === void 0) {
1317
- continue;
1318
- }
1319
- const availableSet = new Set(available);
1320
- for (const chainId of chainIds) {
1321
- const stored = selectionByKey.get(key(protocolId, chainId));
1322
- if (stored === void 0) {
1323
- customizations.push({
1324
- protocolId,
1325
- chainId,
1326
- pools: [],
1327
- autoselect: true
1328
- });
1329
- continue;
1330
- }
1331
- const pools = stored.pools.filter((pool) => availableSet.has(pool));
1332
- customizations.push({
1333
- protocolId,
1334
- chainId,
1335
- pools,
1336
- autoselect: false
1337
- });
1338
- }
1339
- }
1340
- return customizations;
1341
- }
1342
- function deriveProtocols(policy, universe, chainsByProtocol) {
1343
- if (policy.selections.length === 0) {
1344
- return null;
1345
- }
1346
- const selectionByKey = /* @__PURE__ */ new Map();
1347
- for (const selection of policy.selections) {
1348
- selectionByKey.set(key(selection.protocolId, selection.chainId), selection);
1349
- }
1350
- return universe.filter((protocolId) => {
1351
- const chainIds = chainsByProtocol[protocolId];
1352
- if (chainIds === void 0 || chainIds.length === 0) {
1353
- return true;
1354
- }
1355
- const fullyDisabled = chainIds.every((chainId) => {
1356
- const stored = selectionByKey.get(key(protocolId, chainId));
1357
- return stored !== void 0 && stored.pools.length === 0;
1358
- });
1359
- return !fullyDisabled;
1360
- });
1361
- }
1362
- function key(protocolId, chainId) {
1363
- return `${protocolId}:${chainId}`;
1364
- }
1365
- function buildChainsByProtocol(protocols, dashboardChainIds) {
1366
- const allowed = new Set(dashboardChainIds);
1367
- const result = {};
1368
- for (const protocol of protocols) {
1369
- result[protocol.id] = (protocol.chains ?? []).filter(
1370
- (chainId) => allowed.has(chainId)
1371
- );
1372
- }
1373
- return result;
1374
- }
1375
- function flattenAvailablePools(raw) {
1376
- const byChain = raw ?? {};
1377
- const names = [];
1378
- for (const byToken of Object.values(byChain ?? {})) {
1379
- for (const entry of Object.values(byToken ?? {})) {
1380
- for (const pool of entry?.pools ?? []) {
1381
- if (pool?.name) names.push(pool.name);
1382
- }
1383
- }
1384
- }
1385
- return names;
1386
- }
1387
- function protocolListsEqual(a, b) {
1388
- const left = [...a ?? []].sort();
1389
- const right = [...b ?? []].sort();
1390
- if (left.length !== right.length) return false;
1391
- return left.every((value, index) => value === right[index]);
1392
- }
1393
- function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect) {
1394
- if (!current) return true;
1395
- if (current.autoSelectProtocols !== desiredAutoSelect) return true;
1396
- if (desiredProtocols === null) return false;
1397
- return !protocolListsEqual(current.protocols, desiredProtocols);
1398
- }
1399
-
1400
- // src/lib/debug.ts
1401
- var configuredDebug = false;
1402
- function setOwneyDebug(enabled) {
1403
- configuredDebug = enabled;
1404
- }
1405
- function isOwneyDebug() {
1406
- return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
1407
- }
1408
- function debugLog(scope, message, data) {
1409
- if (!isOwneyDebug()) return;
1410
- if (data === void 0) {
1411
- console.log(`[${scope}] ${message}`);
1412
- } else {
1413
- console.log(`[${scope}] ${message}`, data);
1414
- }
1415
- }
1416
-
1417
663
  // src/agents/zyfai/zyfai.agent.ts
1418
664
  var DEFAULT_ZYFAI_RPC_URLS = {
1419
665
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
@@ -1426,12 +672,6 @@ var WETH_ADDRESS_BY_CHAIN = {
1426
672
  1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
1427
673
  };
1428
674
  var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum, 1: import_chains.mainnet };
1429
- var SPLITTING_ASSETS = ["USDC", "WETH"];
1430
- var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
1431
- function isAlreadyActiveSessionKeyError(error) {
1432
- const message = error instanceof Error ? error.message : String(error);
1433
- return /already has an active session key/i.test(message);
1434
- }
1435
675
  var ZyfaiAgent = class {
1436
676
  id = "zyfai";
1437
677
  supportedChainIds = [8453, 42161, 1];
@@ -1658,71 +898,21 @@ var ZyfaiAgent = class {
1658
898
  return await this.sdk.connectAccount(provider, chainId);
1659
899
  }
1660
900
  }
1661
- /** Full connect: SIWE auth + Safe deploy check + session key. */
1662
- async connectFull(state, chainId) {
1663
- await this.connectAuth(state, chainId);
1664
- const validChainId = isValidChainId(chainId);
1665
- const address = this.getAddress();
1666
- const wallet = await this.sdk.getSmartWalletAddress(address, validChainId);
1667
- this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
1668
- if (!wallet.isDeployed) {
1669
- await this.sdk.deploySafe(address, validChainId, "aggressive", false);
1670
- try {
1671
- if (validChainId === 42161) {
1672
- await this.sdk.updateUserProfile({
1673
- asset: "WETH",
1674
- chains: [validChainId],
1675
- protocols: ["c24a33fa-4a87-4376-b756-93bd3957d3ea"],
1676
- // AAVE protocol ID
1677
- autoSelectProtocols: false
1678
- });
1679
- }
1680
- } catch (err) {
1681
- console.warn(
1682
- "Failed to configure AAVE protocol after Safe deployment:",
1683
- err instanceof Error ? err.message : String(err)
1684
- );
1685
- }
1686
- }
1687
- await this.ensureSessionKey(state, address, validChainId);
1688
- }
1689
901
  /**
1690
- * Ensures the user has an active session key, creating one if absent.
1691
- *
1692
- * The Zyfai session key is a single **per-user** record, not per-chain: its
1693
- * config (`/session-keys/config`) spans every supported chain and is signed
1694
- * in one shot, and the backend rejects `POST /session-keys/add` with 400
1695
- * "User already has an active session key" if one exists. So we must NOT try
1696
- * to (re)create a key per chain — that 400s and blocks the deposit. We only
1697
- * create when the user has none.
1698
- *
1699
- * The one wrapper-side hardening that IS needed: a chain/account switch can
1700
- * fire the provider's `accountsChanged` → `disconnectAccount`, tearing down
1701
- * the zyfai connection (walletClient + authenticatedUserId both null) while
1702
- * our own `connectAuth` guard still believes this chain is connected and
1703
- * early-returns without reconnecting. `createSessionKey` then fails with
1704
- * "No account connected". Detect that and reconnect first.
1705
- *
1706
- * NOTE: whether a globally-active session key actually authorizes activity on
1707
- * a freshly-deployed Safe on a new chain is a backend/zyf question — the
1708
- * wrapper cannot force per-chain activation given the global 400 guard.
1709
- * 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".
1710
908
  */
1711
- async ensureSessionKey(state, address, chainId) {
909
+ async ensureConnected(state, chainId) {
910
+ await this.connectAuth(state, chainId);
1712
911
  const internals = this.sdk;
1713
912
  if (!internals.walletClient || internals.authenticatedUserId === null) {
1714
913
  this.connectedChainId = null;
1715
914
  await this.connectAuth(state, chainId);
1716
915
  }
1717
- const user = await this.sdk.getUserDetails();
1718
- if (!user.hasActiveSessionKey) {
1719
- try {
1720
- await this.sdk.createSessionKey(address, chainId);
1721
- } catch (error) {
1722
- if (!isAlreadyActiveSessionKeyError(error)) throw error;
1723
- internals.hasActiveSessionKey = true;
1724
- }
1725
- }
1726
916
  }
1727
917
  /**
1728
918
  * Sync newly-available protocols onto an existing user's profile at wallet
@@ -1792,211 +982,10 @@ var ZyfaiAgent = class {
1792
982
  throw error;
1793
983
  }
1794
984
  }
1795
- /**
1796
- * Apply the org's capital splitting policy to this user's Zyfai profile.
1797
- *
1798
- * Per ASSET, not per chain: Zyfai stores splitting under
1799
- * `assetTypeSettings[asset]`, so one call would only configure USDC and leave a
1800
- * WETH holder's real setting untouched.
1801
- *
1802
- * Non-fatal throughout. This is a side effect of connecting, so a Zyfai hiccup
1803
- * must leave the user connected with their profile unchanged, never fail
1804
- * activation.
1805
- */
1806
- async applyOrgPolicy(_state, _chainId, policy) {
1807
- debugLog("zyfai:splitting", "applying org policy", {
1808
- policy,
1809
- // Per asset because Zyfai stores splitting under assetTypeSettings[asset].
1810
- assets: SPLITTING_ASSETS
1811
- });
1812
- await Promise.all(
1813
- SPLITTING_ASSETS.map((asset) => this.applySplitting(asset, policy))
1814
- );
1815
- if (policy.poolPolicy) {
1816
- await this.applyPoolPolicy(policy.poolPolicy);
1817
- }
1818
- }
1819
- async applySplitting(asset, policy) {
1820
- try {
1821
- const desired = toZyfaiSplitting(policy);
1822
- const current = await this.sdk.getUserDetails(asset);
1823
- const currentSplitting = {
1824
- splitting: current?.splitting,
1825
- minSplits: current?.minSplits
1826
- };
1827
- const needsUpdate = splittingNeedsUpdate(current, desired);
1828
- debugLog("zyfai:splitting", `${asset}: comparing`, {
1829
- mode: policy.splittingMode,
1830
- current: currentSplitting,
1831
- desired,
1832
- // Spelled out because the "why" is the non-obvious part: minSplits is
1833
- // what separates automatic (1) from force (2-4), not `splitting`.
1834
- decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
1835
- });
1836
- if (!needsUpdate) return;
1837
- const result = await this.sdk.updateUserProfile({ asset, ...desired });
1838
- debugLog("zyfai:splitting", `${asset}: wrote updateUserProfile`, {
1839
- sent: { asset, ...desired },
1840
- // updateUserProfile re-reads the profile after PATCHing, so this is
1841
- // Zyfai's own view of the result rather than an echo of what we sent.
1842
- zyfaiNowReports: {
1843
- splitting: result?.splitting,
1844
- minSplits: result?.minSplits
1845
- }
1846
- });
1847
- } catch (error) {
1848
- console.warn(
1849
- `[zyfai] applyOrgPolicy(${asset}) failed (non-fatal):`,
1850
- error instanceof Error ? error.message : String(error)
1851
- );
1852
- }
1853
- }
1854
- /**
1855
- * Apply the org's protocol/pool policy to this user's Zyfai profile.
1856
- *
1857
- * Two Zyfai writes, in a strict order:
1858
- * 1. `updateUserProfile({ protocols, autoSelectProtocols })` per asset —
1859
- * the explicit allow-list. Chain-agnostic, mirrors `applySplitting`.
1860
- * 2. `customizeBatch(...)` once — per (protocol, chain), asset-agnostic
1861
- * pool narrowing.
1862
- *
1863
- * Order matters: constraining a protocol's pools (step 2) while that
1864
- * protocol is not yet in the allow-list (step 1 not yet applied) lands the
1865
- * constraint on something the rebalance engine isn't considering.
1866
- *
1867
- * Wrapped so a failure anywhere is non-fatal — this runs as a side effect of
1868
- * connecting, so a Zyfai hiccup must leave the user connected with their
1869
- * profile unchanged, never fail activation.
1870
- */
1871
- async applyPoolPolicy(policy) {
1872
- try {
1873
- const { protocols: universe } = await this.sdk.getAvailableProtocols(
1874
- this.supportedChainIds[0]
1875
- );
1876
- const universeIds = universe.map((p) => p.id);
1877
- const chainsByProtocol = buildChainsByProtocol(
1878
- universe,
1879
- POOL_POLICY_CHAIN_IDS
1880
- );
1881
- debugLog("zyfai:pools", "protocol universe", {
1882
- universeIds,
1883
- chainsByProtocol
1884
- });
1885
- let strategy;
1886
- const desiredProtocols = deriveProtocols(
1887
- policy,
1888
- universeIds,
1889
- chainsByProtocol
1890
- );
1891
- const desiredAutoSelect = policy.autoApproveProtocols;
1892
- for (const asset of SPLITTING_ASSETS) {
1893
- const current = await this.sdk.getUserDetails(asset);
1894
- if (strategy === void 0 && (current.strategy === "conservative" || current.strategy === "aggressive")) {
1895
- strategy = current.strategy;
1896
- }
1897
- const needsUpdate = protocolsPolicyNeedsUpdate(
1898
- current,
1899
- desiredProtocols,
1900
- desiredAutoSelect
1901
- );
1902
- debugLog("zyfai:pools", `${asset}: comparing protocols`, {
1903
- current: {
1904
- protocols: current.protocols,
1905
- autoSelectProtocols: current.autoSelectProtocols
1906
- },
1907
- desired: {
1908
- protocols: desiredProtocols,
1909
- autoSelectProtocols: desiredAutoSelect
1910
- },
1911
- decision: needsUpdate ? "will write \u2014 profile differs from the org policy" : "skipping \u2014 profile already matches"
1912
- });
1913
- if (!needsUpdate) continue;
1914
- const update = {
1915
- asset,
1916
- autoSelectProtocols: desiredAutoSelect
1917
- };
1918
- if (desiredProtocols !== null) update.protocols = desiredProtocols;
1919
- await this.sdk.updateUserProfile(update);
1920
- debugLog("zyfai:pools", `${asset}: wrote protocols update`, update);
1921
- }
1922
- const poolResults = await Promise.all(
1923
- universe.map(async (protocol) => {
1924
- try {
1925
- const raw = await this.sdk.getAvailablePools(protocol.id, strategy);
1926
- return [protocol.id, flattenAvailablePools(raw)];
1927
- } catch (error) {
1928
- console.warn(
1929
- `[zyfai] applyPoolPolicy: getAvailablePools(${protocol.id}) failed, skipping this protocol (non-fatal):`,
1930
- error instanceof Error ? error.message : String(error)
1931
- );
1932
- return null;
1933
- }
1934
- })
1935
- );
1936
- const availableByProtocol = /* @__PURE__ */ new Map();
1937
- for (const result of poolResults) {
1938
- if (result) availableByProtocol.set(result[0], result[1]);
1939
- }
1940
- const customizations = toZyfaiCustomizations(
1941
- policy,
1942
- availableByProtocol,
1943
- chainsByProtocol
1944
- );
1945
- debugLog("zyfai:pools", "derived customizeBatch payload", {
1946
- strategy,
1947
- availableProtocolCount: availableByProtocol.size,
1948
- customizations
1949
- });
1950
- if (customizations.length === 0) {
1951
- debugLog("zyfai:pools", "skipping customizeBatch \u2014 nothing to send");
1952
- return;
1953
- }
1954
- await this.sdk.customizeBatch(customizations);
1955
- debugLog("zyfai:pools", "wrote customizeBatch", {
1956
- count: customizations.length
1957
- });
1958
- } catch (error) {
1959
- console.warn(
1960
- "[zyfai] applyPoolPolicy failed (non-fatal):",
1961
- error instanceof Error ? error.message : String(error)
1962
- );
1963
- }
1964
- }
1965
- /**
1966
- * Records the deposit with Zyfai, retrying transient failures.
1967
- *
1968
- * This runs AFTER the transfer has already landed on-chain, so it must never
1969
- * fail the deposit — the funds moved. But it is also the ONLY source of the
1970
- * "Top up wallet" entry the history is built from: Zyfai auto-deploys the
1971
- * balance it detects either way, so when this call is lost the user's deposit
1972
- * never appears in Activity (an earlier withdrawal stays the newest row) and
1973
- * nothing ever backfills it. Retry, then log loudly enough to be recoverable.
1974
- */
1975
- async logDepositWithRetry(chainId, txHash, amount, tokenAddress) {
1976
- const ATTEMPTS = 3;
1977
- const RETRY_DELAY_MS = 1e3;
1978
- for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
1979
- try {
1980
- await (tokenAddress ? this.sdk.logDeposit(chainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(chainId, txHash, amount));
1981
- return;
1982
- } catch (logError) {
1983
- if (attempt === ATTEMPTS) {
1984
- console.error(
1985
- "[owney-sdk] Deposit landed on-chain but logDeposit failed \u2014 it will be missing from Zyfai history:",
1986
- { txHash, chainId, amount, tokenAddress, error: logError }
1987
- );
1988
- return;
1989
- }
1990
- await new Promise(
1991
- (resolve) => setTimeout(resolve, RETRY_DELAY_MS * attempt)
1992
- );
1993
- }
1994
- }
1995
- }
1996
985
  async deposit(state, chainId, amount, asset, depositCallback) {
1997
986
  try {
1998
- await this.connectFull(state, chainId);
1999
987
  const validChainId = isValidChainId(chainId);
988
+ await this.ensureConnected(state, validChainId);
2000
989
  if (depositCallback) {
2001
990
  const wallet = await this.sdk.getSmartWalletAddress(
2002
991
  this.getAddress(),
@@ -2008,19 +997,22 @@ var ZyfaiAgent = class {
2008
997
  amount
2009
998
  );
2010
999
  const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
2011
- await this.logDepositWithRetry(
2012
- validChainId,
2013
- txHash,
2014
- amount,
2015
- tokenAddress
2016
- );
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
+ }
2017
1008
  return { txHash, smartWallet: wallet.address, amount };
2018
1009
  }
2019
1010
  const raw = await this.sdk.depositFunds(
2020
1011
  this.getAddress(),
2021
1012
  validChainId,
2022
1013
  amount,
2023
- asset
1014
+ asset,
1015
+ "aggressive"
2024
1016
  );
2025
1017
  return mapDeposit(raw);
2026
1018
  } catch (error) {
@@ -2029,8 +1021,8 @@ var ZyfaiAgent = class {
2029
1021
  }
2030
1022
  // --- IAgent: Fund operations ---
2031
1023
  async withdraw(state, chainId, token, amount) {
2032
- await this.connectFull(state, chainId);
2033
1024
  const validChainId = isValidChainId(chainId);
1025
+ await this.ensureConnected(state, validChainId);
2034
1026
  const raw = await this.sdk.withdrawFunds(
2035
1027
  this.getAddress(),
2036
1028
  validChainId,
@@ -2109,19 +1101,7 @@ var ZyfaiAgent = class {
2109
1101
  }
2110
1102
  async getUserProfile(state, chainId) {
2111
1103
  await this.connectAuth(state, chainId);
2112
- const validChainId = isValidChainId(chainId);
2113
1104
  const raw = await this.sdk.getUserDetails();
2114
- debugLog("zyfai:profile", "getUserProfile \u2014 Zyfai reports", {
2115
- asset: "USDC (default \u2014 no asset passed)",
2116
- splitting: raw.splitting,
2117
- minSplits: raw.minSplits,
2118
- strategy: raw.strategy,
2119
- chains: raw.chains,
2120
- protocolCount: raw.protocols?.length,
2121
- hasActiveSessionKey: raw.hasActiveSessionKey,
2122
- smartWallet: raw.smartWallet
2123
- });
2124
- await this.ensureSessionKey(state, this.getAddress(), validChainId);
2125
1105
  return mapUserProfile(raw, this.connectedAddress);
2126
1106
  }
2127
1107
  async ensureAutoSelectProtocols(state, chainId, asset) {
@@ -2152,46 +1132,8 @@ var ZyfaiAgent = class {
2152
1132
  }
2153
1133
  };
2154
1134
 
2155
- // src/client.ts
2156
- init_errors();
2157
-
2158
1135
  // src/lib/routing-api.ts
2159
- init_errors();
2160
1136
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2161
- async function fetchOrgAgentConfig(apiKey) {
2162
- const url = `${ROUTING_API_BASE_URL}/api/v1/agent/org-config`;
2163
- try {
2164
- const res = await fetch(url, {
2165
- method: "GET",
2166
- headers: {
2167
- "Content-Type": "application/json",
2168
- "x-owney-api-key": `${apiKey}`
2169
- }
2170
- });
2171
- if (!res.ok) {
2172
- if (res.status !== 404) {
2173
- console.warn(
2174
- `[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
2175
- );
2176
- }
2177
- return null;
2178
- }
2179
- const json = await res.json();
2180
- const policy = json.success ? json.data ?? null : null;
2181
- debugLog(
2182
- "owney-sdk",
2183
- policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
2184
- policy ?? void 0
2185
- );
2186
- return policy;
2187
- } catch (error) {
2188
- console.warn(
2189
- "[owney-sdk] Could not read org agent config (non-fatal):",
2190
- error instanceof Error ? error.message : String(error)
2191
- );
2192
- return null;
2193
- }
2194
- }
2195
1137
  async function fetchAgentKeys(apiKey) {
2196
1138
  const url = `${ROUTING_API_BASE_URL}/api/v1/agent/keys`;
2197
1139
  const res = await fetch(url, {
@@ -2221,7 +1163,6 @@ async function fetchAgentKeys(apiKey) {
2221
1163
  }
2222
1164
 
2223
1165
  // src/lib/health-report.ts
2224
- init_errors();
2225
1166
  var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2226
1167
  async function reportAgentFailure(apiKey, agentType, errorCode) {
2227
1168
  try {
@@ -2257,10 +1198,11 @@ async function withFailureReporting(apiKey, agentType, fn) {
2257
1198
  // src/lib/helpers/withdraw-helper.ts
2258
1199
  var import_viem2 = require("viem");
2259
1200
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
1201
+ const target = asset.toUpperCase();
2260
1202
  return agents.map((agent) => {
2261
1203
  const agentBalance = aggregated[agent.id];
2262
1204
  const tokenBalance = agentBalance?.tokens.find(
2263
- (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
1205
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
2264
1206
  );
2265
1207
  if (!tokenBalance) return { agent, balance: 0n };
2266
1208
  return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
@@ -2370,11 +1312,8 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
2370
1312
  }
2371
1313
 
2372
1314
  // src/client.ts
2373
- var import_viem6 = require("viem");
2374
- var import_chains3 = require("viem/chains");
2375
-
2376
- // src/lib/sponsored-deposit.ts
2377
- init_errors();
1315
+ var import_viem5 = require("viem");
1316
+ var import_chains2 = require("viem/chains");
2378
1317
 
2379
1318
  // src/lib/transfer-auth.ts
2380
1319
  var import_viem3 = require("viem");
@@ -2413,13 +1352,12 @@ function randomAuthNonce() {
2413
1352
  }
2414
1353
 
2415
1354
  // src/lib/sponsor-client.ts
2416
- init_errors();
2417
1355
  var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
2418
1356
  async function postSponsorTransferAuth(input) {
2419
- const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1357
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2420
1358
  let res;
2421
1359
  try {
2422
- res = await fetch(`${base4}/api/v1/sponsor/erc20-transfer-auth`, {
1360
+ res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
2423
1361
  method: "POST",
2424
1362
  headers: {
2425
1363
  "content-type": "application/json",
@@ -2456,10 +1394,10 @@ async function postSponsorTransferAuth(input) {
2456
1394
  return parsed.data;
2457
1395
  }
2458
1396
  async function postSponsorPermit2Transfer(input) {
2459
- const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1397
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2460
1398
  let res;
2461
1399
  try {
2462
- res = await fetch(`${base4}/api/v1/sponsor/permit2-transfer`, {
1400
+ res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
2463
1401
  method: "POST",
2464
1402
  headers: {
2465
1403
  "content-type": "application/json",
@@ -2494,11 +1432,11 @@ async function postSponsorPermit2Transfer(input) {
2494
1432
  return parsed.data;
2495
1433
  }
2496
1434
  async function getSponsorRelayerAddress(input) {
2497
- const base4 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1435
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
2498
1436
  let res;
2499
1437
  try {
2500
1438
  res = await fetch(
2501
- `${base4}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
1439
+ `${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2502
1440
  {
2503
1441
  headers: { "x-owney-api-key": input.apiKey }
2504
1442
  }
@@ -2530,8 +1468,85 @@ async function getSponsorRelayerAddress(input) {
2530
1468
  return parsed.data.relayer;
2531
1469
  }
2532
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
+
2533
1549
  // src/lib/sponsored-deposit.ts
2534
- init_permit2();
2535
1550
  var AUTH_WINDOW_SECONDS = 15 * 60;
2536
1551
  function makeSponsoredDepositCallback(deps) {
2537
1552
  const post = deps.httpPost ?? postSponsorTransferAuth;
@@ -2605,8 +1620,6 @@ function makeSponsoredDepositCallback(deps) {
2605
1620
  }
2606
1621
 
2607
1622
  // src/lib/sponsored-weth-deposit.ts
2608
- init_errors();
2609
- init_permit2();
2610
1623
  var PERMIT_WINDOW_SECONDS = 15 * 60;
2611
1624
  function makeSponsoredWethCallback(deps) {
2612
1625
  const get = deps.httpGet ?? getSponsorRelayerAddress;
@@ -2685,7 +1698,6 @@ function makeSponsoredWethCallback(deps) {
2685
1698
  }
2686
1699
 
2687
1700
  // src/client.ts
2688
- init_permit2();
2689
1701
  function encodeMultiAgentCursor(map) {
2690
1702
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
2691
1703
  }
@@ -2712,11 +1724,10 @@ var SPONSORED_USDC_BY_CHAIN = {
2712
1724
  1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
2713
1725
  };
2714
1726
  var VIEM_CHAIN2 = {
2715
- 8453: import_chains3.base,
2716
- 42161: import_chains3.arbitrum,
2717
- 1: import_chains3.mainnet
1727
+ 8453: import_chains2.base,
1728
+ 42161: import_chains2.arbitrum,
1729
+ 1: import_chains2.mainnet
2718
1730
  };
2719
- var AGENT_ELIGIBILITY_ORDER = ["surfliquid", "zyfai", "sail"];
2720
1731
  var SPONSORED_WETH_BY_CHAIN = {
2721
1732
  8453: "0x4200000000000000000000000000000000000006",
2722
1733
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -2736,26 +1747,17 @@ var OwneySDK = class {
2736
1747
  disabledAgents = /* @__PURE__ */ new Set();
2737
1748
  state = null;
2738
1749
  apiKey;
2739
- // The org's agent policy, fetched once per client. Applied to each agent on
2740
- // activation (ROUT-229). `undefined` means "not fetched yet", `null` means
2741
- // "fetched, and the partner has no policy" — which is a real answer meaning
2742
- // leave every user's agent profile alone.
2743
- orgAgentConfig;
2744
- orgAgentConfigPromise = null;
2745
1750
  zyfaiRpcUrls;
2746
1751
  routingApiBaseUrl;
2747
1752
  referralSource;
2748
- surfliquidApiBaseUrl;
2749
1753
  cachedSponsoredCallback = null;
2750
1754
  cachedWethSponsoredCallback = null;
2751
1755
  initializingAgentsPromise = null;
2752
1756
  constructor(config) {
2753
1757
  this.apiKey = config.apiKey;
2754
- if (config.debug) setOwneyDebug(true);
2755
1758
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
2756
1759
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2757
1760
  this.referralSource = config.referralSource;
2758
- this.surfliquidApiBaseUrl = config.surfliquidApiBaseUrl;
2759
1761
  }
2760
1762
  /**
2761
1763
  * Establish connection state for all subsequent SDK calls.
@@ -2853,14 +1855,14 @@ var OwneySDK = class {
2853
1855
  // Casts work around viem's chain-narrowed Client vs the generic
2854
1856
  // PublicClient/WalletClient param types — structurally identical at
2855
1857
  // runtime, but the two share a name TS treats as unrelated.
2856
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
1858
+ getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
2857
1859
  chain: VIEM_CHAIN2[cid],
2858
- transport: (0, import_viem6.custom)(provider)
1860
+ transport: (0, import_viem5.custom)(provider)
2859
1861
  }),
2860
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
1862
+ getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
2861
1863
  account: owner,
2862
1864
  chain: VIEM_CHAIN2[cid],
2863
- transport: (0, import_viem6.custom)(provider)
1865
+ transport: (0, import_viem5.custom)(provider)
2864
1866
  })
2865
1867
  });
2866
1868
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -2886,14 +1888,14 @@ var OwneySDK = class {
2886
1888
  // Casts work around viem's chain-narrowed Client vs the generic
2887
1889
  // PublicClient/WalletClient param types — structurally identical at
2888
1890
  // runtime, but the two share a name TS treats as unrelated.
2889
- getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
1891
+ getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
2890
1892
  chain: VIEM_CHAIN2[cid],
2891
- transport: (0, import_viem6.custom)(provider)
1893
+ transport: (0, import_viem5.custom)(provider)
2892
1894
  }),
2893
- getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
1895
+ getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
2894
1896
  account: owner,
2895
1897
  chain: VIEM_CHAIN2[cid],
2896
- transport: (0, import_viem6.custom)(provider)
1898
+ transport: (0, import_viem5.custom)(provider)
2897
1899
  })
2898
1900
  });
2899
1901
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -2914,57 +1916,14 @@ var OwneySDK = class {
2914
1916
  }
2915
1917
  return active;
2916
1918
  }
2917
- /**
2918
- * Read the org's agent policy once and reuse it. fetchOrgAgentConfig never
2919
- * throws — it degrades to null — so this cannot break activation.
2920
- */
2921
- async getOrgAgentConfig() {
2922
- if (this.orgAgentConfig !== void 0) return this.orgAgentConfig;
2923
- if (!this.orgAgentConfigPromise) {
2924
- this.orgAgentConfigPromise = fetchOrgAgentConfig(this.apiKey).then(
2925
- (config) => {
2926
- this.orgAgentConfig = config;
2927
- return config;
2928
- }
2929
- );
2930
- }
2931
- return this.orgAgentConfigPromise;
2932
- }
2933
- /**
2934
- * Apply the org policy to one agent, after it has activated.
2935
- *
2936
- * Sequenced after activation rather than alongside it because applying the
2937
- * policy needs the authenticated session activation establishes. Every failure
2938
- * mode is swallowed: a partner's splitting preference is not worth failing a
2939
- * user's connect over.
2940
- */
2941
- async applyOrgPolicyTo(agent, state, chainId) {
2942
- if (!agent.applyOrgPolicy) {
2943
- debugLog(
2944
- "owney-sdk",
2945
- `${agent.id}: no applyOrgPolicy \u2014 agent has no org-level knobs, skipping`
2946
- );
2947
- return;
2948
- }
2949
- try {
2950
- const policy = await this.getOrgAgentConfig();
2951
- if (!policy) return;
2952
- await agent.applyOrgPolicy(state, chainId, policy);
2953
- } catch (error) {
2954
- console.warn(
2955
- `[owney-sdk] applyOrgPolicy(${agent.id}) failed (non-fatal):`,
2956
- error instanceof Error ? error.message : String(error)
2957
- );
2958
- }
2959
- }
2960
1919
  async ensureAgentsInitialized() {
2961
1920
  if (this.agents.size > 0) return;
2962
1921
  if (!this.initializingAgentsPromise) {
2963
1922
  this.initializingAgentsPromise = (async () => {
2964
1923
  const agentKeys = await fetchAgentKeys(this.apiKey);
2965
1924
  this.disabledAgents.clear();
2966
- for (const { key: key2, agent_type, project_name, is_enabled } of agentKeys) {
2967
- 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);
2968
1927
  if (!agent) continue;
2969
1928
  this.agents.set(agent_type, agent);
2970
1929
  if (is_enabled === false) {
@@ -2985,21 +1944,9 @@ var OwneySDK = class {
2985
1944
  this.initializingAgentsPromise = null;
2986
1945
  }
2987
1946
  }
2988
- async createAgent(agentId, key2, projectName) {
1947
+ createAgent(agentId, key) {
2989
1948
  if (agentId === "zyfai") {
2990
- return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
2991
- }
2992
- if (agentId === "surfliquid") {
2993
- if (!projectName) return null;
2994
- const { SurfLiquidAgent: SurfLiquidAgent2 } = await Promise.resolve().then(() => (init_surfliquid_agent(), surfliquid_agent_exports));
2995
- return new SurfLiquidAgent2({
2996
- projectName,
2997
- appId: key2,
2998
- // Same-origin proxy override — SurfLiquid auth is an httpOnly cookie,
2999
- // which WebKit (iOS) discards when it is third-party. See
3000
- // OwneySDKConfig.surfliquidApiBaseUrl.
3001
- apiBaseUrl: this.surfliquidApiBaseUrl
3002
- });
1949
+ return new ZyfaiAgent(key, this.zyfaiRpcUrls, this.referralSource);
3003
1950
  }
3004
1951
  return null;
3005
1952
  }
@@ -3053,7 +2000,6 @@ var OwneySDK = class {
3053
2000
  Promise.all(
3054
2001
  agents.map(async (agent) => {
3055
2002
  await agent.activateAgent(state, chainId);
3056
- await this.applyOrgPolicyTo(agent, state, chainId);
3057
2003
  })
3058
2004
  ).catch((error) => {
3059
2005
  console.error("activateAgent background init failed:", error);
@@ -3081,7 +2027,6 @@ var OwneySDK = class {
3081
2027
  Promise.all(
3082
2028
  enabledCompatible.map(async (agent) => {
3083
2029
  await agent.activateAgent(state, chainId);
3084
- await this.applyOrgPolicyTo(agent, state, chainId);
3085
2030
  })
3086
2031
  ).catch((error) => {
3087
2032
  console.error("activateAgent background init failed:", error);
@@ -3155,30 +2100,18 @@ var OwneySDK = class {
3155
2100
  );
3156
2101
  }
3157
2102
  const agentResults = {};
3158
- const agentErrors = {};
3159
2103
  for (const { agent, amount: agentAmount } of agentAmounts) {
3160
- try {
3161
- agentResults[agent.id] = await this.depositWithFallback(
3162
- agent,
3163
- state,
3164
- chainId,
3165
- agentAmount,
3166
- asset,
3167
- effectiveCallback,
3168
- depositCallback
3169
- );
3170
- } catch (error) {
3171
- agentErrors[agent.id] = error instanceof Error ? error.message : String(error);
3172
- }
3173
- }
3174
- if (Object.keys(agentResults).length === 0) {
3175
- throw new OwneyError(
3176
- "DEPOSIT_ALL_FAILED",
3177
- `All ${agentAmounts.length} agent deposit(s) failed.`,
3178
- { agentErrors }
2104
+ agentResults[agent.id] = await this.depositWithFallback(
2105
+ agent,
2106
+ state,
2107
+ chainId,
2108
+ agentAmount,
2109
+ asset,
2110
+ effectiveCallback,
2111
+ depositCallback
3179
2112
  );
3180
2113
  }
3181
- return Object.keys(agentErrors).length > 0 ? { agentResults, agentErrors } : { agentResults };
2114
+ return { agentResults };
3182
2115
  }
3183
2116
  /**
3184
2117
  * Invokes `agent.deposit` with the resolved sponsored callback, composing
@@ -3294,8 +2227,9 @@ var OwneySDK = class {
3294
2227
  async hasExistingBalance(agent, state, chainId, asset) {
3295
2228
  try {
3296
2229
  const balance = await agent.getBalances(state, chainId);
2230
+ const target = asset.toLowerCase();
3297
2231
  const token = balance.tokens.find(
3298
- (t) => t.chainId === chainId && isSameAsset(t.asset, asset)
2232
+ (t) => t.chainId === chainId && t.asset.toLowerCase() === target
3299
2233
  );
3300
2234
  return !!token && Number(token.amount) > 0;
3301
2235
  } catch {
@@ -3334,34 +2268,6 @@ var OwneySDK = class {
3334
2268
  }
3335
2269
  return eligible;
3336
2270
  }
3337
- /**
3338
- * Agent ids the routing API provisioned for this org that support the given
3339
- * chain + asset, ordered by preference ({@link AGENT_ELIGIBILITY_ORDER},
3340
- * surfliquid first). Returns `[]` when the org has no compatible agent — never
3341
- * throws on an empty org. Loads agent keys on first call (apiKey only, no
3342
- * wallet), so the UI can resolve which agent to use before the user connects.
3343
- *
3344
- * This is the source of truth for agent availability: an agent appears here
3345
- * iff the routing API returned its key. No per-app feature flags.
3346
- */
3347
- async getEligibleAgentIds(chainId, asset) {
3348
- try {
3349
- await this.ensureAgentsInitialized();
3350
- } catch (error) {
3351
- if (error instanceof OwneyError && error.code === "API_NO_AGENTS") {
3352
- return [];
3353
- }
3354
- throw error;
3355
- }
3356
- return [...this.agents.values()].filter((agent) => {
3357
- const chainAssets = agent.supportedAssets.find(
3358
- (sa) => sa.chainId === chainId
3359
- );
3360
- return chainAssets?.assets.some((a) => a.symbol === asset) ?? false;
3361
- }).map((agent) => agent.id).sort(
3362
- (a, b) => AGENT_ELIGIBILITY_ORDER.indexOf(a) - AGENT_ELIGIBILITY_ORDER.indexOf(b)
3363
- );
3364
- }
3365
2271
  // --- Fund operations ---
3366
2272
  /**
3367
2273
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
@@ -3397,17 +2303,9 @@ var OwneySDK = class {
3397
2303
  }
3398
2304
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
3399
2305
  if (!amount) {
3400
- const aggregated2 = await this.getBalances();
3401
- const funded = projectAgentBalancesForAsset(
3402
- eligibleAgents,
3403
- aggregated2.agentBalances,
3404
- chainId,
3405
- asset,
3406
- assetInfo.decimals
3407
- ).filter((b) => b.balance > 0n);
3408
2306
  const results2 = {};
3409
2307
  const agentErrors2 = {};
3410
- for (const { agent } of funded) {
2308
+ for (const agent of eligibleAgents) {
3411
2309
  try {
3412
2310
  results2[agent.id] = await agent.withdraw(state, chainId, token);
3413
2311
  } catch (err) {
@@ -3424,10 +2322,9 @@ var OwneySDK = class {
3424
2322
  { asset, agentErrors: agentErrors2 }
3425
2323
  );
3426
2324
  }
3427
- const totalWithdrawn = funded.filter(({ agent }) => results2[agent.id] !== void 0).reduce((sum, { balance }) => sum + balance, 0n);
3428
2325
  return {
3429
2326
  agentResult: results2,
3430
- totalWithdrawn: totalWithdrawn.toString()
2327
+ totalWithdrawn: sumWithdrawnAmount(results2).toString()
3431
2328
  };
3432
2329
  }
3433
2330
  const requested = BigInt(amount);
@@ -3797,10 +2694,10 @@ var OwneySDK = class {
3797
2694
  );
3798
2695
  }
3799
2696
  const provider = this.requireConnectedProvider();
3800
- const wallet = (0, import_viem6.createWalletClient)({
2697
+ const wallet = (0, import_viem5.createWalletClient)({
3801
2698
  account: state.walletAddress,
3802
2699
  chain: VIEM_CHAIN2[chainId],
3803
- transport: (0, import_viem6.custom)(provider)
2700
+ transport: (0, import_viem5.custom)(provider)
3804
2701
  });
3805
2702
  const hash = await wallet.writeContract({
3806
2703
  address: token,
@@ -3810,9 +2707,9 @@ var OwneySDK = class {
3810
2707
  account: state.walletAddress,
3811
2708
  chain: VIEM_CHAIN2[chainId]
3812
2709
  });
3813
- const publicClient = (0, import_viem6.createPublicClient)({
2710
+ const publicClient = (0, import_viem5.createPublicClient)({
3814
2711
  chain: VIEM_CHAIN2[chainId],
3815
- transport: (0, import_viem6.custom)(provider)
2712
+ transport: (0, import_viem5.custom)(provider)
3816
2713
  });
3817
2714
  const receipt = await publicClient.waitForTransactionReceipt({
3818
2715
  hash,
@@ -3920,11 +2817,8 @@ var OwneySDK = class {
3920
2817
  }
3921
2818
  };
3922
2819
 
3923
- // src/index.ts
3924
- init_errors();
3925
-
3926
2820
  // src/agents/zyfai/zyfai.siwx.ts
3927
- var import_viem7 = require("viem");
2821
+ var import_viem6 = require("viem");
3928
2822
  var import_siwe = require("siwe");
3929
2823
  var import_sdk2 = require("@zyfai/sdk");
3930
2824
 
@@ -3947,22 +2841,22 @@ var readLegacySiwxSession = (store, address) => {
3947
2841
  const legacyKeys = [];
3948
2842
  try {
3949
2843
  for (let i = 0; i < store.length; i++) {
3950
- const key2 = store.key(i);
3951
- if (key2?.startsWith(prefix)) legacyKeys.push(key2);
2844
+ const key = store.key(i);
2845
+ if (key?.startsWith(prefix)) legacyKeys.push(key);
3952
2846
  }
3953
2847
  } catch {
3954
2848
  return null;
3955
2849
  }
3956
2850
  let adopted = null;
3957
- for (const key2 of legacyKeys) {
2851
+ for (const key of legacyKeys) {
3958
2852
  if (!adopted) {
3959
2853
  try {
3960
- adopted = JSON.parse(store.getItem(key2) ?? "");
2854
+ adopted = JSON.parse(store.getItem(key) ?? "");
3961
2855
  } catch {
3962
2856
  }
3963
2857
  }
3964
2858
  try {
3965
- store.removeItem(key2);
2859
+ store.removeItem(key);
3966
2860
  } catch {
3967
2861
  }
3968
2862
  }
@@ -3970,11 +2864,11 @@ var readLegacySiwxSession = (store, address) => {
3970
2864
  };
3971
2865
  var readSiwxSession = (address, chainId) => {
3972
2866
  if (typeof window === "undefined") return null;
3973
- const key2 = buildKey2(address);
2867
+ const key = buildKey2(address);
3974
2868
  const store = storage2();
3975
2869
  let raw = null;
3976
2870
  try {
3977
- raw = store?.getItem(key2) ?? null;
2871
+ raw = store?.getItem(key) ?? null;
3978
2872
  } catch {
3979
2873
  raw = null;
3980
2874
  }
@@ -3982,9 +2876,9 @@ var readSiwxSession = (address, chainId) => {
3982
2876
  try {
3983
2877
  return JSON.parse(raw);
3984
2878
  } catch {
3985
- memorySiwxSessions.delete(key2);
2879
+ memorySiwxSessions.delete(key);
3986
2880
  try {
3987
- store?.removeItem(key2);
2881
+ store?.removeItem(key);
3988
2882
  } catch {
3989
2883
  }
3990
2884
  return null;
@@ -3995,24 +2889,24 @@ var readSiwxSession = (address, chainId) => {
3995
2889
  writeSiwxSession(address, chainId, legacy);
3996
2890
  return legacy;
3997
2891
  }
3998
- return memorySiwxSessions.get(key2) ?? null;
2892
+ return memorySiwxSessions.get(key) ?? null;
3999
2893
  };
4000
2894
  var writeSiwxSession = (address, _chainId, session) => {
4001
2895
  if (typeof window === "undefined") return;
4002
- const key2 = buildKey2(address);
4003
- memorySiwxSessions.set(key2, session);
2896
+ const key = buildKey2(address);
2897
+ memorySiwxSessions.set(key, session);
4004
2898
  const store = storage2();
4005
2899
  try {
4006
- store?.setItem(key2, JSON.stringify(session));
2900
+ store?.setItem(key, JSON.stringify(session));
4007
2901
  } catch {
4008
2902
  }
4009
2903
  };
4010
2904
  var clearSiwxSession = (address, _chainId) => {
4011
- const key2 = buildKey2(address);
4012
- memorySiwxSessions.delete(key2);
2905
+ const key = buildKey2(address);
2906
+ memorySiwxSessions.delete(key);
4013
2907
  const store = storage2();
4014
2908
  try {
4015
- store?.removeItem(key2);
2909
+ store?.removeItem(key);
4016
2910
  } catch {
4017
2911
  }
4018
2912
  readLegacySiwxSession(store, address);
@@ -4051,7 +2945,7 @@ function buildSIWXConfig(deps) {
4051
2945
  issuedAt,
4052
2946
  toString() {
4053
2947
  return new import_siwe.SiweMessage({
4054
- address: (0, import_viem7.getAddress)(accountAddress),
2948
+ address: (0, import_viem6.getAddress)(accountAddress),
4055
2949
  chainId: numericChainId(chainId),
4056
2950
  domain,
4057
2951
  uri,
@@ -4142,6 +3036,5 @@ function createOwneySIWX(config) {
4142
3036
  NotConnectedError,
4143
3037
  OwneyError,
4144
3038
  OwneySDK,
4145
- createOwneySIWX,
4146
- setOwneyDebug
3039
+ createOwneySIWX
4147
3040
  });