@owney/sdk 0.7.3 → 0.7.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
@@ -1318,20 +1318,94 @@ var ZyfaiAgent = class {
1318
1318
  );
1319
1319
  }
1320
1320
  }
1321
+ /**
1322
+ * True when `smartWallet` is the backend-managed pool wallet Zyfai assigned
1323
+ * to this EOA — the "new wallet" kind that is predeployed on the chains it
1324
+ * was onboarded to. Legacy wallets (created before @zyfai/sdk 0.2.44) have no
1325
+ * pool record.
1326
+ *
1327
+ * A failed lookup returns false rather than throwing: misclassifying is
1328
+ * recoverable (ensureWalletDeployed tries the other deploy call), but
1329
+ * failing the deposit outright is not.
1330
+ */
1331
+ async isPredeployedPoolWallet(ownerAddress, smartWallet) {
1332
+ try {
1333
+ const byEoa = await this.sdk.getSmartWalletByEOA(ownerAddress);
1334
+ return typeof byEoa?.smartWallet === "string" && byEoa.smartWallet.toLowerCase() === smartWallet.toLowerCase();
1335
+ } catch (error) {
1336
+ console.warn(
1337
+ "[zyfai] getSmartWalletByEOA failed; assuming a legacy wallet:",
1338
+ error instanceof Error ? error.message : String(error)
1339
+ );
1340
+ return false;
1341
+ }
1342
+ }
1343
+ /**
1344
+ * Guarantee the user's Safe has code on `chainId` before anything transfers
1345
+ * funds into it, and return its address.
1346
+ *
1347
+ * Zyfai predeploys wallets created since @zyfai/sdk 0.2.44, but ONLY on the
1348
+ * chains they were onboarded to. Legacy wallets, and any chain a pool wallet
1349
+ * has not been onboarded to, are *counterfactual*: `getSmartWalletAddress`
1350
+ * returns a real deterministic address that holds no code yet. Depositing
1351
+ * there strands the funds until the Safe is deployed, and Owney's own sponsor
1352
+ * relayer refuses to broadcast to an address with no bytecode
1353
+ * (`to-address.guard-helper.ts`), so the deposit fails as an opaque 500.
1354
+ *
1355
+ * ROUT-320 removed the old deploySafe step believing every wallet was
1356
+ * predeployed from 0.2.44 on. Zyf later clarified that only NEW wallets are;
1357
+ * old ones still need the legacy flow. Hence the gate is on the per-chain
1358
+ * `isDeployed` flag rather than on the wallet's age — that covers both a
1359
+ * legacy wallet and a pool wallet on a fresh chain.
1360
+ */
1361
+ async ensureWalletDeployed(ownerAddress, chainId) {
1362
+ const wallet = await this.sdk.getSmartWalletAddress(ownerAddress, chainId);
1363
+ if (wallet.isDeployed) return wallet.address;
1364
+ const poolFirst = await this.isPredeployedPoolWallet(
1365
+ ownerAddress,
1366
+ wallet.address
1367
+ );
1368
+ const deployAsPool = () => this.sdk.deployOnChains([chainId]);
1369
+ 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)
1374
+ );
1375
+ const [first, second] = poolFirst ? [deployAsPool, deployAsLegacy] : [deployAsLegacy, deployAsPool];
1376
+ try {
1377
+ await first();
1378
+ } catch (error) {
1379
+ console.warn(
1380
+ `[zyfai] ${poolFirst ? "deployOnChains" : "deploySafe"} failed on chain ${chainId}; trying the other deploy path:`,
1381
+ error instanceof Error ? error.message : String(error)
1382
+ );
1383
+ await second();
1384
+ }
1385
+ const redeployed = await this.sdk.getSmartWalletAddress(
1386
+ ownerAddress,
1387
+ chainId
1388
+ );
1389
+ if (!redeployed.isDeployed) {
1390
+ throw new OwneyError(
1391
+ "WALLET_NOT_DEPLOYED",
1392
+ `Smart wallet ${redeployed.address} is still not deployed on chain ${chainId}. Depositing would send funds to an address with no code.`,
1393
+ { chainId, smartWallet: redeployed.address },
1394
+ this.id
1395
+ );
1396
+ }
1397
+ return redeployed.address;
1398
+ }
1321
1399
  async deposit(state, chainId, amount, asset, depositCallback) {
1322
1400
  try {
1323
1401
  const validChainId = isValidChainId(chainId);
1324
1402
  await this.ensureConnected(state, validChainId);
1325
1403
  if (depositCallback) {
1326
- const wallet = await this.sdk.getSmartWalletAddress(
1404
+ const smartWallet = await this.ensureWalletDeployed(
1327
1405
  this.getAddress(),
1328
1406
  validChainId
1329
1407
  );
1330
- const txHash = await depositCallback(
1331
- wallet.address,
1332
- validChainId,
1333
- amount
1334
- );
1408
+ const txHash = await depositCallback(smartWallet, validChainId, amount);
1335
1409
  const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
1336
1410
  try {
1337
1411
  await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
@@ -1341,8 +1415,9 @@ var ZyfaiAgent = class {
1341
1415
  logError
1342
1416
  );
1343
1417
  }
1344
- return { txHash, smartWallet: wallet.address, amount };
1418
+ return { txHash, smartWallet, amount };
1345
1419
  }
1420
+ await this.ensureWalletDeployed(this.getAddress(), validChainId);
1346
1421
  const raw = await this.sdk.depositFunds(
1347
1422
  this.getAddress(),
1348
1423
  validChainId,
@@ -1926,6 +2001,41 @@ async function readErc20Balance(publicClient, token, owner) {
1926
2001
  });
1927
2002
  }
1928
2003
 
2004
+ // src/lib/chain-guard.ts
2005
+ var CHAIN_NAMES = {
2006
+ 1: "Ethereum",
2007
+ 8453: "Base",
2008
+ 42161: "Arbitrum"
2009
+ };
2010
+ function chainName(chainId) {
2011
+ return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
2012
+ }
2013
+ async function ensureWalletOnChain(pub, wallet, expected) {
2014
+ const actual = await pub.getChainId();
2015
+ if (actual === expected) return;
2016
+ try {
2017
+ await wallet.switchChain({ id: expected });
2018
+ } catch (error) {
2019
+ throw new OwneyError(
2020
+ "CHAIN_MISMATCH",
2021
+ `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2022
+ {
2023
+ expectedChainId: expected,
2024
+ actualChainId: actual,
2025
+ cause: error instanceof Error ? error.message : String(error)
2026
+ }
2027
+ );
2028
+ }
2029
+ const after = await pub.getChainId();
2030
+ if (after !== expected) {
2031
+ throw new OwneyError(
2032
+ "CHAIN_MISMATCH",
2033
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2034
+ { expectedChainId: expected, actualChainId: after }
2035
+ );
2036
+ }
2037
+ }
2038
+
1929
2039
  // src/lib/sponsored-deposit.ts
1930
2040
  var AUTH_WINDOW_SECONDS = 15 * 60;
1931
2041
  function makeSponsoredDepositCallback(deps) {
@@ -1941,6 +2051,7 @@ function makeSponsoredDepositCallback(deps) {
1941
2051
  }
1942
2052
  const pub = deps.getPublicClient(cid);
1943
2053
  const wallet = deps.getWalletClient(cid);
2054
+ await ensureWalletOnChain(pub, wallet, cid);
1944
2055
  try {
1945
2056
  const balance = await readErc20Balance(pub, token, deps.ownerAddress);
1946
2057
  if (balance < BigInt(amount)) {
@@ -1952,6 +2063,10 @@ function makeSponsoredDepositCallback(deps) {
1952
2063
  }
1953
2064
  } catch (err) {
1954
2065
  if (err instanceof OwneyError) throw err;
2066
+ console.warn(
2067
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2068
+ err instanceof Error ? err.message : String(err)
2069
+ );
1955
2070
  }
1956
2071
  const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
1957
2072
  const validAfter = 0n;
@@ -2015,6 +2130,8 @@ function makeSponsoredWethCallback(deps) {
2015
2130
  }
2016
2131
  const amountWei = BigInt(amount);
2017
2132
  const pub = deps.getPublicClient(cid);
2133
+ const wallet = deps.getWalletClient(cid);
2134
+ await ensureWalletOnChain(pub, wallet, cid);
2018
2135
  try {
2019
2136
  const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2020
2137
  if (balance < amountWei) {
@@ -2026,6 +2143,10 @@ function makeSponsoredWethCallback(deps) {
2026
2143
  }
2027
2144
  } catch (err) {
2028
2145
  if (err instanceof OwneyError) throw err;
2146
+ console.warn(
2147
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2148
+ err instanceof Error ? err.message : String(err)
2149
+ );
2029
2150
  }
2030
2151
  const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2031
2152
  if (allowance < amountWei) {
@@ -2053,7 +2174,6 @@ function makeSponsoredWethCallback(deps) {
2053
2174
  deadline
2054
2175
  }
2055
2176
  });
2056
- const wallet = deps.getWalletClient(cid);
2057
2177
  const signature = await wallet.signTypedData({
2058
2178
  account: deps.ownerAddress,
2059
2179
  ...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" | "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" | "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
@@ -1285,20 +1285,94 @@ var ZyfaiAgent = class {
1285
1285
  );
1286
1286
  }
1287
1287
  }
1288
+ /**
1289
+ * True when `smartWallet` is the backend-managed pool wallet Zyfai assigned
1290
+ * to this EOA — the "new wallet" kind that is predeployed on the chains it
1291
+ * was onboarded to. Legacy wallets (created before @zyfai/sdk 0.2.44) have no
1292
+ * pool record.
1293
+ *
1294
+ * A failed lookup returns false rather than throwing: misclassifying is
1295
+ * recoverable (ensureWalletDeployed tries the other deploy call), but
1296
+ * failing the deposit outright is not.
1297
+ */
1298
+ async isPredeployedPoolWallet(ownerAddress, smartWallet) {
1299
+ try {
1300
+ const byEoa = await this.sdk.getSmartWalletByEOA(ownerAddress);
1301
+ return typeof byEoa?.smartWallet === "string" && byEoa.smartWallet.toLowerCase() === smartWallet.toLowerCase();
1302
+ } catch (error) {
1303
+ console.warn(
1304
+ "[zyfai] getSmartWalletByEOA failed; assuming a legacy wallet:",
1305
+ error instanceof Error ? error.message : String(error)
1306
+ );
1307
+ return false;
1308
+ }
1309
+ }
1310
+ /**
1311
+ * Guarantee the user's Safe has code on `chainId` before anything transfers
1312
+ * funds into it, and return its address.
1313
+ *
1314
+ * Zyfai predeploys wallets created since @zyfai/sdk 0.2.44, but ONLY on the
1315
+ * chains they were onboarded to. Legacy wallets, and any chain a pool wallet
1316
+ * has not been onboarded to, are *counterfactual*: `getSmartWalletAddress`
1317
+ * returns a real deterministic address that holds no code yet. Depositing
1318
+ * there strands the funds until the Safe is deployed, and Owney's own sponsor
1319
+ * relayer refuses to broadcast to an address with no bytecode
1320
+ * (`to-address.guard-helper.ts`), so the deposit fails as an opaque 500.
1321
+ *
1322
+ * ROUT-320 removed the old deploySafe step believing every wallet was
1323
+ * predeployed from 0.2.44 on. Zyf later clarified that only NEW wallets are;
1324
+ * old ones still need the legacy flow. Hence the gate is on the per-chain
1325
+ * `isDeployed` flag rather than on the wallet's age — that covers both a
1326
+ * legacy wallet and a pool wallet on a fresh chain.
1327
+ */
1328
+ async ensureWalletDeployed(ownerAddress, chainId) {
1329
+ const wallet = await this.sdk.getSmartWalletAddress(ownerAddress, chainId);
1330
+ if (wallet.isDeployed) return wallet.address;
1331
+ const poolFirst = await this.isPredeployedPoolWallet(
1332
+ ownerAddress,
1333
+ wallet.address
1334
+ );
1335
+ const deployAsPool = () => this.sdk.deployOnChains([chainId]);
1336
+ 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)
1341
+ );
1342
+ const [first, second] = poolFirst ? [deployAsPool, deployAsLegacy] : [deployAsLegacy, deployAsPool];
1343
+ try {
1344
+ await first();
1345
+ } catch (error) {
1346
+ console.warn(
1347
+ `[zyfai] ${poolFirst ? "deployOnChains" : "deploySafe"} failed on chain ${chainId}; trying the other deploy path:`,
1348
+ error instanceof Error ? error.message : String(error)
1349
+ );
1350
+ await second();
1351
+ }
1352
+ const redeployed = await this.sdk.getSmartWalletAddress(
1353
+ ownerAddress,
1354
+ chainId
1355
+ );
1356
+ if (!redeployed.isDeployed) {
1357
+ throw new OwneyError(
1358
+ "WALLET_NOT_DEPLOYED",
1359
+ `Smart wallet ${redeployed.address} is still not deployed on chain ${chainId}. Depositing would send funds to an address with no code.`,
1360
+ { chainId, smartWallet: redeployed.address },
1361
+ this.id
1362
+ );
1363
+ }
1364
+ return redeployed.address;
1365
+ }
1288
1366
  async deposit(state, chainId, amount, asset, depositCallback) {
1289
1367
  try {
1290
1368
  const validChainId = isValidChainId(chainId);
1291
1369
  await this.ensureConnected(state, validChainId);
1292
1370
  if (depositCallback) {
1293
- const wallet = await this.sdk.getSmartWalletAddress(
1371
+ const smartWallet = await this.ensureWalletDeployed(
1294
1372
  this.getAddress(),
1295
1373
  validChainId
1296
1374
  );
1297
- const txHash = await depositCallback(
1298
- wallet.address,
1299
- validChainId,
1300
- amount
1301
- );
1375
+ const txHash = await depositCallback(smartWallet, validChainId, amount);
1302
1376
  const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
1303
1377
  try {
1304
1378
  await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
@@ -1308,8 +1382,9 @@ var ZyfaiAgent = class {
1308
1382
  logError
1309
1383
  );
1310
1384
  }
1311
- return { txHash, smartWallet: wallet.address, amount };
1385
+ return { txHash, smartWallet, amount };
1312
1386
  }
1387
+ await this.ensureWalletDeployed(this.getAddress(), validChainId);
1313
1388
  const raw = await this.sdk.depositFunds(
1314
1389
  this.getAddress(),
1315
1390
  validChainId,
@@ -1897,6 +1972,41 @@ async function readErc20Balance(publicClient, token, owner) {
1897
1972
  });
1898
1973
  }
1899
1974
 
1975
+ // src/lib/chain-guard.ts
1976
+ var CHAIN_NAMES = {
1977
+ 1: "Ethereum",
1978
+ 8453: "Base",
1979
+ 42161: "Arbitrum"
1980
+ };
1981
+ function chainName(chainId) {
1982
+ return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
1983
+ }
1984
+ async function ensureWalletOnChain(pub, wallet, expected) {
1985
+ const actual = await pub.getChainId();
1986
+ if (actual === expected) return;
1987
+ try {
1988
+ await wallet.switchChain({ id: expected });
1989
+ } catch (error) {
1990
+ throw new OwneyError(
1991
+ "CHAIN_MISMATCH",
1992
+ `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
1993
+ {
1994
+ expectedChainId: expected,
1995
+ actualChainId: actual,
1996
+ cause: error instanceof Error ? error.message : String(error)
1997
+ }
1998
+ );
1999
+ }
2000
+ const after = await pub.getChainId();
2001
+ if (after !== expected) {
2002
+ throw new OwneyError(
2003
+ "CHAIN_MISMATCH",
2004
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2005
+ { expectedChainId: expected, actualChainId: after }
2006
+ );
2007
+ }
2008
+ }
2009
+
1900
2010
  // src/lib/sponsored-deposit.ts
1901
2011
  var AUTH_WINDOW_SECONDS = 15 * 60;
1902
2012
  function makeSponsoredDepositCallback(deps) {
@@ -1912,6 +2022,7 @@ function makeSponsoredDepositCallback(deps) {
1912
2022
  }
1913
2023
  const pub = deps.getPublicClient(cid);
1914
2024
  const wallet = deps.getWalletClient(cid);
2025
+ await ensureWalletOnChain(pub, wallet, cid);
1915
2026
  try {
1916
2027
  const balance = await readErc20Balance(pub, token, deps.ownerAddress);
1917
2028
  if (balance < BigInt(amount)) {
@@ -1923,6 +2034,10 @@ function makeSponsoredDepositCallback(deps) {
1923
2034
  }
1924
2035
  } catch (err) {
1925
2036
  if (err instanceof OwneyError) throw err;
2037
+ console.warn(
2038
+ "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2039
+ err instanceof Error ? err.message : String(err)
2040
+ );
1926
2041
  }
1927
2042
  const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
1928
2043
  const validAfter = 0n;
@@ -1986,6 +2101,8 @@ function makeSponsoredWethCallback(deps) {
1986
2101
  }
1987
2102
  const amountWei = BigInt(amount);
1988
2103
  const pub = deps.getPublicClient(cid);
2104
+ const wallet = deps.getWalletClient(cid);
2105
+ await ensureWalletOnChain(pub, wallet, cid);
1989
2106
  try {
1990
2107
  const balance = await readErc20Balance(pub, token, deps.ownerAddress);
1991
2108
  if (balance < amountWei) {
@@ -1997,6 +2114,10 @@ function makeSponsoredWethCallback(deps) {
1997
2114
  }
1998
2115
  } catch (err) {
1999
2116
  if (err instanceof OwneyError) throw err;
2117
+ console.warn(
2118
+ "[owney-sdk] WETH balance pre-check failed (non-fatal):",
2119
+ err instanceof Error ? err.message : String(err)
2120
+ );
2000
2121
  }
2001
2122
  const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
2002
2123
  if (allowance < amountWei) {
@@ -2024,7 +2145,6 @@ function makeSponsoredWethCallback(deps) {
2024
2145
  deadline
2025
2146
  }
2026
2147
  });
2027
- const wallet = deps.getWalletClient(cid);
2028
2148
  const signature = await wallet.signTypedData({
2029
2149
  account: deps.ownerAddress,
2030
2150
  ...typedData
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",