@owney/sdk 0.6.4-beta.0 → 0.6.5-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ var P_SUPPORTED_CHAINS = {
8
8
  chainIds: [8453, 42161],
9
9
  chains: ["BASE", "ARBITRUM"]
10
10
  };
11
- var SUPPORTED_TOKENS = ["USDC", "USDT"];
11
+ var SUPPORTED_TOKENS = ["USDC", "USDT", "WETH"];
12
12
  var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
13
13
  var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
14
14
  var SupportedAssets = [
@@ -29,6 +29,18 @@ var SupportedAssets = [
29
29
  symbol: "USDT",
30
30
  chainId: 42161,
31
31
  decimals: 6
32
+ },
33
+ {
34
+ address: `0x4200000000000000000000000000000000000006`,
35
+ symbol: "WETH",
36
+ chainId: 8453,
37
+ decimals: 18
38
+ },
39
+ {
40
+ address: `0x82aF49447D8a07e3bd95BD0d56f35241523fBab1`,
41
+ symbol: "WETH",
42
+ chainId: 42161,
43
+ decimals: 18
32
44
  }
33
45
  ];
34
46
 
@@ -513,11 +525,26 @@ var clearSession = (address, chainId) => {
513
525
  }
514
526
  };
515
527
 
528
+ // src/agents/zyfai/autoSelect.ts
529
+ function buildAutoSelectUpdate(current, asset) {
530
+ if (current.autoSelectProtocols === true)
531
+ return null;
532
+ return {
533
+ asset,
534
+ autoSelectProtocols: true,
535
+ ...current.chains ? { chains: current.chains } : {}
536
+ };
537
+ }
538
+
516
539
  // src/agents/zyfai/zyfai.agent.ts
517
540
  var DEFAULT_ZYFAI_RPC_URLS = {
518
541
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
519
542
  42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
520
543
  };
544
+ var WETH_ADDRESS_BY_CHAIN = {
545
+ 8453: "0x4200000000000000000000000000000000000006",
546
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
547
+ };
521
548
  var VIEM_CHAIN = { 8453: base, 42161: arbitrum };
522
549
  var ZyfaiAgent = class {
523
550
  id = "zyfai";
@@ -526,12 +553,18 @@ var ZyfaiAgent = class {
526
553
  {
527
554
  chainId: 8453,
528
555
  chain: "BASE",
529
- assets: [{ symbol: "USDC", minDepositAmount: "1500000" }]
556
+ assets: [
557
+ { symbol: "USDC", minDepositAmount: "1500000" },
558
+ { symbol: "WETH", minDepositAmount: "500000000000000" }
559
+ ]
530
560
  },
531
561
  {
532
562
  chainId: 42161,
533
563
  chain: "ARBITRUM",
534
- assets: [{ symbol: "USDC", minDepositAmount: "1500000" }]
564
+ assets: [
565
+ { symbol: "USDC", minDepositAmount: "1500000" },
566
+ { symbol: "WETH", minDepositAmount: "500000000000000" }
567
+ ]
535
568
  }
536
569
  ];
537
570
  sdk;
@@ -726,6 +759,22 @@ var ZyfaiAgent = class {
726
759
  this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
727
760
  if (!wallet.isDeployed) {
728
761
  await this.sdk.deploySafe(address, validChainId, "aggressive", true);
762
+ try {
763
+ if (validChainId === 42161) {
764
+ await this.sdk.updateUserProfile({
765
+ asset: "WETH",
766
+ chains: [validChainId],
767
+ protocols: ["c24a33fa-4a87-4376-b756-93bd3957d3ea"],
768
+ // AAVE protocol ID
769
+ autoSelectProtocols: false
770
+ });
771
+ }
772
+ } catch (err) {
773
+ console.warn(
774
+ "Failed to configure AAVE protocol after Safe deployment:",
775
+ err instanceof Error ? err.message : String(err)
776
+ );
777
+ }
729
778
  }
730
779
  const user = await this.sdk.getUserDetails();
731
780
  if (!user.hasActiveSessionKey) {
@@ -767,7 +816,7 @@ var ZyfaiAgent = class {
767
816
  throw error;
768
817
  }
769
818
  }
770
- async deposit(state, chainId, amount, depositCallback) {
819
+ async deposit(state, chainId, amount, asset, depositCallback) {
771
820
  try {
772
821
  await this.connectFull(state, chainId);
773
822
  const validChainId = isValidChainId(chainId);
@@ -781,13 +830,15 @@ var ZyfaiAgent = class {
781
830
  validChainId,
782
831
  amount
783
832
  );
784
- await this.sdk.logDeposit(validChainId, txHash, amount);
833
+ const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
834
+ await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
785
835
  return { txHash, smartWallet: wallet.address, amount };
786
836
  }
787
837
  const raw = await this.sdk.depositFunds(
788
838
  this.getAddress(),
789
839
  validChainId,
790
- amount
840
+ amount,
841
+ asset
791
842
  );
792
843
  return mapDeposit(raw);
793
844
  } catch (error) {
@@ -883,6 +934,21 @@ var ZyfaiAgent = class {
883
934
  }
884
935
  return mapUserProfile(raw, this.connectedAddress);
885
936
  }
937
+ async ensureAutoSelectProtocols(state, chainId, asset) {
938
+ await this.connectAuth(state, chainId);
939
+ const current = await this.sdk.getUserDetails(asset);
940
+ const update = buildAutoSelectUpdate(
941
+ {
942
+ autoSelectProtocols: current.autoSelectProtocols,
943
+ chains: current.chains
944
+ },
945
+ asset
946
+ );
947
+ if (!update)
948
+ return false;
949
+ await this.sdk.updateUserProfile(update);
950
+ return true;
951
+ }
886
952
  // --- IAgent: Discovery (no wallet required) ---
887
953
  async getAgentApy(days, options) {
888
954
  const raw = await this.sdk.getAPYPerStrategy(
@@ -1162,6 +1228,70 @@ async function postSponsorTransferAuth(input) {
1162
1228
  }
1163
1229
  return parsed.data;
1164
1230
  }
1231
+ async function postSponsorPermit2Transfer(input) {
1232
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1233
+ let res;
1234
+ try {
1235
+ res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
1236
+ method: "POST",
1237
+ headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
1238
+ body: JSON.stringify(input.body)
1239
+ });
1240
+ } catch (networkError) {
1241
+ throw new OwneyError(
1242
+ "SPONSOR_REQUEST_FAILED",
1243
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1244
+ { cause: String(networkError), safeToFallback: false }
1245
+ );
1246
+ }
1247
+ const text = await res.text();
1248
+ let parsed = null;
1249
+ try {
1250
+ parsed = JSON.parse(text);
1251
+ } catch {
1252
+ }
1253
+ if (!res.ok || !parsed?.success || !parsed.data) {
1254
+ throw new OwneyError(
1255
+ "SPONSOR_REQUEST_FAILED",
1256
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1257
+ {
1258
+ statusCode: res.status,
1259
+ responseBody: text.slice(0, 500),
1260
+ safeToFallback: res.status >= 400 && res.status < 500
1261
+ }
1262
+ );
1263
+ }
1264
+ return parsed.data;
1265
+ }
1266
+ async function getSponsorRelayerAddress(input) {
1267
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1268
+ let res;
1269
+ try {
1270
+ res = await fetch(`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`, {
1271
+ headers: { "x-owney-api-key": input.apiKey }
1272
+ });
1273
+ } catch (networkError) {
1274
+ throw new OwneyError(
1275
+ "SPONSOR_REQUEST_FAILED",
1276
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1277
+ { cause: String(networkError), safeToFallback: true }
1278
+ );
1279
+ }
1280
+ const text = await res.text();
1281
+ let parsed = null;
1282
+ try {
1283
+ parsed = JSON.parse(text);
1284
+ } catch {
1285
+ }
1286
+ if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
1287
+ throw new OwneyError(
1288
+ "SPONSOR_REQUEST_FAILED",
1289
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1290
+ { statusCode: res.status, responseBody: text.slice(0, 500), safeToFallback: true }
1291
+ );
1292
+ }
1293
+ return parsed.data.relayer;
1294
+ }
1165
1295
 
1166
1296
  // src/lib/sponsored-deposit.ts
1167
1297
  var AUTH_WINDOW_SECONDS = 15 * 60;
@@ -1213,6 +1343,114 @@ function makeSponsoredDepositCallback(deps) {
1213
1343
  };
1214
1344
  }
1215
1345
 
1346
+ // src/lib/permit2.ts
1347
+ import { bytesToHex as bytesToHex2 } from "viem";
1348
+ var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
1349
+ var MAX_UINT256 = 2n ** 256n - 1n;
1350
+ var ERC20_ALLOWANCE_ABI = [
1351
+ {
1352
+ type: "function",
1353
+ name: "allowance",
1354
+ stateMutability: "view",
1355
+ inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }],
1356
+ outputs: [{ name: "", type: "uint256" }]
1357
+ },
1358
+ {
1359
+ type: "function",
1360
+ name: "approve",
1361
+ stateMutability: "nonpayable",
1362
+ inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
1363
+ outputs: [{ name: "", type: "bool" }]
1364
+ }
1365
+ ];
1366
+ function buildPermitTransferFromTypedData(input) {
1367
+ return {
1368
+ domain: { name: "Permit2", chainId: input.chainId, verifyingContract: PERMIT2_ADDRESS },
1369
+ types: {
1370
+ PermitTransferFrom: [
1371
+ { name: "permitted", type: "TokenPermissions" },
1372
+ { name: "spender", type: "address" },
1373
+ { name: "nonce", type: "uint256" },
1374
+ { name: "deadline", type: "uint256" }
1375
+ ],
1376
+ TokenPermissions: [
1377
+ { name: "token", type: "address" },
1378
+ { name: "amount", type: "uint256" }
1379
+ ]
1380
+ },
1381
+ primaryType: "PermitTransferFrom",
1382
+ message: input.message
1383
+ };
1384
+ }
1385
+ function randomPermit2Nonce() {
1386
+ const bytes = new Uint8Array(32);
1387
+ globalThis.crypto.getRandomValues(bytes);
1388
+ return BigInt(bytesToHex2(bytes));
1389
+ }
1390
+ async function readPermit2Allowance(publicClient, token, owner) {
1391
+ return publicClient.readContract({
1392
+ address: token,
1393
+ abi: ERC20_ALLOWANCE_ABI,
1394
+ functionName: "allowance",
1395
+ args: [owner, PERMIT2_ADDRESS]
1396
+ });
1397
+ }
1398
+
1399
+ // src/lib/sponsored-weth-deposit.ts
1400
+ var PERMIT_WINDOW_SECONDS = 15 * 60;
1401
+ function makeSponsoredWethCallback(deps) {
1402
+ const get = deps.httpGet ?? getSponsorRelayerAddress;
1403
+ const post = deps.httpPost ?? postSponsorPermit2Transfer;
1404
+ return async (smartWallet, chainId, amount) => {
1405
+ const cid = chainId;
1406
+ const token = deps.tokenAddressByChain[cid];
1407
+ if (!token) {
1408
+ throw new OwneyError("CHAIN_UNSUPPORTED", `No sponsored WETH configured for chain ${chainId}`);
1409
+ }
1410
+ const amountWei = BigInt(amount);
1411
+ const pub = deps.getPublicClient(cid);
1412
+ const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
1413
+ if (allowance < amountWei) {
1414
+ throw new OwneyError(
1415
+ "PERMIT2_APPROVAL_REQUIRED",
1416
+ "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
1417
+ { token, chainId: cid, allowance: allowance.toString(), amount }
1418
+ );
1419
+ }
1420
+ const relayer = await get({ baseUrl: deps.baseUrl, apiKey: deps.apiKey, chainId: cid });
1421
+ const nonce = randomPermit2Nonce();
1422
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS);
1423
+ const typedData = buildPermitTransferFromTypedData({
1424
+ chainId: cid,
1425
+ message: {
1426
+ permitted: { token, amount: amountWei },
1427
+ spender: relayer,
1428
+ nonce,
1429
+ deadline
1430
+ }
1431
+ });
1432
+ const wallet = deps.getWalletClient(cid);
1433
+ const signature = await wallet.signTypedData(
1434
+ { account: deps.ownerAddress, ...typedData }
1435
+ );
1436
+ const result = await post({
1437
+ baseUrl: deps.baseUrl,
1438
+ apiKey: deps.apiKey,
1439
+ body: {
1440
+ chainId: cid,
1441
+ token,
1442
+ from: deps.ownerAddress,
1443
+ to: smartWallet,
1444
+ amount,
1445
+ nonce: nonce.toString(),
1446
+ deadline: deadline.toString(),
1447
+ signature
1448
+ }
1449
+ });
1450
+ return result.txHash;
1451
+ };
1452
+ }
1453
+
1216
1454
  // src/client.ts
1217
1455
  function encodeMultiAgentCursor(map) {
1218
1456
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
@@ -1242,6 +1480,13 @@ var VIEM_CHAIN2 = {
1242
1480
  8453: base2,
1243
1481
  42161: arbitrum2
1244
1482
  };
1483
+ var SPONSORED_WETH_BY_CHAIN = {
1484
+ 8453: "0x4200000000000000000000000000000000000006",
1485
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
1486
+ };
1487
+ function shouldFallbackToUserPaid(error, asset, appCallback) {
1488
+ return asset === "WETH" && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
1489
+ }
1245
1490
  var OwneySDK = class {
1246
1491
  agents = /* @__PURE__ */ new Map();
1247
1492
  activeAgents = /* @__PURE__ */ new Set();
@@ -1255,6 +1500,7 @@ var OwneySDK = class {
1255
1500
  apiKey;
1256
1501
  zyfaiRpcUrls;
1257
1502
  cachedSponsoredCallback = null;
1503
+ cachedWethSponsoredCallback = null;
1258
1504
  initializingAgentsPromise = null;
1259
1505
  constructor(config) {
1260
1506
  this.apiKey = config.apiKey;
@@ -1279,6 +1525,7 @@ var OwneySDK = class {
1279
1525
  }
1280
1526
  this.state = { provider, walletAddress, chainId: null };
1281
1527
  this.cachedSponsoredCallback = null;
1528
+ this.cachedWethSponsoredCallback = null;
1282
1529
  }
1283
1530
  /**
1284
1531
  * Disconnect from all agents and clear connection state.
@@ -1292,6 +1539,7 @@ var OwneySDK = class {
1292
1539
  this.disabledAgents.clear();
1293
1540
  this.state = null;
1294
1541
  this.cachedSponsoredCallback = null;
1542
+ this.cachedWethSponsoredCallback = null;
1295
1543
  }
1296
1544
  isAgentDisabled(agentId) {
1297
1545
  return this.disabledAgents.has(agentId);
@@ -1365,6 +1613,36 @@ var OwneySDK = class {
1365
1613
  });
1366
1614
  return this.cachedSponsoredCallback;
1367
1615
  }
1616
+ /**
1617
+ * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
1618
+ * callback used when the caller omits `depositCallback` for a WETH
1619
+ * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
1620
+ * `PermitTransferFrom` instead of an EIP-3009 authorization.
1621
+ */
1622
+ getDefaultWethSponsoredCallback() {
1623
+ if (this.cachedWethSponsoredCallback)
1624
+ return this.cachedWethSponsoredCallback;
1625
+ const provider = this.requireConnectedProvider();
1626
+ const owner = this.state.walletAddress;
1627
+ this.cachedWethSponsoredCallback = makeSponsoredWethCallback({
1628
+ apiKey: this.apiKey,
1629
+ ownerAddress: owner,
1630
+ tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
1631
+ // Casts work around viem's chain-narrowed Client vs the generic
1632
+ // PublicClient/WalletClient param types — structurally identical at
1633
+ // runtime, but the two share a name TS treats as unrelated.
1634
+ getPublicClient: (cid) => createPublicClient2({
1635
+ chain: VIEM_CHAIN2[cid],
1636
+ transport: custom(provider)
1637
+ }),
1638
+ getWalletClient: (cid) => createWalletClient({
1639
+ account: owner,
1640
+ chain: VIEM_CHAIN2[cid],
1641
+ transport: custom(provider)
1642
+ })
1643
+ });
1644
+ return this.cachedWethSponsoredCallback;
1645
+ }
1368
1646
  getAgent(agentId) {
1369
1647
  const agent = this.agents.get(agentId);
1370
1648
  if (!agent) {
@@ -1518,7 +1796,7 @@ var OwneySDK = class {
1518
1796
  }
1519
1797
  const state = this.requireState();
1520
1798
  const chainId = this.requireChainId();
1521
- const effectiveCallback = depositCallback ?? this.getDefaultSponsoredCallback();
1799
+ const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback() : this.getDefaultSponsoredCallback());
1522
1800
  if (agentId) {
1523
1801
  if (this.isAgentDisabled(agentId)) {
1524
1802
  throw new OwneyError(
@@ -1530,10 +1808,14 @@ var OwneySDK = class {
1530
1808
  const agent = this.getAgent(agentId);
1531
1809
  this.validateAssetSupport(agent, chainId, asset);
1532
1810
  await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
1533
- return withFailureReporting(
1534
- this.apiKey,
1535
- agent.id,
1536
- () => agent.deposit(state, chainId, amount, effectiveCallback)
1811
+ return this.depositWithFallback(
1812
+ agent,
1813
+ state,
1814
+ chainId,
1815
+ amount,
1816
+ asset,
1817
+ effectiveCallback,
1818
+ depositCallback
1537
1819
  );
1538
1820
  }
1539
1821
  const totalAmount = BigInt(amount);
@@ -1564,14 +1846,78 @@ var OwneySDK = class {
1564
1846
  }
1565
1847
  const agentResults = {};
1566
1848
  for (const { agent, amount: agentAmount } of agentAmounts) {
1567
- agentResults[agent.id] = await withFailureReporting(
1568
- this.apiKey,
1569
- agent.id,
1570
- () => agent.deposit(state, chainId, agentAmount, effectiveCallback)
1849
+ agentResults[agent.id] = await this.depositWithFallback(
1850
+ agent,
1851
+ state,
1852
+ chainId,
1853
+ agentAmount,
1854
+ asset,
1855
+ effectiveCallback,
1856
+ depositCallback
1571
1857
  );
1572
1858
  }
1573
1859
  return { agentResults };
1574
1860
  }
1861
+ /**
1862
+ * Invokes `agent.deposit` with the resolved sponsored callback, composing
1863
+ * two independent auto-recovery mechanisms:
1864
+ *
1865
+ * 1. Missing Permit2 allowance: when the app did not supply its own
1866
+ * callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
1867
+ * WETH deposit, this is the wallet's first gasless WETH deposit. We send
1868
+ * the one-time (user-paid) Permit2 approval via `approvePermit2()` and
1869
+ * retry the SAME sponsored attempt once. Bounded to one approval attempt
1870
+ * per call so a wallet/agent that keeps reporting the allowance as
1871
+ * missing can't loop forever. If `approvePermit2()` itself throws (e.g.
1872
+ * the user rejects the wallet prompt), that error propagates as-is —
1873
+ * the user said no to a transaction, so we must not turn around and ask
1874
+ * them to pay for a different one via the fallback below.
1875
+ * 2. `shouldFallbackToUserPaid`: the SDK's own WETH sponsor path failed at
1876
+ * the infrastructure layer in a provably safe-to-retry way, so we warn
1877
+ * and retry once with `undefined`, falling through to a user-paid
1878
+ * native deposit. This still applies after a successful Permit2
1879
+ * approval retry (the retried sponsored attempt can itself hit a safe
1880
+ * sponsor failure).
1881
+ *
1882
+ * Any other error — an app-supplied callback's own failure in particular —
1883
+ * propagates unchanged. Scoped per agent invocation so one agent's failure
1884
+ * doesn't force a retry of sibling agents in a multi-agent split.
1885
+ */
1886
+ async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback) {
1887
+ const attempt = (callback) => withFailureReporting(
1888
+ this.apiKey,
1889
+ agent.id,
1890
+ () => agent.deposit(
1891
+ state,
1892
+ chainId,
1893
+ amount,
1894
+ asset,
1895
+ callback
1896
+ )
1897
+ );
1898
+ let approvalAttempted = false;
1899
+ for (; ; ) {
1900
+ try {
1901
+ return await attempt(effectiveCallback);
1902
+ } catch (error) {
1903
+ if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
1904
+ approvalAttempted = true;
1905
+ console.warn(
1906
+ "[owney-sdk] First WETH deposit: sending one-time Permit2 approval..."
1907
+ );
1908
+ await this.approvePermit2();
1909
+ continue;
1910
+ }
1911
+ if (!shouldFallbackToUserPaid(error, asset, appCallback))
1912
+ throw error;
1913
+ console.warn(
1914
+ "[owney-sdk] WETH sponsorship unavailable, falling back to user-paid deposit:",
1915
+ error
1916
+ );
1917
+ return attempt(void 0);
1918
+ }
1919
+ }
1920
+ }
1575
1921
  getMinDepositAmount(agent, chainId, asset) {
1576
1922
  const chainAssets = agent.supportedAssets.find(
1577
1923
  (sa) => sa.chainId === chainId
@@ -2059,6 +2405,66 @@ var OwneySDK = class {
2059
2405
  agentUserProfile: results
2060
2406
  };
2061
2407
  }
2408
+ /**
2409
+ * Ensure the given asset uses backend protocol auto-selection for the active
2410
+ * chain's account. No-op (returns false) for agents that don't support it.
2411
+ * @param asset - "USDC" or "WETH".
2412
+ * @param agentId - Agent to target. Defaults to "zyfai".
2413
+ * @returns whether a profile write occurred.
2414
+ */
2415
+ async ensureAutoSelectProtocols(asset, agentId = "zyfai") {
2416
+ const state = this.requireState();
2417
+ const chainId = this.requireChainId();
2418
+ const agent = this.getAgent(agentId);
2419
+ if (typeof agent.ensureAutoSelectProtocols !== "function")
2420
+ return false;
2421
+ return agent.ensureAutoSelectProtocols(state, chainId, asset);
2422
+ }
2423
+ /**
2424
+ * One-time, user-paid approval of Permit2 on the sponsored WETH token for
2425
+ * the active chain. Required once per wallet per chain before gasless WETH
2426
+ * deposits; afterwards deposit() is signature-only. Resolves only after the
2427
+ * approval transaction is mined (1 confirmation), so a subsequent deposit()
2428
+ * will see the new allowance; throws if the transaction reverted.
2429
+ * @returns the approval transaction hash.
2430
+ */
2431
+ async approvePermit2(asset = "WETH") {
2432
+ const state = this.requireState();
2433
+ const chainId = this.requireChainId();
2434
+ const token = SPONSORED_WETH_BY_CHAIN[chainId];
2435
+ if (!token) {
2436
+ throw new OwneyError(
2437
+ "CHAIN_UNSUPPORTED",
2438
+ `No sponsored WETH on chain ${chainId}`
2439
+ );
2440
+ }
2441
+ const provider = this.requireConnectedProvider();
2442
+ const wallet = createWalletClient({
2443
+ account: state.walletAddress,
2444
+ chain: VIEM_CHAIN2[chainId],
2445
+ transport: custom(provider)
2446
+ });
2447
+ const hash = await wallet.writeContract({
2448
+ address: token,
2449
+ abi: ERC20_ALLOWANCE_ABI,
2450
+ functionName: "approve",
2451
+ args: [PERMIT2_ADDRESS, MAX_UINT256],
2452
+ account: state.walletAddress,
2453
+ chain: VIEM_CHAIN2[chainId]
2454
+ });
2455
+ const publicClient = createPublicClient2({
2456
+ chain: VIEM_CHAIN2[chainId],
2457
+ transport: custom(provider)
2458
+ });
2459
+ const receipt = await publicClient.waitForTransactionReceipt({
2460
+ hash,
2461
+ confirmations: 1
2462
+ });
2463
+ if (receipt.status !== "success") {
2464
+ throw new Error(`Permit2 approval reverted (tx ${hash})`);
2465
+ }
2466
+ return hash;
2467
+ }
2062
2468
  // --- Discovery (no wallet required) ---
2063
2469
  /**
2064
2470
  * Get the agent's average APY performance over a time period. Does not require a wallet connection.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.6.4-beta.0",
3
+ "version": "0.6.5-beta.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",