@owney/sdk 0.7.7-beta.4 → 0.7.7-beta.5

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
@@ -217,32 +217,6 @@ function resolveChainId(chain) {
217
217
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
218
218
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
219
219
  }
220
- function mapPendingAllocations(raw) {
221
- if (!Array.isArray(raw)) return void 0;
222
- const pending = [];
223
- for (const entry of raw) {
224
- if (typeof entry !== "object" || entry === null) continue;
225
- const e = entry;
226
- if (e.isPending !== true) continue;
227
- if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
228
- if (typeof e.tokenSymbol !== "string" || e.tokenSymbol === "") continue;
229
- if (typeof e.balance !== "string") continue;
230
- let amount;
231
- try {
232
- amount = hexToDecimal(e.balance, tokenDecimals(e.tokenSymbol));
233
- } catch {
234
- continue;
235
- }
236
- if (!Number.isFinite(amount) || amount <= 0) continue;
237
- pending.push({
238
- chainId: e.chainId,
239
- asset: e.tokenSymbol,
240
- amount: String(amount),
241
- ...typeof e.updatedAt === "string" && e.updatedAt !== "" ? { since: e.updatedAt } : {}
242
- });
243
- }
244
- return pending.length > 0 ? pending : void 0;
245
- }
246
220
  function mapBalances(raw, _chainId, smartWallet) {
247
221
  const portfolio = raw.portfolio;
248
222
  const portfolioByChain = portfolio.portfolioByChain ?? {};
@@ -294,16 +268,12 @@ function mapBalances(raw, _chainId, smartWallet) {
294
268
  // structural read keeps it undefined today and auto-populates later.
295
269
  liquidity: p.liquidity
296
270
  }));
297
- const pendingAllocations = mapPendingAllocations(
298
- portfolio.staleBalances
299
- );
300
271
  return {
301
272
  smartWallet,
302
273
  totalBalance: String(totalBalance),
303
274
  totalBalanceAsset: "usdc",
304
275
  tokens,
305
- positions,
306
- ...pendingAllocations ? { pendingAllocations } : {}
276
+ positions
307
277
  };
308
278
  }
309
279
  function sumTokenValues(tokens) {
@@ -909,12 +879,6 @@ var ZyfaiAgent = class {
909
879
  connectedAddress = null;
910
880
  connectedChainId = null;
911
881
  smartWalletCache = /* @__PURE__ */ new Map();
912
- /**
913
- * `${eoa}:${chainId}` keys we have already tried createSessionKey for. Guards
914
- * against re-running it on every activation/deposit when Zyfai's
915
- * `hasActiveSessionKey` does not flip to true — see ensureSessionKey.
916
- */
917
- sessionKeyAttempts = /* @__PURE__ */ new Set();
918
882
  connectAuthPromise = null;
919
883
  rpcUrls;
920
884
  publicClients = /* @__PURE__ */ new Map();
@@ -1372,74 +1336,10 @@ var ZyfaiAgent = class {
1372
1336
  * recoverable (ensureWalletDeployed tries the other deploy call), but
1373
1337
  * failing the deposit outright is not.
1374
1338
  */
1375
- /**
1376
- * Zyfai's own answer to "what kind of wallet is this, and can the agent act on
1377
- * it", read from the positions payload (`GET /data/position`, which
1378
- * `getPositions` wraps).
1379
- *
1380
- * `predeployed` and `hasActiveSessionKey` are both returned at runtime but
1381
- * declared on neither `Portfolio` nor their docs, hence the cast. Verified
1382
- * live: an old wallet reports `{predeployed: false, hasActiveSessionKey:
1383
- * false}` while a pool wallet reports `{true, true}`.
1384
- *
1385
- * Preferred over the SDK's private `isPredeployed`, which is cached from the
1386
- * login response and can go stale mid-session. Returns an empty object on any
1387
- * failure so every caller degrades to its own fallback.
1388
- */
1389
- async readWalletState(ownerAddress) {
1390
- try {
1391
- const { portfolio } = await this.sdk.getPositions(ownerAddress);
1392
- const raw = portfolio;
1393
- debugLog("zyfai:onboard", "wallet state from getPositions", {
1394
- predeployed: raw?.predeployed,
1395
- hasActiveSessionKey: raw?.hasActiveSessionKey
1396
- });
1397
- return {
1398
- predeployed: raw?.predeployed,
1399
- hasActiveSessionKey: raw?.hasActiveSessionKey
1400
- };
1401
- } catch (error) {
1402
- console.warn(
1403
- "[zyfai] getPositions wallet-state read failed (non-fatal):",
1404
- error instanceof Error ? error.message : String(error)
1405
- );
1406
- return {};
1407
- }
1408
- }
1409
1339
  async isPredeployedPoolWallet(ownerAddress, smartWallet) {
1410
- const { predeployed } = await this.readWalletState(ownerAddress);
1411
- if (typeof predeployed === "boolean") {
1412
- debugLog(
1413
- "zyfai:onboard",
1414
- `wallet class: ${predeployed ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
1415
- { source: "getPositions.predeployed", predeployed }
1416
- );
1417
- return predeployed;
1418
- }
1419
- const internals = this.sdk;
1420
- if (typeof internals.isPredeployed === "boolean") {
1421
- debugLog(
1422
- "zyfai:onboard",
1423
- `wallet class: ${internals.isPredeployed ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
1424
- {
1425
- source: "login response (cached)",
1426
- predeployed: internals.isPredeployed
1427
- }
1428
- );
1429
- return internals.isPredeployed;
1430
- }
1431
1340
  try {
1432
1341
  const byEoa = await this.sdk.getSmartWalletByEOA(ownerAddress);
1433
- const isPool = typeof byEoa?.smartWallet === "string" && byEoa.smartWallet.toLowerCase() === smartWallet.toLowerCase();
1434
- debugLog(
1435
- "zyfai:onboard",
1436
- `wallet class: ${isPool ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
1437
- {
1438
- source: "getSmartWalletByEOA fallback",
1439
- poolRecord: byEoa?.smartWallet
1440
- }
1441
- );
1442
- return isPool;
1342
+ return typeof byEoa?.smartWallet === "string" && byEoa.smartWallet.toLowerCase() === smartWallet.toLowerCase();
1443
1343
  } catch (error) {
1444
1344
  console.warn(
1445
1345
  "[zyfai] getSmartWalletByEOA failed; assuming a legacy wallet:",
@@ -1477,24 +1377,8 @@ var ZyfaiAgent = class {
1477
1377
  ownerAddress,
1478
1378
  chainId
1479
1379
  );
1480
- debugLog("zyfai:onboard", `activation check on chain ${chainId}`, {
1481
- smartWallet: wallet.address,
1482
- isDeployed: wallet.isDeployed
1483
- });
1484
- if (!wallet.isDeployed) {
1485
- debugLog(
1486
- "zyfai:onboard",
1487
- "Safe not deployed on this chain; activation does nothing (deposit owns deploys)"
1488
- );
1489
- return;
1490
- }
1491
- const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
1492
- await this.ensureSessionKey(
1493
- ownerAddress,
1494
- chainId,
1495
- wallet.address,
1496
- hasActiveSessionKey
1497
- );
1380
+ if (!wallet.isDeployed) return;
1381
+ await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
1498
1382
  } catch (error) {
1499
1383
  console.warn(
1500
1384
  "[zyfai] session-key check at activation failed (non-fatal):",
@@ -1502,51 +1386,20 @@ var ZyfaiAgent = class {
1502
1386
  );
1503
1387
  }
1504
1388
  }
1505
- async ensureSessionKey(ownerAddress, chainId, smartWallet, hasActiveSessionKey) {
1506
- const attemptKey = `${ownerAddress.toLowerCase()}:${chainId}`;
1507
- if (this.sessionKeyAttempts.has(attemptKey)) {
1508
- debugLog(
1509
- "zyfai:onboard",
1510
- "session key: already attempted this session, skipping",
1511
- { chainId }
1512
- );
1513
- return;
1514
- }
1389
+ async ensureSessionKey(ownerAddress, chainId, smartWallet) {
1515
1390
  try {
1516
- if (hasActiveSessionKey === true) {
1517
- debugLog(
1518
- "zyfai:onboard",
1519
- "session key: already on record -> nothing to do",
1520
- { chainId }
1521
- );
1522
- return;
1523
- }
1524
- if (hasActiveSessionKey === void 0) {
1525
- const installed = await this.readSmartSessionsInstalled(
1526
- chainId,
1527
- smartWallet
1528
- );
1529
- debugLog(
1530
- "zyfai:onboard",
1531
- `session key: Zyfai state unavailable; on-chain validator ${installed ? "INSTALLED" : "MISSING"}`,
1532
- { smartWallet, chainId }
1533
- );
1534
- if (installed) return;
1535
- }
1536
- this.sessionKeyAttempts.add(attemptKey);
1391
+ const installed = await this.getPublicClient(chainId).readContract({
1392
+ address: smartWallet,
1393
+ abi: ERC7579_IS_MODULE_INSTALLED_ABI,
1394
+ functionName: "isModuleInstalled",
1395
+ // moduleTypeId 1 = validator (ERC-7579)
1396
+ args: [1n, import_sdk.SMART_SESSIONS_VALIDATOR, "0x"]
1397
+ });
1398
+ if (installed) return;
1537
1399
  console.warn(
1538
- `[zyfai] no session key for ${smartWallet} on chain ${chainId}; creating one so the agent can allocate.`
1400
+ `[zyfai] Safe ${smartWallet} has no smart-sessions validator on chain ${chainId}; creating a session key so the agent can allocate.`
1539
1401
  );
1540
- debugLog("zyfai:onboard", "session key: calling createSessionKey", {
1541
- chainId,
1542
- smartWallet,
1543
- hasActiveSessionKey
1544
- });
1545
1402
  await this.sdk.createSessionKey(ownerAddress, chainId);
1546
- debugLog("zyfai:onboard", "session key: createSessionKey succeeded", {
1547
- chainId,
1548
- smartWallet
1549
- });
1550
1403
  } catch (error) {
1551
1404
  console.warn(
1552
1405
  "[zyfai] createSessionKey skipped (non-fatal):",
@@ -1554,16 +1407,6 @@ var ZyfaiAgent = class {
1554
1407
  );
1555
1408
  }
1556
1409
  }
1557
- /** Ground truth for "can Zyfai's automation act on this Safe, on this chain". */
1558
- async readSmartSessionsInstalled(chainId, smartWallet) {
1559
- return this.getPublicClient(chainId).readContract({
1560
- address: smartWallet,
1561
- abi: ERC7579_IS_MODULE_INSTALLED_ABI,
1562
- functionName: "isModuleInstalled",
1563
- // moduleTypeId 1 = validator (ERC-7579)
1564
- args: [1n, import_sdk.SMART_SESSIONS_VALIDATOR, "0x"]
1565
- });
1566
- }
1567
1410
  /**
1568
1411
  * Guarantee the user's Safe has code on `chainId` before anything transfers
1569
1412
  * funds into it, and return its address.
@@ -1584,24 +1427,8 @@ var ZyfaiAgent = class {
1584
1427
  */
1585
1428
  async ensureWalletDeployed(ownerAddress, chainId) {
1586
1429
  const wallet = await this.sdk.getSmartWalletAddress(ownerAddress, chainId);
1587
- debugLog("zyfai:onboard", `--- deposit onboarding, chain ${chainId} ---`, {
1588
- eoa: ownerAddress,
1589
- smartWallet: wallet.address,
1590
- isDeployed: wallet.isDeployed,
1591
- isOwner: wallet.isOwner
1592
- });
1593
1430
  if (wallet.isDeployed) {
1594
- debugLog(
1595
- "zyfai:onboard",
1596
- "Safe already has code -> no deploy needed, checking the session key"
1597
- );
1598
- const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
1599
- await this.ensureSessionKey(
1600
- ownerAddress,
1601
- chainId,
1602
- wallet.address,
1603
- hasActiveSessionKey
1604
- );
1431
+ await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
1605
1432
  return wallet.address;
1606
1433
  }
1607
1434
  const poolFirst = await this.isPredeployedPoolWallet(
@@ -1617,27 +1444,14 @@ var ZyfaiAgent = class {
1617
1444
  this.sdk.deploySafe(ownerAddress, chainId, "aggressive", true)
1618
1445
  );
1619
1446
  const [first, second] = poolFirst ? [deployAsPool, deployAsLegacy] : [deployAsLegacy, deployAsPool];
1620
- debugLog(
1621
- "zyfai:onboard",
1622
- `Safe has no code on chain ${chainId} -> deploying via ${poolFirst ? "deployOnChains (pool path)" : "deploySafe(createSessionKey=true) (legacy path)"}`
1623
- );
1624
1447
  try {
1625
1448
  await first();
1626
- debugLog(
1627
- "zyfai:onboard",
1628
- `${poolFirst ? "deployOnChains" : "deploySafe"} succeeded`
1629
- );
1630
1449
  } catch (error) {
1631
1450
  console.warn(
1632
1451
  `[zyfai] ${poolFirst ? "deployOnChains" : "deploySafe"} failed on chain ${chainId}; trying the other deploy path:`,
1633
1452
  error instanceof Error ? error.message : String(error)
1634
1453
  );
1635
- debugLog(
1636
- "zyfai:onboard",
1637
- `falling back to ${poolFirst ? "deploySafe (legacy path)" : "deployOnChains (pool path)"}`
1638
- );
1639
1454
  await second();
1640
- debugLog("zyfai:onboard", "fallback deploy path succeeded");
1641
1455
  }
1642
1456
  const redeployed = await this.sdk.getSmartWalletAddress(
1643
1457
  ownerAddress,
@@ -2025,7 +1839,7 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
2025
1839
  }
2026
1840
 
2027
1841
  // src/client.ts
2028
- var import_viem5 = require("viem");
1842
+ var import_viem6 = require("viem");
2029
1843
  var import_chains2 = require("viem/chains");
2030
1844
 
2031
1845
  // src/lib/transfer-auth.ts
@@ -2455,6 +2269,97 @@ function makeSponsoredWethCallback(deps) {
2455
2269
  };
2456
2270
  }
2457
2271
 
2272
+ // src/lib/sponsored-calls-deposit.ts
2273
+ var import_viem5 = require("viem");
2274
+ var DEFAULT_POLL_INTERVAL_MS = 1500;
2275
+ var DEFAULT_MAX_POLLS = 30;
2276
+ async function paymasterSupported(provider, owner, chainId) {
2277
+ const caps = await provider.request({
2278
+ method: "wallet_getCapabilities",
2279
+ params: [owner]
2280
+ });
2281
+ const forChain = caps?.[(0, import_viem5.toHex)(chainId)] ?? caps?.[String(chainId)];
2282
+ return Boolean(forChain?.paymasterService?.supported);
2283
+ }
2284
+ function makeSponsoredCallsCallback(deps) {
2285
+ const pollIntervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
2286
+ const maxPolls = deps.maxPolls ?? DEFAULT_MAX_POLLS;
2287
+ const absolutePaymasterUrl = () => {
2288
+ const configured = deps.paymasterServiceUrl;
2289
+ if (/^https?:\/\//i.test(configured)) return configured;
2290
+ const origin = globalThis.location?.origin;
2291
+ if (!origin) {
2292
+ throw new OwneyError(
2293
+ "SPONSORED_CALLS_UNSUPPORTED",
2294
+ `paymasterServiceUrl "${configured}" is relative and no page origin is available to resolve it.`
2295
+ );
2296
+ }
2297
+ return new URL(configured, origin).toString();
2298
+ };
2299
+ return async (smartWallet, chainId, amount) => {
2300
+ const cid = chainId;
2301
+ const token = deps.tokenAddressByChain[cid];
2302
+ if (!token) {
2303
+ throw new OwneyError(
2304
+ "CHAIN_UNSUPPORTED",
2305
+ `No sponsored token configured for chain ${chainId}`
2306
+ );
2307
+ }
2308
+ if (!await paymasterSupported(deps.provider, deps.ownerAddress, chainId)) {
2309
+ throw new OwneyError(
2310
+ "SPONSORED_CALLS_UNSUPPORTED",
2311
+ `Wallet does not advertise paymasterService on chain ${chainId}.`,
2312
+ { chainId }
2313
+ );
2314
+ }
2315
+ const data = (0, import_viem5.encodeFunctionData)({
2316
+ abi: import_viem5.erc20Abi,
2317
+ functionName: "transfer",
2318
+ args: [smartWallet, BigInt(amount)]
2319
+ });
2320
+ const sendResult = await deps.provider.request({
2321
+ method: "wallet_sendCalls",
2322
+ params: [
2323
+ {
2324
+ version: "2.0.0",
2325
+ from: deps.ownerAddress,
2326
+ chainId: (0, import_viem5.toHex)(chainId),
2327
+ atomicRequired: false,
2328
+ calls: [{ to: token, value: "0x0", data }],
2329
+ capabilities: {
2330
+ paymasterService: { url: absolutePaymasterUrl() }
2331
+ }
2332
+ }
2333
+ ]
2334
+ });
2335
+ deps.onApproved?.();
2336
+ const callsId = typeof sendResult === "string" ? sendResult : sendResult?.id;
2337
+ if (!callsId) {
2338
+ throw new OwneyError(
2339
+ "SPONSORED_CALLS_NO_ID",
2340
+ "Wallet accepted wallet_sendCalls but returned no calls id.",
2341
+ { chainId }
2342
+ );
2343
+ }
2344
+ for (let attempt = 0; attempt < maxPolls; attempt += 1) {
2345
+ const status = await deps.provider.request({
2346
+ method: "wallet_getCallsStatus",
2347
+ params: [callsId]
2348
+ });
2349
+ const txHash = status?.receipts?.[0]?.transactionHash;
2350
+ if (txHash) return txHash;
2351
+ if (pollIntervalMs > 0) {
2352
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
2353
+ }
2354
+ }
2355
+ throw new OwneyError(
2356
+ "SPONSORED_CALLS_NO_RECEIPT",
2357
+ `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
2358
+ { chainId, callsId }
2359
+ );
2360
+ };
2361
+ }
2362
+
2458
2363
  // src/client.ts
2459
2364
  function encodeMultiAgentCursor(map) {
2460
2365
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
@@ -2516,12 +2421,15 @@ var OwneySDK = class {
2516
2421
  referralSource;
2517
2422
  cachedSponsoredCallback = null;
2518
2423
  cachedWethSponsoredCallback = null;
2424
+ paymasterServiceUrl;
2425
+ cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
2519
2426
  initializingAgentsPromise = null;
2520
2427
  constructor(config) {
2521
2428
  this.apiKey = config.apiKey;
2522
2429
  if (config.debug) setOwneyDebug(true);
2523
2430
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
2524
2431
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2432
+ this.paymasterServiceUrl = config.paymasterServiceUrl;
2525
2433
  this.referralSource = config.referralSource;
2526
2434
  }
2527
2435
  /**
@@ -2544,6 +2452,7 @@ var OwneySDK = class {
2544
2452
  this.state = { provider, walletAddress, chainId: null };
2545
2453
  this.cachedSponsoredCallback = null;
2546
2454
  this.cachedWethSponsoredCallback = null;
2455
+ this.cachedSponsoredCallsCallbacks.clear();
2547
2456
  }
2548
2457
  /**
2549
2458
  * Disconnect from all agents and clear connection state.
@@ -2558,6 +2467,7 @@ var OwneySDK = class {
2558
2467
  this.state = null;
2559
2468
  this.cachedSponsoredCallback = null;
2560
2469
  this.cachedWethSponsoredCallback = null;
2470
+ this.cachedSponsoredCallsCallbacks.clear();
2561
2471
  }
2562
2472
  isAgentDisabled(agentId) {
2563
2473
  return this.disabledAgents.has(agentId);
@@ -2620,14 +2530,14 @@ var OwneySDK = class {
2620
2530
  // Casts work around viem's chain-narrowed Client vs the generic
2621
2531
  // PublicClient/WalletClient param types — structurally identical at
2622
2532
  // runtime, but the two share a name TS treats as unrelated.
2623
- getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
2533
+ getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
2624
2534
  chain: VIEM_CHAIN2[cid],
2625
- transport: (0, import_viem5.custom)(provider)
2535
+ transport: (0, import_viem6.custom)(provider)
2626
2536
  }),
2627
- getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
2537
+ getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
2628
2538
  account: owner,
2629
2539
  chain: VIEM_CHAIN2[cid],
2630
- transport: (0, import_viem5.custom)(provider)
2540
+ transport: (0, import_viem6.custom)(provider)
2631
2541
  })
2632
2542
  });
2633
2543
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -2639,6 +2549,26 @@ var OwneySDK = class {
2639
2549
  * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
2640
2550
  * `PermitTransferFrom` instead of an EIP-3009 authorization.
2641
2551
  */
2552
+ getDefaultSponsoredCallsCallback(asset, onApproved) {
2553
+ const cached = this.cachedSponsoredCallsCallbacks.get(asset);
2554
+ if (!onApproved && cached) return cached;
2555
+ const provider = this.requireConnectedProvider();
2556
+ const callback = makeSponsoredCallsCallback({
2557
+ provider,
2558
+ ownerAddress: this.state.walletAddress,
2559
+ paymasterServiceUrl: this.paymasterServiceUrl,
2560
+ onApproved,
2561
+ tokenAddressByChain: asset === "WETH" ? SPONSORED_WETH_BY_CHAIN : SPONSORED_USDC_BY_CHAIN
2562
+ });
2563
+ if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
2564
+ return callback;
2565
+ }
2566
+ /**
2567
+ * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
2568
+ * callback used when the caller omits `depositCallback` for a WETH deposit.
2569
+ * Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
2570
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
2571
+ */
2642
2572
  getDefaultWethSponsoredCallback(onApproved) {
2643
2573
  if (!onApproved && this.cachedWethSponsoredCallback)
2644
2574
  return this.cachedWethSponsoredCallback;
@@ -2653,14 +2583,14 @@ var OwneySDK = class {
2653
2583
  // Casts work around viem's chain-narrowed Client vs the generic
2654
2584
  // PublicClient/WalletClient param types — structurally identical at
2655
2585
  // runtime, but the two share a name TS treats as unrelated.
2656
- getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
2586
+ getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
2657
2587
  chain: VIEM_CHAIN2[cid],
2658
- transport: (0, import_viem5.custom)(provider)
2588
+ transport: (0, import_viem6.custom)(provider)
2659
2589
  }),
2660
- getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
2590
+ getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
2661
2591
  account: owner,
2662
2592
  chain: VIEM_CHAIN2[cid],
2663
- transport: (0, import_viem5.custom)(provider)
2593
+ transport: (0, import_viem6.custom)(provider)
2664
2594
  })
2665
2595
  });
2666
2596
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -2861,7 +2791,7 @@ var OwneySDK = class {
2861
2791
  }
2862
2792
  const state = this.requireState();
2863
2793
  const chainId = this.requireChainId();
2864
- const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
2794
+ const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
2865
2795
  if (agentId) {
2866
2796
  if (this.isAgentDisabled(agentId)) {
2867
2797
  throw new OwneyError(
@@ -3504,10 +3434,10 @@ var OwneySDK = class {
3504
3434
  );
3505
3435
  }
3506
3436
  const provider = this.requireConnectedProvider();
3507
- const wallet = (0, import_viem5.createWalletClient)({
3437
+ const wallet = (0, import_viem6.createWalletClient)({
3508
3438
  account: state.walletAddress,
3509
3439
  chain: VIEM_CHAIN2[chainId],
3510
- transport: (0, import_viem5.custom)(provider)
3440
+ transport: (0, import_viem6.custom)(provider)
3511
3441
  });
3512
3442
  const hash = await wallet.writeContract({
3513
3443
  address: token,
@@ -3517,9 +3447,9 @@ var OwneySDK = class {
3517
3447
  account: state.walletAddress,
3518
3448
  chain: VIEM_CHAIN2[chainId]
3519
3449
  });
3520
- const publicClient = (0, import_viem5.createPublicClient)({
3450
+ const publicClient = (0, import_viem6.createPublicClient)({
3521
3451
  chain: VIEM_CHAIN2[chainId],
3522
- transport: (0, import_viem5.custom)(provider)
3452
+ transport: (0, import_viem6.custom)(provider)
3523
3453
  });
3524
3454
  const receipt = await publicClient.waitForTransactionReceipt({
3525
3455
  hash,
@@ -3628,7 +3558,7 @@ var OwneySDK = class {
3628
3558
  };
3629
3559
 
3630
3560
  // src/agents/zyfai/zyfai.siwx.ts
3631
- var import_viem6 = require("viem");
3561
+ var import_viem7 = require("viem");
3632
3562
  var import_siwe = require("siwe");
3633
3563
  var import_sdk2 = require("@zyfai/sdk");
3634
3564
 
@@ -3755,7 +3685,7 @@ function buildSIWXConfig(deps) {
3755
3685
  issuedAt,
3756
3686
  toString() {
3757
3687
  return new import_siwe.SiweMessage({
3758
- address: (0, import_viem6.getAddress)(accountAddress),
3688
+ address: (0, import_viem7.getAddress)(accountAddress),
3759
3689
  chainId: numericChainId(chainId),
3760
3690
  domain,
3761
3691
  uri,
package/dist/index.d.cts CHANGED
@@ -30,6 +30,17 @@ interface OwneySDKConfig {
30
30
  * already-deployed app: set `globalThis.__OWNEY_DEBUG__ = true` in devtools.
31
31
  */
32
32
  debug?: boolean;
33
+ /**
34
+ * Absolute URL of an ERC-7677 paymaster service. When set, deposits go out as
35
+ * an EIP-5792 `wallet_sendCalls` with `capabilities.paymasterService`, so a
36
+ * wallet that advertises sponsorship pays no native gas — the case EIP-3009
37
+ * cannot serve, because USDC verifies it with `ecrecover` and a smart account
38
+ * signs via ERC-1271.
39
+ *
40
+ * MUST be absolute: the wallet fetches this URL itself, so a relative path is
41
+ * unresolvable. Omit it and every rail behaves exactly as before.
42
+ */
43
+ paymasterServiceUrl?: string;
33
44
  }
34
45
  interface ConnectionState {
35
46
  provider: any;
@@ -180,21 +191,6 @@ interface OwneyPosition {
180
191
  * response later; undefined until then. */
181
192
  liquidity?: number;
182
193
  }
183
- /**
184
- * Funds that have landed in the agent's wallet but are not yet deployed into a
185
- * protocol. Sourced from Zyfai `staleBalances[].isPending`. Absent when nothing
186
- * is pending, and for agents that do not report it (sail).
187
- */
188
- interface OwneyPendingAllocation {
189
- /** Deliberately loose (not OwneySupportedChainId) because Zyfai reports balances on unsupported chains. */
190
- chainId: number;
191
- /** Raw agent symbol, not normalized to OwneySupportedTokens; carries Zyfai's casing and "eth" alias for WETH. */
192
- asset: string;
193
- /** Decimal string, consistent with `OwneyToken.amount`. */
194
- amount: string;
195
- /** ISO timestamp from Zyfai's `updatedAt`, when the payload carries one. */
196
- since?: string;
197
- }
198
194
  interface AgentBalance {
199
195
  smartWallet?: `0x${string}`;
200
196
  totalBalance: string;
@@ -206,8 +202,6 @@ interface AgentBalance {
206
202
  * them (zyfai). Omitted for agents that don't expose positions (sail).
207
203
  */
208
204
  positions?: OwneyPosition[];
209
- /** Funds received but not yet deployed. Omitted when nothing is pending. */
210
- pendingAllocations?: OwneyPendingAllocation[];
211
205
  }
212
206
  interface OwneyBalances {
213
207
  totalBalance: string;
@@ -410,6 +404,8 @@ declare class OwneySDK {
410
404
  private referralSource?;
411
405
  private cachedSponsoredCallback;
412
406
  private cachedWethSponsoredCallback;
407
+ private paymasterServiceUrl?;
408
+ private cachedSponsoredCallsCallbacks;
413
409
  private initializingAgentsPromise;
414
410
  constructor(config: OwneySDKConfig);
415
411
  /**
@@ -450,6 +446,13 @@ declare class OwneySDK {
450
446
  * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
451
447
  * `PermitTransferFrom` instead of an EIP-3009 authorization.
452
448
  */
449
+ private getDefaultSponsoredCallsCallback;
450
+ /**
451
+ * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
452
+ * callback used when the caller omits `depositCallback` for a WETH deposit.
453
+ * Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
454
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
455
+ */
453
456
  private getDefaultWethSponsoredCallback;
454
457
  private getAgent;
455
458
  private getActiveAgents;
@@ -626,7 +629,7 @@ declare class OwneySDK {
626
629
  getAllocationApy({ agentId, }?: AllocationApyOptions): Promise<OwneyAllocationApy>;
627
630
  }
628
631
 
629
- type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "WALLET_NOT_DEPLOYED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "CHAIN_MISMATCH" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
632
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "WALLET_NOT_DEPLOYED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "CHAIN_MISMATCH" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "SPONSORED_CALLS_UNSUPPORTED" | "SPONSORED_CALLS_NO_ID" | "SPONSORED_CALLS_NO_RECEIPT" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
630
633
  declare class OwneyError extends Error {
631
634
  readonly code: OwneyErrorCode;
632
635
  readonly details?: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -30,6 +30,17 @@ interface OwneySDKConfig {
30
30
  * already-deployed app: set `globalThis.__OWNEY_DEBUG__ = true` in devtools.
31
31
  */
32
32
  debug?: boolean;
33
+ /**
34
+ * Absolute URL of an ERC-7677 paymaster service. When set, deposits go out as
35
+ * an EIP-5792 `wallet_sendCalls` with `capabilities.paymasterService`, so a
36
+ * wallet that advertises sponsorship pays no native gas — the case EIP-3009
37
+ * cannot serve, because USDC verifies it with `ecrecover` and a smart account
38
+ * signs via ERC-1271.
39
+ *
40
+ * MUST be absolute: the wallet fetches this URL itself, so a relative path is
41
+ * unresolvable. Omit it and every rail behaves exactly as before.
42
+ */
43
+ paymasterServiceUrl?: string;
33
44
  }
34
45
  interface ConnectionState {
35
46
  provider: any;
@@ -180,21 +191,6 @@ interface OwneyPosition {
180
191
  * response later; undefined until then. */
181
192
  liquidity?: number;
182
193
  }
183
- /**
184
- * Funds that have landed in the agent's wallet but are not yet deployed into a
185
- * protocol. Sourced from Zyfai `staleBalances[].isPending`. Absent when nothing
186
- * is pending, and for agents that do not report it (sail).
187
- */
188
- interface OwneyPendingAllocation {
189
- /** Deliberately loose (not OwneySupportedChainId) because Zyfai reports balances on unsupported chains. */
190
- chainId: number;
191
- /** Raw agent symbol, not normalized to OwneySupportedTokens; carries Zyfai's casing and "eth" alias for WETH. */
192
- asset: string;
193
- /** Decimal string, consistent with `OwneyToken.amount`. */
194
- amount: string;
195
- /** ISO timestamp from Zyfai's `updatedAt`, when the payload carries one. */
196
- since?: string;
197
- }
198
194
  interface AgentBalance {
199
195
  smartWallet?: `0x${string}`;
200
196
  totalBalance: string;
@@ -206,8 +202,6 @@ interface AgentBalance {
206
202
  * them (zyfai). Omitted for agents that don't expose positions (sail).
207
203
  */
208
204
  positions?: OwneyPosition[];
209
- /** Funds received but not yet deployed. Omitted when nothing is pending. */
210
- pendingAllocations?: OwneyPendingAllocation[];
211
205
  }
212
206
  interface OwneyBalances {
213
207
  totalBalance: string;
@@ -410,6 +404,8 @@ declare class OwneySDK {
410
404
  private referralSource?;
411
405
  private cachedSponsoredCallback;
412
406
  private cachedWethSponsoredCallback;
407
+ private paymasterServiceUrl?;
408
+ private cachedSponsoredCallsCallbacks;
413
409
  private initializingAgentsPromise;
414
410
  constructor(config: OwneySDKConfig);
415
411
  /**
@@ -450,6 +446,13 @@ declare class OwneySDK {
450
446
  * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
451
447
  * `PermitTransferFrom` instead of an EIP-3009 authorization.
452
448
  */
449
+ private getDefaultSponsoredCallsCallback;
450
+ /**
451
+ * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
452
+ * callback used when the caller omits `depositCallback` for a WETH deposit.
453
+ * Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
454
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
455
+ */
453
456
  private getDefaultWethSponsoredCallback;
454
457
  private getAgent;
455
458
  private getActiveAgents;
@@ -626,7 +629,7 @@ declare class OwneySDK {
626
629
  getAllocationApy({ agentId, }?: AllocationApyOptions): Promise<OwneyAllocationApy>;
627
630
  }
628
631
 
629
- type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "WALLET_NOT_DEPLOYED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "CHAIN_MISMATCH" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
632
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "WALLET_NOT_DEPLOYED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "CHAIN_MISMATCH" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "SPONSORED_CALLS_UNSUPPORTED" | "SPONSORED_CALLS_NO_ID" | "SPONSORED_CALLS_NO_RECEIPT" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
630
633
  declare class OwneyError extends Error {
631
634
  readonly code: OwneyErrorCode;
632
635
  readonly details?: Record<string, unknown>;
package/dist/index.js CHANGED
@@ -184,32 +184,6 @@ function resolveChainId(chain) {
184
184
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
185
185
  return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
186
186
  }
187
- function mapPendingAllocations(raw) {
188
- if (!Array.isArray(raw)) return void 0;
189
- const pending = [];
190
- for (const entry of raw) {
191
- if (typeof entry !== "object" || entry === null) continue;
192
- const e = entry;
193
- if (e.isPending !== true) continue;
194
- if (typeof e.chainId !== "number" || !Number.isFinite(e.chainId)) continue;
195
- if (typeof e.tokenSymbol !== "string" || e.tokenSymbol === "") continue;
196
- if (typeof e.balance !== "string") continue;
197
- let amount;
198
- try {
199
- amount = hexToDecimal(e.balance, tokenDecimals(e.tokenSymbol));
200
- } catch {
201
- continue;
202
- }
203
- if (!Number.isFinite(amount) || amount <= 0) continue;
204
- pending.push({
205
- chainId: e.chainId,
206
- asset: e.tokenSymbol,
207
- amount: String(amount),
208
- ...typeof e.updatedAt === "string" && e.updatedAt !== "" ? { since: e.updatedAt } : {}
209
- });
210
- }
211
- return pending.length > 0 ? pending : void 0;
212
- }
213
187
  function mapBalances(raw, _chainId, smartWallet) {
214
188
  const portfolio = raw.portfolio;
215
189
  const portfolioByChain = portfolio.portfolioByChain ?? {};
@@ -261,16 +235,12 @@ function mapBalances(raw, _chainId, smartWallet) {
261
235
  // structural read keeps it undefined today and auto-populates later.
262
236
  liquidity: p.liquidity
263
237
  }));
264
- const pendingAllocations = mapPendingAllocations(
265
- portfolio.staleBalances
266
- );
267
238
  return {
268
239
  smartWallet,
269
240
  totalBalance: String(totalBalance),
270
241
  totalBalanceAsset: "usdc",
271
242
  tokens,
272
- positions,
273
- ...pendingAllocations ? { pendingAllocations } : {}
243
+ positions
274
244
  };
275
245
  }
276
246
  function sumTokenValues(tokens) {
@@ -876,12 +846,6 @@ var ZyfaiAgent = class {
876
846
  connectedAddress = null;
877
847
  connectedChainId = null;
878
848
  smartWalletCache = /* @__PURE__ */ new Map();
879
- /**
880
- * `${eoa}:${chainId}` keys we have already tried createSessionKey for. Guards
881
- * against re-running it on every activation/deposit when Zyfai's
882
- * `hasActiveSessionKey` does not flip to true — see ensureSessionKey.
883
- */
884
- sessionKeyAttempts = /* @__PURE__ */ new Set();
885
849
  connectAuthPromise = null;
886
850
  rpcUrls;
887
851
  publicClients = /* @__PURE__ */ new Map();
@@ -1339,74 +1303,10 @@ var ZyfaiAgent = class {
1339
1303
  * recoverable (ensureWalletDeployed tries the other deploy call), but
1340
1304
  * failing the deposit outright is not.
1341
1305
  */
1342
- /**
1343
- * Zyfai's own answer to "what kind of wallet is this, and can the agent act on
1344
- * it", read from the positions payload (`GET /data/position`, which
1345
- * `getPositions` wraps).
1346
- *
1347
- * `predeployed` and `hasActiveSessionKey` are both returned at runtime but
1348
- * declared on neither `Portfolio` nor their docs, hence the cast. Verified
1349
- * live: an old wallet reports `{predeployed: false, hasActiveSessionKey:
1350
- * false}` while a pool wallet reports `{true, true}`.
1351
- *
1352
- * Preferred over the SDK's private `isPredeployed`, which is cached from the
1353
- * login response and can go stale mid-session. Returns an empty object on any
1354
- * failure so every caller degrades to its own fallback.
1355
- */
1356
- async readWalletState(ownerAddress) {
1357
- try {
1358
- const { portfolio } = await this.sdk.getPositions(ownerAddress);
1359
- const raw = portfolio;
1360
- debugLog("zyfai:onboard", "wallet state from getPositions", {
1361
- predeployed: raw?.predeployed,
1362
- hasActiveSessionKey: raw?.hasActiveSessionKey
1363
- });
1364
- return {
1365
- predeployed: raw?.predeployed,
1366
- hasActiveSessionKey: raw?.hasActiveSessionKey
1367
- };
1368
- } catch (error) {
1369
- console.warn(
1370
- "[zyfai] getPositions wallet-state read failed (non-fatal):",
1371
- error instanceof Error ? error.message : String(error)
1372
- );
1373
- return {};
1374
- }
1375
- }
1376
1306
  async isPredeployedPoolWallet(ownerAddress, smartWallet) {
1377
- const { predeployed } = await this.readWalletState(ownerAddress);
1378
- if (typeof predeployed === "boolean") {
1379
- debugLog(
1380
- "zyfai:onboard",
1381
- `wallet class: ${predeployed ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
1382
- { source: "getPositions.predeployed", predeployed }
1383
- );
1384
- return predeployed;
1385
- }
1386
- const internals = this.sdk;
1387
- if (typeof internals.isPredeployed === "boolean") {
1388
- debugLog(
1389
- "zyfai:onboard",
1390
- `wallet class: ${internals.isPredeployed ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
1391
- {
1392
- source: "login response (cached)",
1393
- predeployed: internals.isPredeployed
1394
- }
1395
- );
1396
- return internals.isPredeployed;
1397
- }
1398
1307
  try {
1399
1308
  const byEoa = await this.sdk.getSmartWalletByEOA(ownerAddress);
1400
- const isPool = typeof byEoa?.smartWallet === "string" && byEoa.smartWallet.toLowerCase() === smartWallet.toLowerCase();
1401
- debugLog(
1402
- "zyfai:onboard",
1403
- `wallet class: ${isPool ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
1404
- {
1405
- source: "getSmartWalletByEOA fallback",
1406
- poolRecord: byEoa?.smartWallet
1407
- }
1408
- );
1409
- return isPool;
1309
+ return typeof byEoa?.smartWallet === "string" && byEoa.smartWallet.toLowerCase() === smartWallet.toLowerCase();
1410
1310
  } catch (error) {
1411
1311
  console.warn(
1412
1312
  "[zyfai] getSmartWalletByEOA failed; assuming a legacy wallet:",
@@ -1444,24 +1344,8 @@ var ZyfaiAgent = class {
1444
1344
  ownerAddress,
1445
1345
  chainId
1446
1346
  );
1447
- debugLog("zyfai:onboard", `activation check on chain ${chainId}`, {
1448
- smartWallet: wallet.address,
1449
- isDeployed: wallet.isDeployed
1450
- });
1451
- if (!wallet.isDeployed) {
1452
- debugLog(
1453
- "zyfai:onboard",
1454
- "Safe not deployed on this chain; activation does nothing (deposit owns deploys)"
1455
- );
1456
- return;
1457
- }
1458
- const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
1459
- await this.ensureSessionKey(
1460
- ownerAddress,
1461
- chainId,
1462
- wallet.address,
1463
- hasActiveSessionKey
1464
- );
1347
+ if (!wallet.isDeployed) return;
1348
+ await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
1465
1349
  } catch (error) {
1466
1350
  console.warn(
1467
1351
  "[zyfai] session-key check at activation failed (non-fatal):",
@@ -1469,51 +1353,20 @@ var ZyfaiAgent = class {
1469
1353
  );
1470
1354
  }
1471
1355
  }
1472
- async ensureSessionKey(ownerAddress, chainId, smartWallet, hasActiveSessionKey) {
1473
- const attemptKey = `${ownerAddress.toLowerCase()}:${chainId}`;
1474
- if (this.sessionKeyAttempts.has(attemptKey)) {
1475
- debugLog(
1476
- "zyfai:onboard",
1477
- "session key: already attempted this session, skipping",
1478
- { chainId }
1479
- );
1480
- return;
1481
- }
1356
+ async ensureSessionKey(ownerAddress, chainId, smartWallet) {
1482
1357
  try {
1483
- if (hasActiveSessionKey === true) {
1484
- debugLog(
1485
- "zyfai:onboard",
1486
- "session key: already on record -> nothing to do",
1487
- { chainId }
1488
- );
1489
- return;
1490
- }
1491
- if (hasActiveSessionKey === void 0) {
1492
- const installed = await this.readSmartSessionsInstalled(
1493
- chainId,
1494
- smartWallet
1495
- );
1496
- debugLog(
1497
- "zyfai:onboard",
1498
- `session key: Zyfai state unavailable; on-chain validator ${installed ? "INSTALLED" : "MISSING"}`,
1499
- { smartWallet, chainId }
1500
- );
1501
- if (installed) return;
1502
- }
1503
- this.sessionKeyAttempts.add(attemptKey);
1358
+ const installed = await this.getPublicClient(chainId).readContract({
1359
+ address: smartWallet,
1360
+ abi: ERC7579_IS_MODULE_INSTALLED_ABI,
1361
+ functionName: "isModuleInstalled",
1362
+ // moduleTypeId 1 = validator (ERC-7579)
1363
+ args: [1n, SMART_SESSIONS_VALIDATOR, "0x"]
1364
+ });
1365
+ if (installed) return;
1504
1366
  console.warn(
1505
- `[zyfai] no session key for ${smartWallet} on chain ${chainId}; creating one so the agent can allocate.`
1367
+ `[zyfai] Safe ${smartWallet} has no smart-sessions validator on chain ${chainId}; creating a session key so the agent can allocate.`
1506
1368
  );
1507
- debugLog("zyfai:onboard", "session key: calling createSessionKey", {
1508
- chainId,
1509
- smartWallet,
1510
- hasActiveSessionKey
1511
- });
1512
1369
  await this.sdk.createSessionKey(ownerAddress, chainId);
1513
- debugLog("zyfai:onboard", "session key: createSessionKey succeeded", {
1514
- chainId,
1515
- smartWallet
1516
- });
1517
1370
  } catch (error) {
1518
1371
  console.warn(
1519
1372
  "[zyfai] createSessionKey skipped (non-fatal):",
@@ -1521,16 +1374,6 @@ var ZyfaiAgent = class {
1521
1374
  );
1522
1375
  }
1523
1376
  }
1524
- /** Ground truth for "can Zyfai's automation act on this Safe, on this chain". */
1525
- async readSmartSessionsInstalled(chainId, smartWallet) {
1526
- return this.getPublicClient(chainId).readContract({
1527
- address: smartWallet,
1528
- abi: ERC7579_IS_MODULE_INSTALLED_ABI,
1529
- functionName: "isModuleInstalled",
1530
- // moduleTypeId 1 = validator (ERC-7579)
1531
- args: [1n, SMART_SESSIONS_VALIDATOR, "0x"]
1532
- });
1533
- }
1534
1377
  /**
1535
1378
  * Guarantee the user's Safe has code on `chainId` before anything transfers
1536
1379
  * funds into it, and return its address.
@@ -1551,24 +1394,8 @@ var ZyfaiAgent = class {
1551
1394
  */
1552
1395
  async ensureWalletDeployed(ownerAddress, chainId) {
1553
1396
  const wallet = await this.sdk.getSmartWalletAddress(ownerAddress, chainId);
1554
- debugLog("zyfai:onboard", `--- deposit onboarding, chain ${chainId} ---`, {
1555
- eoa: ownerAddress,
1556
- smartWallet: wallet.address,
1557
- isDeployed: wallet.isDeployed,
1558
- isOwner: wallet.isOwner
1559
- });
1560
1397
  if (wallet.isDeployed) {
1561
- debugLog(
1562
- "zyfai:onboard",
1563
- "Safe already has code -> no deploy needed, checking the session key"
1564
- );
1565
- const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
1566
- await this.ensureSessionKey(
1567
- ownerAddress,
1568
- chainId,
1569
- wallet.address,
1570
- hasActiveSessionKey
1571
- );
1398
+ await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
1572
1399
  return wallet.address;
1573
1400
  }
1574
1401
  const poolFirst = await this.isPredeployedPoolWallet(
@@ -1584,27 +1411,14 @@ var ZyfaiAgent = class {
1584
1411
  this.sdk.deploySafe(ownerAddress, chainId, "aggressive", true)
1585
1412
  );
1586
1413
  const [first, second] = poolFirst ? [deployAsPool, deployAsLegacy] : [deployAsLegacy, deployAsPool];
1587
- debugLog(
1588
- "zyfai:onboard",
1589
- `Safe has no code on chain ${chainId} -> deploying via ${poolFirst ? "deployOnChains (pool path)" : "deploySafe(createSessionKey=true) (legacy path)"}`
1590
- );
1591
1414
  try {
1592
1415
  await first();
1593
- debugLog(
1594
- "zyfai:onboard",
1595
- `${poolFirst ? "deployOnChains" : "deploySafe"} succeeded`
1596
- );
1597
1416
  } catch (error) {
1598
1417
  console.warn(
1599
1418
  `[zyfai] ${poolFirst ? "deployOnChains" : "deploySafe"} failed on chain ${chainId}; trying the other deploy path:`,
1600
1419
  error instanceof Error ? error.message : String(error)
1601
1420
  );
1602
- debugLog(
1603
- "zyfai:onboard",
1604
- `falling back to ${poolFirst ? "deploySafe (legacy path)" : "deployOnChains (pool path)"}`
1605
- );
1606
1421
  await second();
1607
- debugLog("zyfai:onboard", "fallback deploy path succeeded");
1608
1422
  }
1609
1423
  const redeployed = await this.sdk.getSmartWalletAddress(
1610
1424
  ownerAddress,
@@ -2426,6 +2240,97 @@ function makeSponsoredWethCallback(deps) {
2426
2240
  };
2427
2241
  }
2428
2242
 
2243
+ // src/lib/sponsored-calls-deposit.ts
2244
+ import { encodeFunctionData, erc20Abi, toHex } from "viem";
2245
+ var DEFAULT_POLL_INTERVAL_MS = 1500;
2246
+ var DEFAULT_MAX_POLLS = 30;
2247
+ async function paymasterSupported(provider, owner, chainId) {
2248
+ const caps = await provider.request({
2249
+ method: "wallet_getCapabilities",
2250
+ params: [owner]
2251
+ });
2252
+ const forChain = caps?.[toHex(chainId)] ?? caps?.[String(chainId)];
2253
+ return Boolean(forChain?.paymasterService?.supported);
2254
+ }
2255
+ function makeSponsoredCallsCallback(deps) {
2256
+ const pollIntervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
2257
+ const maxPolls = deps.maxPolls ?? DEFAULT_MAX_POLLS;
2258
+ const absolutePaymasterUrl = () => {
2259
+ const configured = deps.paymasterServiceUrl;
2260
+ if (/^https?:\/\//i.test(configured)) return configured;
2261
+ const origin = globalThis.location?.origin;
2262
+ if (!origin) {
2263
+ throw new OwneyError(
2264
+ "SPONSORED_CALLS_UNSUPPORTED",
2265
+ `paymasterServiceUrl "${configured}" is relative and no page origin is available to resolve it.`
2266
+ );
2267
+ }
2268
+ return new URL(configured, origin).toString();
2269
+ };
2270
+ return async (smartWallet, chainId, amount) => {
2271
+ const cid = chainId;
2272
+ const token = deps.tokenAddressByChain[cid];
2273
+ if (!token) {
2274
+ throw new OwneyError(
2275
+ "CHAIN_UNSUPPORTED",
2276
+ `No sponsored token configured for chain ${chainId}`
2277
+ );
2278
+ }
2279
+ if (!await paymasterSupported(deps.provider, deps.ownerAddress, chainId)) {
2280
+ throw new OwneyError(
2281
+ "SPONSORED_CALLS_UNSUPPORTED",
2282
+ `Wallet does not advertise paymasterService on chain ${chainId}.`,
2283
+ { chainId }
2284
+ );
2285
+ }
2286
+ const data = encodeFunctionData({
2287
+ abi: erc20Abi,
2288
+ functionName: "transfer",
2289
+ args: [smartWallet, BigInt(amount)]
2290
+ });
2291
+ const sendResult = await deps.provider.request({
2292
+ method: "wallet_sendCalls",
2293
+ params: [
2294
+ {
2295
+ version: "2.0.0",
2296
+ from: deps.ownerAddress,
2297
+ chainId: toHex(chainId),
2298
+ atomicRequired: false,
2299
+ calls: [{ to: token, value: "0x0", data }],
2300
+ capabilities: {
2301
+ paymasterService: { url: absolutePaymasterUrl() }
2302
+ }
2303
+ }
2304
+ ]
2305
+ });
2306
+ deps.onApproved?.();
2307
+ const callsId = typeof sendResult === "string" ? sendResult : sendResult?.id;
2308
+ if (!callsId) {
2309
+ throw new OwneyError(
2310
+ "SPONSORED_CALLS_NO_ID",
2311
+ "Wallet accepted wallet_sendCalls but returned no calls id.",
2312
+ { chainId }
2313
+ );
2314
+ }
2315
+ for (let attempt = 0; attempt < maxPolls; attempt += 1) {
2316
+ const status = await deps.provider.request({
2317
+ method: "wallet_getCallsStatus",
2318
+ params: [callsId]
2319
+ });
2320
+ const txHash = status?.receipts?.[0]?.transactionHash;
2321
+ if (txHash) return txHash;
2322
+ if (pollIntervalMs > 0) {
2323
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
2324
+ }
2325
+ }
2326
+ throw new OwneyError(
2327
+ "SPONSORED_CALLS_NO_RECEIPT",
2328
+ `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
2329
+ { chainId, callsId }
2330
+ );
2331
+ };
2332
+ }
2333
+
2429
2334
  // src/client.ts
2430
2335
  function encodeMultiAgentCursor(map) {
2431
2336
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
@@ -2487,12 +2392,15 @@ var OwneySDK = class {
2487
2392
  referralSource;
2488
2393
  cachedSponsoredCallback = null;
2489
2394
  cachedWethSponsoredCallback = null;
2395
+ paymasterServiceUrl;
2396
+ cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
2490
2397
  initializingAgentsPromise = null;
2491
2398
  constructor(config) {
2492
2399
  this.apiKey = config.apiKey;
2493
2400
  if (config.debug) setOwneyDebug(true);
2494
2401
  this.zyfaiRpcUrls = config.zyfaiRpcUrls;
2495
2402
  this.routingApiBaseUrl = config.routingApiBaseUrl;
2403
+ this.paymasterServiceUrl = config.paymasterServiceUrl;
2496
2404
  this.referralSource = config.referralSource;
2497
2405
  }
2498
2406
  /**
@@ -2515,6 +2423,7 @@ var OwneySDK = class {
2515
2423
  this.state = { provider, walletAddress, chainId: null };
2516
2424
  this.cachedSponsoredCallback = null;
2517
2425
  this.cachedWethSponsoredCallback = null;
2426
+ this.cachedSponsoredCallsCallbacks.clear();
2518
2427
  }
2519
2428
  /**
2520
2429
  * Disconnect from all agents and clear connection state.
@@ -2529,6 +2438,7 @@ var OwneySDK = class {
2529
2438
  this.state = null;
2530
2439
  this.cachedSponsoredCallback = null;
2531
2440
  this.cachedWethSponsoredCallback = null;
2441
+ this.cachedSponsoredCallsCallbacks.clear();
2532
2442
  }
2533
2443
  isAgentDisabled(agentId) {
2534
2444
  return this.disabledAgents.has(agentId);
@@ -2610,6 +2520,26 @@ var OwneySDK = class {
2610
2520
  * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
2611
2521
  * `PermitTransferFrom` instead of an EIP-3009 authorization.
2612
2522
  */
2523
+ getDefaultSponsoredCallsCallback(asset, onApproved) {
2524
+ const cached = this.cachedSponsoredCallsCallbacks.get(asset);
2525
+ if (!onApproved && cached) return cached;
2526
+ const provider = this.requireConnectedProvider();
2527
+ const callback = makeSponsoredCallsCallback({
2528
+ provider,
2529
+ ownerAddress: this.state.walletAddress,
2530
+ paymasterServiceUrl: this.paymasterServiceUrl,
2531
+ onApproved,
2532
+ tokenAddressByChain: asset === "WETH" ? SPONSORED_WETH_BY_CHAIN : SPONSORED_USDC_BY_CHAIN
2533
+ });
2534
+ if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
2535
+ return callback;
2536
+ }
2537
+ /**
2538
+ * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
2539
+ * callback used when the caller omits `depositCallback` for a WETH deposit.
2540
+ * Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
2541
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
2542
+ */
2613
2543
  getDefaultWethSponsoredCallback(onApproved) {
2614
2544
  if (!onApproved && this.cachedWethSponsoredCallback)
2615
2545
  return this.cachedWethSponsoredCallback;
@@ -2832,7 +2762,7 @@ var OwneySDK = class {
2832
2762
  }
2833
2763
  const state = this.requireState();
2834
2764
  const chainId = this.requireChainId();
2835
- const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
2765
+ const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
2836
2766
  if (agentId) {
2837
2767
  if (this.isAgentDisabled(agentId)) {
2838
2768
  throw new OwneyError(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.7-beta.4",
3
+ "version": "0.7.7-beta.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",