@owney/sdk 0.7.4 → 0.7.6

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
@@ -825,6 +825,9 @@ function debugLog(scope, message, data) {
825
825
  }
826
826
 
827
827
  // src/agents/zyfai/zyfai.agent.ts
828
+ var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem.parseAbi)([
829
+ "function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
830
+ ]);
828
831
  var DEFAULT_ZYFAI_RPC_URLS = {
829
832
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
830
833
  42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
@@ -1142,7 +1145,12 @@ var ZyfaiAgent = class {
1142
1145
  const validChainId = isValidChainId(chainId);
1143
1146
  await Promise.all([
1144
1147
  this.ensureProtocolsSynced(validChainId, "USDC"),
1145
- this.ensureProtocolsSynced(validChainId, "WETH")
1148
+ this.ensureProtocolsSynced(validChainId, "WETH"),
1149
+ // Retrofit a session key for old wallets that are already deployed and
1150
+ // funded but never got one — otherwise their balance stays idle until
1151
+ // they happen to deposit again. Only touches Safes that are missing the
1152
+ // validator, so pool-wallet users see no prompt. Non-fatal.
1153
+ this.ensureSessionKeyIfDeployed(validChainId)
1146
1154
  ]);
1147
1155
  } catch (error) {
1148
1156
  throw error;
@@ -1340,6 +1348,65 @@ var ZyfaiAgent = class {
1340
1348
  return false;
1341
1349
  }
1342
1350
  }
1351
+ /**
1352
+ * Ensure Zyfai's automation can actually act on the user's Safe for this
1353
+ * chain, by making sure a session key exists.
1354
+ *
1355
+ * Old wallets (pre-0.2.44) are deployed WITHOUT the smart-sessions validator,
1356
+ * so Zyfai's rebalancer has no authorised path to move funds: deposits land
1357
+ * and then sit as raw tokens forever. Pre-deployed pool wallets ship with the
1358
+ * module already installed, which is why they never needed this.
1359
+ *
1360
+ * The gate is the on-chain module, not the wallet's class: it is the exact
1361
+ * condition that matters, it needs no guessing from API shapes, and it means
1362
+ * pool-wallet users are never prompted for a signature they don't need.
1363
+ *
1364
+ * Non-fatal by design. A failure here must not block a deposit — the funds
1365
+ * still arrive safely in the Safe, they just won't be deployed into a pool
1366
+ * until a session key exists.
1367
+ */
1368
+ /**
1369
+ * Activation-time variant: only acts when the Safe already has code on this
1370
+ * chain. Activation must never deploy anything — that is deposit's job, and it
1371
+ * would mean surprise on-chain work just for opening the app.
1372
+ */
1373
+ async ensureSessionKeyIfDeployed(chainId) {
1374
+ try {
1375
+ const ownerAddress = this.getAddress();
1376
+ const wallet = await this.sdk.getSmartWalletAddress(
1377
+ ownerAddress,
1378
+ chainId
1379
+ );
1380
+ if (!wallet.isDeployed) return;
1381
+ await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
1382
+ } catch (error) {
1383
+ console.warn(
1384
+ "[zyfai] session-key check at activation failed (non-fatal):",
1385
+ error instanceof Error ? error.message : String(error)
1386
+ );
1387
+ }
1388
+ }
1389
+ async ensureSessionKey(ownerAddress, chainId, smartWallet) {
1390
+ try {
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;
1399
+ console.warn(
1400
+ `[zyfai] Safe ${smartWallet} has no smart-sessions validator on chain ${chainId}; creating a session key so the agent can allocate.`
1401
+ );
1402
+ await this.sdk.createSessionKey(ownerAddress, chainId);
1403
+ } catch (error) {
1404
+ console.warn(
1405
+ "[zyfai] createSessionKey skipped (non-fatal):",
1406
+ error instanceof Error ? error.message : String(error)
1407
+ );
1408
+ }
1409
+ }
1343
1410
  /**
1344
1411
  * Guarantee the user's Safe has code on `chainId` before anything transfers
1345
1412
  * funds into it, and return its address.
@@ -1360,17 +1427,21 @@ var ZyfaiAgent = class {
1360
1427
  */
1361
1428
  async ensureWalletDeployed(ownerAddress, chainId) {
1362
1429
  const wallet = await this.sdk.getSmartWalletAddress(ownerAddress, chainId);
1363
- if (wallet.isDeployed) return wallet.address;
1430
+ if (wallet.isDeployed) {
1431
+ await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
1432
+ return wallet.address;
1433
+ }
1364
1434
  const poolFirst = await this.isPredeployedPoolWallet(
1365
1435
  ownerAddress,
1366
1436
  wallet.address
1367
1437
  );
1368
1438
  const deployAsPool = () => this.sdk.deployOnChains([chainId]);
1369
1439
  const deployAsLegacy = () => (
1370
- // `createSessionKey: false` — session keys are backend-owned since the
1371
- // predeployed-pool rollout; asking for one here costs a signature prompt
1372
- // and is rejected anyway.
1373
- this.sdk.deploySafe(ownerAddress, chainId, "aggressive", false)
1440
+ // `createSessionKey: true` — per Zyfai, the old-wallet flow is
1441
+ // deploySafe(true) -> createSessionKey() -> deposit. Passing false (which
1442
+ // this did originally) deploys a Safe with no smart-sessions validator, so
1443
+ // deposits land and are never allocated into a pool.
1444
+ this.sdk.deploySafe(ownerAddress, chainId, "aggressive", true)
1374
1445
  );
1375
1446
  const [first, second] = poolFirst ? [deployAsPool, deployAsLegacy] : [deployAsLegacy, deployAsPool];
1376
1447
  try {
@@ -1394,6 +1465,7 @@ var ZyfaiAgent = class {
1394
1465
  this.id
1395
1466
  );
1396
1467
  }
1468
+ await this.ensureSessionKey(ownerAddress, chainId, redeployed.address);
1397
1469
  return redeployed.address;
1398
1470
  }
1399
1471
  async deposit(state, chainId, amount, asset, depositCallback) {
@@ -2001,6 +2073,41 @@ async function readErc20Balance(publicClient, token, owner) {
2001
2073
  });
2002
2074
  }
2003
2075
 
2076
+ // src/lib/chain-guard.ts
2077
+ var CHAIN_NAMES = {
2078
+ 1: "Ethereum",
2079
+ 8453: "Base",
2080
+ 42161: "Arbitrum"
2081
+ };
2082
+ function chainName(chainId) {
2083
+ return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
2084
+ }
2085
+ async function ensureWalletOnChain(pub, wallet, expected) {
2086
+ const actual = await pub.getChainId();
2087
+ if (actual === expected) return;
2088
+ try {
2089
+ await wallet.switchChain({ id: expected });
2090
+ } catch (error) {
2091
+ throw new OwneyError(
2092
+ "CHAIN_MISMATCH",
2093
+ `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2094
+ {
2095
+ expectedChainId: expected,
2096
+ actualChainId: actual,
2097
+ cause: error instanceof Error ? error.message : String(error)
2098
+ }
2099
+ );
2100
+ }
2101
+ const after = await pub.getChainId();
2102
+ if (after !== expected) {
2103
+ throw new OwneyError(
2104
+ "CHAIN_MISMATCH",
2105
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2106
+ { expectedChainId: expected, actualChainId: after }
2107
+ );
2108
+ }
2109
+ }
2110
+
2004
2111
  // src/lib/sponsored-deposit.ts
2005
2112
  var AUTH_WINDOW_SECONDS = 15 * 60;
2006
2113
  function makeSponsoredDepositCallback(deps) {
@@ -2016,6 +2123,7 @@ function makeSponsoredDepositCallback(deps) {
2016
2123
  }
2017
2124
  const pub = deps.getPublicClient(cid);
2018
2125
  const wallet = deps.getWalletClient(cid);
2126
+ await ensureWalletOnChain(pub, wallet, cid);
2019
2127
  try {
2020
2128
  const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2021
2129
  if (balance < BigInt(amount)) {
@@ -2027,6 +2135,10 @@ function makeSponsoredDepositCallback(deps) {
2027
2135
  }
2028
2136
  } catch (err) {
2029
2137
  if (err instanceof OwneyError) throw err;
2138
+ console.warn(
2139
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2140
+ err instanceof Error ? err.message : String(err)
2141
+ );
2030
2142
  }
2031
2143
  const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2032
2144
  const validAfter = 0n;
@@ -2090,6 +2202,8 @@ function makeSponsoredWethCallback(deps) {
2090
2202
  }
2091
2203
  const amountWei = BigInt(amount);
2092
2204
  const pub = deps.getPublicClient(cid);
2205
+ const wallet = deps.getWalletClient(cid);
2206
+ await ensureWalletOnChain(pub, wallet, cid);
2093
2207
  try {
2094
2208
  const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2095
2209
  if (balance < amountWei) {
@@ -2101,6 +2215,10 @@ function makeSponsoredWethCallback(deps) {
2101
2215
  }
2102
2216
  } catch (err) {
2103
2217
  if (err instanceof OwneyError) throw err;
2218
+ console.warn(
2219
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2220
+ err instanceof Error ? err.message : String(err)
2221
+ );
2104
2222
  }
2105
2223
  const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2106
2224
  if (allowance < amountWei) {
@@ -2128,7 +2246,6 @@ function makeSponsoredWethCallback(deps) {
2128
2246
  deadline
2129
2247
  }
2130
2248
  });
2131
- const wallet = deps.getWalletClient(cid);
2132
2249
  const signature = await wallet.signTypedData({
2133
2250
  account: deps.ownerAddress,
2134
2251
  ...typedData
package/dist/index.d.cts CHANGED
@@ -609,7 +609,7 @@ declare class OwneySDK {
609
609
  getAllocationApy({ agentId, }?: AllocationApyOptions): Promise<OwneyAllocationApy>;
610
610
  }
611
611
 
612
- 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" | "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";
612
+ 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";
613
613
  declare class OwneyError extends Error {
614
614
  readonly code: OwneyErrorCode;
615
615
  readonly details?: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -609,7 +609,7 @@ declare class OwneySDK {
609
609
  getAllocationApy({ agentId, }?: AllocationApyOptions): Promise<OwneyAllocationApy>;
610
610
  }
611
611
 
612
- 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" | "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";
612
+ 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";
613
613
  declare class OwneyError extends Error {
614
614
  readonly code: OwneyErrorCode;
615
615
  readonly details?: Record<string, unknown>;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/agents/zyfai/zyfai.agent.ts
2
- import { ZyfaiSDK } from "@zyfai/sdk";
3
- import { createPublicClient, http } from "viem";
2
+ import { SMART_SESSIONS_VALIDATOR, ZyfaiSDK } from "@zyfai/sdk";
3
+ import { createPublicClient, http, parseAbi } from "viem";
4
4
  import { base, arbitrum, mainnet } from "viem/chains";
5
5
 
6
6
  // src/types/config.ts
@@ -792,6 +792,9 @@ function debugLog(scope, message, data) {
792
792
  }
793
793
 
794
794
  // src/agents/zyfai/zyfai.agent.ts
795
+ var ERC7579_IS_MODULE_INSTALLED_ABI = parseAbi([
796
+ "function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
797
+ ]);
795
798
  var DEFAULT_ZYFAI_RPC_URLS = {
796
799
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
797
800
  42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
@@ -1109,7 +1112,12 @@ var ZyfaiAgent = class {
1109
1112
  const validChainId = isValidChainId(chainId);
1110
1113
  await Promise.all([
1111
1114
  this.ensureProtocolsSynced(validChainId, "USDC"),
1112
- this.ensureProtocolsSynced(validChainId, "WETH")
1115
+ this.ensureProtocolsSynced(validChainId, "WETH"),
1116
+ // Retrofit a session key for old wallets that are already deployed and
1117
+ // funded but never got one — otherwise their balance stays idle until
1118
+ // they happen to deposit again. Only touches Safes that are missing the
1119
+ // validator, so pool-wallet users see no prompt. Non-fatal.
1120
+ this.ensureSessionKeyIfDeployed(validChainId)
1113
1121
  ]);
1114
1122
  } catch (error) {
1115
1123
  throw error;
@@ -1307,6 +1315,65 @@ var ZyfaiAgent = class {
1307
1315
  return false;
1308
1316
  }
1309
1317
  }
1318
+ /**
1319
+ * Ensure Zyfai's automation can actually act on the user's Safe for this
1320
+ * chain, by making sure a session key exists.
1321
+ *
1322
+ * Old wallets (pre-0.2.44) are deployed WITHOUT the smart-sessions validator,
1323
+ * so Zyfai's rebalancer has no authorised path to move funds: deposits land
1324
+ * and then sit as raw tokens forever. Pre-deployed pool wallets ship with the
1325
+ * module already installed, which is why they never needed this.
1326
+ *
1327
+ * The gate is the on-chain module, not the wallet's class: it is the exact
1328
+ * condition that matters, it needs no guessing from API shapes, and it means
1329
+ * pool-wallet users are never prompted for a signature they don't need.
1330
+ *
1331
+ * Non-fatal by design. A failure here must not block a deposit — the funds
1332
+ * still arrive safely in the Safe, they just won't be deployed into a pool
1333
+ * until a session key exists.
1334
+ */
1335
+ /**
1336
+ * Activation-time variant: only acts when the Safe already has code on this
1337
+ * chain. Activation must never deploy anything — that is deposit's job, and it
1338
+ * would mean surprise on-chain work just for opening the app.
1339
+ */
1340
+ async ensureSessionKeyIfDeployed(chainId) {
1341
+ try {
1342
+ const ownerAddress = this.getAddress();
1343
+ const wallet = await this.sdk.getSmartWalletAddress(
1344
+ ownerAddress,
1345
+ chainId
1346
+ );
1347
+ if (!wallet.isDeployed) return;
1348
+ await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
1349
+ } catch (error) {
1350
+ console.warn(
1351
+ "[zyfai] session-key check at activation failed (non-fatal):",
1352
+ error instanceof Error ? error.message : String(error)
1353
+ );
1354
+ }
1355
+ }
1356
+ async ensureSessionKey(ownerAddress, chainId, smartWallet) {
1357
+ try {
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;
1366
+ console.warn(
1367
+ `[zyfai] Safe ${smartWallet} has no smart-sessions validator on chain ${chainId}; creating a session key so the agent can allocate.`
1368
+ );
1369
+ await this.sdk.createSessionKey(ownerAddress, chainId);
1370
+ } catch (error) {
1371
+ console.warn(
1372
+ "[zyfai] createSessionKey skipped (non-fatal):",
1373
+ error instanceof Error ? error.message : String(error)
1374
+ );
1375
+ }
1376
+ }
1310
1377
  /**
1311
1378
  * Guarantee the user's Safe has code on `chainId` before anything transfers
1312
1379
  * funds into it, and return its address.
@@ -1327,17 +1394,21 @@ var ZyfaiAgent = class {
1327
1394
  */
1328
1395
  async ensureWalletDeployed(ownerAddress, chainId) {
1329
1396
  const wallet = await this.sdk.getSmartWalletAddress(ownerAddress, chainId);
1330
- if (wallet.isDeployed) return wallet.address;
1397
+ if (wallet.isDeployed) {
1398
+ await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
1399
+ return wallet.address;
1400
+ }
1331
1401
  const poolFirst = await this.isPredeployedPoolWallet(
1332
1402
  ownerAddress,
1333
1403
  wallet.address
1334
1404
  );
1335
1405
  const deployAsPool = () => this.sdk.deployOnChains([chainId]);
1336
1406
  const deployAsLegacy = () => (
1337
- // `createSessionKey: false` — session keys are backend-owned since the
1338
- // predeployed-pool rollout; asking for one here costs a signature prompt
1339
- // and is rejected anyway.
1340
- this.sdk.deploySafe(ownerAddress, chainId, "aggressive", false)
1407
+ // `createSessionKey: true` — per Zyfai, the old-wallet flow is
1408
+ // deploySafe(true) -> createSessionKey() -> deposit. Passing false (which
1409
+ // this did originally) deploys a Safe with no smart-sessions validator, so
1410
+ // deposits land and are never allocated into a pool.
1411
+ this.sdk.deploySafe(ownerAddress, chainId, "aggressive", true)
1341
1412
  );
1342
1413
  const [first, second] = poolFirst ? [deployAsPool, deployAsLegacy] : [deployAsLegacy, deployAsPool];
1343
1414
  try {
@@ -1361,6 +1432,7 @@ var ZyfaiAgent = class {
1361
1432
  this.id
1362
1433
  );
1363
1434
  }
1435
+ await this.ensureSessionKey(ownerAddress, chainId, redeployed.address);
1364
1436
  return redeployed.address;
1365
1437
  }
1366
1438
  async deposit(state, chainId, amount, asset, depositCallback) {
@@ -1972,6 +2044,41 @@ async function readErc20Balance(publicClient, token, owner) {
1972
2044
  });
1973
2045
  }
1974
2046
 
2047
+ // src/lib/chain-guard.ts
2048
+ var CHAIN_NAMES = {
2049
+ 1: "Ethereum",
2050
+ 8453: "Base",
2051
+ 42161: "Arbitrum"
2052
+ };
2053
+ function chainName(chainId) {
2054
+ return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
2055
+ }
2056
+ async function ensureWalletOnChain(pub, wallet, expected) {
2057
+ const actual = await pub.getChainId();
2058
+ if (actual === expected) return;
2059
+ try {
2060
+ await wallet.switchChain({ id: expected });
2061
+ } catch (error) {
2062
+ throw new OwneyError(
2063
+ "CHAIN_MISMATCH",
2064
+ `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2065
+ {
2066
+ expectedChainId: expected,
2067
+ actualChainId: actual,
2068
+ cause: error instanceof Error ? error.message : String(error)
2069
+ }
2070
+ );
2071
+ }
2072
+ const after = await pub.getChainId();
2073
+ if (after !== expected) {
2074
+ throw new OwneyError(
2075
+ "CHAIN_MISMATCH",
2076
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2077
+ { expectedChainId: expected, actualChainId: after }
2078
+ );
2079
+ }
2080
+ }
2081
+
1975
2082
  // src/lib/sponsored-deposit.ts
1976
2083
  var AUTH_WINDOW_SECONDS = 15 * 60;
1977
2084
  function makeSponsoredDepositCallback(deps) {
@@ -1987,6 +2094,7 @@ function makeSponsoredDepositCallback(deps) {
1987
2094
  }
1988
2095
  const pub = deps.getPublicClient(cid);
1989
2096
  const wallet = deps.getWalletClient(cid);
2097
+ await ensureWalletOnChain(pub, wallet, cid);
1990
2098
  try {
1991
2099
  const balance = await readErc20Balance(pub, token, deps.ownerAddress);
1992
2100
  if (balance < BigInt(amount)) {
@@ -1998,6 +2106,10 @@ function makeSponsoredDepositCallback(deps) {
1998
2106
  }
1999
2107
  } catch (err) {
2000
2108
  if (err instanceof OwneyError) throw err;
2109
+ console.warn(
2110
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2111
+ err instanceof Error ? err.message : String(err)
2112
+ );
2001
2113
  }
2002
2114
  const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2003
2115
  const validAfter = 0n;
@@ -2061,6 +2173,8 @@ function makeSponsoredWethCallback(deps) {
2061
2173
  }
2062
2174
  const amountWei = BigInt(amount);
2063
2175
  const pub = deps.getPublicClient(cid);
2176
+ const wallet = deps.getWalletClient(cid);
2177
+ await ensureWalletOnChain(pub, wallet, cid);
2064
2178
  try {
2065
2179
  const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2066
2180
  if (balance < amountWei) {
@@ -2072,6 +2186,10 @@ function makeSponsoredWethCallback(deps) {
2072
2186
  }
2073
2187
  } catch (err) {
2074
2188
  if (err instanceof OwneyError) throw err;
2189
+ console.warn(
2190
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2191
+ err instanceof Error ? err.message : String(err)
2192
+ );
2075
2193
  }
2076
2194
  const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2077
2195
  if (allowance < amountWei) {
@@ -2099,7 +2217,6 @@ function makeSponsoredWethCallback(deps) {
2099
2217
  deadline
2100
2218
  }
2101
2219
  });
2102
- const wallet = deps.getWalletClient(cid);
2103
2220
  const signature = await wallet.signTypedData({
2104
2221
  account: deps.ownerAddress,
2105
2222
  ...typedData
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",